Configuration and code improvements
This commit is contained in:
@@ -74,7 +74,7 @@ def postbuild(source, target, env):
|
||||
uploadfiles = uploadparts[-6:]
|
||||
for i in range(1, len(uploadfiles), 2):
|
||||
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
|
||||
offset = uploadfiles[0]
|
||||
|
||||
|
||||
@@ -1,9 +1,95 @@
|
||||
#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 {
|
||||
private:
|
||||
Preferences *prefs;
|
||||
Preferences& prefs;
|
||||
std::map<String, ConfigValue> values;
|
||||
std::map<String, const ConfigDef*> defs;
|
||||
public:
|
||||
Config();
|
||||
bool loadConfig();
|
||||
explicit Config(Preferences& prefs);
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
#define STRINGIFY_IMPL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_IMPL(x)
|
||||
@@ -87,14 +88,24 @@
|
||||
#define BUTTON_6 5
|
||||
#define BUTTON_DST 6
|
||||
|
||||
enum class ButtonPressType : uint8_t
|
||||
{
|
||||
enum class ButtonPressType : uint8_t {
|
||||
SHORT, // < 1 second
|
||||
MEDIUM, // >= 1 second and < 3 seconds
|
||||
LONG // >= 3 seconds
|
||||
};
|
||||
struct ButtonEvent
|
||||
{
|
||||
|
||||
struct ButtonEvent {
|
||||
uint8_t buttonId;
|
||||
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
11
include/webserver.h
Normal 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);
|
||||
|
||||
@@ -36,7 +36,7 @@ build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-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
|
||||
-DCONFIG_LOG_TX_BUF_SIZE=4096
|
||||
-DCONFIG_LOG_TX_BUF_SIZE=8192
|
||||
-std=gnu++17
|
||||
build_unflags =
|
||||
-std=gnu++11
|
||||
|
||||
284
src/config.cpp
284
src/config.cpp
@@ -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();
|
||||
}
|
||||
|
||||
234
src/main.cpp
234
src/main.cpp
@@ -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");
|
||||
}
|
||||
}
|
||||
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
190
src/webserver.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user