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) 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/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 #include +#include #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;ihasChangedValue){ - 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;iupdateValue(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/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/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 08a6a8f..8b6dd9f 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -57,6 +57,52 @@ //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_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/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/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); } } 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 +#include #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/platformio.ini b/platformio.ini index 1d576e2..d6d1109 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,6 +59,25 @@ 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: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 lib_deps = ${env.lib_deps} diff --git a/src/main.cpp b/src/main.cpp index 138a4e9..557c35a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -141,47 +141,12 @@ GwWebServer webserver(&logger,&mainQueue,80); GwCounter countNMEA2KIn("count2Kin"); GwCounter 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> 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; status["heap"]=(long)xPortGetFreeHeapSize(); @@ -477,71 +442,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()); - ok = false; - error += name; - error += "="; - error += value; - error += ","; - } - logger.flush(); - } - if (ok) - { - 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(), - (long)xPortGetMinimumEverFreeHeapSize() - ); - logger.flush(); - delayedRestart(); - } - else - { - GwJsonDocument rt(100); - rt["status"] = error; - serializeJson(rt, result); - } - } -}; + class ResetConfigRequest : public GwRequestMessage { String hash; @@ -558,7 +459,7 @@ protected: result=JSON_INVALID_PASS; return; } - config.reset(true); + config.reset(); logger.logDebug(GwLog::ERROR,"reset config, restart"); result = JSON_OK; delayedRestart(); @@ -627,6 +528,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[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"); + //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; + } + } + } + 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 (valueDone) break; + } + 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,"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)); + } + } + 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); + logger.flush(); + logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", + (long)xPortGetFreeHeapSize(), + (long)xPortGetMinimumEverFreeHeapSize() + ); + logger.flush(); + delayedRestart(); + } + } + else{ + request->send(200,"application/json","{\"status\":\"missing _hash\"}"); + } + } +} + void setup() { mainLock=xSemaphoreCreateMutex(); @@ -667,12 +696,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 * @@ -691,7 +714,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..28ea82f 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) {