Added the OTAUpdater class which wraps the ESP8266httpUpdate to be compatible with my OTA service php file

This commit is contained in:
anschrammh 2022-09-27 08:05:35 +02:00
parent 2eb604d24d
commit e3e01da80b
2 changed files with 334 additions and 0 deletions

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