Compare commits

...

16 Commits

Author SHA1 Message Date
anschrammh
e3e01da80b Added the OTAUpdater class which wraps the ESP8266httpUpdate to be compatible with my OTA service php file 2022-09-27 08:05:35 +02:00
anschrammh
2eb604d24d Changed default LWIP option to use the Low memory no features one due to issues with OTA updates when using the Low memory one (OTA binary is slow to be received and times out) 2022-09-27 08:04:25 +02:00
anschrammh
04a5eb9643 Registered a new rest api endpoint for remote OTA updates, renamed getIoManager to getIOManager 2022-09-27 08:02:30 +02:00
anschrammh
9988b03306 Updated the soft version from 1.6.15 to 1.7.0 2022-09-27 08:00:52 +02:00
anschrammh
beefe8e355 Added OTA events callbacks 2022-09-27 08:00:19 +02:00
anschrammh
967e5ccdbe Added new api endpoint to query and apply OTA updates, renamed getIoManager to getIOManager 2022-09-27 07:35:48 +02:00
anschrammh
09aee48f27 Added more information about the system (ie dev mac address + current software version) and added OTA checks (querying the update server for available updates) 2022-09-27 07:34:18 +02:00
anschrammh
0dcb532007 Added the public key used for signed binary verification downloaded when doing OTA updates. Not yet in use. 2022-09-27 07:31:26 +02:00
anschrammh
63b6cf626f Added the use of the OTAManager class in the SAB class 2022-09-27 07:29:48 +02:00
anschrammh
d0d5df2b98 Added 2 new settings which indicates which root folder to use for the web and ftp server. This config file is not used yet in the app, will come soon 2022-09-27 07:29:10 +02:00
anschrammh
86452fb1b8 Added a new config file used to store OTA settings 2022-09-27 07:27:00 +02:00
anschrammh
f547c8fc07 Added a new OTAManager class which wraps the OTAUpdater class and configures it using parameters read from a cfg file 2022-09-27 07:26:12 +02:00
anschrammh
ab493ef6d8 Did some cleaning, added the flush statement to make sure the request is sent when needed and added some methodes to retrieve and reset the connection retry count 2022-09-27 07:24:22 +02:00
anschrammh
636acb1be1 Allowed more characters to be parsed between quoted values (added '.' and '/' 2022-09-27 07:21:36 +02:00
anschrammh
ff16dbcfce Now using strcasecmp to make case insensitive string compariso 2022-09-27 07:18:56 +02:00
anschrammh
636c3093de Added the on board LED Pin attribute 2022-09-27 07:17:33 +02:00
21 changed files with 711 additions and 63 deletions

8
.vscode/tasks.json vendored
View File

@ -6,9 +6,9 @@
{
"label": "Build",
"type": "shell",
"command": "arduino-cli compile -v --warnings all -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=921600 -e \"`pwd`/src/app\"",
"command": "arduino-cli compile -v --warnings all -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2n,dbg=Disabled,lvl=None____,wipe=none,baud=921600 -e \"`pwd`/src/app\"",
"windows":{
"command": "arduino-cli compile -v --warnings all -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=921600 -e ((pwd).path + '/src/app')"
"command": "arduino-cli compile -v --warnings all -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2n,dbg=Disabled,lvl=None____,wipe=none,baud=921600 -e ((pwd).path + '/src/app')"
},
"group": "build",
"presentation": {
@ -23,9 +23,9 @@
{
"label": "Flash",
"type": "shell",
"command": "arduino-cli upload -v -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=921600 \"`pwd`/src/app\" -p ${input:com_port}",
"command": "arduino-cli upload -v -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2n,dbg=Disabled,lvl=None____,wipe=none,baud=921600 \"`pwd`/src/app\" -p ${input:com_port}",
"windows":{
"command": "arduino-cli upload -v -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=921600 ((pwd).path + '/src/app') -p ${input:com_port}"
"command": "arduino-cli upload -v -b esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,stacksmash=enabled,ssl=basic,mmu=3232,non32xfer=fast,eesz=4M,led=2,ip=lm2n,dbg=Disabled,lvl=None____,wipe=none,baud=921600 ((pwd).path + '/src/app') -p ${input:com_port}"
},
"group": "build",
"presentation": {

View File

@ -0,0 +1,11 @@
#This config file stores the configuration needed for OTA updates
#If this file is not present or if there is an error at parsing time
#the OTA update feature will be disabled.
#If the OTA service does not require any auth key then set this field empty ie ''
#The blank new line at then end of the file is mendatory. Without it, the last parameter won't be read.
ENABLED : 'true'
OTA_SERVER_ADDRESS : 'OTA server IP address'
OTA_SERVER_PORT : 80
OTA_SERVICE_PATH : 'OTA service url path on the server'
OTA_SERVICE_AUTH_KEY : 'OTA service auth key'

View File

@ -8,9 +8,11 @@
WEB_ENABLED : 'true'
WEB_PORT : 80
WEB_MAX_CLIENT : 0
WEB_WWW_DIR : '/WWW'
FTP_ENABLED : 'true'
FTP_LOGIN : 'ESP8266'
FTP_PASSWORD : '12345678'
FTP_ROOT_DIR : '/FTP'
FTP_PORT : 21
FTP_DATA_PORT : 1024
FTP_MAX_CLIENT : 0

View File

@ -6,6 +6,7 @@ const uint16_t screenHeight,
const uint16_t I2C_IOExpanderAddress,
const uint16_t _I2C_RTCFlashAddress,
const Pin SPI_SDCard_cs,
const Pin onBoard_LED,
const Pin I2C_sda,
const Pin I2C_scl,
const Pin SPI_mosi,
@ -19,6 +20,7 @@ _SPI_mosi(SPI_mosi == DEFAULT_PIN ? GPIO_13_MOSI : SPI_mosi),
_SPI_miso(SPI_miso == DEFAULT_PIN ? GPIO_12_MISO : SPI_miso),
_SPI_clk(SPI_clk == DEFAULT_PIN ? GPIO_14_CLK : SPI_clk),
_SPI_SDCard_cs(SPI_SDCard_cs == DEFAULT_PIN ? GPIO_2 : SPI_SDCard_cs),
_onBoard_LED(onBoard_LED == DEFAULT_PIN ? GPIO_2 : onBoard_LED),
_I2C_screenAddress(I2C_screenAddress),
_I2C_IOExpanderAddress(I2C_IOExpanderAddress),
_I2C_RTCFlashAddress(_I2C_RTCFlashAddress),
@ -59,6 +61,11 @@ Pin BoardConfig::getSPI_SDCard_cs() const
return _SPI_SDCard_cs;
}
Pin BoardConfig::getOnBoard_LED() const
{
return _onBoard_LED;
}
uint16_t BoardConfig::getI2C_screenAddress() const
{
return _I2C_screenAddress;
@ -69,12 +76,11 @@ uint16_t BoardConfig::getI2C_IOExpanderAddress() const
return _I2C_IOExpanderAddress;
}
uint16_t BoardConfig::getRTCFlashAddress() const
uint16_t BoardConfig::getI2C_RTCFlashAddress() const
{
return _I2C_RTCFlashAddress;
}
uint32_t BoardConfig::getSPISpeed() const
{
return _SPISpeed;

View File

@ -17,6 +17,7 @@ class BoardConfig
const uint16_t I2C_IOExpanderAddress = 0x27,
const uint16_t _I2C_RTCFlashAddress = 0x0,
const Pin SPI_SDCard_cs = GPIO_2,
const Pin onBoard_LED = GPIO_2,
const Pin I2C_sda = GPIO_4_SDA,
const Pin I2C_scl = GPIO_5_SCL,
const Pin SPI_mosi = GPIO_13_MOSI,
@ -32,10 +33,11 @@ class BoardConfig
Pin getSPI_miso() const;
Pin getSPI_clk() const;
Pin getSPI_SDCard_cs() const;
Pin getOnBoard_LED() const;
uint16_t getI2C_screenAddress() const;
uint16_t getI2C_IOExpanderAddress() const;
uint16_t getRTCFlashAddress() const;
uint16_t getI2C_RTCFlashAddress() const;
uint32_t getSPISpeed() const;
@ -51,6 +53,7 @@ class BoardConfig
const Pin _SPI_miso;
const Pin _SPI_clk;
const Pin _SPI_SDCard_cs;
const Pin _onBoard_LED;
//2) I2C Addresses
const uint16_t _I2C_screenAddress;

View File

@ -72,7 +72,7 @@ void *CFGFileParser::parseFile()
}
}
else if(readChar == ' ') _state = PARAM_SECTION;
else if((readChar >= 65 && readChar <= 90) || (readChar >= 97 && readChar <= 122) || readChar == '_' || readChar == '-' || (readChar >= 48 && readChar <= 57))
else if((readChar >= 65 && readChar <= 90) /*A to Z*/ || (readChar >= 97 && readChar <= 122) /*a to z*/ || (readChar >= 48 && readChar <= 57) /*0 to 9*/ || readChar == '_' || readChar == '-')
{
if(_type == PARAMETER)
{
@ -100,7 +100,9 @@ void *CFGFileParser::parseFile()
else
_quotedValue = true;
}
else if((readChar >= 65 && readChar <= 90) || (readChar >= 97 && readChar <= 122) || readChar == ' ' || readChar == '_' || readChar == '-' || (readChar >= 48 && readChar <= 57))
else if((readChar >= 65 && readChar <= 90) /*A to Z*/ || (readChar >= 97 && readChar <= 122) /*a to z*/ || (readChar >= 48 && readChar <= 57) /*0 to 9*/ ||
readChar == ' ' || readChar == '_' || readChar == '-' || readChar == '.' || readChar == '/'
)
{
//printf("%c",readChar);
if(_type == PARAMETER)
@ -123,7 +125,7 @@ void *CFGFileParser::parseFile()
_type = VALUE;
if(readChar == '\'')_state = OPENING_QUOTE;
else if(readChar == ' ') _state = PARAM_SECTION;
else if((readChar >= 65 && readChar <= 90) || (readChar >= 97 && readChar <= 122) || (readChar >= 48 && readChar <= 57) || readChar == '-' || readChar == '_')
else if((readChar >= 65 && readChar <= 90) /*A to Z*/ || (readChar >= 97 && readChar <= 122) /*a to z*/ || (readChar >= 48 && readChar <= 57) /*0 to 9*/ || readChar == '_' || readChar == '-')
{
_state = PARAM_SECTION;

View File

@ -24,7 +24,7 @@ public:
{
if(_value == NULL)
return false;
return strcmp(_value,"true") == 0 || strcmp(_value,"TRUE") == 0 ? true : false;
return strcasecmp(_value,"true") == 0 ? true : false;
}
const char *getParameter() const{return _parameter == NULL ? "" : _parameter;}
bool isQuotedParameter()const{return _quotedParameter;}

View File

@ -10,7 +10,6 @@
HttpClient::HttpClient(const char *address, uint16_t port, uint32_t timeout) : WiFiClient(), _pAddress(address), _port(port)
{
setTimeout(timeout);
connectByHostOrIp();
}
HttpClient::HttpClient(const char *address, const char *resource, uint16_t port, uint32_t timeout) : HttpClient(address, port, timeout)
@ -44,7 +43,10 @@ HttpClient::HttpClient(const HttpClient &object) : WiFiClient()
HttpClient::~HttpClient()
{
if(_resource != NULL)free(_resource);
if(connected())
stop();
if(_resource != NULL)
free(_resource);
}
boolean HttpClient::connectByHostOrIp()
@ -53,6 +55,17 @@ boolean HttpClient::connectByHostOrIp()
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("About to connect\n");
#endif
//If we constructed the HttpClient with a NULL server address, we don't go further.
if(!_pAddress)
{
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("IP Address is NULL !\n");
#endif
_connectionStatus = FAILED;
return false;
}
if(ipAddress.fromString(_pAddress))
{
_isIp = true;
@ -93,12 +106,15 @@ int HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHel
//We reset this two flags
_httpCode = HTTP_CODE::UNDEFINED_CODE;
_httpCodeParsed = false;
#ifdef DEBUG_HTTP_CLIENT
if(_keepAlive)
Serial.printf("Link status : %d\n", status());
#endif
//If we did not want to keep the connection alive and it is still open, then we first close it.
if(!_keepAlive && connected())stop();
if(!connected() || _connectionStatus == FAILED)
{
if(_retries == _maxRetries) return -__LINE__;
@ -111,9 +127,9 @@ int HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHel
#ifdef DEBUG_HTTP_CLIENT
if(_keepAlive)
Serial.printf("Link broken, we try to reconnect : addr : %s port %u\nretries : %u\n", _pAddress, _port, _retries);
Serial.printf("Link broken, we try to reconnect : addr : %s port %u\nretries : %u\n", _pAddress ? _pAddress : "NULL", _port, _retries);
else
Serial.printf("We start a new connection : %s port %u\nretries : %u\n", _pAddress, _port, _retries);
Serial.printf("We start a new connection : %s port %u\nretries : %u\n", _pAddress ? _pAddress : "NULL", _port, _retries);
#endif
if(!connectByHostOrIp())
@ -131,7 +147,7 @@ int HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHel
if(connected())
{
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("Server is listening\n", status());
Serial.printf("Server is listening\n");
#endif
switch(method)
@ -142,6 +158,7 @@ int HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHel
//Here we send the parameters
sendUriWithGetParams(getData);
sendHeader(HttpMIMEType::UNKNOWN_MIME, 0, headerData);
flush(); //We force the send of the request to prevent any timeout on reception !
break;
case HttpRequestMethod::POST:
//It is necessary to compute the content length
@ -150,17 +167,18 @@ int HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHel
sendUriWithGetParams(getData);
sendHeader(HttpMIMEType::APPLICATION_X_WWW_FORM_URLENCODED, computeBodyLength(postData), headerData);
sendPostData(postData);
flush(); //We force the send of the request to prevent any timeout on reception !
break;
default:
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("Http verb unspecified\n", status());
Serial.printf("Http verb unspecified\n");
#endif
if(!_keepAlive)stop();
return -__LINE__;
break;
}
}
return 0;
}
@ -274,7 +292,7 @@ HttpClient::HTTP_CODE HttpClient::isReplyAvailable(uint16_t timeout)
#ifdef DEBUG_HTTP_CLIENT
safeSize = available() > 99 ? 99 : available();
buffer[peekBytes((uint8_t*)buffer,safeSize)] = '\0';
Serial.printf("Body chunk is : %s\n",buffer);
Serial.printf("Body chunk is : %s\n", buffer);
#endif
break;
}
@ -285,7 +303,7 @@ HttpClient::HTTP_CODE HttpClient::isReplyAvailable(uint16_t timeout)
}
#ifdef DEBUG_HTTP_CLIENT
Serial.println("\nAfter timeout or all data is received");
Serial.printf("\nAfter timeout or all data is received, HTTP_CODE is : %d\n", _httpCode);
#endif
return _httpCode;
@ -408,5 +426,20 @@ uint64_t HttpClient::computeBodyLength(Dictionary<DictionaryHelper::StringEntity
void HttpClient::setMaxRetries(int16_t retries)
{
_maxRetries = retries;
_maxRetries = retries < 0 ? -1 : retries;
}
int16_t HttpClient::getMaxRetries(void) const
{
return _maxRetries;
}
uint16_t HttpClient::retriesCount(void) const
{
return _retries;
}
void HttpClient::resetRetriesCount(void)
{
_retries = 0;
}

View File

@ -37,7 +37,10 @@ class HttpClient : public WiFiClient, public HttpConstants
Dictionary<DictionaryHelper::StringEntity> *headerData = NULL);
void keepAlive(boolean enabled);
void setMaxRetries(int16_t retries);
//100 ms is the default timeout
int16_t getMaxRetries(void) const;
uint16_t retriesCount(void) const;
void resetRetriesCount(void);
//10s is the default timeout
HTTP_CODE isReplyAvailable(uint16_t timeout = 10000);
uint16_t readHttpBody(uint8_t *buffer, uint32_t size);

114
src/app/OTAManager.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "OTAManager.h"
#include "definition.h"
#define DEBUG_OTA_MANAGER(...) do {} while(0)
//#define DEBUG_OTA_MANAGER(...) do { Serial.printf(__VA_ARGS__); Serial.println();} while(0)
OTAManager::OTAManager(SDCardManager &sdCardManager, const BoardConfig &boardConfig) : _sdCardManager(&sdCardManager), _boardConfig(&boardConfig)
{ }
OTAManager::~OTAManager()
{ }
boolean OTAManager::init(void)
{
//If the SDCardManager is not available, then we disable the OTA service
if(!_sdCardManager)
{
DEBUG_OTA_MANAGER("SDCardMng is NULL !");
return true;
}
//If the SDCardManager is available, then we try to read the config file
CFGDictionary<CFGParameterValue> *otaCfg = _sdCardManager->getCFGFile(OTA_CFG_FILE);
boolean toReturn(true);
//If we did not find the file
if(!otaCfg)
{
DEBUG_OTA_MANAGER("otaCfg is NULL !");
return false;
}
if((*otaCfg)("ENABLED") != nullptr)
{
if((*otaCfg)("ENABLED")->booleanValue())
{
DEBUG_OTA_MANAGER("ENABLED : %s", (*otaCfg)("ENABLED")->stringValue());
if((*otaCfg)("OTA_SERVER_ADDRESS") != nullptr) //This is the only required parameter
{
_isServiceEnabled = true;
_otaUpdater.setLedPin(_boardConfig->getOnBoard_LED(), LOW);
_otaUpdater.onStart(std::bind(&OTAManager::updateStartedCb, this));
_otaUpdater.onError(std::bind(&OTAManager::updateErrorCb, this, std::placeholders::_1));
_otaUpdater.onProgress(std::bind(&OTAManager::updateProgressdCb, this, std::placeholders::_1, std::placeholders::_2));
_otaUpdater.onEnd(std::bind(&OTAManager::updateFinishedCb, this));
_otaUpdater.setServerAddress((*otaCfg)("OTA_SERVER_ADDRESS")->stringValue());
if((*otaCfg)("OTA_SERVER_PORT") != nullptr)
{
DEBUG_OTA_MANAGER("OTA_SERVER_PORT : %s", (*otaCfg)("OTA_SERVER_PORT")->stringValue());
_otaUpdater.setPort((*otaCfg)("OTA_SERVER_PORT")->uintValue());
}
if((*otaCfg)("OTA_SERVICE_PATH") != nullptr)
{
DEBUG_OTA_MANAGER("OTA_SERVICE_PATH : %s", (*otaCfg)("OTA_SERVICE_PATH")->stringValue());
_otaUpdater.setPath((*otaCfg)("OTA_SERVICE_PATH")->stringValue());
}
if((*otaCfg)("OTA_SERVICE_AUTH_KEY") != nullptr)
{
DEBUG_OTA_MANAGER("OTA_SERVICE_AUTH_KEY : %s", (*otaCfg)("OTA_SERVICE_AUTH_KEY")->stringValue());
_otaUpdater.setOtaAuthKey((*otaCfg)("OTA_SERVICE_AUTH_KEY")->stringValue());
}
}
else
{
DEBUG_OTA_MANAGER("OTA_SERVER_ADDRESS is NULL !");
toReturn = false;
}
}
}
else
{
DEBUG_OTA_MANAGER("ENABLED is NULL !");
toReturn = false;
}
delete otaCfg;
return toReturn;
}
boolean OTAManager::isEnabled(void) const
{
return _isServiceEnabled;
}
OTAUpdater& OTAManager::getOTAUpdater(void)
{
return _otaUpdater;
}
void OTAManager::updateStartedCb(void)
{
Serial.println("CALLBACK: HTTP update process started");
}
void OTAManager::updateFinishedCb(void)
{
Serial.println("CALLBACK: HTTP update process finished");
}
void OTAManager::updateProgressdCb(int cur, int total)
{
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
}
void OTAManager::updateErrorCb(int err)
{
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
}

32
src/app/OTAManager.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef OTAMANAGER_H
#define OTAMANAGER_H
#include "OTAUpdater.h"
#include "SDCardManager.h"
#include "BoardConfig.h"
class OTAManager
{
friend class SAB;
public:
~OTAManager();
boolean init(void);
boolean isEnabled(void) const;
OTAUpdater& getOTAUpdater(void);
protected:
OTAManager(SDCardManager &sdCardManager, const BoardConfig &boardConfig);
OTAManager();
private:
void updateStartedCb(void);
void updateFinishedCb(void);
void updateProgressdCb(int cur, int total);
void updateErrorCb(int err);
OTAUpdater _otaUpdater;
SDCardManager *_sdCardManager = nullptr;
const BoardConfig * _boardConfig = nullptr;
boolean _isServiceEnabled = false;
};
#endif //OTAMANAGER_H

231
src/app/OTAUpdater.cpp Normal file
View File

@ -0,0 +1,231 @@
#include "OTAUpdater.h"
#include "HttpClient.h"
//#define DEBUG_OTA_UPDATER
OTAUpdater::OTAUpdater(const char *serverAddress, const char *path, const char *ota_auth_key, uint16_t port) : _port(port)
{
if(serverAddress)
_serverAddress = strdup(serverAddress);
if(path)
_path = strdup(path);
if(ota_auth_key)
_ota_auth_key = strdup(ota_auth_key);
}
OTAUpdater::OTAUpdater(int httpClientTimeout,const char *serverAddress, const char *path, const char *ota_auth_key, uint16_t port) : ESP8266HTTPUpdate(httpClientTimeout), _port(port)
{
if(serverAddress)
_serverAddress = strdup(serverAddress);
if(path)
_path = strdup(path);
if(ota_auth_key)
_ota_auth_key = strdup(ota_auth_key);
}
OTAUpdater::~OTAUpdater()
{
if(_publicKey != nullptr)
delete _publicKey;
if(_hash != nullptr)
delete _hash;
if(_signingVerifier != nullptr)
delete _signingVerifier;
free(_serverAddress);
free(_path);
free(_ota_auth_key);
}
//Enables the binary signing verification
void OTAUpdater::enableSHA256UpdateVerification(const char *publicKey)
{
if(_publicKey != nullptr)
delete _publicKey;
if(_hash != nullptr)
delete _hash;
if(_signingVerifier != nullptr)
delete _signingVerifier;
_publicKey = new BearSSL::PublicKey(publicKey);
_hash = new BearSSL::HashSHA256();
_signingVerifier = new BearSSL::SigningVerifier(_publicKey);
Update.installSignature(_hash, _signingVerifier);
}
void OTAUpdater::setServerAddress(const char *serverAddress)
{
if(!serverAddress)
return;
free(_serverAddress);
_serverAddress = strdup(serverAddress);
}
void OTAUpdater::setPath(const char *path)
{
//The path can be empty
if(!path)
{
free(_path);
_path = nullptr;
}
else
{
free(_path);
_path = strdup(path);
}
}
void OTAUpdater::setOtaAuthKey(const char *ota_auth_key)
{
//The authKey can be empty
if(!ota_auth_key)
{
free(_ota_auth_key);
_ota_auth_key = nullptr;
}
else
{
free(_ota_auth_key);
_ota_auth_key = strdup(ota_auth_key);
}
}
void OTAUpdater::setPort(uint16_t port)
{
_port = port;
}
OTAUpdater::UpdateInfo OTAUpdater::fetchUpdateInfo(const char *softwareVersion, uint32_t timeout)
{
HttpClient httpClient(_serverAddress, _path, _port, timeout);
UpdateInfo ui;
if(!softwareVersion)
{
ui.info = OTAUpdater::UpdateInfo::HTTP_NO_UPDATE;
return ui;
}
Dictionary<DictionaryHelper::StringEntity> getData;
getData.add("authKey", DictionaryHelper::StringEntity(_ota_auth_key));
getData.add("checkonly", NULL);
Dictionary<DictionaryHelper::StringEntity> headerData;
headerData.add("x-ESP8266-version",DictionaryHelper::StringEntity(softwareVersion));
headerData.add("x-ESP8266-STA-MAC",DictionaryHelper::StringEntity(WiFi.macAddress().c_str()));
headerData.add("User-Agent",DictionaryHelper::StringEntity("ESP8266-http-Update"));
if(httpClient.sendHttpQuery(HttpClient::HttpRequestMethod::GET, &getData, NULL, &headerData) == 0)
{
HttpConstants::HTTP_CODE result = httpClient.isReplyAvailable(1000);
switch(result)
{
case HttpConstants::HTTP_CODE::HTTP_CODE_OK:
{
char buffer[100];
httpClient.readHttpBody((uint8_t *)buffer, 100);
#ifdef DEBUG_OTA_UPDATER
Serial.printf("Response from update service : %s\n", buffer);
#endif
char *p = strstr_P(buffer, PSTR("bin_version"));
if(p)
{
p+=14;
char *end = strchr(p, '"');
if(end)
{
*end = '\0';
ui.version = (char *)malloc(strlen(p) * sizeof(char) + 1);
if(ui.version)
strcpy(ui.version, p);
}
}
ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_AVAILABLE;
}
break;
case HttpConstants::HTTP_CODE::HTTP_CODE_NOT_MODIFIED:
ui.info = OTAUpdater::UpdateInfo::HTTP_NO_UPDATE;
break;
case HttpConstants::HTTP_CODE::HTTP_CODE_FORBIDDEN:
ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_AUTH_ERROR;
break;
case HttpConstants::HTTP_CODE::HTTP_CODE_NOT_FOUND:
ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR;
break;
default:
#ifdef DEBUG_OTA_UPDATER
Serial.printf("HTTP ERROR CODE IS : %u\n", result);
#endif
ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_UNDEFINED_ERROR;
}
httpClient.stop();
}
else
ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR;
return ui;
}
#define DEBUG_OTA_UPDATER
HTTPUpdateResult OTAUpdater::update(const char *softwareVersion)
{
if(!softwareVersion)
{
#ifdef DEBUG_OTA_UPDATER
Serial.printf("Missing softwareVersion !\n");
#endif
return HTTPUpdateResult::HTTP_UPDATE_NO_UPDATES;
}
if(!_serverAddress)
{
#ifdef DEBUG_OTA_UPDATER
Serial.printf("Missing server address !\n");
#endif
return HTTPUpdateResult::HTTP_UPDATE_FAILED;
}
//Depending on the presence or not of the path and the authKey, we compute the size we must allocate for the full path
uint16_t length = 1 /*for the \0*/;
length += _path ? strlen(_path) : 0;
length += _ota_auth_key ? strlen(_ota_auth_key) + 9/*for : ?authKey=*/ : 0;
char *fullpath = (char *)malloc(length * sizeof(char));
if(!fullpath)return HTTP_UPDATE_FAILED;
fullpath[0] = '\0';
WiFiClient client;
if(_path)strcpy(fullpath, _path);
if(_ota_auth_key)
{
strcat(fullpath, "?authKey=");
strcat(fullpath, _ota_auth_key);
}
#ifdef DEBUG_OTA_UPDATER
Serial.printf("The fullpath is : %s\n", fullpath);
#endif
HTTPUpdateResult result = ESP8266HTTPUpdate::update(client, _serverAddress, _port, fullpath, softwareVersion);
free(fullpath);
return result;
}

103
src/app/OTAUpdater.h Normal file
View File

@ -0,0 +1,103 @@
#ifndef OTAUPDATER_H
#define OTAUPDATER_H
#include <ESP8266httpUpdate.h>
#define OTAUPDATER_STRING //PSTR
class OTAUpdater : public ESP8266HTTPUpdate
{
public:
struct UpdateInfo
{
enum HTTPUpdateInfo {
HTTP_NO_UPDATE, //There is no new update available
HTTP_UPDATE_AVAILABLE, //There is an update ready to be applied
HTTP_UPDATE_AUTH_ERROR, //There was an error during the authentification process by the update service
HTTP_UPDATE_REACH_ERROR, //Unable to reach the update service
HTTP_UPDATE_UNDEFINED_ERROR
};
UpdateInfo(){}
UpdateInfo(const UpdateInfo &object)
{
info = object.info;
if(object.version)
{
version = (char *) malloc((strlen(object.version) + 1) * sizeof(char));
if(version != NULL)
strcpy(version, object.version);
}
}
~UpdateInfo(){ dispose(); }
UpdateInfo& operator=(const UpdateInfo &object)
{
info = object.info;
if(version)
{
free(version);version = NULL;
}
if(object.version)
{
version = (char *) malloc((strlen(object.version) + 1) * sizeof(char));
if(version != NULL)
strcpy(version, object.version);
}
return *this;
}
void dispose()
{
if(version)
{
free(version);version = NULL;
}
}
const char *HTTPUpdateInfoToString(void)
{
switch(info)
{
case HTTP_NO_UPDATE:
return OTAUPDATER_STRING("HTTP_NO_UPDATE");
break;
case HTTP_UPDATE_AVAILABLE:
return OTAUPDATER_STRING("HTTP_UPDATE_AVAILABLE");
break;
case HTTP_UPDATE_AUTH_ERROR:
return OTAUPDATER_STRING("HTTP_UPDATE_AUTH_ERROR");
break;
case HTTP_UPDATE_REACH_ERROR:
return OTAUPDATER_STRING("HTTP_UPDATE_REACH_ERROR");
break;
default:
return OTAUPDATER_STRING("HTTP_UPDATE_UNDEFINED_ERROR");
break;
}
}
HTTPUpdateInfo info = HTTP_NO_UPDATE;
char *version = NULL;
};
OTAUpdater(const char *serverAddress = NULL, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80);
OTAUpdater(int httpClientTimeout, const char *serverAddress, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80);
~OTAUpdater();
//Enables the binary signing verification
void enableSHA256UpdateVerification(const char *publicKey);
void setServerAddress(const char *serverAddress);
void setPath(const char *path = NULL);
void setOtaAuthKey(const char *ota_auth_key = NULL);
void setPort(uint16_t port = 80);
UpdateInfo fetchUpdateInfo(const char *softwareVersion, uint32_t timeout = 500);
HTTPUpdateResult update(const char *softwareVersion);
protected:
BearSSL::PublicKey *_publicKey = nullptr;
BearSSL::HashSHA256 *_hash = nullptr;
BearSSL::SigningVerifier *_signingVerifier = nullptr;
char *_serverAddress = nullptr;
char *_path = nullptr;
char *_ota_auth_key = nullptr;
uint16_t _port;
private:
};
#endif //OTAUPDATER_H

View File

@ -6,6 +6,7 @@ _screenManager(_display, &_sdCardManager),
_connectivityManager(_sdCardManager),
_webServer(80, &_sdCardManager, 10),
_ftpServer(21, &_sdCardManager, "ESP8266", "12345678", 10),
_otaManager(_sdCardManager, _boardConfig),
_dbWSServer(81),
_pcf(_boardConfig.getI2C_IOExpanderAddress(), Wire),
_ioManager(_pcf),
@ -21,6 +22,7 @@ _screenManager(_display, &_sdCardManager),
_connectivityManager(_sdCardManager),
_webServer(webServerPort, &_sdCardManager, 10),
_ftpServer(ftpServerPort, &_sdCardManager, "ESP8266", "12345678", 10),
_otaManager(_sdCardManager, _boardConfig),
_dbWSServer(81),
_pcf(_boardConfig.getI2C_IOExpanderAddress(), Wire),
_ioManager(_pcf),
@ -39,12 +41,18 @@ void SAB::initCommonConfig()
//We initialize the pins for the I2C communication
Wire.begin(_boardConfig.getI2C_sda(), _boardConfig.getI2C_scl());
if(!_rtc.begin()) _error |= RTC_BEGIN_ERR;
if(!_rtc.begin())
_error |= RTC_BEGIN_ERR;
else
{
_rtcManager.setDateTime(_rtc.now());
}
if(!_display.begin(SSD1306_SWITCHCAPVCC, _boardConfig.getI2C_screenAddress())){ _error |= DISP_BEGIN_ERR;}
if(!_display.begin(SSD1306_SWITCHCAPVCC, _boardConfig.getI2C_screenAddress()))
{
_error |= DISP_BEGIN_ERR;
}
if(!_sdCardManager.mountSD())
{
_error |= SDCARD_INIT_ERR;
@ -52,9 +60,11 @@ void SAB::initCommonConfig()
_boardConfig.getSPISpeed(),
_boardConfig.getSPI_SDCard_cs());
}
_screenManager.init();
if(!_connectivityManager.connect()){ _error |= CONNECT_ERR;}
if(!_pcf.begin()){_error |= IO_INIT_ERR;}
if(!_otaManager.init()){_error |= OTA_INIT_ERR;}
//We set the different servers :
_webServer.setWWWDir(WWW_DIR);
@ -104,7 +114,12 @@ FTPServer<FTPClient>& SAB::getFtpServer()
return _ftpServer;
}
IOManager& SAB::getIoManager()
OTAManager& SAB::getOTAManager()
{
return _otaManager;
}
IOManager& SAB::getIOManager()
{
return _ioManager;
}

View File

@ -12,6 +12,7 @@
#include "IOManager.h"
#include "TaskSchedulerManager.h"
#include "PowerManager.h"
#include "OTAManager.h"
#include "versions.h"
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
@ -20,7 +21,7 @@
class SAB
{
public:
enum Error {RTC_BEGIN_ERR = 1, DISP_BEGIN_ERR = 2, SDCARD_INIT_ERR = 4, IO_INIT_ERR = 8, CONNECT_ERR = 16};
enum Error {RTC_BEGIN_ERR = 1, DISP_BEGIN_ERR = 2, SDCARD_INIT_ERR = 4, IO_INIT_ERR = 8, CONNECT_ERR = 16, OTA_INIT_ERR = 32};
SAB();
SAB(const BoardConfig boardConfig, const unsigned int webServerPort = 80, const unsigned int ftpServerPort = 21);
@ -33,10 +34,10 @@ class SAB
RTC_DS3231& getRTC_DS3231();
SDCardManager& getSdCardManager();
ConnectivityManager& getConnectivityManager();
//WEBServerManager& getWebServerManager();
WEBServer<WEBClient>& getWebServer();
FTPServer<FTPClient>& getFtpServer();
IOManager& getIoManager();
OTAManager& getOTAManager();
IOManager& getIOManager();
TaskSchedulerManager& getTaskSchedulerManager();
PowerManager& getPowerManager();
TimeSpan getUpTime() const;
@ -56,10 +57,10 @@ class SAB
RTC_DS3231 _rtc;
RtcManager _rtcManager;
ConnectivityManager _connectivityManager;
//WEBServerManager _webServerManager;
WEBServer<WEBClient> _webServer;
FTPServer<FTPClient> _ftpServer;
DashboardWSServer _dbWSServer;
OTAManager _otaManager;
DashboardWSServer _dbWSServer;
PCF8574 _pcf;
IOManager _ioManager;
TaskSchedulerManager _taskSchedulerManager;

View File

@ -95,9 +95,10 @@ void setup()
sab.getWebServer().addApiRoutine("/sab/io/get/mode", &(ioGetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/io/set/mode", &(ioSetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/sw/version", &(swVersionApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/ota/update", &(otaUpdateApi), NULL, WEBServer<WEBClient>::POST);
sab.getWebServer().addApiRoutine("/sab/ota/update/upload", &(otaUpdateUploadApi), NULL, WEBServer<WEBClient>::POST);
sab.getWebServer().addApiRoutine("/sab/ota/update/device", &(otaUpdateRemoteApi), &sab, WEBServer<WEBClient>::GET);
sab.getIoManager().setISROnIOChange(&(ioISR), GPIO_3_RX);
sab.getIOManager().setISROnIOChange(&(ioISR), GPIO_3_RX);
sab.getTaskSchedulerManager().addTask((uint16_t)0, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setMillis(5000), &(task_blink), &sab);
sab.getTaskSchedulerManager().addTask(1, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setSeconds(10), &(task_sys_info), &v1p);
@ -121,8 +122,8 @@ void loop()
vstap.ipAddr = sab.getConnectivityManager().localIP();
vstap.sigStrength = sab.getConnectivityManager().RSSI();
sab.getIoManager().getPcf().digitalReadAll(vio.ioState);
sab.getIoManager().getPcf().getPinModeAll(vio.ioMode);
sab.getIOManager().getPcf().digitalReadAll(vio.ioState);
sab.getIOManager().getPcf().getPinModeAll(vio.ioMode);
if(ioStateChange)
{

View File

@ -35,10 +35,24 @@ typedef enum { GPIO_0 = 0,
#define STA_CFG_FILE "/CONFIG/STA.CFG"
#define SCREEN_CFG_FILE "/CONFIG/SCREEN.CFG"
#define SERVER_CFG_FILE "/CONFIG/SERVER.CFG"
#define OTA_CFG_FILE "/CONFIG/OTA.CFG"
#define WWW_DIR "/WWW"
#define LOG_DIR "/LOGS"
#define FTP_DIR "/FTP"
//OTA public key for binary verification
const char otaVerificationPubKey[] PROGMEM = R"DEL(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh6eFU0pdfir5CsQqs0v
vi7ip7MtRuNgcMtjl7lpXwQuMG5yoso1iR+Fzbz+Cso4lcqarG5uHV8nKYWQ+C39
+3OfKx9LSfabCnRAeFFhXuGWEfRcZ9aeW3Jv0Mg2+3sTOfXQGjdgNwOAqZNrL4kr
574F+c3o/PAwlr4VLMy7KygorNnaYzC5JF1H5DhqaKNniKVEqGXiVQKW10C04SsM
mGV/rKRSPpBVzyBrIf8GNPyVf7W4D0s9gSiCrRWMU2aU3tX35tezwlicsb86MKKM
o9ySHAMsIihx3zdwAGOELxE47Ylodq9cwc53fz7pfcdyL+N6FKKWM0bYJAkwNhdJ
NQIDAQAB
-----END PUBLIC KEY-----
)DEL";
typedef enum { OR_0 = 2, OR_90 = 3, OR_180 = 0, OR_270 = 1 } Orientation;
typedef enum { BIT = 0, BYTE, KBIT, KBYTE, MBIT, MBYTE, GBIT, GBYTE } SizeUnit;

View File

@ -3,7 +3,7 @@
boolean task_blink(void *pData)
{
SAB *p = (SAB *) pData;
p->getIoManager().getPcf().togglePin(PCF8574::P2);
p->getIOManager().getPcf().togglePin(PCF8574::P2);
return true;
}
@ -19,17 +19,36 @@ boolean task_sys_info(void *pData)
TimeSpan ts(p->sab->getUpTime());
Serial.printf_P(PSTR("BATT SENSING...\nUp Time : %d d %d h %d m %d s\nSystem error : %u\nStation IP : %s, %d dBm\nAP IP : %s\nWEB Server clients : %u\nMemory info:\n\tFree RAM : %u\n\tHeap frag : %u\n\tMax block %u\n"),
Serial.printf_P(PSTR("BATT SENSING...\nSoftware version : %s\nUp Time : %d d %d h %d m %d s\nSystem error : %u\nStation IP : %s, %d dBm\nAP IP : %s\nDevice MAC : %s\nWEB Server clients : %u\nMemory info:\n\tFree RAM : %u\n\tHeap frag : %u\n\tMax block %u\n"),
p->sab->getSoftVersion(),
ts.days(), ts.hours(), ts.minutes(), ts.seconds(),
p->sab->getError(),
p->sab->getConnectivityManager().localIP().toString().c_str(),
p->sab->getConnectivityManager().RSSI(),
p->sab->getConnectivityManager().softAPIP().toString().c_str(),
p->sab->getConnectivityManager().macAddress().c_str(),
p->sab->getWebServer().getConnectedClientsCount(),
freeRAM,
HEAPfrag,
biggestContigMemBlock);
p->powerInfo = p->sab->getPowerManager().getPowerInfo();
if(p->sab->getOTAManager().isEnabled())
{
OTAUpdater::UpdateInfo updateInfo = p->sab->getOTAManager().getOTAUpdater().fetchUpdateInfo(p->sab->getSoftVersion());
if(updateInfo.version != NULL)
{
Serial.printf("OTA : %s -> version(%s)\n", updateInfo.HTTPUpdateInfoToString(), updateInfo.version);
}
else
{
Serial.printf("OTA : %s\n", updateInfo.HTTPUpdateInfoToString());
}
}
else
{
Serial.println("OTA updates disabled");
}
return true;
}
@ -63,7 +82,7 @@ boolean task_post_data_logger(void * pData)
postData.add("post4", NULL);
if(p->client.sendHttpQuery(HttpClient::HttpRequestMethod::POST, &getData, &postData))
if(p->client.sendHttpQuery(HttpClient::HttpRequestMethod::POST, &getData, &postData) == 0)
{
Serial.println("Send successful");
p->rdy = false;

View File

@ -44,5 +44,7 @@
//#define SOFT_VERSION "1.6.12" //WEBServer parsing http query parameters differently, allowing for longer URI and RAM savings.
//#define SOFT_VERSION "1.6.13" //ScreenManager now providing and handling display auto power off functionality to save power as well as to fight OLED burn-in.
//#define SOFT_VERSION "1.6.14" //Corrected a major stack overflow in the WEBServer and fixed a potential one in the FTPServer.
#define SOFT_VERSION "1.6.15" //WEBServer now lists files and folder of a resource when it is a folder.
//#define SOFT_VERSION "1.6.15" //WEBServer now lists files and folder of a resource when it is a folder.
#define SOFT_VERSION "1.7.0" //OTA update of the device is now possible through a rest API endpoint.
#endif //VERSIONS_H

View File

@ -400,38 +400,38 @@ boolean ioSetLevelApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc
if(HRD.getParams("P0") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P0,atoi(HRD.getParams("P0")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P0,atoi(HRD.getParams("P0")->getString()));
}
if(HRD.getParams("P1") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P1,atoi(HRD.getParams("P1")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P1,atoi(HRD.getParams("P1")->getString()));
}
if(HRD.getParams("P2") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P2,atoi(HRD.getParams("P2")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P2,atoi(HRD.getParams("P2")->getString()));
}
if(HRD.getParams("P3") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P3,atoi(HRD.getParams("P3")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P3,atoi(HRD.getParams("P3")->getString()));
}
if(HRD.getParams("P4") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P4,atoi(HRD.getParams("P4")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P4,atoi(HRD.getParams("P4")->getString()));
}
if(HRD.getParams("P5") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P5,atoi(HRD.getParams("P5")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P5,atoi(HRD.getParams("P5")->getString()));
}
if(HRD.getParams("P6") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P6,atoi(HRD.getParams("P6")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P6,atoi(HRD.getParams("P6")->getString()));
}
if(HRD.getParams("P7") != NULL)
{
p->getIoManager().getPcf().digitalWrite(PCF8574::P7,atoi(HRD.getParams("P7")->getString()));
p->getIOManager().getPcf().digitalWrite(PCF8574::P7,atoi(HRD.getParams("P7")->getString()));
}
p->getIoManager().getPcf().digitalReadAll(ioState);//We retrieve the IO state
p->getIOManager().getPcf().digitalReadAll(ioState);//We retrieve the IO state
sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%d\",\"P1\":\"%d\",\"P2\":\"%d\",\"P3\":\"%d\",\"P4\":\"%d\",\"P5\":\"%d\",\"P6\":\"%d\",\"P7\":\"%d\"}",ioState[0],ioState[1],ioState[2],ioState[3],ioState[4],ioState[5],ioState[6],ioState[7]);
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
@ -447,7 +447,7 @@ boolean ioGetModeApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc,
char helperBuffer[20] = "";
const char * const IN = "IN", * const OUT = "OUT";
p->getIoManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT
p->getIOManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT
if(HRD.getParams.count() == 0)//We send every IO mode
{
@ -521,38 +521,38 @@ boolean ioSetModeApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc,
if(HRD.getParams("P0") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P0,strcmp(HRD.getParams("P0")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P0,strcmp(HRD.getParams("P0")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P1") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P1,strcmp(HRD.getParams("P1")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P1,strcmp(HRD.getParams("P1")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P2") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P2,strcmp(HRD.getParams("P2")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P2,strcmp(HRD.getParams("P2")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P3") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P3,strcmp(HRD.getParams("P3")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P3,strcmp(HRD.getParams("P3")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P4") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P4,strcmp(HRD.getParams("P4")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P4,strcmp(HRD.getParams("P4")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P5") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P5,strcmp(HRD.getParams("P5")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P5,strcmp(HRD.getParams("P5")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P6") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P6,strcmp(HRD.getParams("P6")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P6,strcmp(HRD.getParams("P6")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
if(HRD.getParams("P7") != NULL)
{
p->getIoManager().getPcf().pinMode(PCF8574::P7,strcmp(HRD.getParams("P7")->getString(), IN) == 0 ? INPUT:OUTPUT);
p->getIOManager().getPcf().pinMode(PCF8574::P7,strcmp(HRD.getParams("P7")->getString(), IN) == 0 ? INPUT:OUTPUT);
}
p->getIoManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT
p->getIOManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT
sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%s\",\"P1\":\"%s\",\"P2\":\"%s\",\"P3\":\"%s\",\"P4\":\"%s\",\"P5\":\"%s\",\"P6\":\"%s\",\"P7\":\"%s\"}",ioMode[0] ? OUT:IN,ioMode[1] ? OUT:IN,ioMode[2] ? OUT:IN,ioMode[3] ? OUT:IN,ioMode[4] ? OUT:IN,ioMode[5] ? OUT:IN,ioMode[6] ? OUT:IN,ioMode[7] ? OUT:IN);
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
@ -563,22 +563,22 @@ boolean ioSetModeApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc,
boolean swVersionApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
(void)HRD;
(void)pData;
SAB *p = (SAB *)pData;
char buffer[100] = "";
sprintf(buffer ,"{\"status\":\"ok\",\"version\":\"%s\"}", SOFT_VERSION);
sprintf(buffer ,"{\"status\":\"ok\",\"version\":\"%s\"}", p->getSoftVersion());
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
wc->print(buffer);
return true;
}
boolean otaUpdateApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
boolean otaUpdateUploadApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
(void)HRD;
(void)pData;
Serial.printf("OTA Update resquest\n#");
Serial.printf("OTA Update Upload resquest\n#");
char buffer[30] = "";
size_t read(0), cnt(0);
@ -602,3 +602,58 @@ boolean otaUpdateApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc,
return true;
}
boolean otaUpdateRemoteApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
SAB *p = (SAB *)pData;
char buffer[100] = "";
boolean updateDevice(false);
if(!p->getOTAManager().isEnabled())
{
strcpy(buffer ,"{\"status\":\"ok\",\"ota\":\"disabled\"}");
}
else
{
OTAUpdater::UpdateInfo updateInfo = p->getOTAManager().getOTAUpdater().fetchUpdateInfo(p->getSoftVersion());
switch(updateInfo.info)
{
case OTAUpdater::UpdateInfo::HTTP_NO_UPDATE :
strcpy(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"No update available\"}");
break;
case OTAUpdater::UpdateInfo::HTTP_UPDATE_AUTH_ERROR :
strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"OTA auth error\"}");
break;
case OTAUpdater::UpdateInfo::HTTP_UPDATE_AVAILABLE :
//Then we update the firmware
if(HRD.getParams("update") != NULL)
{
sprintf(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"Updating the device, please wait\",\"version\":\"%s\"}", updateInfo.version);
updateDevice = true;
}
else //This means, we only want to get the new version number
{
sprintf(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"Update available\",\"version\":\"%s\"}", updateInfo.version);
}
break;
case OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR :
strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"OTA reach error\"}");
break;
default:
strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"Undefined error\"}");
break;
}
}
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
wc->print(buffer);
if(updateDevice)
{
p->getOTAManager().getOTAUpdater().update(p->getSoftVersion());
}
return true;
}

View File

@ -31,6 +31,7 @@ boolean ioSetLevelApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*
boolean ioGetModeApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
boolean ioSetModeApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
boolean swVersionApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
boolean otaUpdateApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
boolean otaUpdateUploadApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
boolean otaUpdateRemoteApi(WEBServer<WEBClient>::HttpRequestData&, WiFiClient*, void*);
#endif