From e70591eb4fc9aeaf922275cc272b77ecf2286016 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 14 Jan 2022 21:07:54 +0100 Subject: [PATCH 1/6] add changes from Homberger for AIS compatibility with raymarine --- lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h | 75 +++++++++++++++-------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h b/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h index a45ad2d..bd22fd6 100644 --- a/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h +++ b/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h @@ -83,11 +83,24 @@ class MyAisDecoder : public AIS::AisDecoder tN2kMsg N2kMsg; // PGN129038 - SetN2kAISClassAPosition(N2kMsg, _uMsgType, (tN2kAISRepeat)_Repeat, _uMmsi, - _iPosLat / 600000.0, _iPosLon / 600000.0, - _bPosAccuracy, _Raim, _timestamp, - decodeCog(_iCog) , _uSog * knToms / 10.0, - decodeHeading(_iHeading), decodeRot(_iRot), (tN2kAISNavStatus)_uNavstatus); + + N2kMsg.SetPGN(129038L); + N2kMsg.Priority = 4; + N2kMsg.AddByte((_Repeat & 0x03) << 6 | (_uMsgType & 0x3f)); + N2kMsg.Add4ByteUInt(_uMmsi); + N2kMsg.Add4ByteDouble(_iPosLon / 600000.0, 1e-07); + N2kMsg.Add4ByteDouble(_iPosLat / 600000.0, 1e-07); + N2kMsg.AddByte((_timestamp & 0x3f) << 2 | (_Raim & 0x01) << 1 | (_bPosAccuracy & 0x01)); + N2kMsg.Add2ByteUDouble(decodeCog(_iCog), 1e-04); + N2kMsg.Add2ByteUDouble(_uSog * knToms/10.0, 0.01); + N2kMsg.AddByte(0x00); // Communication State (19 bits) + N2kMsg.AddByte(0x00); + N2kMsg.AddByte(0x00); // AIS transceiver information (5 bits) + N2kMsg.Add2ByteUDouble(decodeHeading(_iHeading), 1e-04); + N2kMsg.Add2ByteDouble(decodeRot(_iRot), 3.125E-05); // 1e-3/32.0 + N2kMsg.AddByte(0xF0 | (_uNavstatus & 0x0f)); + N2kMsg.AddByte(0xff); // Reserved + N2kMsg.AddByte(0xff); // SID (NA) send(N2kMsg); } @@ -124,17 +137,23 @@ class MyAisDecoder : public AIS::AisDecoder uint16_t eta_days = tNMEA0183Msg::makeTime(tm) / (24UL * 3600UL); tN2kMsg N2kMsg; - char CS[30]; - char Name[30]; - char Dest[30]; - strncpy(CS, _strCallsign.c_str(), sizeof(CS)-1); - CS[29]=0; - strncpy(Name, _strName.c_str(), sizeof(Name)-1); - Name[29]=0; - strncpy(Dest, _strDestination.c_str(), sizeof(Dest)-1); - Dest[29]=0; + char CS[8]; + char Name[21]; + char Dest[21]; + strncpy(CS, _strCallsign.c_str(), sizeof(CS) - 1); + CS[7] = 0; + for (int i = strlen(CS); i < 7; i++) CS[i] = 32; + + strncpy(Name, _strName.c_str(), sizeof(Name) - 1); + Name[20] = 0; + for (int i = strlen(Name); i < 20; i++) Name[i] = 32; + + strncpy(Dest, _strDestination.c_str(), sizeof(Dest) - 1); + Dest[20] = 0; + for (int i = strlen(Dest); i < 20; i++) Dest[i] = 32; + // PGN129794 SetN2kAISClassAStatic(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi, _uImo, CS, Name, _uType, _uToBow + _uToStern, @@ -205,9 +224,10 @@ class MyAisDecoder : public AIS::AisDecoder // PGN129040 - char Name[21] = ""; + char Name[21]; strncpy(Name, _strName.c_str(), sizeof(Name)-1); Name[20]=0; + for (int i = strlen(Name); i < 20; i++) Name[i] = 32; N2kMsg.SetPGN(129040UL); N2kMsg.Priority = 4; @@ -230,6 +250,7 @@ class MyAisDecoder : public AIS::AisDecoder N2kMsg.AddStr(Name, 20); N2kMsg.AddByte((_dte & 0x01) | (_assigned & 0x01) << 1) ; N2kMsg.AddByte(0); + N2kMsg.AddByte(0xff); // Sequence ID (Not Available) send(N2kMsg); } @@ -244,9 +265,11 @@ class MyAisDecoder : public AIS::AisDecoder //Serial.println("24A"); tN2kMsg N2kMsg; - char Name[30]; - strncpy(Name, _strName.c_str(), sizeof(Name)-1); - Name[29]=0; + char Name[21]; + strncpy(Name, _strName.c_str(), sizeof(Name) - 1); + Name[20]=0; + for (int i = strlen(Name); i < 20; i++) Name[i] = 32; + // PGN129809 SetN2kAISClassBStaticPartA(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi, Name); @@ -263,13 +286,17 @@ class MyAisDecoder : public AIS::AisDecoder tN2kMsg N2kMsg; - char CS[30]; - char Vendor[30]; + char CS[8]; + char Vendor[8]; - strncpy(CS, _strCallsign.c_str(), sizeof(CS)-1); - CS[29]=0; - strncpy(Vendor, _strVendor.c_str(), sizeof(Vendor)-1); - Vendor[29]=0; + strncpy(CS, _strCallsign.c_str(), sizeof(CS) - 1); + CS[7] = 0; + for (int i = strlen(CS); i < 7; i++) CS[i] = 32; + strncpy(Vendor, _strVendor.c_str(), sizeof(Vendor) - 1); + Vendor[7] = 0; + for (int i = strlen(Vendor); i < 7; i++) Vendor[i] = 32; + + // PGN129810 SetN2kAISClassBStaticPartB(N2kMsg, _uMsgType, (tN2kAISRepeat)_repeat, _uMmsi, From 966ef0c4fbce7222a72e1739df37a8713a89dfb2 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 14 Jan 2022 23:16:43 +0100 Subject: [PATCH 2/6] allow to hide config items from the ui and allow to change config values in the init function --- lib/config/GWConfig.cpp | 10 ++++++++++ lib/config/GWConfig.h | 8 ++++++++ lib/exampletask/GwExampleTask.cpp | 8 ++++++++ lib/exampletask/GwExampleTask.h | 3 +++ src/main.cpp | 6 +++++- web/index.js | 24 +++++++++++++++--------- 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index c6c8532..d8da398 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -114,6 +114,16 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{ if (!i) return defaultv; return i->asInt(); } +void GwConfigHandler::stopChanges(){ + allowChanges=false; +} +bool GwConfigHandler::setValue(String name,String value){ + if (! allowChanges) return false; + GwConfigInterface *i=getConfigItem(name,false); + if (!i) return false; + i->value=value; + return true; +} void GwNmeaFilter::handleToken(String token, int index){ switch(index){ diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index 3de56b0..1319f48 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -14,11 +14,13 @@ class GwConfigHandler: public GwConfigDefinitions{ GwLog *logger; typedef std::map StringMap; StringMap changedValues; + boolean allowChanges=true; public: public: GwConfigHandler(GwLog *logger); bool loadConfig(); bool saveConfig(); + void stopChanges(); bool updateValue(String name, String value); bool reset(bool save); String toString() const; @@ -27,6 +29,12 @@ class GwConfigHandler: public GwConfigDefinitions{ bool getBool(const String name,bool defaultv=false) const ; int getInt(const String name,int defaultv=0) const; GwConfigInterface * getConfigItem(const String name, bool dummy=false) const; + /** + * change the value of a config item + * will become a noop after stopChanges has been called + * !use with care! no checks of the value + */ + bool setValue(String name, String value); private: }; #endif \ No newline at end of file diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 18c4a77..88d9cc0 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -10,6 +10,13 @@ */ void exampleInit(GwApi *api){ api->getLogger()->logDebug(GwLog::LOG,"example init running"); + //this example is a more or less useless example how you could set some + //config value to a fixed value + //you can only set config values within the init function + //you could also compute this value from some own configuration + //for this example it would make a lot of sense to declare a capability + //to hide this config item from the UI - see header file + api->getConfig()->setValue(api->getConfig()->minXdrInterval,"50"); } #define INVALID_COORD -99999 class GetBoatDataRequest: public GwMessage{ @@ -84,6 +91,7 @@ void exampleTask(GwApi *api){ bool hasPosition=false; bool hasPosition2=false; LOG_DEBUG(GwLog::DEBUG,"example switch ist %s",exampleSwitch?"true":"false"); + LOG_DEBUG(GwLog::DEBUG,"minXdrInterval=%d",api->getConfig()->getInt(api->getConfig()->minXdrInterval)); GwApi::BoatValue *longitude=new GwApi::BoatValue(F("Longitude")); GwApi::BoatValue *latitude=new GwApi::BoatValue(F("Latitude")); GwApi::BoatValue *testValue=new GwApi::BoatValue(boatItemName); diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index 4fa5a90..b759c93 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -43,4 +43,7 @@ DECLARE_INITFUNCTION(exampleInit); //elements when this capability is set correctly DECLARE_CAPABILITY(testboard,true); DECLARE_CAPABILITY(testboard2,true); +//hide some config value +//just set HIDE + the name of the config item to true +DECLARE_CAPABILITY(HIDEminXdrInterval,true); #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0bbe859..3d9175b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -599,6 +599,10 @@ void setup() { logger.prefix="FALLBACK:"; #endif userCodeHandler.startInitTasks(MIN_USER_TASK); + config.stopChanges(); + //maybe the user code changed the level + level=config.getInt(config.logLevel,LOGLEVEL); + logger.setLevel(level); gwWifi.setup(); MDNS.begin(config.getConfigItem(config.systemName)->asCString()); channels.begin(fallbackSerial); @@ -682,7 +686,7 @@ void setup() { NMEA2000.SetProductInformation("1", // Manufacturer's Model serial code 100, // Manufacturer's product code systemName->asCString(), // Manufacturer's Model ID - VERSION, // Manufacturer's Software version code + FIRMWARE_TYPE, // Manufacturer's Software version code VERSION, // Manufacturer's Model version, N2K_LOAD_LEVEL, 0xffff, //Version diff --git a/web/index.js b/web/index.js index 0724ae8..7990241 100644 --- a/web/index.js +++ b/web/index.js @@ -953,17 +953,23 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) { category = item.category; } let showItem=true; - if (item.capabilities !== undefined) { - for (let capability in item.capabilities) { - let values = item.capabilities[capability]; - let found = false; - if (! (values instanceof Array)) values=[values]; - values.forEach(function (v) { + let itemCapabilities=item.capabilities||{}; + itemCapabilities['HIDE'+item.name]=null; + for (let capability in itemCapabilities) { + let values = itemCapabilities[capability]; + let found = false; + if (! (values instanceof Array)) values=[values]; + values.forEach(function (v) { + if ( v === null){ + if (capabilities[capability] === undefined) found=true; + } + else{ if (capabilities[capability] == v) found = true; - }); - if (!found) showItem=false; - } + } + }); + if (!found) showItem=false; } + if (showItem) { currentCategoryPopulated=true; let row = addEl('div', 'row', categoryEl); From 9f6e41a5982648f669388abfa3b7e3d7114b99d7 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 14 Jan 2022 23:38:51 +0100 Subject: [PATCH 3/6] allow to globally switch of the sending of NMEA2000 data --- src/main.cpp | 25 +++++++++++++++---------- web/config.json | 8 ++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3d9175b..b50889b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,6 +109,7 @@ GwWifi gwWifi(&config,&logger,fixedApPass); GwChannelList channels(&logger,&config); GwBoatData boatData(&logger); GwXDRMappings xdrMappings(&logger,&config); +bool sendOutN2k=true; int NodeAddress; // To store last Node Address @@ -205,7 +206,7 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,int sourceId, bool isConverted=false if (! isConverted){ nmea0183Converter->HandleMsg(n2kMsg,sourceId); } - if (sourceId != N2K_CHANNEL_ID){ + if (sourceId != N2K_CHANNEL_ID && sendOutN2k){ countNMEA2KOut.add(n2kMsg.PGN); NMEA2000.SendMsg(n2kMsg); } @@ -603,6 +604,8 @@ void setup() { //maybe the user code changed the level level=config.getInt(config.logLevel,LOGLEVEL); logger.setLevel(level); + sendOutN2k=config.getBool(config.sendN2k,true); + logger.logDebug(GwLog::LOG,"send N2k=%s",(sendOutN2k?"true":"false")); gwWifi.setup(); MDNS.begin(config.getConfigItem(config.systemName)->asCString()); channels.begin(fallbackSerial); @@ -711,17 +714,19 @@ void setup() { logger.flush(); NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress); NMEA2000.SetForwardOwnMessages(false); - // Set the information for other bus devices, which messages we support - unsigned long *pgns=toN2KConverter->handledPgns(); - if (logger.isActive(GwLog::DEBUG)){ - unsigned long *op=pgns; - while (*op != 0){ - logger.logDebug(GwLog::DEBUG,"add transmit pgn %ld",(long)(*op)); - logger.flush(); - op++; + if (sendOutN2k){ + // Set the information for other bus devices, which messages we support + unsigned long *pgns=toN2KConverter->handledPgns(); + if (logger.isActive(GwLog::DEBUG)){ + unsigned long *op=pgns; + while (*op != 0){ + logger.logDebug(GwLog::DEBUG,"add transmit pgn %ld",(long)(*op)); + logger.flush(); + op++; + } } + NMEA2000.ExtendTransmitMessages(pgns); } - NMEA2000.ExtendTransmitMessages(pgns); NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns()); NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){ handleN2kMessage(n2kMsg,N2K_CHANNEL_ID); diff --git a/web/config.json b/web/config.json index 7446957..74ddf5f 100644 --- a/web/config.json +++ b/web/config.json @@ -175,6 +175,14 @@ "description": "min interval in ms between 2 NMEA 2000 records with the same PGN and the same source (> 5)", "category": "converter" }, + { + "name":"sendN2k", + "label":"NMEA2000 out", + "type":"boolean", + "default":"true", + "description":"send out the converted data on the NMEA2000 bus", + "category":"converter" + }, { "name": "usbActisense", "label": "USB mode", From 99cd87bed2cb914e488a2bf826060517c3dfd59b Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 14 Jan 2022 23:42:56 +0100 Subject: [PATCH 4/6] slightly improve the help for sendN2k --- web/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/config.json b/web/config.json index 74ddf5f..c2e5df5 100644 --- a/web/config.json +++ b/web/config.json @@ -180,7 +180,7 @@ "label":"NMEA2000 out", "type":"boolean", "default":"true", - "description":"send out the converted data on the NMEA2000 bus", + "description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.", "category":"converter" }, { From bafac073328141551d79738b9c2c32c40f06c10a Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 14 Jan 2022 23:46:42 +0100 Subject: [PATCH 5/6] update readme --- Readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Readme.md b/Readme.md index 787e500..fa84e22 100644 --- a/Readme.md +++ b/Readme.md @@ -145,6 +145,12 @@ For details refer to the [example description](lib/exampletask/Readme.md). Changelog --------- +[20220114](../../releases/tag/20220114) +********* +* incorporate some changes from [Homberger](https://github.com/AK-Homberger/NMEA2000-AIS-Gateway) to improve AIS compatibility with Raymarine displays +* introduce a global switch to prevent sending out converted NMEA2000 data +* extension API improvements (hide config values, set config values) + [20220112](../../releases/tag/20220112) ********* * correctly send out seasmart if NMEA out is not configured From 799fd496962e2f9f49eb913b60ab45e018b3f7be Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 24 Jan 2022 14:51:53 +0100 Subject: [PATCH 6/6] make the serial input/outout working again, show it's counters correctly --- Readme.md | 3 +++ lib/channel/GwChannelList.cpp | 4 ++-- web/index.js | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index fa84e22..d799ec2 100644 --- a/Readme.md +++ b/Readme.md @@ -145,6 +145,9 @@ For details refer to the [example description](lib/exampletask/Readme.md). Changelog --------- +[20220124](../../releases/tag/20220124) +********* +* make the serial input and output working again [20220114](../../releases/tag/20220114) ********* * incorporate some changes from [Homberger](https://github.com/AK-Homberger/NMEA2000-AIS-Gateway) to improve AIS compatibility with Raymarine displays diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 537b629..635799a 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -102,8 +102,8 @@ void GwChannelList::begin(bool fallbackSerial){ theChannels.push_back(channel); //serial 1 - bool serCanRead=false; - bool serCanWrite=false; + bool serCanRead=true; + bool serCanWrite=true; int serialrx=-1; int serialtx=-1; #ifdef GWSERIAL_MODE diff --git a/web/index.js b/web/index.js index 7990241..0e17a38 100644 --- a/web/index.js +++ b/web/index.js @@ -320,8 +320,8 @@ let counters={ countTCPClientout: 'TCPclient out', countUSBin: 'USB in', countUSBout: 'USB out', - countSERIn: 'Serial in', - countSEROut: 'Serial out' + countSERin: 'Serial in', + countSERout: 'Serial out' } function showOverlay(text, isHtml) { let el = document.getElementById('overlayContent');