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

@@ -74,7 +74,7 @@ def postbuild(source, target, env):
uploadfiles = uploadparts[-6:] uploadfiles = uploadparts[-6:]
for i in range(1, len(uploadfiles), 2): for i in range(1, len(uploadfiles), 2):
if not os.path.isfile(uploadfiles[i]): if not os.path.isfile(uploadfiles[i]):
print("file %s for combine not found"%uploadfiles[i]) print("file {} for combine not found".format(uploadfiles[i]))
return return
offset = uploadfiles[0] offset = uploadfiles[0]

View File

@@ -1,9 +1,95 @@
#pragma once #pragma once
#include <Preferences.h>
#include <ArduinoJson.h>
#include <variant>
#include <map>
#include "main.h"
enum class ConfigType {
BYTE,
SHORT,
INT,
BOOL,
FLOAT,
CHAR,
STRING
};
using ConfigValue = std::variant<
uint8_t, // BYTE unsigned
int16_t, // SHORT
int32_t, // INT
bool, // BOOL
float, // FLOAT
char, // CHAR
String // STRING
>;
struct ConfigDef {
const char* key;
ConfigType type;
ConfigValue defval;
};
// keys have to match names in config.json
static const ConfigDef configdefs[] = {
{"systemName", ConfigType::STRING, String("OBPkp61")},
{"systemMode", ConfigType::CHAR, 'K'},
{"logLevel", ConfigType::BYTE, uint8_t(0)},
{"adminPassword", ConfigType::STRING, String("obpkp61")},
{"useAdminPass", ConfigType::BOOL, true},
{"apEnable", ConfigType::BOOL, true},
{"apPassword", ConfigType::STRING, String("obpkp61")},
{"apIp", ConfigType::STRING, String("192.168.15.1")},
{"apMask", ConfigType::STRING, String("255.255.255.0")},
{"stopApTime", ConfigType::SHORT, int16_t(0)},
{"cpuSpeed", ConfigType::SHORT, int16_t(160)},
{"ledBrightness", ConfigType::BYTE, uint8_t(96)},
{"ledRgbBrightness", ConfigType::BYTE, uint8_t(96)},
{"tempFormat", ConfigType::CHAR, 'C'},
{"switchBank", ConfigType::BYTE, uint8_t(0)},
{"key1", ConfigType::BYTE, uint8_t(1)},
{"key1long", ConfigType::BYTE, uint8_t(11)},
{"key2", ConfigType::BYTE, uint8_t(2)},
{"key2long", ConfigType::BYTE, uint8_t(12)},
{"key3", ConfigType::BYTE, uint8_t(3)},
{"key3long", ConfigType::BYTE, uint8_t(13)},
{"key4", ConfigType::BYTE, uint8_t(4)},
{"key4long", ConfigType::BYTE, uint8_t(14)},
{"key5", ConfigType::BYTE, uint8_t(5)},
{"key5long", ConfigType::BYTE, uint8_t(15)},
{"key6", ConfigType::BYTE, uint8_t(6)},
{"key6long", ConfigType::BYTE, uint8_t(16)},
{"n2kDestA", ConfigType::STRING, String("")},
{"n2kDestB", ConfigType::STRING, String("")},
{"n2kDestC", ConfigType::STRING, String("")},
{"envInterval", ConfigType::SHORT, int16_t(5000)},
// no user access
{"LastNodeId", ConfigType::BYTE, uint8_t(N2K_DEFAULT_NODEID)}
};
constexpr size_t configdefsCount = sizeof(configdefs) / sizeof(configdefs[0]);
class Config { class Config {
private: private:
Preferences *prefs; Preferences& prefs;
std::map<String, ConfigValue> values;
std::map<String, const ConfigDef*> defs;
public: public:
Config(); explicit Config(Preferences& prefs);
bool loadConfig(); void load();
bool save(JsonObject json);
void dump();
template<typename T>
T get(const char* key) const;
uint8_t getByte(const char* key) const;
int16_t getShort(const char* key) const;
int32_t getInt(const char* key) const;
bool getBool(const char* key) const;
float getFloat(const char* key) const;
char getChar(const char* key) const;
const String& getString(const char* key) const;
}; };

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include <Arduino.h>
#define STRINGIFY_IMPL(x) #x #define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x) #define STRINGIFY(x) STRINGIFY_IMPL(x)
@@ -87,14 +88,24 @@
#define BUTTON_6 5 #define BUTTON_6 5
#define BUTTON_DST 6 #define BUTTON_DST 6
enum class ButtonPressType : uint8_t enum class ButtonPressType : uint8_t {
{
SHORT, // < 1 second SHORT, // < 1 second
MEDIUM, // >= 1 second and < 3 seconds MEDIUM, // >= 1 second and < 3 seconds
LONG // >= 3 seconds LONG // >= 3 seconds
}; };
struct ButtonEvent
{ struct ButtonEvent {
uint8_t buttonId; uint8_t buttonId;
ButtonPressType pressType; ButtonPressType pressType;
}; };
extern uint64_t chipid;
extern uint8_t led_brightness;
extern uint8_t rgb_brightness;
extern uint8_t keycode[6];
extern uint8_t longcode[6];
extern float temp;
extern float hum;
String uptime_with_unit();

11
include/webserver.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
extern AsyncWebServer server;
void webserver_init();
void send_embedded_file(String name, AsyncWebServerRequest *request);

View File

@@ -36,7 +36,7 @@ build_flags =
-DBOARD_HAS_PSRAM -DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DCORE_DEBUG_LEVEL=4 # Max. possible loglevel: 0=None, 1=Error, 2=Warning, 3=Info, 4=Debug, 5=Verbose -DCORE_DEBUG_LEVEL=4 # Max. possible loglevel: 0=None, 1=Error, 2=Warning, 3=Info, 4=Debug, 5=Verbose
-DCONFIG_LOG_TX_BUF_SIZE=4096 -DCONFIG_LOG_TX_BUF_SIZE=8192
-std=gnu++17 -std=gnu++17
build_unflags = build_unflags =
-std=gnu++11 -std=gnu++11

View File

@@ -1,12 +1,284 @@
#include <Preferences.h>
#include "main.h"
#include "config.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; 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 "main.h"
#include <Preferences.h> #include "config.h"
#include "webserver.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <WiFi.h> #include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Update.h>
#include <Wire.h> #include <Wire.h>
#include <SHT31.h> // temp. sensor #include <SHT31.h> // temp. sensor
#include <NMEA2000.h> #include <NMEA2000.h>
@@ -65,50 +62,17 @@ String get_sha256(String payload) {
} }
// Logging // Logging
static const char* TAG = "main.cpp"; static const char* TAG = "MAIN";
Preferences preferences; // persistent storage for configuration Preferences preferences; // persistent storage for configuration
Config config(preferences); // configuration object
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");
}
}
uint64_t chipid = ESP.getEfuseMac(); uint64_t chipid = ESP.getEfuseMac();
const char* wifi_ssid = "OBPKP61"; const char* wifi_ssid = "OBPKP61";
const char* wifi_pass = "keypad61"; const char* wifi_pass = "keypad61";
bool apEnabled = false; bool apEnabled = false;
AsyncWebServer server(80); // AsyncWebServer server(80);
unsigned long firstStart = 0; unsigned long firstStart = 0;
unsigned long lastSensor = 0; unsigned long lastSensor = 0;
@@ -316,24 +280,9 @@ void cpuFreqTimerCallback(TimerHandle_t xTimer) {
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
// while (!Serial) delay(10); verhindert Booten ohne USB-Verbindung while (!Serial) delay(10); // verhindert Booten ohne USB-Verbindung
delay(500); 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 // Configure I/O pins
// internal user led (red) // internal user led (red)
@@ -381,6 +330,22 @@ void setup() {
ESP_LOGI(TAG, "Starting ..."); 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(); esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_EXT0) { if (cause == ESP_SLEEP_WAKEUP_EXT0) {
ESP_LOGI(TAG, " Wake up by key"); ESP_LOGI(TAG, " Wake up by key");
@@ -391,15 +356,21 @@ void setup() {
// N2K basics // N2K basics
nodeid = N2K_DEFAULT_NODEID; nodeid = N2K_DEFAULT_NODEID;
ESP_LOGI(TAG, "N2K default node is %d", 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); 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(); preferences.end();
ESP_LOGI(TAG, "N2K node id set to %d from preferences", nodeid); 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 // Setup webserver
WiFi.persistent(false); WiFi.persistent(false);
WiFi.mode(WIFI_MODE_AP); WiFi.mode(WIFI_MODE_AP);
@@ -423,32 +394,9 @@ void setup() {
WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet); WiFi.softAPConfig(ap_addr, ap_gateway, ap_subnet);
apEnabled = true; apEnabled = true;
// Route for root / web page // Initialize WebGUI
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { webserver_init();
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/devicelist", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/api/devicelist", HTTP_GET, [](AsyncWebServerRequest *request){
// NMEA2000 device list // NMEA2000 device list
AsyncResponseStream *response = request->beginResponseStream("application/json"); AsyncResponseStream *response = request->beginResponseStream("application/json");
@@ -474,116 +422,8 @@ void setup() {
response->print("]"); response->print("]");
request->send(response); 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(); server.begin();
// NMEA2000 configuration // 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);
}
}
});
}