diff --git a/README b/README index 11d1c1d..f1cd691 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ -OBP Keypad -========== +OBP Keypad 6/1 +============== - Stromversorgung über M12-Anschluß über NMEA2000 - Eingangsbereich 6~21V -- Konfigurationsmodus durch langen Tastendruck auf DST +- Konfigurationsmodus durch langen Tastendruck (>3s) auf DST - Tiefschlaf und Reset aus Konfigmodus heraus auswählbar - Konfiguration über Web-Interface - Buzzer für Tastendruck-Feedback @@ -12,7 +12,7 @@ OBP Keypad Optionen für später - Helligkeitssensor z.B. zum automatischen LED dimmen - Pins umbelegen: + Pins umbelegen?: I²C -> D0, D1 (GPIO 44, 43) LEDs umsortieren: A0 bis A5 für die 6 LEDs A6 als analoger Input für Sensor @@ -72,11 +72,14 @@ Anschlußmöglichkeiten Bemerkungen ----------- -Bei den aktuell verwendeten Tasten sind die Anschlußdrähte extrem -filigran. Leichtes Brechen und schlechte Verarbeitung. +Bei den aktuell verwendeten vorverkabelten Tasten sind die Anschlußdrähte +extrem filigran. Leichtes Brechen und schlechte Verarbeitung. +Besser Taster ohne Kabel verwenden. Schaltdraht mit 0,25mm² scheint am +besten geeignet zu sein, sowohl auf Tastenseite als auch zum Einführen +in den Terminalblock. Es gibt verschiedene Varianten mit unterschiedlicher Federkraft. -Auswahl muß noch erfolgen +Finale Tasten-Auswahl muß noch erfolgen. Beschaltung MCU Nano @@ -106,8 +109,8 @@ ggf. ein E-Paper angeschlossen werden. 3 B D4 GPIO7 4 B D5 GPIO8 5 B D6 GPIO9 - 6 Y D7 GPIO10 Illumination - DST Y D8 GPIO17 Destination, On/Off + 6 Y D7 GPIO10 + DST Y D8 GPIO17 Destination, Konfiguration LED Pin Remarks ------ ---------- ---------------------- @@ -124,7 +127,7 @@ ggf. ein E-Paper angeschlossen werden. RX D10 GPIO21 BUZZER - TBD + GPIO43 temporär, piept allerdings beim flashen über USB und beim Reset Bauteilliste (WIP) ------------ @@ -142,7 +145,9 @@ Bauteilliste (WIP) 1x Buzzer 12V, passiv 1x MOSFET 2N7000 1x Widerstand 150 Ohm - 1x Kabelsatz für Tasten, 0,25 bis 0,5 mm² + 1x Kabelsatz für Tasten, 0,25 mm², je 15cm lang + 8x schwarz (GND) + 7x farbig (Signal) 1x Terminalblock 2pol. 2,54mm schraubbar 1x 3D-Gehäuse bestehend auf Front- und Rückseite 1x Mutternwerkzeug 3D-Druck @@ -155,6 +160,7 @@ Bauteilliste (WIP) Stiftleiste 2,54mm 2x Jumper 1x Polyfuse + Schrumpfschlauch Konfiguration ------------- @@ -168,6 +174,10 @@ Konfiguration NMEA2000 -------- +Für verbesserten Zugriff auf die Geräteliste der NMEA2000-Bibliothek +wird eine erweiterte Funktion GetDeviceByIndex benötigt. +Aus diesem Grund wird mit einem Fork der Bibliothek gearbeitet. + Es werden keine eingehenden Pakete verarbeitet bis auf die ISO-Pflichtpakete Es wird eine Geräteliste geführt diff --git a/platformio.ini b/platformio.ini index be1c1a7..6e2493b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ lib_deps = ESP32Async/AsyncTCP@3.4.9 ESP32Async/ESPAsyncWebServer@3.9.1 ttlappalainen/NMEA2000-library@4.24 + # https://github.com/thooge/NMEA2000.git robtillaart/SHT31@^0.5.2 # only these files will be emedded into firmware @@ -20,6 +21,7 @@ board_build.embed_files = lib/generated/index.html.gz lib/generated/index.js.gz lib/generated/sha256.js.gz + lib/generated/md5.js.gz lib/generated/index.css.gz lib/generated/config.json.gz diff --git a/src/main.cpp b/src/main.cpp index 57d4741..c9e899b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include // temp. sensor #include @@ -11,6 +12,7 @@ #include #include "main.h" #include "Nmea2kTwai.h" +#include "N2kDeviceList.h" #include #include "mbedtls/md.h" // for SHA256 @@ -112,6 +114,7 @@ uint8_t rgb_brightness = 64; uint buzzerpower = 50; // TBD make use of this uint8_t keycode[6]; // configurable keycodes +uint8_t longcode[6]; // configurable keycodes for long pressed keys SHT31 sht(SHT31_ADDRESS); bool sht_available = false; @@ -120,7 +123,7 @@ float hum = 0.0; int nodeid; // NMEA2000 id on bus Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(CAN_TX, CAN_RX, CAN_RECOVERY_PERIOD)); - +tN2kDeviceList *pN2kDeviceList; String processor(const String& var) { // dummy for now @@ -300,6 +303,12 @@ void setup() { keycode[3] = preferences.getInt("key4", 4); keycode[4] = preferences.getInt("key5", 5); keycode[5] = preferences.getInt("key6", 6); + longcode[0] = preferences.getInt("key1long", 11); + longcode[1] = preferences.getInt("key2long", 12); + longcode[2] = preferences.getInt("key3long", 13); + longcode[3] = preferences.getInt("key4long", 14); + longcode[4] = preferences.getInt("key5long", 15); + longcode[5] = preferences.getInt("key6long", 16); preferences.end(); // Configure I/O pins @@ -404,6 +413,31 @@ void setup() { serializeJson(doc, out); request->send(200, "application/json", out); }); + server.on("/api/devicelist", HTTP_GET, [](AsyncWebServerRequest *request){ + // NMEA2000 device list + AsyncResponseStream *response = request->beginResponseStream("application/json"); + response->print("["); + bool first = true; + for (int i = 0; i <= 252; i++) { + const tNMEA2000::tDevice *d = pN2kDeviceList->FindDeviceBySource(i); + if (d == nullptr) { + continue; + } + uint64_t NAME = d->GetName(); + char hex_name[17]; + snprintf(hex_name, sizeof(hex_name), "%08X%08X", (uint32_t)(NAME >> 32), (uint32_t)(NAME & 0xFFFFFFFF)); + if (!first) { + response->print(","); + } else { + first = false; + } + // TODO last seen? + response->printf("{\"source\":%d,\"name\":\"%s\",\"manuf\":\"%d\",\"model\":\"%s\"}", + i, hex_name, d->GetManufacturerCode(), d->GetModelID()); + } + response->print("]"); + request->send(response); + }); server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){ StaticJsonDocument<512> doc; doc["systemName"] = "Keypad1"; @@ -428,6 +462,13 @@ void setup() { doc["key4"] = keycode[BUTTON_4]; doc["key5"] = keycode[BUTTON_5]; doc["key6"] = keycode[BUTTON_6]; + doc["key1long"] = longcode[BUTTON_1]; + doc["key2long"] = longcode[BUTTON_2]; + doc["key3long"] = longcode[BUTTON_3]; + doc["key4long"] = longcode[BUTTON_4]; + doc["key5long"] = longcode[BUTTON_5]; + doc["key6long"] = longcode[BUTTON_6]; + doc["envInterval"] = 5; String out; serializeJson(doc, out); request->send(200, "application/json", out); @@ -466,11 +507,33 @@ void setup() { request->send(200, "application/json", out); }); server.on("/api/update", HTTP_POST, [](AsyncWebServerRequest *request){ + // the request handler is triggered after the upload has finished... + // create the response, add header, and send response + StaticJsonDocument<100> doc; doc["status"] = "FAILED"; String out; serializeJson(doc, out); request->send(200, "application/json", out); + }, [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + // this is the new image upload part + Serial.print("Retrieving firmware image named: "); + Serial.println(filename); + + if (index == 0) { + if (! Update.begin(UPDATE_SIZE_UNKNOWN)) { + Update.printError(Serial); + } + } + if (Update.write(data, len) != len) { + Update.printError(Serial); + } + if (final) { + if (!Update.end(true)) { + Update.printError(Serial); + } + } + }); // TODO POST vom Client entgegennehmen @@ -501,21 +564,31 @@ void setup() { 4 // Industrygoup=Marine ); - NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); - // Debug + // Debug Start // NMEA2000.SetForwardStream(&Serial); + // NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); + // NMEA2000.SetForwardOwnMessages(true); // NMEA2000.SetDebugMode(tNMEA2000::dm_2); + // Debug End - NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, nodeid); + //TODO: N2km_NodeOnly N2km_ListenAndNode? + NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, nodeid); NMEA2000.SetForwardOwnMessages(false); NMEA2000.SetHeartbeatIntervalAndOffset(NMEA2000_HEARTBEAT_INTERVAL); - const unsigned long TransmitPGNs[] = { - 127502UL, + // Features? + // 130311 environment + // 130316 temperature extended range + const unsigned long TransmitPGNs[] PROGMEM= { + 127502UL, // switch bank control + 130312UL, // temperature + 130313UL, // humidity 0 }; NMEA2000.ExtendTransmitMessages(TransmitPGNs); + pN2kDeviceList = new tN2kDeviceList(&NMEA2000); + // Debug: NMEA2000.EnableForward(true); NMEA2000.Open(); // internal user led (red) @@ -618,6 +691,41 @@ void atariKeyclick() { ledcWriteTone(LEDC_CHANNEL, 0); } +void print_n2k_devicelist() { + if (!pN2kDeviceList) { + Serial.println("Devicelist not initialized"); + } + if (!pN2kDeviceList->ReadResetIsListUpdated()) { + // no changes, nothing to do + Serial.println("Devicelist empty or unchanged"); + return; + } + Serial.println("---- NMEA2000 Device List ----"); + + for (int i = 0; i <= 252; i++) { + const tNMEA2000::tDevice *d = pN2kDeviceList->FindDeviceBySource(i); + if (d == nullptr) { + continue; + } + // age in milliseconds + unsigned long lastseen = pN2kDeviceList->GetDeviceLastMessageTime(i); + Serial.printf("Device %d age %d ms\n", i, lastseen); + uint64_t NAME = d->GetName(); + char hex_name[17]; + snprintf(hex_name, sizeof(hex_name), "%08X%08X", + (uint32_t)(NAME >> 32), (uint32_t)(NAME & 0xFFFFFFFF)); + Serial.printf("Src:%d Man:%d Model:%s (0x%s)\n", + d->GetSource(), + d->GetManufacturerCode(), + d->GetModelID(), + hex_name + ); + } + + Serial.println("------------------------------"); +} + +// rename to: void sendSwitchBank() void SendToN2K(uint8_t keycode) { tN2kMsg N2kMsg; tN2kBinaryStatus bankstatus; @@ -636,6 +744,20 @@ void SendToN2K(uint8_t keycode) { Serial.println(" Action=On"); } +void send_sensor_temphum(float temp_k, float hum_perc) { + tN2kMsg N2kMsg; + static unsigned char SID = 0; + unsigned char instance = 0; + tN2kTempSource temp_src = N2kts_OutsideTemperature; // 1=outside, 2=inside + tN2kHumiditySource hum_src = N2khs_OutsideHumidity; // 0=inside, 1=outside + Serial.printf("Sending temp=%f K, hum=%f %%\n", temp_k, hum_perc); + SetN2kPGN130312(N2kMsg, SID, instance, temp_src, temp_k); + NMEA2000.SendMsg(N2kMsg); + SetN2kPGN130313(N2kMsg, SID, instance, hum_src, hum_perc); + NMEA2000.SendMsg(N2kMsg); + SID = (SID + 1) % 256; +} + void loop() { ButtonEvent event; @@ -684,17 +806,29 @@ void loop() { if (mode == 'N') { mode = 'C'; analogWrite(RGBLED_B, rgb_brightness); // blue status indicator + Serial.println("Entering config mode"); } else { mode = 'N'; analogWrite(RGBLED_B, 0); + Serial.println("Leaving config mode"); } } } else { // normal button if (mode == 'N') { - // TODO send key code to destination + // Send key code to destination atariKeyclick(); - SendToN2K(keycode[event.buttonId]); + if ((event.pressType == ButtonPressType::SHORT)) { + SendToN2K(keycode[event.buttonId]); + } else if ((event.pressType == ButtonPressType::MEDIUM)) { + SendToN2K(longcode[event.buttonId]); + } + // Debug: + if ((event.buttonId == BUTTON_1) + and (event.pressType == ButtonPressType::MEDIUM)) + { + print_n2k_devicelist(); + } } else { // online config mode switch (event.buttonId) { @@ -777,8 +911,11 @@ void loop() { if (millis() - lastSensor >= 5000) { lastSensor = millis(); sht.read(); - temp = sht.getTemperature(); - hum = sht.getHumidity(); + temp = sht.getTemperature(); // °C + hum = sht.getHumidity(); // Percent + + // Send environment data to NMEA2000 + send_sensor_temphum(temp + 273.15, hum); } // development heartbeat diff --git a/web/config.json b/web/config.json index 50e7653..2a924ba 100644 --- a/web/config.json +++ b/web/config.json @@ -136,6 +136,16 @@ "description": "The keycode to send for key 1 (leftmost) [1 .. 28]", "category": "Keys" }, +{ + "name": "key1long", + "label": "Key 1 long code", + "type": "number", + "default": 12, + "min": 1, + "max": 28, + "description": "The keycode to send for key 1 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, { "name": "key2", "label": "Key 2 code", @@ -146,6 +156,16 @@ "description": "The keycode to send for key 2 (second from left) [1 .. 28]", "category": "Keys" }, +{ + "name": "key2long", + "label": "Key 2 long code", + "type": "number", + "default": 12, + "min": 1, + "max": 28, + "description": "The keycode to send for key 2 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, { "name": "key3", "label": "Key 3 code", @@ -156,6 +176,16 @@ "description": "The keycode to send for key 3 (third from left) [1 .. 28]", "category": "Keys" }, +{ + "name": "key3long", + "label": "Key 3 long code", + "type": "number", + "default": 13, + "min": 1, + "max": 28, + "description": "The keycode to send for key 3 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, { "name": "key4", "label": "Key 4 code", @@ -166,6 +196,16 @@ "description": "The keycode to send for key 4 (fourth from left) [1 .. 28]", "category": "Keys" }, +{ + "name": "key4long", + "label": "Key 4 long code", + "type": "number", + "default": 14, + "min": 1, + "max": 28, + "description": "The keycode to send for key 4 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, { "name": "key5", "label": "Key 5 code", @@ -176,6 +216,16 @@ "description": "The keycode to send for key 5 (fifth from left) [1 .. 28]", "category": "Keys" }, +{ + "name": "key5long", + "label": "Key 5 long code", + "type": "number", + "default": 15, + "min": 1, + "max": 28, + "description": "The keycode to send for key 5 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, { "name": "key6", "label": "Key 6 code", @@ -185,5 +235,47 @@ "max": 28, "description": "The keycode to send for key 6 (rightmost) [1 .. 28]", "category": "Keys" +}, +{ + "name": "key6long", + "label": "Key 6 long code", + "type": "number", + "default": 16, + "min": 1, + "max": 28, + "description": "The keycode to send for key 6 long pressed (leftmost) [1 .. 28]", + "category": "Keys" +}, +{ + "name": "n2kDestA", + "label": "Destination A", + "type": "list", + "description": "Destination device A", + "category": "NMEA2000" +}, +{ + "name": "n2kDestB", + "label": "Destination B", + "type": "list", + "description": "Destination device B", + "category": "NMEA2000" +}, +{ + "name": "n2kDestC", + "label": "Destination C", + "type": "list", + "description": "Destination device C", + "category": "NMEA2000" +}, +{ + "name": "envInterval", + "label":"Environment Data Interval", + "type": "number", + "default": "5", + "check": "checkMinMax", + "min": 1, + "max": 300, + "description": "interval seconds to send environment data [1..300]", + "category": "NMEA2000" } ] diff --git a/web/index.html b/web/index.html index 0c3da1a..ca9037f 100644 --- a/web/index.html +++ b/web/index.html @@ -12,11 +12,12 @@ if (!window.isSecureContext) { } +
-

OBPkb

+

OBPkb61

disconnected @@ -24,6 +25,7 @@ if (!window.isSecureContext) {
Status
Config
+
Devices
Update
Help
@@ -98,6 +100,15 @@ if (!window.isSecureContext) {
+ +