handle and configure a serial device

This commit is contained in:
andreas 2021-11-02 21:18:11 +01:00
parent 64dd637d9d
commit 301783ae40
6 changed files with 209 additions and 81 deletions

View File

@ -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();
}

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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"

View File

@ -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');