From df84066b58b202d56c24fcfadda1c7625f020de0 Mon Sep 17 00:00:00 2001 From: free-x <oroitburd@gmail.com> Date: Sat, 30 Apr 2022 09:59:36 +0200 Subject: [PATCH 1/8] Fix ArduinoJson library deps for platformio --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index c0c0101..9627f84 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,7 +22,7 @@ framework = arduino lib_deps = ttlappalainen/NMEA2000-library @ 4.17.2 ttlappalainen/NMEA0183 @ 1.7.1 - bblanchon/ArduinoJson@6.18.5 + ArduinoJson@6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 fastled/FastLED @ 3.4.0 board_build.embed_files = From ca7af513e471e48b2efa8b6f4704243bad0fe7a2 Mon Sep 17 00:00:00 2001 From: free-x <oroitburd@gmail.com> Date: Sat, 30 Apr 2022 17:38:48 +0200 Subject: [PATCH 2/8] Pining espressif32 platform --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 9627f84..314b345 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,12 +17,12 @@ extra_configs= lib/*task*/platformio.ini [env] -platform = espressif32 +platform = espressif32 @ 3.4.0 framework = arduino lib_deps = ttlappalainen/NMEA2000-library @ 4.17.2 ttlappalainen/NMEA0183 @ 1.7.1 - ArduinoJson@6.18.5 + ArduinoJson @ 6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 fastled/FastLED @ 3.4.0 board_build.embed_files = From 4087311922039815fa78bc6ba492e0fc27dcacc8 Mon Sep 17 00:00:00 2001 From: free-x <oroitburd@gmail.com> Date: Sat, 25 Jun 2022 08:24:51 +0200 Subject: [PATCH 3/8] Add rs232 atom board --- lib/hardware/GwHardware.h | 23 +++++++++++++++++++++++ platformio.ini | 10 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 08a6a8f..15776e5 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -57,6 +57,29 @@ //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif + +#ifdef BOARD_M5ATOM_RS232_CANUNIT +#define ESP32_CAN_TX_PIN GPIO_NUM_26 +#define ESP32_CAN_RX_PIN GPIO_NUM_32 +//if using rs232 +#define GWSERIAL_TX 19 +#define GWSERIAL_RX 22 +#define GWSERIAL_MODE "BI" +#define GWBUTTON_PIN GPIO_NUM_39 +#define GWBUTTON_ACTIVE LOW +//if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown +#define GWBUTTON_PULLUPDOWN +//led handling +//if we define GWLED_FASTNET the arduino fastnet lib is used +#define GWLED_FASTLED +#define GWLED_TYPE SK6812 +//color schema for fastled +#define GWLED_SCHEMA GRB +#define GWLED_PIN GPIO_NUM_27 +//brightness 0...255 +#define GWLED_BRIGHTNESS 64 +#endif + #ifdef BOARD_M5STICK_CANUNIT #define ESP32_CAN_TX_PIN GPIO_NUM_32 #define ESP32_CAN_RX_PIN GPIO_NUM_33 diff --git a/platformio.ini b/platformio.ini index 1d576e2..e85fdb1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,6 +59,16 @@ build_flags = upload_port = /dev/esp32 upload_protocol = esptool +[env:m5stack-atom-rs232-canunit] +board = m5stack-atom +lib_deps = ${env.lib_deps} +build_flags = + -D BOARD_M5ATOM_RS232_CANUNIT + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool + + [env:m5stickc-atom-canunit] board = m5stick-c lib_deps = ${env.lib_deps} From a4731e67d77ad6082ab1aed260ff516658b08958 Mon Sep 17 00:00:00 2001 From: free-x <oroitburd@gmail.com> Date: Sat, 25 Jun 2022 08:29:49 +0200 Subject: [PATCH 4/8] Add rs485 atom board --- lib/hardware/GwHardware.h | 23 +++++++++++++++++++++++ platformio.ini | 9 +++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 15776e5..8b6dd9f 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -80,6 +80,29 @@ #define GWLED_BRIGHTNESS 64 #endif +#ifdef BOARD_M5ATOM_RS485_CANUNIT +#define ESP32_CAN_TX_PIN GPIO_NUM_26 +#define ESP32_CAN_RX_PIN GPIO_NUM_32 +//if using rs232 +#define GWSERIAL_TX 19 +#define GWSERIAL_RX 22 +#define GWSERIAL_MODE "UNI" +#define GWBUTTON_PIN GPIO_NUM_39 +#define GWBUTTON_ACTIVE LOW +//if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown +#define GWBUTTON_PULLUPDOWN +//led handling +//if we define GWLED_FASTNET the arduino fastnet lib is used +#define GWLED_FASTLED +#define GWLED_TYPE SK6812 +//color schema for fastled +#define GWLED_SCHEMA GRB +#define GWLED_PIN GPIO_NUM_27 +//brightness 0...255 +#define GWLED_BRIGHTNESS 64 +#endif + + #ifdef BOARD_M5STICK_CANUNIT #define ESP32_CAN_TX_PIN GPIO_NUM_32 #define ESP32_CAN_RX_PIN GPIO_NUM_33 diff --git a/platformio.ini b/platformio.ini index e85fdb1..d6d1109 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,6 +68,15 @@ build_flags = upload_port = /dev/esp32 upload_protocol = esptool +[env:m5stack-atom-rs485-canunit] +board = m5stack-atom +lib_deps = ${env.lib_deps} +build_flags = + -D BOARD_M5ATOM_RS485_CANUNIT + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool + [env:m5stickc-atom-canunit] board = m5stick-c From 62a715f53e514383e4fa34cb74467b33269c4062 Mon Sep 17 00:00:00 2001 From: free-x <oroitburd@gmail.com> Date: Sat, 25 Jun 2022 08:52:00 +0200 Subject: [PATCH 5/8] Add documentation about rs232 and rs485 kits --- doc/Hardware.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/Hardware.md b/doc/Hardware.md index 79209a0..988c8d6 100644 --- a/doc/Hardware.md +++ b/doc/Hardware.md @@ -50,6 +50,20 @@ M5 Atom CAN with M5 RS485 Module With this set up you get basically all the features from the plain AtomCAN and the Tal485 combined. You still can connect via USB but have the NMEA0183 connection in parallel. +M5 Atom RS485 with M5 CAN Unit +-------------------------------- +* Hardware: [ATOM RS485](https://docs.m5stack.com/en/atom/atomic485) + [CAN Unit](http://docs.m5stack.com/en/unit/can) +* Prebuild Binary: m5stack-atom-rs485-canunit-all.bin +* Build Define: BOARD_M5ATOM_RS485_CANUNIT +* Power: 12V via RS485 Module or via USB + +M5 Atom RS232 with M5 CAN Unit +-------------------------------- +* Hardware: [ATOM RS232](https://docs.m5stack.com/en/atom/atomic232) + [CAN Unit](http://docs.m5stack.com/en/unit/can) +* Prebuild Binary: m5stack-atom-rs232-canunit-all.bin +* Build Define: BOARD_M5ATOM_RS232_CANUNIT +* Power: 12V via RS232 Module or via USB + M5 Stack Atom Canunit --------------------- * Hardware: [M5_ATOM](http://docs.m5stack.com/en/core/atom_lite) + [CAN Unit](http://docs.m5stack.com/en/unit/can) From 86139fc445e96ddfec77e36ca7274f8a5d296ca9 Mon Sep 17 00:00:00 2001 From: wellenvogel <andreas@wellenvogel.de> Date: Tue, 15 Nov 2022 22:44:48 +0100 Subject: [PATCH 6/8] 1st step to post request for config --- lib/buttons/GwButtons.cpp | 2 +- lib/config/GWConfig.cpp | 71 ++++++++++--- lib/config/GWConfig.h | 7 +- lib/webserver/GwWebServer.cpp | 8 ++ lib/webserver/GwWebServer.h | 2 + src/main.cpp | 188 +++++++++++++++++++++++++--------- web/index.js | 14 ++- 7 files changed, 220 insertions(+), 72 deletions(-) diff --git a/lib/buttons/GwButtons.cpp b/lib/buttons/GwButtons.cpp index cf64329..c169e48 100644 --- a/lib/buttons/GwButtons.cpp +++ b/lib/buttons/GwButtons.cpp @@ -14,7 +14,7 @@ class FactoryResetRequest: public GwMessage{ protected: virtual void processImpl(){ api->getLogger()->logDebug(GwLog::LOG,"reset request processing"); - api->getConfig()->reset(true); + api->getConfig()->reset(); xTaskCreate([](void *p){ delay(500); ESP.restart(); diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index f7316df..cdbfdbe 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -1,6 +1,7 @@ #include "GWConfig.h" #include <ArduinoJson.h> #include <string.h> +#include <MD5Builder.h> #define B(v) (v?"true":"false") @@ -53,6 +54,7 @@ GwConfigInterface * GwConfigHandler::getConfigItem(const String name, bool dummy #define PREF_NAME "gwprefs" GwConfigHandler::GwConfigHandler(GwLog *logger): GwConfigDefinitions(){ this->logger=logger; + saltBase=esp_random(); } bool GwConfigHandler::loadConfig(){ prefs.begin(PREF_NAME,true); @@ -63,18 +65,6 @@ bool GwConfigHandler::loadConfig(){ prefs.end(); return true; } -bool GwConfigHandler::saveConfig(){ - prefs.begin(PREF_NAME,false); - for (int i=0;i<getNumConfig();i++){ - if (configs[i]->hasChangedValue){ - LOG_DEBUG(GwLog::LOG,"saving %s=%s",configs[i]->getName().c_str(),configs[i]->changedValue.c_str()); - prefs.putString(configs[i]->getName().c_str(),configs[i]->changedValue); - } - } - prefs.end(); - LOG_DEBUG(GwLog::LOG,"saved config"); - return true; -} bool GwConfigHandler::updateValue(String name, String value){ GwConfigInterface *i=getConfigItem(name); @@ -83,18 +73,24 @@ bool GwConfigHandler::updateValue(String name, String value){ LOG_DEBUG(GwLog::LOG,"skip empty password %s",name.c_str()); } else{ + if (i->asString() == value){ + return false; + } LOG_DEBUG(GwLog::LOG,"update config %s=>%s",name.c_str(),i->isSecret()?"***":value.c_str()); - i->updateValue(value); + prefs.begin(PREF_NAME,false); + prefs.putString(i->getName().c_str(),value); + prefs.end(); } return true; } -bool GwConfigHandler::reset(bool save){ +bool GwConfigHandler::reset(){ LOG_DEBUG(GwLog::LOG,"reset config"); + prefs.begin(PREF_NAME,false); for (int i=0;i<getNumConfig();i++){ - configs[i]->updateValue(configs[i]->getDefault()); + prefs.putString(configs[i]->getName().c_str(),configs[i]->getDefault()); } - if (!save) return true; - return saveConfig(); + prefs.end(); + return true; } String GwConfigHandler::getString(const String name, String defaultv) const{ GwConfigInterface *i=getConfigItem(name,false); @@ -122,6 +118,47 @@ bool GwConfigHandler::setValue(String name,String value){ return true; } +bool GwConfigHandler::checkPass(String hash){ + if (! getBool(useAdminPass)) return true; + String pass=getString(adminPassword); + unsigned long now=millis()/1000UL & ~0x7UL; + MD5Builder builder; + char buffer[2*sizeof(now)+1]; + for (int i=0;i< 5 ;i++){ + unsigned long base=saltBase+now; + toHex(base,buffer,2*sizeof(now)+1); + builder.begin(); + builder.add(buffer); + builder.add(pass); + builder.calculate(); + String md5=builder.toString(); + bool rt=hash == md5; + logger->logDebug(GwLog::DEBUG,"checking pass %s, base=%ld, hash=%s, res=%d", + hash.c_str(),base,md5.c_str(),(int)rt); + if (rt) return true; + now -= 8; + } + return false; +} +static char hv(uint8_t nibble){ + nibble=nibble&0xf; + if (nibble < 10) return (char)('0'+nibble); + return (char)('A'+nibble-10); +} +void GwConfigHandler::toHex(unsigned long v, char *buffer, size_t bsize) +{ + uint8_t *bp = (uint8_t *)&v; + size_t i = 0; + for (; i < sizeof(v) && (2 * i + 1) < bsize; i++) + { + buffer[2 * i] = hv((*bp) >> 4); + buffer[2 * i + 1] = hv(*bp); + bp++; + } + if ((2 * i) < bsize) + buffer[2 * i] = 0; +} + void GwNmeaFilter::handleToken(String token, int index){ switch(index){ case 0: diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index 1dcb28c..4d0b105 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -18,22 +18,25 @@ class GwConfigHandler: public GwConfigDefinitions{ public: GwConfigHandler(GwLog *logger); bool loadConfig(); - bool saveConfig(); void stopChanges(); bool updateValue(String name, String value); - bool reset(bool save); + bool reset(); String toString() const; String toJson() 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; GwConfigInterface * getConfigItem(const String name, bool dummy=false) const; + bool checkPass(String hash); /** * 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); + static void toHex(unsigned long v,char *buffer,size_t bsize); + unsigned long getSaltBase(){return saltBase;} private: + unsigned long saltBase=0; }; #endif \ No newline at end of file diff --git a/lib/webserver/GwWebServer.cpp b/lib/webserver/GwWebServer.cpp index eb4dcef..2b3a79a 100644 --- a/lib/webserver/GwWebServer.cpp +++ b/lib/webserver/GwWebServer.cpp @@ -118,4 +118,12 @@ bool GwWebServer::registerMainHandler(const char *url,RequestCreator creator){ return true; } +bool GwWebServer::registerPostHandler(const char *url, ArRequestHandlerFunction requestHandler, + ArBodyHandlerFunction bodyHandler){ + server->on(url,HTTP_POST,requestHandler, + [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){}, + bodyHandler); + return true; +} + diff --git a/lib/webserver/GwWebServer.h b/lib/webserver/GwWebServer.h index 35b5cba..795988e 100644 --- a/lib/webserver/GwWebServer.h +++ b/lib/webserver/GwWebServer.h @@ -1,6 +1,7 @@ #ifndef _GWWEBSERVER_H #define _GWWEBSERVER_H #include <ESPAsyncWebServer.h> +#include <functional> #include "GwMessage.h" #include "GwLog.h" class GwWebServer{ @@ -14,6 +15,7 @@ class GwWebServer{ ~GwWebServer(); void begin(); bool registerMainHandler(const char *url,RequestCreator creator); + bool registerPostHandler(const char *url, ArRequestHandlerFunction requestHandler, ArBodyHandlerFunction bodyHandler); void handleAsyncWebRequest(AsyncWebServerRequest *request, GwRequestMessage *msg); AsyncWebServer * getServer(){return server;} }; diff --git a/src/main.cpp b/src/main.cpp index becdce6..0f586aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -141,47 +141,12 @@ GwWebServer webserver(&logger,&mainQueue,80); GwCounter<unsigned long> countNMEA2KIn("count2Kin"); GwCounter<unsigned long> countNMEA2KOut("count2Kout"); -unsigned long saltBase=esp_random(); - -char hv(uint8_t nibble){ - nibble=nibble&0xf; - if (nibble < 10) return (char)('0'+nibble); - return (char)('A'+nibble-10); -} -void toHex(unsigned long v,char *buffer,size_t bsize){ - uint8_t *bp=(uint8_t *)&v; - size_t i=0; - for (;i<sizeof(v) && (2*i +1)< bsize;i++){ - buffer[2*i]=hv((*bp) >> 4); - buffer[2*i+1]=hv(*bp); - bp++; - } - if ((2*i) < bsize) buffer[2*i]=0; -} bool checkPass(String hash){ - if (! config.getBool(config.useAdminPass)) return true; - String pass=config.getString(config.adminPassword); - unsigned long now=millis()/1000UL & ~0x7UL; - MD5Builder builder; - char buffer[2*sizeof(now)+1]; - for (int i=0;i< 5 ;i++){ - unsigned long base=saltBase+now; - toHex(base,buffer,2*sizeof(now)+1); - builder.begin(); - builder.add(buffer); - builder.add(pass); - builder.calculate(); - String md5=builder.toString(); - bool rt=hash == md5; - logger.logDebug(GwLog::DEBUG,"checking pass %s, base=%ld, hash=%s, res=%d", - hash.c_str(),base,md5.c_str(),(int)rt); - if (rt) return true; - now -= 8; - } - return false; + return config.checkPass(hash); } + GwUpdate updater(&logger,&webserver,&checkPass); GwConfigInterface *systemName=config.getConfigItem(config.systemName,true); @@ -398,9 +363,9 @@ protected: status["clientIP"] = WiFi.localIP().toString(); status["apIp"] = gwWifi.apIP(); size_t bsize=2*sizeof(unsigned long)+1; - unsigned long base=saltBase + ( millis()/1000UL & ~0x7UL); + unsigned long base=config.getSaltBase() + ( millis()/1000UL & ~0x7UL); char buffer[bsize]; - toHex(base,buffer,bsize); + GwConfigHandler::toHex(base,buffer,bsize); status["salt"] = buffer; status["fwtype"]= firmwareType; //nmea0183Converter->toJson(status); @@ -512,7 +477,6 @@ protected: if (!rt) { logger.logDebug(GwLog::ERROR,"ERR: unable to update %s to %s", name.c_str(), value.c_str()); - ok = false; error += name; error += "="; error += value; @@ -524,7 +488,6 @@ protected: { result = JSON_OK; logger.logDebug(GwLog::ERROR,"update config and restart"); - config.saveConfig(); logger.flush(); logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", (long)xPortGetFreeHeapSize(), @@ -557,7 +520,7 @@ protected: result=JSON_INVALID_PASS; return; } - config.reset(true); + config.reset(); logger.logDebug(GwLog::ERROR,"reset config, restart"); result = JSON_OK; delayedRestart(); @@ -626,6 +589,134 @@ protected: }; +void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + typedef struct{ + char notFirst; + char hashChecked; + char parsingValue; + int bName; + char name[16]; + int bValue; + char value[512]; + }RequestNV; + logger.logDebug(GwLog::DEBUG,"handleConfigRequestData len=%d,idx=%d,total=%d",(int)len,(int)index,(int)total); + if (request->_tempObject == NULL){ + logger.logDebug(GwLog::DEBUG,"handleConfigRequestData create receive struct"); + //we cannot use new here as it will be deleted with free + request->_tempObject=malloc(sizeof(RequestNV)); + memset(request->_tempObject,0,sizeof(RequestNV)); + } + RequestNV *nv=(RequestNV*)(request->_tempObject); + if (nv->notFirst && ! nv->hashChecked){ + return; //ignore data + } + int parsed=0; + while (parsed < len) + { + if (!nv->parsingValue) + { + int maxSize = sizeof(RequestNV::name) - 1; + if (nv->bName >= maxSize) + { + nv->name[maxSize] = 0; + logger.logDebug(GwLog::DEBUG, "parse error name too long %s", nv->name); + nv->bName = 0; + } + while (nv->bName < maxSize && parsed < len) + { + bool endName = *data == '='; + nv->name[nv->bName] = endName ? 0 : *data; + nv->bName++; + parsed++; + data++; + if (endName) + { + nv->parsingValue = 1; + break; + } + if (nv->bName >= maxSize) + { + nv->name[maxSize] = 0; + logger.logDebug(GwLog::DEBUG, "parse error name too long %s", nv->name); + nv->bName = 0; + } + } + } + bool valueDone = false; + if (nv->parsingValue) + { + int maxSize = sizeof(RequestNV::value) - 1; + if (nv->bValue >= maxSize) + { + nv->value[maxSize] = 0; + logger.logDebug(GwLog::DEBUG, "parse error value too long %s:%s", nv->name, nv->value); + nv->bValue = 0; + } + while (nv->bValue < maxSize && parsed < len) + { + valueDone = *data == '&'; + nv->value[nv->bValue] = valueDone ? 0 : *data; + nv->bValue++; + parsed++; + data++; + if (nv->bValue >= maxSize) + { + nv->value[maxSize] = 0; + logger.logDebug(GwLog::DEBUG, "parse error value too long %s:%s", nv->name, nv->value); + nv->bValue = 0; + } + } + if (! valueDone){ + if (parsed >= len && (len+index) >= total){ + //request ends here + nv->value[nv->bValue]=0; + valueDone=true; + } + } + if (valueDone){ + String name(nv->name); + String value(nv->value); + if (! nv->notFirst){ + nv->notFirst=1; + //we expect the _hash as first parameter + if (name != String("_hash")){ + logger.logDebug(GwLog::ERROR,"missing first parameter _hash in setConfig"); + request->send(200,"application/json","{\"status\":\"missing _hash\"}"); + return; + } + if (! config.checkPass(request->urlDecode(value))){ + request->send(200,"application/json",JSON_INVALID_PASS); + return; + } + else{ + nv->hashChecked=1; + } + } + else{ + if (nv->hashChecked){ + logger.logDebug(GwLog::DEBUG,"update value ns=%d,n=%d,vs=%d,v=%d",nv->bName,nv->name,nv->bValue,nv->value); + config.updateValue(request->urlDecode(name),request->urlDecode(value)); + } + } + nv->parsingValue=0; + nv->bName=0; + nv->bValue=0; + } + } + } + if (parsed >= len && (len+index)>= total){ + if (nv->notFirst){ + if (nv->hashChecked){ + request->send(200,"application/json",JSON_OK); + delayedRestart(); + } + } + else{ + request->send(200,"application/json","{\"status\":\"missing _hash\"}"); + } + } +} + void setup() { mainLock=xSemaphoreCreateMutex(); @@ -666,12 +757,6 @@ void setup() { { return new StatusRequest(); }); webserver.registerMainHandler("/api/config", [](AsyncWebServerRequest *request)->GwRequestMessage * { return new ConfigRequest(); }); - webserver.registerMainHandler("/api/setConfig", - [](AsyncWebServerRequest *request)->GwRequestMessage * - { - SetConfigRequest *msg = new SetConfigRequest(request); - return msg; - }); webserver.registerMainHandler("/api/resetConfig", [](AsyncWebServerRequest *request)->GwRequestMessage * { return new ResetConfigRequest(request->arg("_hash")); }); webserver.registerMainHandler("/api/boatData", [](AsyncWebServerRequest *request)->GwRequestMessage * @@ -690,7 +775,12 @@ void setup() { { String hash=request->arg("hash"); return new CheckPassRequest(hash); - }); + }); + webserver.registerPostHandler("/api/setConfig", + [](AsyncWebServerRequest *request){ + + }, + handleConfigRequestData); webserver.begin(); xdrMappings.begin(); diff --git a/web/index.js b/web/index.js index 509d7a6..c7f399f 100644 --- a/web/index.js +++ b/web/index.js @@ -236,16 +236,24 @@ function changeConfig() { ensurePass() .then(function (pass) { let newAdminPass; - let url = "/api/setConfig?_hash="+encodeURIComponent(pass)+"&"; + let url = "/api/setConfig" + let body="hash="+encodeURIComponent(pass)+"&"; let allValues=getAllConfigs(); if (!allValues) return; for (let name in allValues){ if (name == 'adminPassword'){ newAdminPass=allValues[name]; } - url += name + "=" + encodeURIComponent(allValues[name]) + "&"; + body += encodeURIComponent(name) + "=" + encodeURIComponent(allValues[name]) + "&"; } - getJson(url) + fetch(url,{ + method:'POST', + headers:{ + 'Content-Type': 'application/octet-stream' //we must lie here + }, + body: body + }) + .then((rs)=>rs.json()) .then(function (status) { if (status.status == 'OK') { if (newAdminPass !== undefined) { From c02be797db73f9b99b44140c494f381b0eb49a86 Mon Sep 17 00:00:00 2001 From: wellenvogel <andreas@wellenvogel.de> Date: Tue, 15 Nov 2022 23:33:49 +0100 Subject: [PATCH 7/8] correctly handle pass and sizes --- lib/config/GwConfigItem.h | 12 ----- lib/log/GWLog.cpp | 2 + lib/log/GwLog.h | 2 + src/main.cpp | 92 +++++++-------------------------------- web/index.js | 2 +- 5 files changed, 20 insertions(+), 90 deletions(-) diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index 44f8a06..adfb161 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -10,18 +10,6 @@ class GwConfigInterface{ const char * initialValue; String value; bool secret=false; - String changedValue; - bool hasChangedValue=false; - void updateValue(String value) - { - hasChangedValue = false; - if (value != this->value) - { - changedValue = value; - hasChangedValue = true; - } - } - public: GwConfigInterface(const String &name, const char * initialValue, bool secret=false){ this->name=name; diff --git a/lib/log/GWLog.cpp b/lib/log/GWLog.cpp index 80af69b..6601f2a 100644 --- a/lib/log/GWLog.cpp +++ b/lib/log/GWLog.cpp @@ -21,6 +21,7 @@ void GwLog::logString(const char *fmt,...){ va_list args; va_start(args,fmt); xSemaphoreTake(locker, portMAX_DELAY); + recordCounter++; vsnprintf(buffer,bufferSize-1,fmt,args); buffer[bufferSize-1]=0; if (! writer) { @@ -40,6 +41,7 @@ void GwLog::logDebug(int level,const char *fmt,...){ va_list args; va_start(args,fmt); xSemaphoreTake(locker, portMAX_DELAY); + recordCounter++; vsnprintf(buffer,bufferSize-1,fmt,args); buffer[bufferSize-1]=0; if (! writer) { diff --git a/lib/log/GwLog.h b/lib/log/GwLog.h index 7aea6f8..e04c7b5 100644 --- a/lib/log/GwLog.h +++ b/lib/log/GwLog.h @@ -15,6 +15,7 @@ class GwLog{ int logLevel=1; GwLogWriter *writer; SemaphoreHandle_t locker; + long long recordCounter=0; public: static const int LOG=1; static const int ERROR=0; @@ -29,6 +30,7 @@ class GwLog{ int isActive(int level){return level <= logLevel;}; void flush(); void setLevel(int level){this->logLevel=level;} + long long getRecordCounter(){return recordCounter;} }; #define LOG_DEBUG(level,...){ if (logger != NULL && logger->isActive(level)) logger->logDebug(level,__VA_ARGS__);} diff --git a/src/main.cpp b/src/main.cpp index 0f586aa..757945e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -441,69 +441,7 @@ protected: } }; -class SetConfigRequest : public GwRequestMessage -{ -public: - //we rely on the message living not longer then the request - AsyncWebServerRequest *request; - SetConfigRequest(AsyncWebServerRequest *rq) : GwRequestMessage(F("application/json"),F("setConfig")), - request(rq) - {}; - virtual int getTimeout(){return 4000;} -protected: - virtual void processRequest() - { - bool ok = true; - const char * hashArg="_hash"; - String error; - String hash; - if (request->hasArg(hashArg)){ - hash=request->arg(hashArg); - } - if (! checkPass(hash)){ - result=JSON_INVALID_PASS; - return; - } - logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", - (long)xPortGetFreeHeapSize(), - (long)xPortGetMinimumEverFreeHeapSize() - ); - for (int i = 0; i < request->args(); i++){ - String name=request->argName(i); - String value=request->arg(i); - if (name.indexOf("_")>= 0) continue; - if (name == GwConfigDefinitions::apPassword && fixedApPass) continue; - bool rt = config.updateValue(name, value); - if (!rt) - { - logger.logDebug(GwLog::ERROR,"ERR: unable to update %s to %s", name.c_str(), value.c_str()); - error += name; - error += "="; - error += value; - error += ","; - } - logger.flush(); - } - if (ok) - { - result = JSON_OK; - logger.logDebug(GwLog::ERROR,"update config and restart"); - logger.flush(); - logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", - (long)xPortGetFreeHeapSize(), - (long)xPortGetMinimumEverFreeHeapSize() - ); - logger.flush(); - delayedRestart(); - } - else - { - GwJsonDocument rt(100); - rt["status"] = error; - serializeJson(rt, result); - } - } -}; + class ResetConfigRequest : public GwRequestMessage { String hash; @@ -595,10 +533,11 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size char hashChecked; char parsingValue; int bName; - char name[16]; + char name[33]; int bValue; char value[512]; }RequestNV; + long long lastRecords=logger.getRecordCounter(); logger.logDebug(GwLog::DEBUG,"handleConfigRequestData len=%d,idx=%d,total=%d",(int)len,(int)index,(int)total); if (request->_tempObject == NULL){ logger.logDebug(GwLog::DEBUG,"handleConfigRequestData create receive struct"); @@ -634,12 +573,6 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size nv->parsingValue = 1; break; } - if (nv->bName >= maxSize) - { - nv->name[maxSize] = 0; - logger.logDebug(GwLog::DEBUG, "parse error name too long %s", nv->name); - nv->bName = 0; - } } } bool valueDone = false; @@ -659,12 +592,7 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size nv->bValue++; parsed++; data++; - if (nv->bValue >= maxSize) - { - nv->value[maxSize] = 0; - logger.logDebug(GwLog::DEBUG, "parse error value too long %s:%s", nv->name, nv->value); - nv->bValue = 0; - } + if (valueDone) break; } if (! valueDone){ if (parsed >= len && (len+index) >= total){ @@ -694,7 +622,11 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size } else{ if (nv->hashChecked){ - logger.logDebug(GwLog::DEBUG,"update value ns=%d,n=%d,vs=%d,v=%d",nv->bName,nv->name,nv->bValue,nv->value); + logger.logDebug(GwLog::DEBUG,"value ns=%d,n=%s,vs=%d,v=%s",nv->bName,nv->name,nv->bValue,nv->value); + if ((logger.getRecordCounter() - lastRecords) > 20){ + logger.flush(); + lastRecords=logger.getRecordCounter(); + } config.updateValue(request->urlDecode(name),request->urlDecode(value)); } } @@ -708,6 +640,12 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size if (nv->notFirst){ if (nv->hashChecked){ request->send(200,"application/json",JSON_OK); + logger.flush(); + logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", + (long)xPortGetFreeHeapSize(), + (long)xPortGetMinimumEverFreeHeapSize() + ); + logger.flush(); delayedRestart(); } } diff --git a/web/index.js b/web/index.js index c7f399f..28ea82f 100644 --- a/web/index.js +++ b/web/index.js @@ -237,7 +237,7 @@ function changeConfig() { .then(function (pass) { let newAdminPass; let url = "/api/setConfig" - let body="hash="+encodeURIComponent(pass)+"&"; + let body="_hash="+encodeURIComponent(pass)+"&"; let allValues=getAllConfigs(); if (!allValues) return; for (let name in allValues){ From b4d5e976195424932e139849b8282a642ab8cf1c Mon Sep 17 00:00:00 2001 From: wellenvogel <andreas@wellenvogel.de> Date: Wed, 16 Nov 2022 11:46:14 +0100 Subject: [PATCH 8/8] #44: implement workaround factor for ROT --- lib/boatData/GwBoatData.h | 4 ++++ lib/nmea0183ton2k/NMEA0183DataToN2K.cpp | 1 + lib/nmea2kto0183/N2kDataToNMEA0183.cpp | 3 +-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h index 5ddebde..71742cf 100644 --- a/lib/boatData/GwBoatData.h +++ b/lib/boatData/GwBoatData.h @@ -7,6 +7,10 @@ #define GW_BOAT_VALUE_LEN 32 #define GWSC(name) static constexpr const __FlashStringHelper* name=F(#name) +//see https://github.com/wellenvogel/esp32-nmea2000/issues/44 +//factor to convert from N2k/SI rad/s to current NMEA rad/min +#define ROT_WA_FACTOR 60 + class GwJsonDocument; class GwBoatItemBase{ public: diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index 0258f8c..cd4fbc8 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -887,6 +887,7 @@ private: LOG_DEBUG(GwLog::DEBUG,"unable to parse ROT %s",msg.line); return; } + ROT=ROT / ROT_WA_FACTOR; if (! updateDouble(boatData->ROT,ROT,msg.sourceId)) return; tN2kMsg n2kMsg; SetN2kRateOfTurn(n2kMsg,1,ROT); diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index 3e900fd..1e61d8e 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -32,7 +32,6 @@ - N2kDataToNMEA0183::N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, String talkerId) { @@ -1055,7 +1054,7 @@ private: } if (!updateDouble(boatData->ROT,ROT)) return; tNMEA0183Msg nmeamsg; - if (NMEA0183SetROT(nmeamsg,ROT,talkerId)){ + if (NMEA0183SetROT(nmeamsg,ROT * ROT_WA_FACTOR,talkerId)){ SendMessage(nmeamsg); } }