Configuration and code improvements

This commit is contained in:
2026-01-26 20:06:05 +01:00
parent af98a52fd3
commit 107339b3d3
8 changed files with 623 additions and 213 deletions

View File

@@ -1,12 +1,284 @@
#include <Preferences.h>
#include "main.h"
#include "config.h"
Config::Config() {
// Logging
static const char* TAG = "CFG";
Config::Config(Preferences& prefs)
: prefs(prefs) {
// init defs mapping
for (size_t i = 0; i < configdefsCount; ++i) {
defs[configdefs[i].key] = &configdefs[i];
}
}
bool Config::loadConfig() {
prefs->begin(PREF_NAME);
prefs->end();
void Config::load() {
ESP_LOGI(TAG, "Loading configuration");
prefs.begin(PREF_NAME, true);
for (const auto& def : configdefs) {
if (prefs.isKey(def.key)) {
ESP_LOGI(TAG, "Config option '%s' loaded from NVS", def.key);
switch (def.type) {
case ConfigType::BYTE:
values[def.key] = (uint8_t)prefs.getUChar(def.key, std::get<uint8_t>(def.defval));
break;
case ConfigType::SHORT:
values[def.key] = (int16_t)prefs.getShort(def.key, std::get<int16_t>(def.defval));
break;
case ConfigType::INT:
values[def.key] = prefs.getInt(def.key, std::get<int32_t>(def.defval));
break;
case ConfigType::BOOL:
values[def.key] = prefs.getBool(def.key, std::get<bool>(def.defval));
break;
case ConfigType::FLOAT:
values[def.key] = prefs.getFloat(def.key, std::get<float>(def.defval));
break;
case ConfigType::CHAR:
values[def.key] = (char)prefs.getChar(def.key, std::get<char>(def.defval));
break;
case ConfigType::STRING:
values[def.key] = prefs.getString(def.key, std::get<String>(def.defval));
break;
}
} else {
ESP_LOGI(TAG, "Using default for '%s'", def.key);
switch (def.type) {
case ConfigType::BYTE:
values[def.key] = std::get<uint8_t>(def.defval);
break;
case ConfigType::SHORT:
values[def.key] = std::get<int16_t>(def.defval);
break;
case ConfigType::INT:
values[def.key] = std::get<int32_t>(def.defval);
break;
case ConfigType::BOOL:
values[def.key] = std::get<bool>(def.defval);
break;
case ConfigType::FLOAT:
values[def.key] = std::get<float>(def.defval);
break;
case ConfigType::CHAR:
values[def.key] = std::get<char>(def.defval);
break;
case ConfigType::STRING:
values[def.key] = std::get<String>(def.defval);
break;
}
}
}
prefs.end();
}
bool Config::save(JsonObject json) {
// TODO Not fail if one single value is wrong?
prefs.begin(PREF_NAME, false);
int errors = 0;
for (JsonPair kv : json) {
const char* key = kv.key().c_str();
JsonVariant v = kv.value();
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Unexpected missing key: %s", key);
return false;
}
auto itdef = defs.find(key);
if (itdef == defs.end()) {
ESP_LOGE(TAG, "Unexpected missing defs key: %s", key);
return false;
}
// const ConfigDef* def = itdef->second;
switch (itdef->second->type) {
case ConfigType::BYTE: {
if (!v.is<uint8_t>()) {
errors++;
break;
}
uint8_t newval = v.as<uint8_t>();
uint8_t *curval = std::get_if<uint8_t>(&values[key]);
if (newval != *curval) {
values[key] = newval;
//prefs.putUChar(key, newval);
ESP_LOGI(TAG, "changing %s, replacing %d with %d", key, *curval, newval);
}
break;
}
/* case ConfigType::SHORT:
if (!v.is<int16_t>()) return false;
values[key] = v.as<int16_t>();
prefs.putShort(key, v.as<int16_t>());
break;
case ConfigType::INT:
if (!v.is<int32_t>()) return false;
values[key] = v.as<int32_t>();
prefs.putInt(key, v.as<int32_t>());
break;
case ConfigType::BOOL:
if (!v.is<bool>()) return false;
values[key] = v.as<bool>();
prefs.putBool(key, v.as<bool>());
break;
case ConfigType::FLOAT:
if (!v.is<float>()) return false;
values[key] = v.as<float>();
prefs.putFloat(key, v.as<float>());
break;
case ConfigType::CHAR:
if (!v.is<const char*>()) return false;
values[key] = v.as<const char*>()[0];
prefs.putChar(key, v.as<const char*>()[0]);
break;
case ConfigType::STRING:
if (!v.is<const char*>()) return false;
values[key] = String(v.as<const char*>());
prefs.putString(key, v.as<const char*>());
break; */
default:
ESP_LOGE("CFG", "Unsupported type for key %s", key);
}
}
prefs.end();
return true;
}
void Config::dump() {
// only for debugging purposes,
ESP_LOGI(TAG, "========== Config options ==========");
for (const auto& def : configdefs) {
auto it = values.find(def.key);
if (it == values.end()) {
ESP_LOGW(TAG, "%s = <missing>", def.key);
continue;
}
const ConfigValue& value = it->second;
switch (def.type) {
case ConfigType::BYTE:
if (auto v = std::get_if<uint8_t>(&value))
ESP_LOGI(TAG, "%s = %u", def.key, *v);
break;
case ConfigType::SHORT:
if (auto v = std::get_if<int16_t>(&value))
ESP_LOGI(TAG, "%s = %d", def.key, *v);
break;
case ConfigType::INT:
if (auto v = std::get_if<int32_t>(&value))
ESP_LOGI(TAG, "%s = %d", def.key, *v);
break;
case ConfigType::BOOL:
if (auto v = std::get_if<bool>(&value))
ESP_LOGI(TAG, "%s = %s", def.key, *v ? "true" : "false");
break;
case ConfigType::FLOAT:
if (auto v = std::get_if<float>(&value))
ESP_LOGI(TAG, "%s = %.3f", def.key, *v);
break;
case ConfigType::CHAR:
if (auto v = std::get_if<char>(&value))
ESP_LOGI(TAG, "%s = '%c'", def.key, *v);
break;
case ConfigType::STRING:
if (auto v = std::get_if<String>(&value))
ESP_LOGI(TAG, "%s = \"%s\"", def.key, v->c_str());
break;
}
}
ESP_LOGI(TAG, "====================================");
}
template<typename T>
T Config::get(const char* key) const {
return std::get<T>(values.at(key));
}
uint8_t Config::getByte(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<uint8_t>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
int16_t Config::getShort(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<int16_t>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
int32_t Config::getInt(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<int32_t>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
bool Config::getBool(const char* key) const {
auto it = values.find(key);
if (it == values.end())
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
if (auto v = std::get_if<bool>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
float Config::getFloat(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<float>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
char Config::getChar(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<char>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}
const String& Config::getString(const char* key) const {
auto it = values.find(key);
if (it == values.end()) {
ESP_LOGE(TAG, "Missing config key: %s", key);
abort();
}
if (auto v = std::get_if<String>(&it->second))
return *v;
ESP_LOGE(TAG, "Type mismatch for key: %s", key);
abort();
}

View File

@@ -1,11 +1,8 @@
#include <Arduino.h>
#include "main.h"
#include <Preferences.h>
#include "config.h"
#include "webserver.h"
#include <ArduinoJson.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Update.h>
#include <Wire.h>
#include <SHT31.h> // temp. sensor
#include <NMEA2000.h>
@@ -65,50 +62,17 @@ String get_sha256(String payload) {
}
// Logging
static const char* TAG = "main.cpp";
static const char* TAG = "MAIN";
Preferences preferences; // persistent storage for configuration
class EmbeddedFile;
static std::map<String, EmbeddedFile*> embeddedFiles;
class EmbeddedFile {
public:
const uint8_t *start;
int len;
String contentType;
EmbeddedFile(String name, String contentType, const uint8_t *start, int len) {
this->start = start;
this->len = len;
this->contentType = contentType;
embeddedFiles[name] = this;
}
} ;
#define EMBED_GZ_FILE(fileName, binName, contentType) \
extern const uint8_t binName##_File[] asm("_binary_" #binName "_start"); \
extern const uint8_t binName##_FileLen[] asm("_binary_" #binName "_size"); \
const EmbeddedFile binName##_Config(fileName,contentType,(const uint8_t*)binName##_File,(int)binName##_FileLen);
#include "embeddedfiles.h"
void send_embedded_file(String name, AsyncWebServerRequest *request)
{
std::map<String, EmbeddedFile*>::iterator it = embeddedFiles.find(name);
if (it != embeddedFiles.end()) {
EmbeddedFile* found = it->second;
AsyncWebServerResponse *response = request->beginResponse(200, found->contentType, found->start, found->len);
response->addHeader(F("Content-Encoding"), F("gzip"));
request->send(response);
} else {
request->send(404, "text/plain", "Not found");
}
}
Preferences preferences; // persistent storage for configuration
Config config(preferences); // configuration object
uint64_t chipid = ESP.getEfuseMac();
const char* wifi_ssid = "OBPKP61";
const char* wifi_pass = "keypad61";
bool apEnabled = false;
AsyncWebServer server(80);
// AsyncWebServer server(80);
unsigned long firstStart = 0;
unsigned long lastSensor = 0;
@@ -316,24 +280,9 @@ void cpuFreqTimerCallback(TimerHandle_t xTimer) {
void setup() {
Serial.begin(115200);
// while (!Serial) delay(10); verhindert Booten ohne USB-Verbindung
while (!Serial) delay(10); // verhindert Booten ohne USB-Verbindung
delay(500);
preferences.begin("nvs", false);
keycode[0] = preferences.getInt("key1", 1);
keycode[1] = preferences.getInt("key2", 2);
keycode[2] = preferences.getInt("key3", 3);
keycode[3] = preferences.getInt("key4", 4);
keycode[4] = preferences.getInt("key5", 5);
keycode[5] = preferences.getInt("key6", 6);
longcode[0] = preferences.getInt("key1long", 11);
longcode[1] = preferences.getInt("key2long", 12);
longcode[2] = preferences.getInt("key3long", 13);
longcode[3] = preferences.getInt("key4long", 14);
longcode[4] = preferences.getInt("key5long", 15);
longcode[5] = preferences.getInt("key6long", 16);
preferences.end();
// Configure I/O pins
// internal user led (red)
@@ -381,6 +330,22 @@ void setup() {
ESP_LOGI(TAG, "Starting ...");
config.load();
config.dump();
keycode[0] = config.getByte("key1");
keycode[1] = config.getByte("key2");
keycode[2] = config.getByte("key3");
keycode[3] = config.getByte("key4");
keycode[4] = config.getByte("key5");
keycode[5] = config.getByte("key6");
longcode[0] = config.getByte("key1long");
longcode[1] = config.getByte("key2long");
longcode[2] = config.getByte("key3long");
longcode[3] = config.getByte("key4long");
longcode[4] = config.getByte("key5long");
longcode[5] = config.getByte("key6long");
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
ESP_LOGI(TAG, " Wake up by key");
@@ -391,15 +356,21 @@ void setup() {
// N2K basics
nodeid = N2K_DEFAULT_NODEID;
ESP_LOGI(TAG, "N2K default node is %d", nodeid);
preferences.begin("nvs", false);
preferences.begin(PREF_NAME, false);
nodeid = preferences.getInt("LastNodeId", N2K_DEFAULT_NODEID);
cpuspeed = preferences.getInt("cpuSpeed", 160);
int apstoptime = preferences.getInt("stopApTime", 0);
String apip = preferences.getString("apIp", "192.168.15.1");
String apmask = preferences.getString("apMask", "255.255.255.0");
preferences.end();
ESP_LOGI(TAG, "N2K node id set to %d from preferences", nodeid);
//cpuspeed = preferences.getInt("cpuSpeed", 160);
cpuspeed = config.getShort("cpuSpeed");
//int apstoptime = preferences.getInt("stopApTime", 0);
int16_t apstoptime = config.getShort("stopApTime");
//String apip = preferences.getString("apIp", "192.168.15.1");
//String apmask = preferences.getString("apMask", "255.255.255.0");
String apip = config.getString("apIp");
String apmask = config.getString("apMask");
//preferences.end();
// Setup webserver
WiFi.persistent(false);
WiFi.mode(WIFI_MODE_AP);
@@ -423,32 +394,9 @@ void setup() {
WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet);
apEnabled = true;
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
send_embedded_file("index.html", request);
});
// Route for all other defined pages
for (auto it = embeddedFiles.begin(); it != embeddedFiles.end(); it++) {
String uri = String("/") + it->first;
server.on(uri.c_str(), HTTP_GET, [it](AsyncWebServerRequest *request) {
send_embedded_file(it->first, request);
});
}
// API fast hack
server.on("/api/capabilities", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["apPwChange"] = "true";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/checkpass", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
// Initialize WebGUI
webserver_init();
server.on("/api/devicelist", HTTP_GET, [](AsyncWebServerRequest *request){
// NMEA2000 device list
AsyncResponseStream *response = request->beginResponseStream("application/json");
@@ -474,116 +422,8 @@ void setup() {
response->print("]");
request->send(response);
});
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<512> doc;
doc["systemName"] = "Keypad1";
doc["logLevel"] = 0;
doc["version"] = VERSION;
doc["fwtype"] = "unknown";
doc["salt"] = "secret";
doc["AdminPassword"] = "********";
doc["useAdminPass"] = false;
doc["apEnable"] = true;
doc["apIp"] = "192.168.15.1";
doc["apMask"] = "255.255.255.0";
doc["apPassword"] = "********";
doc["stopApTime"] = 0;
doc["cpuSpeed"] = 160;
doc["tempFormat"] = "C";
doc["ledBrightness"] = led_brightness;
doc["ledRgbBrightness"] = rgb_brightness;
doc["tempFormat"] = "C";
doc["switchBank"] = 0;
doc["key1"] = keycode[BUTTON_1];
doc["key2"] = keycode[BUTTON_2];
doc["key3"] = keycode[BUTTON_3];
doc["key4"] = keycode[BUTTON_4];
doc["key5"] = keycode[BUTTON_5];
doc["key6"] = keycode[BUTTON_6];
doc["key1long"] = longcode[BUTTON_1];
doc["key2long"] = longcode[BUTTON_2];
doc["key3long"] = longcode[BUTTON_3];
doc["key4long"] = longcode[BUTTON_4];
doc["key5long"] = longcode[BUTTON_5];
doc["key6long"] = longcode[BUTTON_6];
doc["envInterval"] = 5;
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/resetconfig", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<200> doc;
doc["version"] = VERSION;
int cpu_freq = esp_clk_cpu_freq() / 1000000;
doc["cpuspeed"] = String(cpu_freq) + "MHz";
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
doc["chipid"] = String(ssid);
doc["uptime"] = uptime_with_unit();
doc["heap"]=(long)xPortGetFreeHeapSize();
doc["temp"] = String(temp, 1);
doc["hum"] = String(hum, 1);
doc["status"] = "OK";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/fwinfo", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<200> doc;
doc["version"] = VERSION;
doc["build_date"] = BUILD_DATE;
doc["build_time"] = BUILD_TIME;
});
server.on("/api/setconfig", HTTP_POST, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/update", HTTP_POST, [](AsyncWebServerRequest *request){
// the request handler is triggered after the upload has finished...
// create the response, add header, and send response
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
}, [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
// this is the new image upload part
Serial.print("Retrieving firmware image named: ");
Serial.println(filename);
if (index == 0) {
if (! Update.begin(UPDATE_SIZE_UNKNOWN)) {
Update.printError(Serial);
}
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
}
if (final) {
if (!Update.end(true)) {
Update.printError(Serial);
}
}
});
// TODO POST vom Client entgegennehmen
// Start HTTP Webserver
server.begin();
// NMEA2000 configuration

190
src/webserver.cpp Normal file
View File

@@ -0,0 +1,190 @@
#include "main.h"
#include "webserver.h"
#include <map>
#include <AsyncTCP.h>
#include <ArduinoJson.h>
#include <esp32/clk.h> // for cpu frequency
#include <Update.h>
AsyncWebServer server(80);
class EmbeddedFile;
static std::map<String, EmbeddedFile*> embeddedFiles;
class EmbeddedFile {
public:
const uint8_t *start;
int len;
String contentType;
EmbeddedFile(String name, String contentType, const uint8_t *start, int len) {
this->start = start;
this->len = len;
this->contentType = contentType;
embeddedFiles[name] = this;
}
};
#define EMBED_GZ_FILE(fileName, binName, contentType) \
extern const uint8_t binName##_File[] asm("_binary_" #binName "_start"); \
extern const uint8_t binName##_FileLen[] asm("_binary_" #binName "_size"); \
const EmbeddedFile binName##_Config(fileName,contentType,(const uint8_t*)binName##_File,(int)binName##_FileLen);
#include "embeddedfiles.h"
void send_embedded_file(String name, AsyncWebServerRequest *request)
{
std::map<String, EmbeddedFile*>::iterator it = embeddedFiles.find(name);
if (it != embeddedFiles.end()) {
EmbeddedFile* found = it->second;
AsyncWebServerResponse *response = request->beginResponse(200, found->contentType, found->start, found->len);
response->addHeader(F("Content-Encoding"), F("gzip"));
request->send(response);
} else {
request->send(404, "text/plain", "Not found");
}
}
void webserver_init() {
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
send_embedded_file("index.html", request);
});
// Route for all other defined pages
for (auto it = embeddedFiles.begin(); it != embeddedFiles.end(); it++) {
String uri = String("/") + it->first;
server.on(uri.c_str(), HTTP_GET, [it](AsyncWebServerRequest *request) {
send_embedded_file(it->first, request);
});
}
// API fast hack
server.on("/api/capabilities", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["apPwChange"] = "true";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/checkpass", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<512> doc;
doc["systemName"] = "Keypad1";
doc["logLevel"] = 0;
doc["version"] = VERSION;
doc["fwtype"] = "unknown";
doc["salt"] = "secret";
doc["AdminPassword"] = "********";
doc["useAdminPass"] = false;
doc["apEnable"] = true;
doc["apIp"] = "192.168.15.1";
doc["apMask"] = "255.255.255.0";
doc["apPassword"] = "********";
doc["stopApTime"] = 0;
doc["cpuSpeed"] = 160;
doc["tempFormat"] = "C";
doc["ledBrightness"] = led_brightness;
doc["ledRgbBrightness"] = rgb_brightness;
doc["tempFormat"] = "C";
doc["switchBank"] = 0;
doc["key1"] = keycode[BUTTON_1];
doc["key2"] = keycode[BUTTON_2];
doc["key3"] = keycode[BUTTON_3];
doc["key4"] = keycode[BUTTON_4];
doc["key5"] = keycode[BUTTON_5];
doc["key6"] = keycode[BUTTON_6];
doc["key1long"] = longcode[BUTTON_1];
doc["key2long"] = longcode[BUTTON_2];
doc["key3long"] = longcode[BUTTON_3];
doc["key4long"] = longcode[BUTTON_4];
doc["key5long"] = longcode[BUTTON_5];
doc["key6long"] = longcode[BUTTON_6];
doc["envInterval"] = 5;
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/resetconfig", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<200> doc;
doc["version"] = VERSION;
int cpu_freq = esp_clk_cpu_freq() / 1000000;
doc["cpuspeed"] = String(cpu_freq) + "MHz";
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
doc["chipid"] = String(ssid);
doc["uptime"] = uptime_with_unit();
doc["heap"]=(long)xPortGetFreeHeapSize();
doc["temp"] = String(temp, 1);
doc["hum"] = String(hum, 1);
doc["status"] = "OK";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/fwinfo", HTTP_GET, [](AsyncWebServerRequest *request){
StaticJsonDocument<200> doc;
doc["version"] = VERSION;
doc["build_date"] = BUILD_DATE;
doc["build_time"] = BUILD_TIME;
});
server.on("/api/setconfig", HTTP_POST, [](AsyncWebServerRequest *request){
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
});
server.on("/api/update", HTTP_POST, [](AsyncWebServerRequest *request){
// the request handler is triggered after the upload has finished...
// create the response, add header, and send response
StaticJsonDocument<100> doc;
doc["status"] = "FAILED";
String out;
serializeJson(doc, out);
request->send(200, "application/json", out);
}, [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
// this is the new image upload part
Serial.print("Retrieving firmware image named: ");
Serial.println(filename);
if (index == 0) {
if (! Update.begin(UPDATE_SIZE_UNKNOWN)) {
Update.printError(Serial);
}
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
}
if (final) {
if (!Update.end(true)) {
Update.printError(Serial);
}
}
});
}