From 3729fcb0f1455ecfbb81e649ca6b483d3fa6c77d Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sat, 31 Jan 2026 15:14:26 +0100 Subject: [PATCH] NMEA2000 manufacturer names and a bit more logging --- lib/Nmea2kTwai/Nmea2kTwai.cpp | 233 +++++++++++++++++++++++++++++++--- lib/Nmea2kTwai/Nmea2kTwai.h | 29 +++-- src/main.cpp | 6 +- web/config.json | 16 ++- 4 files changed, 247 insertions(+), 37 deletions(-) diff --git a/lib/Nmea2kTwai/Nmea2kTwai.cpp b/lib/Nmea2kTwai/Nmea2kTwai.cpp index e78de57..0a077d1 100644 --- a/lib/Nmea2kTwai/Nmea2kTwai.cpp +++ b/lib/Nmea2kTwai/Nmea2kTwai.cpp @@ -55,7 +55,7 @@ bool Nmea2kTwai::CANOpen() } esp_err_t rt = twai_start(); if (rt != ESP_OK) { - LOGE(TAG, "CANOpen failed: %x", (int)rt); + LOGE(TAG, "CANOpen failed: %x", rt); return false; } else { LOGI(TAG, "CANOpen ok"); @@ -101,9 +101,9 @@ void Nmea2kTwai::initDriver() twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); esp_err_t rt = twai_driver_install(&g_config, &t_config, &f_config); if (rt == ESP_OK) { - // logDebug(LOG_INFO,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); + LOGI(TAG, "twai driver initialzed, rx=%d,tx=%d", RxPin, TxPin); } else { - // logDebug(LOG_ERR,"twai driver init failed: %x",(int)rt); + LOGE(TAG,"twai driver init failed: %x", rt); } } @@ -115,7 +115,7 @@ void Nmea2kTwai::initDriver() void Nmea2kTwai::InitCANFrameBuffers() { if (disabled) { - // logDebug(LOG_INFO,"twai init - disabled"); + LOGI(TAG, "twai init - disabled"); } else{ initDriver(); } @@ -170,11 +170,11 @@ bool Nmea2kTwai::checkRecovery() if (canState.state == Nmea2kTwai::ST_BUS_OFF) { strt = true; bool rt = startRecovery(); - // logDebug(LOG_INFO, "twai BUS_OFF: start can recovery - result %d", (int)rt); + LOGI(TAG, "twai BUS_OFF: start can recovery - result %d", rt); } if (canState.state == Nmea2kTwai::ST_STOPPED) { bool rt = CANOpen(); - // logDebug(LOG_INFO, "twai STOPPED: restart can driver - result %d", (int)rt); + LOGI(TAG, "twai STOPPED: restart can driver - result %d", rt); } } return strt; @@ -185,20 +185,22 @@ void Nmea2kTwai::loop() if (disabled) { return; } + LOGW(TAG, "twai loop() not implemented"); + // TODO? // timers.loop(); } Nmea2kTwai::Status Nmea2kTwai::logStatus() { Status canState = getStatus(); - /* logDebug(LOG_INFO, "twai state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", - stateStr(canState.state), - canState.rx_errors, - canState.tx_errors, - canState.tx_failed, - canState.tx_timeouts, - canState.rx_missed, - canState.rx_overrun); */ + LOGI(TAG, "twai state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", + stateStr(canState.state), + canState.rx_errors, + canState.tx_errors, + canState.tx_failed, + canState.tx_timeouts, + canState.rx_missed, + canState.rx_overrun); return canState; } @@ -210,7 +212,7 @@ bool Nmea2kTwai::startRecovery() lastRecoveryStart = millis(); esp_err_t rt = twai_driver_uninstall(); if (rt != ESP_OK) { - // logDebug(LOG_ERR,"twai: deinit for recovery failed with %x",(int)rt); + LOGE(TAG, "twai: deinit for recovery failed with %x", rt); } initDriver(); bool frt = CANOpen(); @@ -229,3 +231,204 @@ const char * Nmea2kTwai::stateStr(const Nmea2kTwai::STATE &st) } return "ERROR"; } +const Nmea2kTwai::ManufacturerEntry Nmea2kTwai::ManufacturerTable[] = { + {0, "Reserved"}, + {69, "ARKS Enterprises, Inc."}, + {78, "FW Murphy/Enovation Controls"}, + {80, "Twin Disc"}, + {85, "Kohler Power Systems"}, + {88, "Hemisphere GPS Inc"}, + {116, "BEP Marine"}, + {135, "Airmar"}, + {137, "Maretron"}, + {140, "Lowrance"}, + {144, "Mercury Marine"}, + {147, "Nautibus Electronic GmbH"}, + {148, "Blue Water Data"}, + {154, "Westerbeke"}, + {161, "Offshore Systems (UK) Ltd."}, + {163, "Evinrude/BRP"}, + {165, "CPAC Systems AB"}, + {168, "Xantrex Technology Inc."}, + {172, "Yanmar Marine"}, + {174, "Volvo Penta"}, + {175, "Honda Marine"}, + {176, "Carling Technologies Inc. (Moritz Aerospace)"}, + {185, "Beede Instruments"}, + {192, "Floscan Instrument Co. Inc."}, + {193, "Nobletec"}, + {198, "Mystic Valley Communications"}, + {199, "Actia"}, + {200, "Honda Marine"}, + {201, "Disenos Y Technologia"}, + {211, "Digital Switching Systems"}, + {215, "Xintex/Atena"}, + {224, "EMMI NETWORK S.L."}, + {225, "Honda Marine"}, + {228, "ZF"}, + {229, "Garmin"}, + {233, "Yacht Monitoring Solutions"}, + {235, "Sailormade Marine Telemetry/Tetra Technology LTD"}, + {243, "Eride"}, + {250, "Honda Marine"}, + {257, "Honda Motor Company LTD"}, + {272, "Groco"}, + {273, "Actisense"}, + {274, "Amphenol LTW Technology"}, + {275, "Navico"}, + {283, "Hamilton Jet"}, + {285, "Sea Recovery"}, + {286, "Coelmo SRL Italy"}, + {295, "BEP Marine"}, + {304, "Empir Bus"}, + {305, "NovAtel"}, + {306, "Sleipner Motor AS"}, + {307, "MBW Technologies"}, + {311, "Fischer Panda"}, + {315, "ICOM"}, + {328, "Qwerty"}, + {329, "Dief"}, + {341, "Böning Automationstechnologie GmbH & Co. KG"}, + {345, "Korean Maritime University"}, + {351, "Thrane and Thrane"}, + {355, "Mastervolt"}, + {356, "Fischer Panda Generators"}, + {358, "Victron Energy"}, + {370, "Rolls Royce Marine"}, + {373, "Electronic Design"}, + {374, "Northern Lights"}, + {378, "Glendinning"}, + {381, "B & G"}, + {384, "Rose Point Navigation Systems"}, + {385, "Johnson Outdoors Marine Electronics Inc Geonav"}, + {394, "Capi 2"}, + {396, "Beyond Measure"}, + {400, "Livorsi Marine"}, + {404, "ComNav"}, + {409, "Chetco"}, + {419, "Fusion Electronics"}, + {421, "Standard Horizon"}, + {422, "True Heading AB"}, + {426, "Egersund Marine Electronics AS"}, + {427, "em-trak Marine Electronics"}, + {431, "Tohatsu Co, JP"}, + {437, "Digital Yacht"}, + {438, "Comar Systems Limited"}, + {440, "Cummins"}, + {443, "VDO (aka Continental-Corporation)"}, + {451, "Parker Hannifin aka Village Marine Tech"}, + {459, "Alltek Marine Electronics Corp"}, + {460, "SAN GIORGIO S.E.I.N"}, + {466, "Veethree Electronics & Marine"}, + {467, "Humminbird Marine Electronics"}, + {470, "SI-TEX Marine Electronics"}, + {471, "Sea Cross Marine AB"}, + {475, "GME aka Standard Communications Pty LTD"}, + {476, "Humminbird Marine Electronics"}, + {478, "Ocean Sat BV"}, + {481, "Chetco Digitial Instruments"}, + {493, "Watcheye"}, + {499, "Lcj Capteurs"}, + {502, "Attwood Marine"}, + {503, "Naviop S.R.L."}, + {504, "Vesper Marine Ltd"}, + {510, "Marinesoft Co. LTD"}, + {517, "NoLand Engineering"}, + {518, "Transas USA"}, + {529, "National Instruments Korea"}, + {532, "Onwa Marine"}, + {571, "Marinecraft (South Korea)"}, + {573, "McMurdo Group aka Orolia LTD"}, + {578, "Advansea"}, + {579, "KVH"}, + {580, "San Jose Technology"}, + {583, "Yacht Control"}, + {586, "Suzuki Motor Corporation"}, + {591, "US Coast Guard"}, + {595, "Ship Module aka Customware"}, + {600, "Aquatic AV"}, + {605, "Aventics GmbH"}, + {606, "Intellian"}, + {612, "SamwonIT"}, + {614, "Arlt Tecnologies"}, + {637, "Bavaria Yacts"}, + {641, "Diverse Yacht Services"}, + {644, "Wema U.S.A dba KUS"}, + {645, "Garmin"}, + {658, "Shenzhen Jiuzhou Himunication"}, + {688, "Rockford Corp"}, + {704, "JL Audio"}, + {715, "Autonnic"}, + {717, "Yacht Devices"}, + {734, "REAP Systems"}, + {735, "Au Electronics Group"}, + {739, "LxNav"}, + {743, "DaeMyung"}, + {744, "Woosung"}, + {773, "Clarion US"}, + {776, "HMI Systems"}, + {777, "Ocean Signal"}, + {778, "Seekeeper"}, + {781, "Poly Planar"}, + {785, "Fischer Panda DE"}, + {795, "Broyda Industries"}, + {796, "Canadian Automotive"}, + {797, "Tides Marine"}, + {798, "Lumishore"}, + {799, "Still Water Designs and Audio"}, + {802, "BJ Technologies (Beneteau)"}, + {803, "Gill Sensors"}, + {811, "Blue Water Desalination"}, + {815, "FLIR"}, + {824, "Undheim Systems"}, + {838, "TeamSurv"}, + {844, "Fell Marine"}, + {847, "Oceanvolt"}, + {862, "Prospec"}, + {868, "Data Panel Corp"}, + {890, "L3 Technologies"}, + {894, "Rhodan Marine Systems"}, + {896, "Nexfour Solutions"}, + {905, "ASA Electronics"}, + {909, "Marines Co (South Korea)"}, + {911, "Nautic-on"}, + {930, "Ecotronix"}, + {962, "Timbolier Industries"}, + {963, "TJC Micro"}, + {968, "Cox Powertrain"}, + {969, "Blue Seas"}, + {1850, "Teleflex Marine (SeaStar Solutions)"}, + {1851, "Raymarine"}, + {1852, "Navionics"}, + {1853, "Japan Radio Co"}, + {1854, "Northstar Technologies"}, + {1855, "Furuno"}, + {1856, "Trimble"}, + {1857, "Simrad"}, + {1858, "Litton"}, + {1859, "Kvasar AB"}, + {1860, "MMP"}, + {1861, "Vector Cantech"}, + {1862, "Yamaha Marine"}, + {1863, "Faria Instruments"}, + {2046, "Open Boat Projects"} +}; + +const char* Nmea2kTwai::GetManufacturerName(uint16_t code) { + constexpr size_t ManufacturerCount = + sizeof(Nmea2kTwai::ManufacturerTable) / + sizeof(Nmea2kTwai::ManufacturerTable[0]); + int low = 0; + int high = ManufacturerCount - 1; + + while (low <= high) { + int mid = (low + high) / 2; + uint16_t midCode = ManufacturerTable[mid].code; + + if (midCode == code) return ManufacturerTable[mid].name; + if (midCode < code) low = mid + 1; + else high = mid - 1; + } + return "Unknown"; +} + diff --git a/lib/Nmea2kTwai/Nmea2kTwai.h b/lib/Nmea2kTwai/Nmea2kTwai.h index f3b7fe5..1dd5ab3 100644 --- a/lib/Nmea2kTwai/Nmea2kTwai.h +++ b/lib/Nmea2kTwai/Nmea2kTwai.h @@ -1,5 +1,4 @@ -#ifndef _NMEA2KTWAI_H -#define _NMEA2KTWAI_H +#pragma once #include "NMEA2000.h" // #include "GwTimer.h" @@ -15,7 +14,7 @@ public: ST_DISABLED, ST_ERROR } STATE; - typedef struct{ + typedef struct { //see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html#_CPPv418twai_status_info_t uint32_t rx_errors = 0; uint32_t tx_errors = 0; @@ -31,10 +30,12 @@ public: static const char *stateStr(const STATE &st); virtual bool CANOpen(); virtual ~Nmea2kTwai() {}; - // static const int LOG_ERR = 0; - // static const int LOG_INFO = 1; - // static const int LOG_DEBUG = 2; - // static const int LOG_MSG = 3; + + struct ManufacturerEntry { + uint16_t code; + const char* name; + }; + const char* GetManufacturerName(uint16_t code); protected: virtual bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true); @@ -44,19 +45,19 @@ protected: to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. */ virtual void InitCANFrameBuffers(); - virtual void logDebug(int level,const char *fmt,...){} - + // virtual void logDebug(int level,const char *fmt,...){} + static const ManufacturerEntry ManufacturerTable[]; + private: void initDriver(); bool startRecovery(); bool checkRecovery(); - Status logStatus(); - gpio_num_t TxPin; + Status logStatus(); + gpio_num_t TxPin; gpio_num_t RxPin; uint32_t txTimeouts = 0; // GwIntervalRunner timers; bool disabled = false; - unsigned long lastRecoveryStart=0; -}; + unsigned long lastRecoveryStart = 0; -#endif +}; diff --git a/src/main.cpp b/src/main.cpp index 42c461b..ab3cf05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -500,7 +500,7 @@ void print_n2k_devicelist() { } // rename to: void sendSwitchBank() -void SendToN2K(uint8_t keycode) { +void send_switchbank(uint8_t keycode) { tN2kMsg N2kMsg; tN2kBinaryStatus bankstatus; N2kResetBinaryStatus(bankstatus); @@ -575,9 +575,9 @@ void loop() { // Send key code to destination atariKeyclick(); if ((event.pressType == ButtonPressType::SHORT)) { - SendToN2K(keycode[event.buttonId]); + send_switchbank(keycode[event.buttonId]); } else if ((event.pressType == ButtonPressType::MEDIUM)) { - SendToN2K(longcode[event.buttonId]); + send_switchbank(longcode[event.buttonId]); } // Debug: if ((event.buttonId == BUTTON_1) diff --git a/web/config.json b/web/config.json index 4cfe729..953c4ad 100644 --- a/web/config.json +++ b/web/config.json @@ -153,8 +153,8 @@ "type": "number", "default": 0, "min": 0, - "max": 252, - "description": "The number uf switch bank the keys belong to\nRange [0 .. 252] default 0", + "max": 255, + "description": "The number uf switch bank the keys belong to\nRange [0 .. 255] default 0", "category": "Keys" }, { @@ -280,21 +280,27 @@ { "name": "n2kDestA", "label": "Destination A", - "type": "list", + "type": "dynlist", + "source": "devicelist", + "interval": "5000", "description": "Destination device A", "category": "NMEA2000" }, { "name": "n2kDestB", "label": "Destination B", - "type": "list", + "type": "dynlist", + "source": "devicelist", + "interval": "5000", "description": "Destination device B", "category": "NMEA2000" }, { "name": "n2kDestC", "label": "Destination C", - "type": "list", + "type": "dynlist", + "source": "devicelist", + "interval": "5000", "description": "Destination device C", "category": "NMEA2000" },