mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-09-26 23:29:14 +08:00
feat: Initial implementation of Elegant OTA with support for API server (#41)
--------- Co-authored-by: ZanzyTHEbar <pyr0ndet0s97@gmail.com>
This commit is contained in:
parent
926134abc1
commit
58989b4567
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -13,7 +13,7 @@ enableadhoc = 0
|
||||
adhocchannel = 1
|
||||
|
||||
[ota]
|
||||
enableota = 1
|
||||
otaserverip = "openiristracker.local"
|
||||
otalogin = "openiris"
|
||||
otapassword = "12345678"
|
||||
otaserverport = 3232
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
17
ESP/lib/src/network/api/baseAPI/Hash.h
Normal file
17
ESP/lib/src/network/api/baseAPI/Hash.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
1801
ESP/lib/src/network/api/baseAPI/elegantWebpage.h
Normal file
1801
ESP/lib/src/network/api/baseAPI/elegantWebpage.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user