🚚 Moves firmware to src and include

This commit is contained in:
Rune Harlyk
2025-07-11 12:12:07 +02:00
committed by Rune Harlyk
parent 743aa073b7
commit a3be035f98
60 changed files with 2 additions and 3 deletions
+115
View File
@@ -0,0 +1,115 @@
#pragma once
#include <WiFi.h>
#include <IPAddress.h>
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <utils/string_utils.h>
#include <utils/ip_utils.h>
#include <template/state_result.h>
#include <DNSServer.h>
#include <IPAddress.h>
#ifndef FACTORY_AP_PROVISION_MODE
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED
#endif
#ifndef FACTORY_AP_SSID
#define FACTORY_AP_SSID "ESP32-SvelteKit-#{unique_id}"
#endif
#ifndef FACTORY_AP_PASSWORD
#define FACTORY_AP_PASSWORD "esp-sveltekit"
#endif
#ifndef FACTORY_AP_LOCAL_IP
#define FACTORY_AP_LOCAL_IP "192.168.4.1"
#endif
#ifndef FACTORY_AP_GATEWAY_IP
#define FACTORY_AP_GATEWAY_IP "192.168.4.1"
#endif
#ifndef FACTORY_AP_SUBNET_MASK
#define FACTORY_AP_SUBNET_MASK "255.255.255.0"
#endif
#ifndef FACTORY_AP_CHANNEL
#define FACTORY_AP_CHANNEL 1
#endif
#ifndef FACTORY_AP_SSID_HIDDEN
#define FACTORY_AP_SSID_HIDDEN false
#endif
#ifndef FACTORY_AP_MAX_CLIENTS
#define FACTORY_AP_MAX_CLIENTS 4
#endif
#define AP_MODE_ALWAYS 0
#define AP_MODE_DISCONNECTED 1
#define AP_MODE_NEVER 2
#define MANAGE_NETWORK_DELAY 10000
#define DNS_PORT 53
enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
class APSettings {
public:
uint8_t provisionMode;
String ssid;
String password;
uint8_t channel;
bool ssidHidden;
uint8_t maxClients;
IPAddress localIP;
IPAddress gatewayIP;
IPAddress subnetMask;
bool operator==(const APSettings &settings) const {
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password &&
channel == settings.channel && ssidHidden == settings.ssidHidden && maxClients == settings.maxClients &&
localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
}
static void read(APSettings &settings, JsonVariant &root) {
root["provision_mode"] = settings.provisionMode;
root["ssid"] = settings.ssid;
root["password"] = settings.password;
root["channel"] = settings.channel;
root["ssid_hidden"] = settings.ssidHidden;
root["max_clients"] = settings.maxClients;
root["local_ip"] = settings.localIP.toString();
root["gateway_ip"] = settings.gatewayIP.toString();
root["subnet_mask"] = settings.subnetMask.toString();
}
static StateUpdateResult update(JsonVariant &root, APSettings &settings) {
APSettings newSettings = {};
newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE;
switch (settings.provisionMode) {
case AP_MODE_ALWAYS:
case AP_MODE_DISCONNECTED:
case AP_MODE_NEVER: break;
default: newSettings.provisionMode = AP_MODE_DISCONNECTED;
}
newSettings.ssid = root["ssid"] | format(FACTORY_AP_SSID);
newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL;
newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN;
newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS;
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
if (newSettings == settings) {
return StateUpdateResult::UNCHANGED;
}
settings = newSettings;
return StateUpdateResult::CHANGED;
}
};
+109
View File
@@ -0,0 +1,109 @@
#pragma once
namespace Camera {
#include <ArduinoJson.h>
#include <template/state_result.h>
#include <esp_camera.h>
class CameraSettings {
public:
pixformat_t pixformat;
framesize_t framesize; // 0 - 10
uint8_t quality; // 0 - 63
int8_t brightness; //-2 - 2
int8_t contrast; //-2 - 2
int8_t saturation; //-2 - 2
int8_t sharpness; //-2 - 2
uint8_t denoise;
gainceiling_t gainceiling;
uint8_t whitebal;
uint8_t special_effect; // 0 - 6
uint8_t wb_mode; // 0 - 4
uint8_t awb;
uint8_t exposure_ctrl;
uint8_t awb_gain;
uint8_t gain_ctrl;
uint8_t aec;
uint8_t aec2;
int8_t ae_level; //-2 - 2
uint16_t aec_value; // 0 - 1200
uint8_t agc;
uint8_t agc_gain; // 0 - 30
uint8_t bpc;
uint8_t wpc;
uint8_t raw_gma;
uint8_t lenc;
uint8_t hmirror;
uint8_t vflip;
uint8_t dcw;
uint8_t colorbar;
static void read(CameraSettings &settings, JsonVariant &root) {
root["pixformat"] = settings.pixformat;
root["framesize"] = settings.framesize;
root["quality"] = settings.quality;
root["brightness"] = settings.brightness;
root["contrast"] = settings.contrast;
root["saturation"] = settings.saturation;
root["sharpness"] = settings.sharpness;
root["denoise"] = settings.denoise;
root["special_effect"] = settings.special_effect;
root["wb_mode"] = settings.wb_mode;
root["exposure_ctrl"] = settings.exposure_ctrl;
root["gain_ctrl"] = settings.gain_ctrl;
root["awb"] = settings.awb;
root["awb_gain"] = settings.awb_gain;
root["aec"] = settings.aec;
root["aec2"] = settings.aec2;
root["ae_level"] = settings.ae_level;
root["aec_value"] = settings.aec_value;
root["agc"] = settings.agc;
root["agc_gain"] = settings.agc_gain;
root["gainceiling"] = settings.gainceiling;
root["bpc"] = settings.bpc;
root["wpc"] = settings.wpc;
root["raw_gma"] = settings.raw_gma;
root["lenc"] = settings.lenc;
root["hmirror"] = settings.hmirror;
root["vflip"] = settings.vflip;
root["dcw"] = settings.dcw;
root["colorbar"] = settings.colorbar;
}
static StateUpdateResult update(JsonVariant &root, CameraSettings &settings) {
settings.pixformat = root["pixformat"];
settings.framesize = root["framesize"];
settings.brightness = root["brightness"];
settings.contrast = root["contrast"];
settings.quality = root["quality"];
settings.contrast = root["contrast"];
settings.saturation = root["saturation"];
settings.sharpness = root["sharpness"];
settings.denoise = root["denoise"];
settings.exposure_ctrl = root["exposure_ctrl"];
settings.gain_ctrl = root["gain_ctrl"];
settings.special_effect = root["special_effect"];
settings.wb_mode = root["wb_mode"];
settings.awb = root["awb"];
settings.awb_gain = root["awb_gain"];
settings.aec = root["aec"];
settings.aec2 = root["aec2"];
settings.ae_level = root["ae_level"];
settings.aec_value = root["aec_value"];
settings.agc = root["agc"];
settings.agc_gain = root["agc_gain"];
settings.gainceiling = root["gainceiling"];
settings.bpc = root["bpc"];
settings.wpc = root["wpc"];
settings.raw_gma = root["raw_gma"];
settings.lenc = root["lenc"];
settings.hmirror = root["hmirror"];
settings.vflip = root["vflip"];
settings.dcw = root["dcw"];
settings.colorbar = root["colorbar"];
return StateUpdateResult::CHANGED;
};
};
} // namespace Camera
+139
View File
@@ -0,0 +1,139 @@
#pragma once
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <utils/string_utils.h>
#include <template/state_result.h>
#include <filesystem.h>
#ifndef FACTORY_MDNS_HOSTNAME
#define FACTORY_MDNS_HOSTNAME "esp32"
#endif
#ifndef FACTORY_MDNS_INSTANCE
#define FACTORY_MDNS_INSTANCE "ESP32 Device"
#endif
typedef struct {
String key;
String value;
void serialize(JsonVariant &json) const {
json["key"] = key;
json["value"] = value;
}
bool deserialize(const JsonVariant &json) {
key = json["key"].as<String>();
value = json["value"].as<String>();
return key.length() > 0;
}
} mdns_txt_record_t;
typedef struct {
String service;
String protocol;
uint16_t port;
std::vector<mdns_txt_record_t> txtRecords;
void serialize(JsonVariant &json) const {
json["service"] = service;
json["protocol"] = protocol;
json["port"] = port;
if (txtRecords.size() > 0) {
JsonArray txtArray = json["txt_records"].to<JsonArray>();
for (const auto &txt : txtRecords) {
JsonVariant txtObj = txtArray.add<JsonVariant>();
txt.serialize(txtObj);
}
}
}
bool deserialize(const JsonVariant &json) {
service = json["service"].as<String>();
protocol = json["protocol"].as<String>();
port = json["port"] | 0;
txtRecords.clear();
if (json["txt_records"].is<JsonArray>()) {
JsonArray txtArray = json["txt_records"];
for (JsonVariant txtObj : txtArray) {
mdns_txt_record_t txt;
if (txt.deserialize(txtObj)) {
txtRecords.push_back(txt);
}
}
}
return service.length() > 0 && protocol.length() > 0 && port > 0;
}
} mdns_service_t;
class MDNSSettings {
public:
String hostname;
String instance;
std::vector<mdns_service_t> services;
std::vector<mdns_txt_record_t> globalTxtRecords;
static void read(MDNSSettings &settings, JsonVariant &root) {
root["hostname"] = settings.hostname;
root["instance"] = settings.instance;
JsonArray servicesArray = root["services"].to<JsonArray>();
for (const auto &service : settings.services) {
JsonVariant serviceObj = servicesArray.add<JsonVariant>();
service.serialize(serviceObj);
}
JsonArray txtArray = root["global_txt_records"].to<JsonArray>();
for (const auto &txt : settings.globalTxtRecords) {
JsonVariant txtObj = txtArray.add<JsonVariant>();
txt.serialize(txtObj);
}
}
static StateUpdateResult update(JsonVariant &root, MDNSSettings &settings) {
settings.hostname = root["hostname"] | FACTORY_MDNS_HOSTNAME;
settings.instance = root["instance"] | FACTORY_MDNS_INSTANCE;
settings.services.clear();
if (root["services"].is<JsonArray>()) {
JsonArray servicesArray = root["services"];
for (JsonVariant serviceObj : servicesArray) {
mdns_service_t service;
if (service.deserialize(serviceObj)) {
settings.services.push_back(service);
}
}
}
if (settings.services.empty()) {
mdns_service_t httpService = {.service = "http", .protocol = "tcp", .port = 80};
settings.services.push_back(httpService);
mdns_service_t wsService = {.service = "ws", .protocol = "tcp", .port = 80};
settings.services.push_back(wsService);
}
settings.globalTxtRecords.clear();
if (root["global_txt_records"].is<JsonArray>()) {
JsonArray txtArray = root["global_txt_records"];
for (JsonVariant txtObj : txtArray) {
mdns_txt_record_t txt;
if (txt.deserialize(txtObj)) {
settings.globalTxtRecords.push_back(txt);
}
}
}
if (settings.globalTxtRecords.empty()) {
mdns_txt_record_t firmwareVersion = {.key = "Firmware Version", .value = APP_VERSION};
settings.globalTxtRecords.push_back(firmwareVersion);
}
return StateUpdateResult::CHANGED;
}
};
+42
View File
@@ -0,0 +1,42 @@
#include <Arduino.h>
#include <template/state_result.h>
#include <ArduinoJson.h>
#ifndef FACTORY_NTP_ENABLED
#define FACTORY_NTP_ENABLED true
#endif
#ifndef FACTORY_NTP_TIME_ZONE_LABEL
#define FACTORY_NTP_TIME_ZONE_LABEL "Europe/London"
#endif
#ifndef FACTORY_NTP_TIME_ZONE_FORMAT
#define FACTORY_NTP_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0"
#endif
#ifndef FACTORY_NTP_SERVER
#define FACTORY_NTP_SERVER "time.google.com"
#endif
class NTPSettings {
public:
bool enabled;
String tzLabel;
String tzFormat;
String server;
static void read(NTPSettings &settings, JsonVariant &root) {
root["enabled"] = settings.enabled;
root["server"] = settings.server;
root["tz_label"] = settings.tzLabel;
root["tz_format"] = settings.tzFormat;
}
static StateUpdateResult update(JsonVariant &root, NTPSettings &settings) {
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
settings.server = root["server"] | FACTORY_NTP_SERVER;
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
return StateUpdateResult::CHANGED;
}
};
@@ -0,0 +1,49 @@
#pragma once
#include <vector>
#include <ArduinoJson.h>
#include <template/state_result.h>
/*
* I2C software connection
*/
#ifndef SDA_PIN
#define SDA_PIN SDA
#endif
#ifndef SCL_PIN
#define SCL_PIN SCL
#endif
#ifndef I2C_FREQUENCY
#define I2C_FREQUENCY 1000000UL
#endif
class PinConfig {
public:
int pin;
String mode;
String type;
String role;
PinConfig(int p, String m, String t, String r) : pin(p), mode(m), type(t), role(r) {}
};
class PeripheralsConfiguration {
public:
int sda = SDA_PIN;
int scl = SCL_PIN;
long frequency = I2C_FREQUENCY;
std::vector<PinConfig> pins;
static void read(PeripheralsConfiguration &settings, JsonVariant &root) {
root["sda"] = settings.sda;
root["scl"] = settings.scl;
root["frequency"] = settings.frequency;
}
static StateUpdateResult update(JsonVariant &root, PeripheralsConfiguration &settings) {
settings.sda = root["sda"] | SDA_PIN;
settings.scl = root["scl"] | SCL_PIN;
settings.frequency = root["frequency"] | I2C_FREQUENCY;
return StateUpdateResult::CHANGED;
};
};
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <ArduinoJson.h>
#include <template/state_result.h>
typedef struct {
float centerPwm;
float direction;
float centerAngle;
float conversion;
const char *name;
// bool enable;
} servo_settings_t;
class ServoSettings {
public:
servo_settings_t servos[12] = {
{306, -1, 0, 2.2, "Servo1"}, {306, 1, -45, 2.1055555, "Servo2"}, {306, 1, 90, 1.96923, "Servo3"},
{306, -1, 0, 2.2, "Servo4"}, {306, -1, 45, 2.1055555, "Servo5"}, {306, -1, -90, 1.96923, "Servo6"},
{306, 1, 0, 2.2, "Servo7"}, {306, 1, -45, 2.1055555, "Servo8"}, {306, 1, 90, 1.96923, "Servo9"},
{306, 1, 0, 2.2, "Servo10"}, {306, -1, 45, 2.1055555, "Servo11"}, {306, -1, -90, 1.96923, "Servo12"}};
static void read(ServoSettings &settings, JsonVariant &root) {
JsonArray servos = root["servos"].to<JsonArray>();
for (auto &servo : settings.servos) {
JsonVariant newServo = servos.add<JsonVariant>();
newServo["center_pwm"] = servo.centerPwm;
newServo["direction"] = servo.direction;
newServo["center_angle"] = servo.centerAngle;
newServo["conversion"] = servo.conversion;
}
}
static StateUpdateResult update(JsonVariant &root, ServoSettings &settings) {
if (root["servos"].is<JsonArray>()) {
JsonArray servosJson = root["servos"];
int i = 0;
for (auto servo : servosJson) {
JsonVariant servoObject = servo.as<JsonVariant>();
uint8_t servoId = i; // servoObject["id"].as<uint8_t>();
settings.servos[servoId].centerPwm = servoObject["center_pwm"].as<float>();
settings.servos[servoId].centerAngle = servoObject["center_angle"].as<float>();
settings.servos[servoId].direction = servoObject["direction"].as<float>();
settings.servos[servoId].conversion = servoObject["conversion"].as<float>();
i++;
}
}
ESP_LOGI("ServoController", "Updating servo data");
return StateUpdateResult::CHANGED;
};
};
+136
View File
@@ -0,0 +1,136 @@
#pragma once
#include <WiFi.h>
#include <IPAddress.h>
#include <ArduinoJson.h>
#include <utils/json_utils.h>
#include <utils/ip_utils.h>
#include <utils/string_utils.h>
#include <template/state_result.h>
#ifndef FACTORY_WIFI_SSID
#define FACTORY_WIFI_SSID ""
#endif
#ifndef FACTORY_WIFI_PASSWORD
#define FACTORY_WIFI_PASSWORD ""
#endif
#ifndef FACTORY_WIFI_HOSTNAME
#define FACTORY_WIFI_HOSTNAME "#{platform}-#{unique_id}"
#endif
#ifndef FACTORY_WIFI_RSSI_THRESHOLD
#define FACTORY_WIFI_RSSI_THRESHOLD -80
#endif
typedef struct {
String ssid;
uint8_t bssid[6];
int32_t channel;
String password;
bool staticIPConfig;
IPAddress localIP;
IPAddress gatewayIP;
IPAddress subnetMask;
IPAddress dnsIP1;
IPAddress dnsIP2;
bool available;
void serialize(JsonVariant &json) const {
json["ssid"] = ssid;
json["password"] = password;
json["static_ip_config"] = staticIPConfig;
if (staticIPConfig) {
JsonUtils::writeIP(json, "local_ip", localIP);
JsonUtils::writeIP(json, "gateway_ip", gatewayIP);
JsonUtils::writeIP(json, "subnet_mask", subnetMask);
JsonUtils::writeIP(json, "dns_ip_1", dnsIP1);
JsonUtils::writeIP(json, "dns_ip_2", dnsIP2);
}
}
bool deserialize(const JsonVariant &json) {
String newSsid = json["ssid"].as<String>();
String newPassword = json["password"].as<String>();
if (newSsid.length() < 1 || newSsid.length() > 31 || newPassword.length() > 64) {
ESP_LOGE("WiFiSettings", "SSID or password length is invalid");
return false;
}
ssid = newSsid;
password = newPassword;
staticIPConfig = json["static_ip_config"] | false;
if (staticIPConfig) {
JsonUtils::readIP(json, "local_ip", localIP);
JsonUtils::readIP(json, "gateway_ip", gatewayIP);
JsonUtils::readIP(json, "subnet_mask", subnetMask);
JsonUtils::readIP(json, "dns_ip_1", dnsIP1);
JsonUtils::readIP(json, "dns_ip_2", dnsIP2);
if (IPUtils::isNotSet(dnsIP1) && IPUtils::isSet(dnsIP2)) {
dnsIP1 = dnsIP2;
dnsIP2 = INADDR_NONE;
}
if (IPUtils::isNotSet(localIP) || IPUtils::isNotSet(gatewayIP) || IPUtils::isNotSet(subnetMask)) {
staticIPConfig = false;
ESP_LOGW("WiFiSettings", "Invalid static IP configuration - falling back to DHCP");
}
}
available = false;
return true;
}
} wifi_settings_t;
inline wifi_settings_t createDefaultWiFiSettings() {
return wifi_settings_t {.ssid = FACTORY_WIFI_SSID,
.password = FACTORY_WIFI_PASSWORD,
.staticIPConfig = false,
.localIP = INADDR_NONE,
.gatewayIP = INADDR_NONE,
.subnetMask = INADDR_NONE,
.dnsIP1 = INADDR_NONE,
.dnsIP2 = INADDR_NONE,
.available = false};
}
class WiFiSettings {
public:
String hostname;
bool priorityBySignalStrength;
std::vector<wifi_settings_t> wifiSettings;
static void read(WiFiSettings &settings, JsonVariant &root) {
root["hostname"] = settings.hostname;
root["priority_RSSI"] = settings.priorityBySignalStrength;
JsonArray wifiNetworks = root["wifi_networks"].to<JsonArray>();
for (const auto &wifi : settings.wifiSettings) {
JsonVariant wifiNetwork = wifiNetworks.add<JsonVariant>();
wifi.serialize(wifiNetwork);
}
ESP_LOGV("WiFiSettings", "WiFi Settings read");
}
static StateUpdateResult update(JsonVariant &root, WiFiSettings &settings) {
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
settings.priorityBySignalStrength = root["priority_RSSI"] | true;
settings.wifiSettings.clear();
if (root["wifi_networks"].is<JsonArray>()) {
JsonArray wifiNetworks = root["wifi_networks"];
int networkCount = 0;
for (JsonVariant wifiNetwork : wifiNetworks) {
if (networkCount >= 5) {
ESP_LOGE("WiFiSettings", "Too many wifi networks");
break;
}
wifi_settings_t newSettings;
if (newSettings.deserialize(wifiNetwork)) {
settings.wifiSettings.push_back(newSettings);
networkCount++;
}
}
} else if (String(FACTORY_WIFI_SSID).length() > 0) {
ESP_LOGI("WiFiSettings", "No WiFi config found - using factory settings");
settings.wifiSettings.push_back(createDefaultWiFiSettings());
}
ESP_LOGV("WiFiSettings", "WiFi Settings updated");
return StateUpdateResult::CHANGED;
}
};