diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp new file mode 100644 index 0000000..fa6d15c --- /dev/null +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -0,0 +1,132 @@ +#include "NMEA0183DataToN2K.h" +#include "NMEA0183Messages.h" +NMEA0183DataToN2K::NMEA0183DataToN2K(GwLog *logger, GwBoatData *boatData,N2kSender callback) +{ + this->sender = callback; + this->logger = logger; + this->boatData=boatData; + LOG_DEBUG(GwLog::LOG,"NMEA0183DataToN2K created %p",this); +} + +bool NMEA0183DataToN2K::parseAndSend(const char *buffer, int sourceId) { + LOG_DEBUG(GwLog::DEBUG,"NMEA0183DataToN2K[%d] parsing %s",sourceId,buffer) + return false; +} + + +class NMEA0183DataToN2KFunctions : public NMEA0183DataToN2K{ + public: + typedef void (NMEA0183DataToN2KFunctions::*Converter)(const tNMEA0183Msg &msg); + private: + class ConverterEntry + { + public: + unsigned long count = 0; + unsigned long *pgn; + unsigned int numPgn=0; + Converter converter; + ConverterEntry(){ + pgn=NULL; + converter=NULL; + } + ConverterEntry(unsigned long pgn,Converter cv = NULL) { + converter = cv; + numPgn=1; + this->pgn=new unsigned long[1]; + this->pgn[0]=pgn; + } + ConverterEntry(unsigned long pgn1,unsigned long pgn2,Converter cv = NULL) { + converter = cv; + numPgn=2; + this->pgn=new unsigned long[2]; + this->pgn[0]=pgn1; + this->pgn[1]=pgn2; + } + ConverterEntry(unsigned long pgn1,unsigned long pgn2,unsigned long pgn3,Converter cv = NULL) { + converter = cv; + numPgn=3; + this->pgn=new unsigned long[3]; + this->pgn[0]=pgn1; + this->pgn[1]=pgn2; + this->pgn[2]=pgn3; + } + }; + typedef std::map ConverterMap; + ConverterMap converters; + + /** + * register a converter + * each of the converter functions must be registered in the constructor + **/ + void registerConverter(unsigned long pgn, String sentence,Converter converter) + { + ConverterEntry e(pgn,converter); + converters[sentence] = e; + } + void registerConverter(unsigned long pgn,unsigned long pgn2, String sentence,Converter converter) + { + ConverterEntry e(pgn,pgn2,converter); + converters[sentence] = e; + } + + void convertRMB(const tNMEA0183Msg &msg){ + LOG_DEBUG(GwLog::DEBUG+1,"convert RMB"); + } + public: + virtual bool parseAndSend(const char *buffer, int sourceId) { + LOG_DEBUG(GwLog::DEBUG+1,"NMEA0183DataToN2K[%d] parsing %s",sourceId,buffer) + tNMEA0183Msg msg; + if (! msg.SetMessage(buffer)){ + LOG_DEBUG(GwLog::DEBUG,"NMEA0183DataToN2K[%d] invalid message %s",sourceId,buffer) + return false; + } + String code=String(msg.MessageCode()); + ConverterMap::iterator it=converters.find(code); + if (it != converters.end()){ + (it->second).count++; + //call to member function - see e.g. https://isocpp.org/wiki/faq/pointers-to-members + ((*this).*((it->second).converter))(msg); + } + else{ + LOG_DEBUG(GwLog::DEBUG,"NMEA0183DataToN2K[%d] no handler for %s",sourceId,code.c_str()); + return false; + } + return true; + } + virtual unsigned long *handledPgns() + { + logger->logString("CONV: # %d handled PGNS", (int)converters.size()); + //for now max 3 pgns per converter + unsigned long *rt = new unsigned long[converters.size() *3 + 1]; + int idx = 0; + for (ConverterMap::iterator it = converters.begin(); + it != converters.end(); it++) + { + for (int i=0;isecond.numPgn && i < 3;i++){ + bool found=false; + for (int e=0;esecond.pgn[i]){ + found=true; + break; + } + } + if (! found){ + rt[idx] = it->second.pgn[i]; + idx++; + } + } + } + rt[idx] = 0; + return rt; + } + NMEA0183DataToN2KFunctions(GwLog *logger,GwBoatData *boatData,N2kSender callback) + :NMEA0183DataToN2K(logger,boatData,callback){ + registerConverter(129283UL,String(F("RMB")),&NMEA0183DataToN2KFunctions::convertRMB); + } + +}; + +NMEA0183DataToN2K* NMEA0183DataToN2K::create(GwLog *logger,GwBoatData *boatData,N2kSender callback){ + return new NMEA0183DataToN2KFunctions(logger, boatData,callback); + +} diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.h b/lib/nmea0183ton2k/NMEA0183DataToN2K.h new file mode 100644 index 0000000..e7c980a --- /dev/null +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.h @@ -0,0 +1,20 @@ +#ifndef _NMEA0183DATATON2K_H +#define _NMEA0183DATATON2K_H +#include "GwLog.h" +#include "GwBoatData.h" +#include "N2kMessages.h" + +class NMEA0183DataToN2K{ + public: + typedef bool (*N2kSender)(const tN2kMsg &msg); + protected: + GwLog * logger; + GwBoatData *boatData; + N2kSender sender; + public: + NMEA0183DataToN2K(GwLog *logger,GwBoatData *boatData,N2kSender callback); + virtual bool parseAndSend(const char *buffer, int sourceId); + virtual unsigned long *handledPgns()=0; + static NMEA0183DataToN2K* create(GwLog *logger,GwBoatData *boatData,N2kSender callback); +}; +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 271ac26..93e0f03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwMessage.h" #include "GwSerial.h" #include "GwWebServer.h" +#include "NMEA0183DataToN2K.h" //NMEA message channels @@ -70,11 +71,8 @@ int NodeAddress; // To store last Node Address Preferences preferences; // Nonvolatile storage on ESP32 - To store LastDeviceAddress N2kDataToNMEA0183 *nmea0183Converter=NULL; +NMEA0183DataToN2K *toN2KConverter=NULL; -// Set the information for other bus devices, which messages we support -const unsigned long TransmitMessages[] PROGMEM = {127489L, // Engine dynamic - 0 -}; void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg,int id); @@ -86,6 +84,8 @@ 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); class GwSerialLog : public GwLogWriter{ @@ -254,6 +254,8 @@ void setup() { 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); MDNS.begin(config.getConfigItem(config.systemName)->asCString()); gwWifi.setup(); @@ -288,6 +290,11 @@ void setup() { nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000, SendNMEA0183Message, N2K_CHANNEL_ID); + + toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{ + logger.logDebug(GwLog::DEBUG+1,"send N2K"); + return true; + }); NMEA2000.SetN2kCANMsgBufSize(8); NMEA2000.SetN2kCANReceiveFrameBufSize(250); @@ -321,8 +328,8 @@ void setup() { logger.logDebug(GwLog::LOG,"NodeAddress=%d\n", NodeAddress); NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress); - - NMEA2000.ExtendTransmitMessages(TransmitMessages); + // Set the information for other bus devices, which messages we support + NMEA2000.ExtendTransmitMessages(toN2KConverter->handledPgns()); NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns()); NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){ numCan++; @@ -367,8 +374,10 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId) { } void handleReceivedNmeaMessage(const char *buf, int sourceId){ - //TODO - for now only send out again - //add the conversion to N2K here + if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())|| + (sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean()) + ) + toN2KConverter->parseAndSend(buf,sourceId); sendBufferToChannels(buf,sourceId); } diff --git a/web/config.json b/web/config.json index bd852ac..2b22d56 100644 --- a/web/config.json +++ b/web/config.json @@ -29,6 +29,13 @@ "default": "true", "description": "receive NMEA data on the USB port" }, + { + "name": "usbToN2k", + "label": "USB to NMEA2000", + "type": "boolean", + "default": "true", + "description": "convert NMEA0183 from the USB port to NMEA2000" + }, { "name": "serverPort", "label": "TCP port", @@ -53,11 +60,18 @@ }, { "name": "readTCP", - "label": "NMEA from TCP", + "label": "TCP to NMEA2000", "type": "boolean", "default": "true", "description": "receive NMEA data from connected TCP clients" }, + { + "name": "tcpToN2k", + "label": "NMEA from TCP", + "type": "boolean", + "default": "true", + "description": "convert NMEA0183 from TCP clients to NMEA2000" + }, { "name": "sendSeasmart", "label": "Seasmart to TCP",