handle and configure a serial device
This commit is contained in:
parent
64dd637d9d
commit
301783ae40
|
@ -31,8 +31,9 @@ String GwConfigHandler::toString() const{
|
|||
|
||||
String GwConfigHandler::toJson() const{
|
||||
String rt;
|
||||
DynamicJsonDocument jdoc(400);
|
||||
for (int i=0;i<getNumConfig();i++){
|
||||
int num=getNumConfig();
|
||||
DynamicJsonDocument jdoc(JSON_OBJECT_SIZE(num*2));
|
||||
for (int i=0;i<num;i++){
|
||||
jdoc[configs[i]->getName()]=configs[i]->asCString();
|
||||
}
|
||||
serializeJson(jdoc,rt);
|
||||
|
@ -98,18 +99,18 @@ bool GwConfigHandler::reset(bool save){
|
|||
if (!save) return true;
|
||||
return saveConfig();
|
||||
}
|
||||
String GwConfigHandler::getString(const String name) const{
|
||||
String GwConfigHandler::getString(const String name, String defaultv) const{
|
||||
GwConfigInterface *i=getConfigItem(name,false);
|
||||
if (!i) return String();
|
||||
if (!i) return defaultv;
|
||||
return i->asString();
|
||||
}
|
||||
bool GwConfigHandler::getBool(const String name) const{
|
||||
bool GwConfigHandler::getBool(const String name, bool defaultv) const{
|
||||
GwConfigInterface *i=getConfigItem(name,false);
|
||||
if (!i) return false;
|
||||
if (!i) return defaultv;
|
||||
return i->asBoolean();
|
||||
}
|
||||
int GwConfigHandler::getInt(const String name) const{
|
||||
int GwConfigHandler::getInt(const String name,int defaultv) const{
|
||||
GwConfigInterface *i=getConfigItem(name,false);
|
||||
if (!i) return 0;
|
||||
if (!i) return defaultv;
|
||||
return i->asInt();
|
||||
}
|
|
@ -21,9 +21,9 @@ class GwConfigHandler: public GwConfigDefinitions{
|
|||
bool reset(bool save);
|
||||
String toString() const;
|
||||
String toJson() const;
|
||||
String getString(const String name) const;
|
||||
bool getBool(const String name) const ;
|
||||
int getInt(const String name) const;
|
||||
String getString(const String name,const String defaultv="") const;
|
||||
bool getBool(const String name,bool defaultv=false) const ;
|
||||
int getInt(const String name,int defaultv=0) const;
|
||||
GwConfigItem * findConfig(const String name, bool dummy=false);
|
||||
GwConfigInterface * getConfigItem(const String name, bool dummy=false) const;
|
||||
private:
|
||||
|
|
|
@ -13,10 +13,16 @@
|
|||
*/
|
||||
#ifndef _GWHARDWARE_H
|
||||
#define _GWHARDWARE_H
|
||||
|
||||
//SERIAL_MODE can be: UNI (RX or TX only), BI (both), RX, TX
|
||||
//board specific pins
|
||||
#ifdef BOARD_M5ATOM
|
||||
#define ESP32_CAN_TX_PIN GPIO_NUM_22
|
||||
#define ESP32_CAN_RX_PIN GPIO_NUM_19
|
||||
//if using tail485
|
||||
#define GWSERIAL_TX 26
|
||||
#define GWSERIAL_RX 32
|
||||
#define GWSERIAL_MODE "UNI"
|
||||
#elif BOARD_M5ATOM_CANUNIT
|
||||
#define ESP32_CAN_TX_PIN GPIO_NUM_26
|
||||
#define ESP32_CAN_RX_PIN GPIO_NUM_32
|
||||
|
@ -26,6 +32,8 @@
|
|||
#else
|
||||
#define ESP32_CAN_TX_PIN GPIO_NUM_5 // Set CAN TX port to 5 (Caution!!! Pin 2 before)
|
||||
#define ESP32_CAN_RX_PIN GPIO_NUM_4 // Set CAN RX port to 4
|
||||
#define GWSERIAL_RX GPIO_NUM_16
|
||||
#define GWSERIAL_MODE "RX"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
101
src/main.cpp
101
src/main.cpp
|
@ -12,7 +12,7 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define VERSION "0.3.1"
|
||||
#define VERSION "0.4.0"
|
||||
|
||||
// #define GW_MESSAGE_DEBUG_ENABLED
|
||||
// #define FALLBACK_SERIAL
|
||||
|
@ -81,19 +81,26 @@ GwRequestQueue mainQueue(&logger,20);
|
|||
GwWebServer webserver(&logger,&mainQueue,80);
|
||||
|
||||
//configs that we need in main
|
||||
GwConfigInterface *sendUsb=NULL;
|
||||
GwConfigInterface *sendTCP=NULL;
|
||||
GwConfigInterface *sendSeasmart=NULL;
|
||||
GwConfigInterface *systemName=NULL;
|
||||
GwConfigInterface *n2kFromUSB=NULL;
|
||||
GwConfigInterface *n2kFromTCP=NULL;
|
||||
|
||||
GwSerial usbSerial(NULL, UART_NUM_0, USB_CHANNEL_ID);
|
||||
GwConfigInterface *sendUsb=config.getConfigItem(config.sendUsb,true);
|
||||
GwConfigInterface *sendTCP=config.getConfigItem(config.sendTCP,true);
|
||||
GwConfigInterface *sendSeasmart=config.getConfigItem(config.sendSeasmart,true);
|
||||
GwConfigInterface *systemName=config.getConfigItem(config.systemName,true);
|
||||
GwConfigInterface *n2kFromTCP=config.getConfigItem(config.tcpToN2k,true);
|
||||
GwConfigInterface *n2kFromUSB=config.getConfigItem(config.usbToN2k,true);
|
||||
GwConfigInterface *receiveSerial=config.getConfigItem(config.receiveSerial,true);
|
||||
GwConfigInterface *sendSerial=config.getConfigItem(config.sendSerial,true);
|
||||
GwConfigInterface *n2kFromSerial=config.getConfigItem(config.serialToN2k,true);
|
||||
|
||||
GwSerial *usbSerial = new GwSerial(NULL, UART_NUM_0, USB_CHANNEL_ID);
|
||||
GwSerial *serial1=NULL;
|
||||
|
||||
|
||||
class GwSerialLog : public GwLogWriter{
|
||||
public:
|
||||
virtual ~GwSerialLog(){}
|
||||
virtual void write(const char *data){
|
||||
usbSerial.sendToClients(data,-1); //ignore any errors
|
||||
usbSerial->sendToClients(data,-1); //ignore any errors
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -145,6 +152,22 @@ protected:
|
|||
serializeJson(status, result);
|
||||
}
|
||||
};
|
||||
|
||||
class CapabilitiesRequest : public GwRequestMessage{
|
||||
public:
|
||||
CapabilitiesRequest() : GwRequestMessage(F("application/json"),F("capabilities")){};
|
||||
protected:
|
||||
virtual void processRequest(){
|
||||
DynamicJsonDocument json(JSON_OBJECT_SIZE(2));
|
||||
#ifdef GWSERIAL_MODE
|
||||
String serial(F(GWSERIAL_MODE));
|
||||
#else
|
||||
String serial(F("NONE"));
|
||||
#endif
|
||||
json["serialmode"]=serial;
|
||||
serializeJson(json,result);
|
||||
}
|
||||
};
|
||||
class ConfigRequest : public GwRequestMessage
|
||||
{
|
||||
public:
|
||||
|
@ -236,7 +259,7 @@ void setup() {
|
|||
#ifdef FALLBACK_SERIAL
|
||||
int st=-1;
|
||||
#else
|
||||
int st=usbSerial.setup(baud,3,1); //TODO: PIN defines
|
||||
int st=usbSerial->setup(baud,3,1); //TODO: PIN defines
|
||||
#endif
|
||||
if (st < 0){
|
||||
//falling back to old style serial for logging
|
||||
|
@ -251,21 +274,48 @@ void setup() {
|
|||
logger.logDebug(GwLog::LOG,"created GwSerial for USB port");
|
||||
}
|
||||
logger.logDebug(GwLog::LOG,"config: %s", config.toString().c_str());
|
||||
sendUsb=config.getConfigItem(config.sendUsb,true);
|
||||
sendTCP=config.getConfigItem(config.sendTCP,true);
|
||||
sendSeasmart=config.getConfigItem(config.sendSeasmart,true);
|
||||
systemName=config.getConfigItem(config.systemName,true);
|
||||
n2kFromTCP=config.getConfigItem(config.tcpToN2k,true);
|
||||
n2kFromUSB=config.getConfigItem(config.usbToN2k,true);
|
||||
#ifdef GWSERIAL_MODE
|
||||
int serialrx=UART_PIN_NO_CHANGE;
|
||||
int serialtx=UART_PIN_NO_CHANGE;
|
||||
#ifdef GWSERIAL_TX
|
||||
serialtx=GWSERIAL_TX;
|
||||
#endif
|
||||
#ifdef GWSERIAL_RX
|
||||
serialrx=GWSERIAL_RX;
|
||||
#endif
|
||||
String serialDirection=config.getString(config.serialDirection);
|
||||
//we only consider the direction if mode is UNI
|
||||
String serialMode(F(GWSERIAL_MODE));
|
||||
if (serialMode != String("UNI")){
|
||||
serialDirection=String("");
|
||||
}
|
||||
if (serialDirection == "receive" || serialDirection == "off") serialtx=UART_PIN_NO_CHANGE;
|
||||
if (serialDirection == "send" || serialDirection == "off") serialrx=UART_PIN_NO_CHANGE;
|
||||
logger.logDebug(GwLog::DEBUG,"serial set up: mode=%s,direction=%s,rx=%d,tx=%d",
|
||||
serialMode.c_str(),serialDirection.c_str(),serialrx,serialtx
|
||||
);
|
||||
if (serialtx != UART_PIN_NO_CHANGE || serialrx != UART_PIN_NO_CHANGE){
|
||||
logger.logDebug(GwLog::LOG,"creating serial interface rx=%d, tx=%d",serialrx,serialtx);
|
||||
serial1=new GwSerial(&logger,UART_NUM_1,SERIAL1_CHANNEL_ID,true);
|
||||
}
|
||||
if (serial1){
|
||||
serial1->setup(config.getInt(config.serialBaud,115200),serialrx,serialtx);
|
||||
}
|
||||
#endif
|
||||
|
||||
MDNS.begin(config.getConfigItem(config.systemName)->asCString());
|
||||
gwWifi.setup();
|
||||
|
||||
// Start TCP server
|
||||
socketServer.begin();
|
||||
usbSerial->flush();
|
||||
|
||||
webserver.registerMainHandler("/api/reset", [](AsyncWebServerRequest *request)->GwRequestMessage *{
|
||||
return new ResetRequest();
|
||||
});
|
||||
webserver.registerMainHandler("/api/capabilities", [](AsyncWebServerRequest *request)->GwRequestMessage *{
|
||||
return new CapabilitiesRequest();
|
||||
});
|
||||
webserver.registerMainHandler("/api/status", [](AsyncWebServerRequest *request)->GwRequestMessage *
|
||||
{ return new StatusRequest(); });
|
||||
webserver.registerMainHandler("/api/config", [](AsyncWebServerRequest *request)->GwRequestMessage *
|
||||
|
@ -328,7 +378,7 @@ void setup() {
|
|||
preferences.end();
|
||||
|
||||
logger.logDebug(GwLog::LOG,"NodeAddress=%d", NodeAddress);
|
||||
usbSerial.flush();
|
||||
usbSerial->flush();
|
||||
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress);
|
||||
NMEA2000.SetForwardOwnMessages(false);
|
||||
// Set the information for other bus devices, which messages we support
|
||||
|
@ -337,7 +387,7 @@ void setup() {
|
|||
unsigned long *op=pgns;
|
||||
while (*op != 0){
|
||||
logger.logDebug(GwLog::DEBUG,"add transmit pgn %ld",(long)(*op));
|
||||
usbSerial.flush();
|
||||
usbSerial->flush();
|
||||
op++;
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +418,10 @@ void sendBufferToChannels(const char * buffer, int sourceId){
|
|||
socketServer.sendToClients(buffer,sourceId);
|
||||
}
|
||||
if (sendUsb->asBoolean()){
|
||||
usbSerial.sendToClients(buffer,sourceId);
|
||||
usbSerial->sendToClients(buffer,sourceId);
|
||||
}
|
||||
if (sendSerial->asBoolean() && serial1){
|
||||
serial1->sendToClients(buffer,sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,7 +441,8 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId) {
|
|||
|
||||
void handleReceivedNmeaMessage(const char *buf, int sourceId){
|
||||
if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())||
|
||||
(sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())
|
||||
(sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())||
|
||||
(sourceId == SERIAL1_CHANNEL_ID && n2kFromSerial)
|
||||
)
|
||||
toN2KConverter->parseAndSend(buf,sourceId);
|
||||
sendBufferToChannels(buf,sourceId);
|
||||
|
@ -396,7 +450,8 @@ void handleReceivedNmeaMessage(const char *buf, int sourceId){
|
|||
|
||||
void handleSendAndRead(bool handleRead){
|
||||
socketServer.loop(handleRead);
|
||||
usbSerial.loop(handleRead);
|
||||
usbSerial->loop(handleRead);
|
||||
if (serial1) serial1->loop(handleRead);
|
||||
}
|
||||
class NMEAMessageReceiver : public GwBufferWriter{
|
||||
uint8_t buffer[GwBuffer::RX_BUFFER_SIZE+4];
|
||||
|
@ -468,7 +523,9 @@ void loop() {
|
|||
//read channels
|
||||
socketServer.readMessages(&receiver);
|
||||
receiver.id=USB_CHANNEL_ID;
|
||||
usbSerial.readMessages(&receiver);
|
||||
usbSerial->readMessages(&receiver);
|
||||
receiver.id=SERIAL1_CHANNEL_ID;
|
||||
if (serial1) serial1->readMessages(&receiver);
|
||||
|
||||
//handle message requests
|
||||
GwMessage *msg=mainQueue.fetchMessage(0);
|
||||
|
|
|
@ -36,6 +36,48 @@
|
|||
"default": "true",
|
||||
"description": "convert NMEA0183 from the USB port to NMEA2000"
|
||||
},
|
||||
{
|
||||
"name": "serialDirection",
|
||||
"label": "serial direction",
|
||||
"type": "list",
|
||||
"default": "receive",
|
||||
"list": ["send","receive","off"],
|
||||
"description": "use the serial port to send or receive data",
|
||||
"capabilities":{"serialmode":["UNI"]}
|
||||
},
|
||||
{
|
||||
"name": "serialBaud",
|
||||
"label": "serial baud rate",
|
||||
"type": "list",
|
||||
"default": "115200",
|
||||
"description": "baud rate for the serial port",
|
||||
"list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800],
|
||||
"capabilities":{"serialmode":["RX","TX","UNI","BI"]}
|
||||
},
|
||||
{
|
||||
"name": "sendSerial",
|
||||
"label": "NMEA to Serial",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data on the serial port",
|
||||
"capabilities":{"serialmode":["TX","BI"]}
|
||||
},
|
||||
{
|
||||
"name": "receiveSerial",
|
||||
"label": "NMEA from Serial",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data on the serial port",
|
||||
"capabilities":{"serialmode":["RX","BI"]}
|
||||
},
|
||||
{
|
||||
"name": "serialToN2k",
|
||||
"label": "serial to NMEA2000",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from the serial port to NMEA2000",
|
||||
"capabilities":{"serialmode":["RX","BI","UNI"]}
|
||||
},
|
||||
{
|
||||
"name": "serverPort",
|
||||
"label": "TCP port",
|
||||
|
@ -60,14 +102,14 @@
|
|||
},
|
||||
{
|
||||
"name": "readTCP",
|
||||
"label": "TCP to NMEA2000",
|
||||
"label": "NMEA from TCP",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data from connected TCP clients"
|
||||
},
|
||||
{
|
||||
"name": "tcpToN2k",
|
||||
"label": "NMEA from TCP",
|
||||
"label": "TCP to NMEA2000",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from TCP clients to NMEA2000"
|
||||
|
|
112
web/index.html
112
web/index.html
|
@ -7,7 +7,9 @@
|
|||
<script type="text/javascript">
|
||||
let self=this;
|
||||
let lastUpdate=(new Date()).getTime();
|
||||
let reloadConfig=false;
|
||||
function alertRestart(){
|
||||
reloadConfig=true;
|
||||
alert("Board reset triggered, reconnect WLAN if necessary");
|
||||
}
|
||||
function getJson(url){
|
||||
|
@ -41,6 +43,10 @@
|
|||
}
|
||||
}
|
||||
lastUpdate=(new Date()).getTime();
|
||||
if (reloadConfig){
|
||||
reloadConfig=false;
|
||||
resetForm();
|
||||
}
|
||||
})
|
||||
}
|
||||
function resetForm(ev){
|
||||
|
@ -180,53 +186,67 @@
|
|||
return el;
|
||||
}
|
||||
let configDefinitions;
|
||||
function loadConfigDefinitions(){
|
||||
getJson("config.json")
|
||||
.then(function(defs){
|
||||
let frame=document.querySelector('.configFormRows');
|
||||
if (! frame) throw Error("no config form");
|
||||
frame.innerHTML='';
|
||||
configDefinitions=defs;
|
||||
defs.forEach(function(item){
|
||||
if (! item.type) return;
|
||||
let row=document.createElement('div');
|
||||
row.classList.add('row');
|
||||
let label=item.label || item.name;
|
||||
let labelEl=document.createElement('span');
|
||||
labelEl.classList.add('label');
|
||||
labelEl.textContent=label;
|
||||
row.appendChild(labelEl);
|
||||
let valueEl=createInput(item);
|
||||
if (!valueEl) return;
|
||||
valueEl.setAttribute('data-default',item.default);
|
||||
valueEl.addEventListener('change',function(ev){
|
||||
let el=ev.target;
|
||||
checkChange(el);
|
||||
})
|
||||
if (item.check) valueEl.setAttribute('data-check',item.check);
|
||||
row.appendChild(valueEl);
|
||||
let bt=document.createElement('button');
|
||||
bt.classList.add('defaultButton');
|
||||
bt.setAttribute('data-default',item.default);
|
||||
bt.addEventListener('click',function(ev){
|
||||
valueEl.value=valueEl.getAttribute('data-default');
|
||||
checkChange(valueEl);
|
||||
})
|
||||
bt.textContent="X";
|
||||
row.appendChild(bt);
|
||||
bt=document.createElement('button');
|
||||
bt.classList.add('infoButton');
|
||||
bt.addEventListener('click',function(ev){
|
||||
alert(item.description);
|
||||
});
|
||||
bt.textContent="?";
|
||||
row.appendChild(bt);
|
||||
frame.appendChild(row);
|
||||
function loadConfigDefinitions() {
|
||||
getJson("api/capabilities")
|
||||
.then(function (capabilities) {
|
||||
getJson("config.json")
|
||||
.then(function (defs) {
|
||||
let frame = document.querySelector('.configFormRows');
|
||||
if (!frame) throw Error("no config form");
|
||||
frame.innerHTML = '';
|
||||
configDefinitions = defs;
|
||||
defs.forEach(function (item) {
|
||||
if (!item.type) return;
|
||||
if (item.capabilities !== undefined){
|
||||
for (let capability in item.capabilities){
|
||||
let values=item.capabilities[capability];
|
||||
if ( !capabilities[capability]) return;
|
||||
let found =false;
|
||||
values.forEach(function(v){
|
||||
if (capabilities[capability] == v) found=true;
|
||||
});
|
||||
if (! found) return;
|
||||
}
|
||||
}
|
||||
let row = document.createElement('div');
|
||||
row.classList.add('row');
|
||||
let label = item.label || item.name;
|
||||
let labelEl = document.createElement('span');
|
||||
labelEl.classList.add('label');
|
||||
labelEl.textContent = label;
|
||||
row.appendChild(labelEl);
|
||||
let valueEl = createInput(item);
|
||||
if (!valueEl) return;
|
||||
valueEl.setAttribute('data-default', item.default);
|
||||
valueEl.addEventListener('change', function (ev) {
|
||||
let el = ev.target;
|
||||
checkChange(el);
|
||||
})
|
||||
if (item.check) valueEl.setAttribute('data-check', item.check);
|
||||
row.appendChild(valueEl);
|
||||
let bt = document.createElement('button');
|
||||
bt.classList.add('defaultButton');
|
||||
bt.setAttribute('data-default', item.default);
|
||||
bt.addEventListener('click', function (ev) {
|
||||
valueEl.value = valueEl.getAttribute('data-default');
|
||||
checkChange(valueEl);
|
||||
})
|
||||
bt.textContent = "X";
|
||||
row.appendChild(bt);
|
||||
bt = document.createElement('button');
|
||||
bt.classList.add('infoButton');
|
||||
bt.addEventListener('click', function (ev) {
|
||||
alert(item.description);
|
||||
});
|
||||
bt.textContent = "?";
|
||||
row.appendChild(bt);
|
||||
frame.appendChild(row);
|
||||
})
|
||||
resetForm();
|
||||
})
|
||||
})
|
||||
resetForm();
|
||||
})
|
||||
.catch(function(err){alert("unable to load config: "+err)})
|
||||
}
|
||||
.catch(function (err) { alert("unable to load config: " + err) })
|
||||
}
|
||||
window.setInterval(update,1000);
|
||||
window.addEventListener('load',function(){
|
||||
let buttons=document.querySelectorAll('button');
|
||||
|
|
Loading…
Reference in New Issue