Merge branch 'master' of github.com:wellenvogel/esp32-nmea2000
This commit is contained in:
commit
ef2a79fc8b
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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__);}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;}
|
||||
};
|
||||
|
|
|
@ -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}
|
||||
|
|
250
src/main.cpp
250
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;
|
||||
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 *
|
||||
|
@ -692,6 +715,11 @@ void setup() {
|
|||
String hash=request->arg("hash");
|
||||
return new CheckPassRequest(hash);
|
||||
});
|
||||
webserver.registerPostHandler("/api/setConfig",
|
||||
[](AsyncWebServerRequest *request){
|
||||
|
||||
},
|
||||
handleConfigRequestData);
|
||||
|
||||
webserver.begin();
|
||||
xdrMappings.begin();
|
||||
|
|
14
web/index.js
14
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) {
|
||||
|
|
Loading…
Reference in New Issue