feat: Initial implementation of Elegant OTA with support for API server (#41)

---------

Co-authored-by: ZanzyTHEbar <pyr0ndet0s97@gmail.com>
This commit is contained in:
Zdzislaw Goik 2023-03-24 21:40:47 +01:00 committed by lorow
parent 926134abc1
commit 58989b4567
19 changed files with 2166 additions and 280 deletions

View File

@ -1,11 +1,11 @@
---
BasedOnStyle: Chromium
AllowShortBlocksOnASingleLine: 'false'
AllowShortBlocksOnASingleLine: "false"
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: 'false'
CompactNamespaces: 'false'
AllowShortLoopsOnASingleLine: "false"
CompactNamespaces: "false"
Language: Cpp
NamespaceIndentation: All
PointerAlignment: Left
ReflowComments: 'true'
ReflowComments: "true"
UseTab: Never

View File

@ -28,17 +28,17 @@ build_flags =
-DENABLE_ADHOC=${wifi.enableadhoc}
-DADHOC_CHANNEL=${wifi.adhocchannel}
-DWIFI_CHANNEL=${wifi.channel}
-DDEBUG_ESP_PORT=Serial ; set the debug port
'-DMDNS_HOSTNAME=${wifi.mDNSName}' ; Set the OTA password
-DDEBUG_ESP_PORT=Serial
'-DMDNS_HOSTNAME=${wifi.mdnsname}'
'-DWIFI_SSID=${wifi.ssid}'
'-DWIFI_PASSWORD=${wifi.password}'
'-DWIFI_AP_SSID=${wifi.ap_ssid}'
'-DWIFI_AP_PASSWORD=${wifi.ap_password}'
'-DWIFI_AP_CHANNEL=${wifi.adhocchannel}'
-DENABLE_OTA=${ota.enableota}
-DOTA_SERVER_PORT=${ota.otaserverport}
'-DOTA_PASSWORD=${ota.otapassword}' ; Set the OTA password
'-DOTA_LOGIN=${ota.otalogin}'
'-DOTA_IP=${ota.otaserverip}' ; Set the OTA password
-O2 ; optimize for speed

View File

@ -12,7 +12,7 @@ build_flags = -DENABLE_ADHOC=${wifi.enableadhoc}
-DADHOC_CHANNEL=${wifi.adhocchannel}
-DWIFI_CHANNEL=${wifi.channel}
-DDEBUG_ESP_PORT=Serial ; set the debug port
'-DMDNS_HOSTNAME=${wifi.mDNSName}' ; Set the OTA password
'-DMDNS_HOSTNAME=${wifi.mdnsname}' ; Set the OTA password
# required for simulator
'-DWIFI_SSID=${sim.ssid}'
'-DWIFI_PASSWORD=${sim.password}'
@ -20,9 +20,9 @@ build_flags = -DENABLE_ADHOC=${wifi.enableadhoc}
'-DWIFI_AP_PASSWORD=${wifi.ap_password}'
'-DWIFI_AP_CHANNEL=${wifi.adhocchannel}'
-DENABLE_OTA=${ota.enableota}
-DOTA_SERVER_PORT=${ota.otaserverport}
'-DOTA_PASSWORD=${ota.otapassword}' ; Set the OTA password
'-DOTA_LOGIN=${ota.otalogin}'
'-DOTA_IP=${ota.otaserverip}' ; Set the OTA password
-O2 ; optimize for speed

View File

@ -13,7 +13,7 @@ enableadhoc = 0
adhocchannel = 1
[ota]
enableota = 1
otaserverip = "openiristracker.local"
otalogin = "openiris"
otapassword = "12345678"
otaserverport = 3232

View File

@ -30,10 +30,7 @@ void ProjectConfig::initConfig() {
! Do not initialize the WiFiConfig_t struct here,
! as it will create a blank network which breaks the WiFiManager
*/
this->config.device = {
"12345678",
3232,
};
this->config.device = {OTA_LOGIN, OTA_PASSWORD, 3232};
if (_mdnsName.empty()) {
log_e("MDNS name is null\n Autoassigning name to 'openiristracker'");
@ -113,6 +110,7 @@ void ProjectConfig::wifiConfigSave() {
void ProjectConfig::deviceConfigSave() {
/* Device Config */
putString("OTAPassword", this->config.device.OTAPassword.c_str());
putString("OTALogin", this->config.device.OTALogin.c_str());
putInt("OTAPort", this->config.device.OTAPort);
}
@ -149,6 +147,7 @@ void ProjectConfig::load() {
}
/* Device Config */
this->config.device.OTALogin = getString("OTALogin", "openiris").c_str();
this->config.device.OTAPassword =
getString("OTAPassword", "12345678").c_str();
this->config.device.OTAPort = getInt("OTAPort", 3232);
@ -210,18 +209,14 @@ void ProjectConfig::load() {
//! DeviceConfig
//*
//**********************************************************************************************************************
void ProjectConfig::setDeviceConfig(const std::string& OTAPassword,
void ProjectConfig::setDeviceConfig(const std::string& OTALogin,
const std::string& OTAPassword,
int OTAPort,
const std::string& binaryName,
bool shouldNotify) {
log_d("Updating device config");
this->config.device.OTALogin.assign(OTALogin);
this->config.device.OTAPassword.assign(OTAPassword);
this->config.device.OTAPort = OTAPort;
// check if binary name is empty
if (binaryName.empty())
log_w("Binary name is empty, using previous value");
else
this->config.device.binaryName.assign(binaryName);
if (shouldNotify)
this->notify(ObserverEvent::deviceConfigUpdated);
@ -361,8 +356,9 @@ void ProjectConfig::setAPWifiConfig(const std::string& ssid,
std::string ProjectConfig::DeviceConfig_t::toRepresentation() {
std::string json = Helpers::format_string(
"\"device_config\": {\"OTAPassword\": \"%s\", \"OTAPort\": %u}",
this->OTAPassword.c_str(), this->OTAPort);
"\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", "
"\"OTAPort\": %u}",
this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort);
return json;
}

View File

@ -27,9 +27,9 @@ class ProjectConfig : public Preferences, public ISubject {
void initConfig();
struct DeviceConfig_t {
std::string OTALogin;
std::string OTAPassword;
int OTAPort;
std::string binaryName;
std::string toRepresentation();
};
@ -102,9 +102,9 @@ class ProjectConfig : public Preferences, public ISubject {
MDNSConfig_t* getMDNSConfig();
WiFiTxPower_t* getWiFiTxPowerConfig();
void setDeviceConfig(const std::string& OTAPassword,
void setDeviceConfig(const std::string& OTALogin,
const std::string& OTAPassword,
int OTAPort,
const std::string& binaryName,
bool shouldNotify);
void setMDNSConfig(const std::string& hostname,
const std::string& service,

View File

@ -46,6 +46,21 @@ void Network_Utilities::my_delay(volatile long delay_time)
;
}
// a function to generate the device ID
std::string Network_Utilities::generateDeviceID() {
uint32_t chipId = 0;
for (int i = 0; i < 17; i = i + 8) {
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
}
log_i("ESP Chip model = %s Rev %d\n", ESP.getChipModel(),
ESP.getChipRevision());
log_i("This chip has %d cores\n", ESP.getChipCores());
log_i("Chip ID: %d", chipId);
std::string deviceID = (const char*)chipId;
return deviceID;
}
/**
* @brief Function to map the WiFi status to the WiFiState_e enum
*
@ -57,24 +72,25 @@ void Network_Utilities::checkWiFiState()
{
return;
}
switch (WiFi.status())
{
case wl_status_t::WL_IDLE_STATUS:
wifiStateManager.setState(WiFiState_e::WiFiState_Idle);
case wl_status_t::WL_NO_SSID_AVAIL:
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
case wl_status_t::WL_SCAN_COMPLETED:
wifiStateManager.setState(WiFiState_e::WiFiState_None);
case wl_status_t::WL_CONNECTED:
wifiStateManager.setState(WiFiState_e::WiFiState_Connected);
case wl_status_t::WL_CONNECT_FAILED:
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
case wl_status_t::WL_CONNECTION_LOST:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
case wl_status_t::WL_DISCONNECTED:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
default:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
case wl_status_t::WL_IDLE_STATUS:
wifiStateManager.setState(WiFiState_e::WiFiState_Idle);
case wl_status_t::WL_NO_SSID_AVAIL:
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
case wl_status_t::WL_SCAN_COMPLETED:
wifiStateManager.setState(WiFiState_e::WiFiState_None);
case wl_status_t::WL_CONNECTED:
wifiStateManager.setState(WiFiState_e::WiFiState_Connected);
case wl_status_t::WL_CONNECT_FAILED:
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
case wl_status_t::WL_CONNECTION_LOST:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
case wl_status_t::WL_DISCONNECTED:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
default:
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
}
}

View File

@ -3,16 +3,14 @@
#define UTILITIES_hpp
#include <Arduino.h>
#include <WiFi.h>
#include <unordered_map>
#include <data/StateManager/StateManager.hpp>
#include "mbedtls/md.h"
namespace Network_Utilities
{
bool loopWifiScan();
void setupWifiScan();
void my_delay(volatile long delay_time);
int getStrength(int points);
void checkWiFiState();
}
#endif // !UTILITIES_hpp
#include <unordered_map>
namespace Network_Utilities {
bool loopWifiScan();
void setupWifiScan();
void my_delay(volatile long delay_time);
void checkWiFiState();
std::string generateDeviceID();
int getStrength(int points);
} // namespace Network_Utilities
#endif // !UTILITIES_hpp

View File

@ -1,70 +0,0 @@
#include "OTA.hpp"
OTA::OTA(ProjectConfig* _deviceConfig)
: _deviceConfig(_deviceConfig), _bootTimestamp(0), _isOtaEnabled(true) {}
OTA::~OTA() {}
void OTA::begin() {
log_i("Setting up OTA updates");
auto localConfig = _deviceConfig->getDeviceConfig();
if (localConfig->OTAPassword.empty()) {
log_e("THE PASSWORD IS REQUIRED, [[ABORTING]]");
return;
}
ArduinoOTA.setPort(localConfig->OTAPort);
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
})
.onEnd([]() { Serial.println("OTA updated finished successfully!"); })
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
log_e("Error[%u]: ", error);
switch (error) {
case OTA_AUTH_ERROR:
log_e("Auth Failed");
break;
case OTA_BEGIN_ERROR:
log_e("Begin Failed");
break;
case OTA_CONNECT_ERROR:
log_e("Connect Failed");
break;
case OTA_RECEIVE_ERROR:
log_e("Receive Failed");
break;
case OTA_END_ERROR:
log_e("End Failed");
break;
}
});
log_i("Starting up basic OTA server");
log_i(
"OTA will be live for 5 minutes, after which it will be disabled until "
"restart");
auto mdnsConfig = _deviceConfig->getMDNSConfig();
ArduinoOTA.setHostname(mdnsConfig->hostname.c_str());
ArduinoOTA.begin();
_bootTimestamp = millis();
}
void OTA::handleOTAUpdate() {
if (_isOtaEnabled) {
if (_bootTimestamp + (60000 * 5) < millis()) {
// we're disabling ota after first 5 minutes so that nothing bad happens
// during runtime
_isOtaEnabled = false;
log_i("From now on, OTA is disabled");
return;
}
ArduinoOTA.handle();
}
}

View File

@ -1,20 +0,0 @@
#ifndef OTA_HPP
#define OTA_HPP
#include <ArduinoOTA.h>
#include <HTTPClient.h>
#include "data/config/project_config.hpp"
class OTA
{
public:
OTA(ProjectConfig *_deviceConfig);
virtual ~OTA();
void begin();
void handleOTAUpdate();
private:
ProjectConfig *_deviceConfig;
unsigned long _bootTimestamp;
bool _isOtaEnabled;
};
#endif // OTA_HPP

View File

@ -0,0 +1,17 @@
#ifndef HASH_H_
#define HASH_H_
#include <stdint.h>
#include <string>
// #define DEBUG_SHA1
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
void sha1(const char* data, uint32_t size, uint8_t hash[20]);
void sha1(const std::string& data, uint8_t hash[20]);
std::string sha1(const uint8_t* data, uint32_t size);
std::string sha1(const char* data, uint32_t size);
std::string sha1(const std::string& data);
#endif /* HASH_H_ */

View File

@ -10,16 +10,17 @@
// const char *BaseAPI::MIMETYPE_ICO{"image/x-icon"};
const char* BaseAPI::MIMETYPE_JSON{"application/json"};
BaseAPI::BaseAPI(int CONTROL_PORT,
ProjectConfig* projectConfig,
BaseAPI::BaseAPI(ProjectConfig* projectConfig,
CameraHandler* camera,
StateManager<WiFiState_e>* WiFiStateManager,
const std::string& api_url)
: server(CONTROL_PORT),
projectConfig(projectConfig),
const std::string& api_url,
int port)
: projectConfig(projectConfig),
camera(camera),
wiFiStateManager(wiFiStateManager),
api_url(api_url) {}
server(port),
api_url(api_url),
_authRequired(false) {}
BaseAPI::~BaseAPI() {}
@ -172,7 +173,7 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) {
std::string hostname;
std::string service;
std::string ota_password;
std::string firmware_name;
std::string ota_login;
int ota_port;
for (int i = 0; i < params; i++) {
@ -189,16 +190,15 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) {
service.assign(param->value().c_str());
} else if (param->name() == "ota_port") {
ota_port = atoi(param->value().c_str());
} else if (param->name() == "ota_login") {
ota_login.assign(param->value().c_str());
} else if (param->name() == "ota_password") {
ota_password.assign(param->value().c_str());
} else if (param->name() == "firmware_name") {
firmware_name.assign(param->value().c_str());
}
}
// note: We're passing empty params by design, this is done to reset
// specific fields
projectConfig->setDeviceConfig(ota_password, ota_port, firmware_name,
true);
projectConfig->setDeviceConfig(ota_login, ota_password, ota_port, true);
projectConfig->setMDNSConfig(hostname, service, true);
request->send(200, MIMETYPE_JSON,
"{\"msg\":\"Done. Device Config has been set.\"}");
@ -329,6 +329,10 @@ void BaseAPI::restartCamera(AsyncWebServerRequest* request) {
"{\"msg\":\"Done. Camera had been restarted.\"}");
}
//*********************************************************************************************
//! General Command Functions
//*********************************************************************************************
void BaseAPI::ping(AsyncWebServerRequest* request) {
request->send(200, MIMETYPE_JSON, "{\"msg\": \"ok\" }");
}
@ -345,3 +349,129 @@ void BaseAPI::rssi(AsyncWebServerRequest* request) {
snprintf(_rssiBuffer, sizeof(_rssiBuffer), "{\"rssi\": %d }", rssi);
request->send(200, MIMETYPE_JSON, _rssiBuffer);
}
//*********************************************************************************************
//! OTA Command Functions
//*********************************************************************************************
void BaseAPI::checkAuthentication(AsyncWebServerRequest* request,
const char* login,
const char* password) {
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
if (_authRequired) {
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
log_i("Auth required");
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
if (!request->authenticate(login, password, NULL, false)) {
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
return request->requestAuthentication(NULL, false);
}
}
}
void BaseAPI::beginOTA() {
// NOTE: Code adapted from: https://github.com/ayushsharma82/AsyncElegantOTA/
auto device_config = projectConfig->getDeviceConfig();
auto mdns_config = projectConfig->getMDNSConfig();
if (device_config->OTAPassword.empty()) {
log_e(
"Password is empty, you need to provide a password in order to setup "
"the OTA server");
return;
}
log_i("[OTA Server]: Initializing OTA Server");
log_i(
"[OTA Server]: Navigate to http://%s.local:81/update to update the "
"firmware",
mdns_config->hostname.c_str());
log_d("[OTA Server]: Username: %s, Password: %s",
device_config->OTALogin.c_str(), device_config->OTAPassword.c_str());
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
const char* login = device_config->OTALogin.c_str();
const char* password = device_config->OTAPassword.c_str();
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
// Note: HTTP_GET
server.on(
"/update/identity", 0b00000001, [&](AsyncWebServerRequest* request) {
checkAuthentication(request, login, password);
String _id = String((uint32_t)ESP.getEfuseMac(), HEX);
_id.toUpperCase();
request->send(200, "application/json",
"{\"id\": \"" + _id + "\", \"hardware\": \"ESP32\"}");
});
// Note: HTT_GET
server.on("/update", 0b00000001, [&](AsyncWebServerRequest* request) {
log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap());
checkAuthentication(request, login, password);
// turn off the camera and stop the stream
esp_camera_deinit(); // deinitialize the camera driver
digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module
AsyncWebServerResponse* response = request->beginResponse_P(
200, "text/html", ELEGANT_HTML, ELEGANT_HTML_SIZE);
response->addHeader("Content-Encoding", "gzip");
request->send(response);
});
// Note: HTT_POST
server.on(
"/update", 0b00000010,
[&](AsyncWebServerRequest* request) {
checkAuthentication(request, login, password);
// the request handler is triggered after the upload has finished...
// create the response, add header, and send response
AsyncWebServerResponse* response = request->beginResponse(
(Update.hasError()) ? 500 : 200, "text/plain",
(Update.hasError()) ? "FAIL" : "OK");
response->addHeader("Connection", "close");
response->addHeader("Access-Control-Allow-Origin", "*");
request->send(response);
this->save(request);
},
[&](AsyncWebServerRequest* request, String filename, size_t index,
uint8_t* data, size_t len, bool final) {
// Upload handler chunks in data
checkAuthentication(request, login, password);
if (!index) {
if (!request->hasParam("MD5", true)) {
return request->send(400, "text/plain", "MD5 parameter missing");
}
if (!Update.setMD5(request->getParam("MD5", true)->value().c_str())) {
return request->send(400, "text/plain", "MD5 parameter invalid");
}
int cmd = (filename == "filesystem") ? U_SPIFFS : U_FLASH;
if (!Update.begin(UPDATE_SIZE_UNKNOWN,
cmd)) { // Start with max available size
Update.printError(Serial);
return request->send(400, "text/plain", "OTA could not begin");
}
}
// Write chunked data to the free sketch space
if (len) {
if (Update.write(data, len) != len) {
return request->send(400, "text/plain", "OTA could not begin");
}
}
if (final) { // if the final flag is set then this is the last frame of
// data
if (!Update.end(
true)) { // true to set the size to the current progress
Update.printError(Serial);
return request->send(400, "text/plain", "Could not end OTA");
}
} else {
return;
}
});
}

View File

@ -1,8 +1,10 @@
#ifndef BASEAPI_HPP
#define BASEAPI_HPP
#include <unordered_map>
#include <string>
#include <unordered_map>
#include <stdlib_noniso.h>
#define WEBSERVER_H
@ -15,102 +17,112 @@
#define XHTTP_OPTIONS 0b01000000;
#define XHTTP_ANY 0b01111111; */
//constexpr int HTTP_GET = 0b00000001;
// constexpr int HTTP_GET = 0b00000001;
constexpr int HTTP_ANY = 0b01111111;
#include <ESPAsyncWebServer.h>
#include <Update.h>
#include <esp_int_wdt.h>
#include <esp_task_wdt.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include "Hash.h"
#include "data/utilities/network_utilities.hpp"
#include "tasks/tasks.hpp"
#include "data/config/project_config.hpp"
#include "data/StateManager/StateManager.hpp"
#include "data/config/project_config.hpp"
#include "elegantWebpage.h"
#include "io/camera/cameraHandler.hpp"
class BaseAPI
{
protected:
std::string api_url;
class BaseAPI {
protected:
std::string api_url;
bool _authRequired;
static const char *MIMETYPE_HTML;
/* static const char *MIMETYPE_CSS; */
/* static const char *MIMETYPE_JS; */
/* static const char *MIMETYPE_PNG; */
/* static const char *MIMETYPE_JPG; */
/* static const char *MIMETYPE_ICO; */
static const char *MIMETYPE_JSON;
static const char* MIMETYPE_HTML;
/* static const char *MIMETYPE_CSS; */
/* static const char *MIMETYPE_JS; */
/* static const char *MIMETYPE_PNG; */
/* static const char *MIMETYPE_JPG; */
/* static const char *MIMETYPE_ICO; */
static const char* MIMETYPE_JSON;
protected:
/* Commands */
void setWiFi(AsyncWebServerRequest *request);
void setWiFiTXPower(AsyncWebServerRequest *request);
void getJsonConfig(AsyncWebServerRequest *request);
void factoryReset(AsyncWebServerRequest *request);
void setDeviceConfig(AsyncWebServerRequest *request);
void rebootDevice(AsyncWebServerRequest *request);
void ping(AsyncWebServerRequest *request);
void save(AsyncWebServerRequest *request);
void rssi(AsyncWebServerRequest *request);
protected:
/* Commands */
void setWiFi(AsyncWebServerRequest* request);
void setWiFiTXPower(AsyncWebServerRequest* request);
void getJsonConfig(AsyncWebServerRequest* request);
void factoryReset(AsyncWebServerRequest* request);
void setDeviceConfig(AsyncWebServerRequest* request);
void rebootDevice(AsyncWebServerRequest* request);
void ping(AsyncWebServerRequest* request);
void save(AsyncWebServerRequest* request);
void rssi(AsyncWebServerRequest* request);
/* Camera Handlers */
void setCamera(AsyncWebServerRequest *request);
void restartCamera(AsyncWebServerRequest *request);
/* Camera Handlers */
void setCamera(AsyncWebServerRequest* request);
void restartCamera(AsyncWebServerRequest* request);
/* Route Command types */
using route_method = void (BaseAPI::*)(AsyncWebServerRequest *);
typedef std::unordered_map<std::string, route_method> route_t;
typedef std::unordered_map<std::string, route_t> route_map_t;
/* Route Command types */
using route_method = void (BaseAPI::*)(AsyncWebServerRequest*);
typedef std::unordered_map<std::string, route_method> route_t;
typedef std::unordered_map<std::string, route_t> route_map_t;
route_t routes;
route_map_t route_map;
route_t routes;
route_map_t route_map;
std::unordered_map<int, std::string> _networkMethodsMap = {
{0b00000001, "GET"},
{0b00000010, "POST"},
{0b00001000, "PUT"},
{0b00000100, "DELETE"},
{0b00010000, "PATCH"},
{0b01000000, "OPTIONS"},
};
std::unordered_map<int, std::string> _networkMethodsMap = {
{0b00000001, "GET"}, {0b00000010, "POST"}, {0b00001000, "PUT"},
{0b00000100, "DELETE"}, {0b00010000, "PATCH"}, {0b01000000, "OPTIONS"},
};
enum RequestMethods
{
GET,
POST,
PUT,
DELETE,
PATCH,
OPTIONS,
};
enum RequestMethods {
GET,
POST,
PUT,
DELETE,
PATCH,
OPTIONS,
};
std::unordered_map<int, RequestMethods> _networkMethodsMap_enum = {
{0b00000001, GET},
{0b00000010, POST},
{0b00001000, PUT},
{0b00000100, DELETE},
{0b00010000, PATCH},
{0b01000000, OPTIONS},
};
std::unordered_map<int, RequestMethods> _networkMethodsMap_enum = {
{0b00000001, GET}, {0b00000010, POST}, {0b00001000, PUT},
{0b00000100, DELETE}, {0b00010000, PATCH}, {0b01000000, OPTIONS},
};
typedef std::unordered_map<std::string, WebRequestMethodComposite> networkMethodsMap_t;
typedef std::unordered_map<std::string, WebRequestMethodComposite>
networkMethodsMap_t;
ProjectConfig *projectConfig;
/// @brief Local instance of the AsyncWebServer - so that we dont need to use new and delete
AsyncWebServer server;
CameraHandler *camera;
StateManager<WiFiState_e> *wiFiStateManager;
ProjectConfig* projectConfig;
/// @brief Local instance of the AsyncWebServer - so that we dont need to use
/// new and delete
CameraHandler* camera;
StateManager<WiFiState_e>* wiFiStateManager;
AsyncWebServer server;
public:
BaseAPI(int CONTROL_PORT,
ProjectConfig *projectConfig,
CameraHandler *camera,
StateManager<WiFiState_e> *wiFiStateManager,
const std::string &api_url);
public:
BaseAPI(ProjectConfig* projectConfig,
CameraHandler* camera,
StateManager<WiFiState_e>* wiFiStateManager,
const std::string& api_url,
#ifndef SIM_ENABLED
int port = 81
#else
int port = 80
#endif
);
virtual ~BaseAPI();
virtual void begin();
void notFound(AsyncWebServerRequest *request) const;
virtual ~BaseAPI();
virtual void begin();
void checkAuthentication(AsyncWebServerRequest* request,
const char* login,
const char* password);
void beginOTA();
void notFound(AsyncWebServerRequest* request) const;
};
#endif // BASEAPI_HPP
#endif // BASEAPI_HPP

File diff suppressed because it is too large Load Diff

View File

@ -4,16 +4,15 @@
//! API Server
//*********************************************************************************************
APIServer::APIServer(int CONTROL_PORT,
ProjectConfig* projectConfig,
APIServer::APIServer(ProjectConfig* projectConfig,
CameraHandler* camera,
StateManager<WiFiState_e>* wiFiStateManager,
const std::string& api_url)
: BaseAPI(CONTROL_PORT, projectConfig, camera, wiFiStateManager, api_url) {}
: BaseAPI(projectConfig, camera, wiFiStateManager, api_url) {}
APIServer::~APIServer() {}
void APIServer::begin() {
void APIServer::setup() {
log_d("Initializing REST API Server");
this->setupServer();
BaseAPI::begin();
@ -23,8 +22,16 @@ void APIServer::begin() {
"^\\%s\\/([a-zA-Z0-9]+)\\/command\\/([a-zA-Z0-9]+)$",
this->api_url.c_str());
log_d("API URL: %s", buffer);
server.on(buffer, 0b01111111,
[&](AsyncWebServerRequest* request) { handleRequest(request); });
this->server.on(buffer, 0b01111111, [&](AsyncWebServerRequest* request) {
handleRequest(request);
});
// Note: Start OTA after all routes have been added
#ifndef SIM_ENABLED
//this->_authRequired = true;
#endif // SIM_ENABLED
this->beginOTA();
server.begin();
}

View File

@ -4,23 +4,25 @@
#include "network/api/baseAPI/baseAPI.hpp"
class APIServer : public BaseAPI
{
public:
APIServer(int CONTROL_PORT,
ProjectConfig *projectConfig,
CameraHandler *camera,
StateManager<WiFiState_e> *wiFiStateManager,
const std::string &api_url);
class APIServer : public BaseAPI {
public:
APIServer(ProjectConfig* projectConfig,
CameraHandler* camera,
StateManager<WiFiState_e>* wiFiStateManager,
const std::string& api_url);
virtual ~APIServer();
void begin();
void setupServer();
void findParam(AsyncWebServerRequest *request, const char *param, std::string &value);
void addRouteMap(const std::string &index, route_t route, std::vector<std::string> &indexes);
void handleRequest(AsyncWebServerRequest *request);
virtual ~APIServer();
void setup();
void setupServer();
void findParam(AsyncWebServerRequest* request,
const char* param,
std::string& value);
void addRouteMap(const std::string& index,
route_t route,
std::vector<std::string>& indexes);
void handleRequest(AsyncWebServerRequest* request);
public:
std::vector<std::string> indexes;
public:
std::vector<std::string> indexes;
};
#endif // WEBSERVERHANDLER_HPP
#endif // WEBSERVERHANDLER_HPP

View File

@ -1,9 +1,11 @@
#include "tasks.hpp"
void OpenIrisTasks::ScheduleRestart(int milliseconds) {
int initialTime = millis();
yield();
int initialTime = millis();
while (millis() - initialTime <= milliseconds) {
continue;
}
yield();
ESP.restart();
}
}

View File

@ -6,21 +6,18 @@
#include <network/mDNS/MDNSManager.hpp>
#include <network/stream/streamServer.hpp>
#if ENABLE_OTA
#include <network/OTA/OTA.hpp>
#endif // ENABLE_OTA
#include <data/config/project_config.hpp>
#include <logo/logo.hpp>
int STREAM_SERVER_PORT = 80;
int CONTROL_SERVER_PORT = 81;
/**
* @brief ProjectConfig object
* @brief This is the main configuration object for the project
* @param name The name of the project config partition
* @param mdnsName The mDNS hostname to use
*/
ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME);
#if ENABLE_OTA
OTA ota(&deviceConfig);
#endif // ENABLE_OTA
LEDManager ledManager(33, &ledStateManager);
#ifndef SIM_ENABLED
@ -32,11 +29,15 @@ WiFiHandler wifiHandler(&deviceConfig,
WIFI_SSID,
WIFI_PASSWORD,
WIFI_CHANNEL);
APIServer apiServer(CONTROL_SERVER_PORT,
&deviceConfig,
#ifndef SIM_ENABLED
APIServer apiServer(&deviceConfig,
&cameraHandler,
&wifiStateManager,
"/control");
#else
APIServer apiServer(&deviceConfig, NULL, &wifiStateManager, "/control");
#endif // SIM_ENABLED
MDNSHandler mdnsHandler(&mdnsStateManager, &deviceConfig);
#ifndef SIM_ENABLED
@ -66,20 +67,20 @@ void setup() {
}
case WiFiState_e::WiFiState_ADHOC: {
#ifndef SIM_ENABLED
streamServer.startStreamServer();
log_d("[SETUP]: Starting Stream Server");
streamServer.startStreamServer();
#endif // SIM_ENABLED
apiServer.begin();
log_d("[SETUP]: Starting API Server");
apiServer.setup();
break;
}
case WiFiState_e::WiFiState_Connected: {
#ifndef SIM_ENABLED
streamServer.startStreamServer();
log_d("[SETUP]: Starting Stream Server");
streamServer.startStreamServer();
#endif // SIM_ENABLED
apiServer.begin();
log_d("[SETUP]: Starting API Server");
apiServer.setup();
break;
}
case WiFiState_e::WiFiState_Connecting: {
@ -91,14 +92,8 @@ void setup() {
break;
}
}
#if ENABLE_OTA
ota.begin();
#endif // ENABLE_OTA
}
void loop() {
#if ENABLE_OTA
ota.handleOTAUpdate();
#endif // ENABLE_OTA
ledManager.handleLED();
}

View File

@ -1,7 +1,7 @@
[wokwi]
version = 1
elf = ".pio/build/esp32AIThinker_sim/OpenIris-v1.9.4-esp32AIThinker_sim-b4d56af-master.elf"
firmware = ".pio/build/esp32AIThinker_sim/OpenIris-v1.9.4-esp32AIThinker_sim-b4d56af-master.bin"
elf = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-44d44d5-feautre-elegant-ota.elf"
firmware = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-44d44d5-feautre-elegant-ota.bin"
[[net.forward]]
from = "localhost:8180"
to = "target:80"