mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-10-28 11:14:58 +00:00
The C6 autodetect picked the CUSTOM profile, but load_board_profile() has no entry for it, so Rx, Tx and the button stayed at the placeholder 99. The startup settings load then failed the GPIO check and fell back to an empty settings object (blank Board Profile, Tx Mode and Bus ID), and every Application settings save returned 400 until valid pins were sent together with CUSTOM through the API. Start a C6 without a profile as CUSTOM with pins for the Seeed XIAO ESP32-C6 (the c6_xiao_4M build): Rx GPIO20 (D9), Tx GPIO19 (D8), button GPIO9 (BOOT), no LED, no Dallas sensor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
593 lines
28 KiB
C++
593 lines
28 KiB
C++
/*
|
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
|
* Copyright 2020-2026 emsesp.org
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "emsesp.h"
|
|
|
|
namespace emsesp {
|
|
|
|
uint16_t WebSettings::flags_ = 0;
|
|
|
|
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
|
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
|
, _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE) {
|
|
securityManager->addEndpoint(server, EMSESP_BOARD_PROFILE_SERVICE_PATH, AuthenticationPredicates::IS_AUTHENTICATED, [this](AsyncWebServerRequest * request) {
|
|
board_profile(request);
|
|
});
|
|
|
|
addUpdateHandler([this] { onUpdate(); }, false);
|
|
}
|
|
|
|
void WebSettings::read(WebSettings & settings, JsonObject root) {
|
|
root["version"] = settings.version;
|
|
root["board_profile"] = settings.board_profile;
|
|
root["platform"] = EMSESP_PLATFORM;
|
|
root["system_name"] = settings.system_name;
|
|
root["locale"] = settings.locale;
|
|
root["tx_mode"] = settings.tx_mode;
|
|
root["ems_bus_id"] = settings.ems_bus_id;
|
|
root["syslog_enabled"] = settings.syslog_enabled;
|
|
root["syslog_level"] = settings.syslog_level;
|
|
root["trace_raw"] = settings.trace_raw;
|
|
root["syslog_mark_interval"] = settings.syslog_mark_interval;
|
|
root["syslog_host"] = settings.syslog_host;
|
|
root["syslog_port"] = settings.syslog_port;
|
|
root["boiler_heatingoff"] = settings.boiler_heatingoff;
|
|
root["remote_timeout"] = settings.remote_timeout;
|
|
root["remote_timeout_en"] = settings.remote_timeout_enabled;
|
|
root["shower_timer"] = settings.shower_timer;
|
|
root["shower_alert"] = settings.shower_alert;
|
|
root["shower_alert_coldshot"] = settings.shower_alert_coldshot;
|
|
root["shower_alert_trigger"] = settings.shower_alert_trigger;
|
|
root["shower_min_duration"] = settings.shower_min_duration;
|
|
root["rx_gpio"] = settings.rx_gpio;
|
|
root["tx_gpio"] = settings.tx_gpio;
|
|
root["dallas_gpio"] = settings.dallas_gpio;
|
|
root["dallas_parasite"] = settings.dallas_parasite;
|
|
root["led_gpio"] = settings.led_gpio;
|
|
root["hide_led"] = settings.hide_led;
|
|
root["led_type"] = settings.led_type;
|
|
root["low_clock"] = settings.low_clock;
|
|
root["telnet_enabled"] = settings.telnet_enabled;
|
|
root["notoken_api"] = settings.notoken_api;
|
|
root["readonly_mode"] = settings.readonly_mode;
|
|
root["analog_enabled"] = settings.analog_enabled;
|
|
root["pbutton_gpio"] = settings.pbutton_gpio;
|
|
root["solar_maxflow"] = settings.solar_maxflow;
|
|
root["fahrenheit"] = settings.fahrenheit;
|
|
root["bool_format"] = settings.bool_format;
|
|
root["bool_dashboard"] = settings.bool_dashboard;
|
|
root["enum_format"] = settings.enum_format;
|
|
root["weblog_level"] = settings.weblog_level;
|
|
root["weblog_buffer"] = settings.weblog_buffer;
|
|
root["weblog_compact"] = settings.weblog_compact;
|
|
root["phy_type"] = settings.phy_type;
|
|
root["eth_power"] = settings.eth_power;
|
|
root["eth_phy_addr"] = settings.eth_phy_addr;
|
|
root["eth_clock_mode"] = settings.eth_clock_mode;
|
|
root["modbus_enabled"] = settings.modbus_enabled;
|
|
root["modbus_port"] = settings.modbus_port;
|
|
root["modbus_max_clients"] = settings.modbus_max_clients;
|
|
root["modbus_timeout"] = settings.modbus_timeout;
|
|
root["developer_mode"] = settings.developer_mode;
|
|
root["disable_reset"] = settings.disable_reset;
|
|
root["auto_fw_check"] = settings.auto_fw_check;
|
|
root["email_enabled"] = settings.email_enabled;
|
|
root["email_security"] = settings.email_security;
|
|
root["email_server"] = settings.email_server;
|
|
root["email_port"] = settings.email_port;
|
|
root["email_login"] = settings.email_login;
|
|
root["email_pass"] = settings.email_pass;
|
|
root["email_sender"] = settings.email_sender;
|
|
root["email_recp"] = settings.email_recp;
|
|
root["email_subject"] = settings.email_subject;
|
|
}
|
|
|
|
// call on initialization and also when settings are updated/saved via web or console
|
|
// note, settings is empty when the service starts
|
|
StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|
// make a copy of the settings to compare to later
|
|
const WebSettings original_settings(settings);
|
|
|
|
// make a snapshot of the current GPIOs
|
|
std::vector<int8_t> used_gpios;
|
|
std::vector<int8_t> system_gpios;
|
|
EMSESP::system_.make_snapshot_gpios(used_gpios, system_gpios);
|
|
|
|
settings.version = root["version"] | EMSESP_APP_VERSION; // save the version, we use it later in System::check_upgrade()
|
|
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
|
|
|
// get current values that are related to the board profile
|
|
settings.led_gpio = root["led_gpio"];
|
|
settings.dallas_gpio = root["dallas_gpio"];
|
|
settings.rx_gpio = root["rx_gpio"];
|
|
settings.tx_gpio = root["tx_gpio"];
|
|
settings.pbutton_gpio = root["pbutton_gpio"];
|
|
settings.phy_type = root["phy_type"];
|
|
settings.eth_power = root["eth_power"];
|
|
settings.eth_phy_addr = root["eth_phy_addr"];
|
|
settings.eth_clock_mode = root["eth_clock_mode"];
|
|
settings.led_type = root["led_type"]; // 1 = RGB-LED
|
|
|
|
reset_flags();
|
|
|
|
// before loading new board profile free old gpios from used list to allow remapping
|
|
EMSESP::system_.remove_optional_gpio(original_settings.led_gpio);
|
|
EMSESP::system_.remove_optional_gpio(original_settings.dallas_gpio);
|
|
EMSESP::system_.remove_gpio(original_settings.pbutton_gpio);
|
|
EMSESP::system_.remove_optional_gpio(original_settings.rx_gpio);
|
|
EMSESP::system_.remove_optional_gpio(original_settings.tx_gpio);
|
|
|
|
// see if the user has changed the board profile
|
|
// this will set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type
|
|
// this will always run when EMS-ESP starts since original_settings{} is empty
|
|
if (original_settings.board_profile != settings.board_profile || original_settings.board_profile == "default"
|
|
|| original_settings.board_profile.length() == 0) {
|
|
set_board_profile(settings);
|
|
add_flags(ChangeFlags::RESTART);
|
|
}
|
|
|
|
check_flag(original_settings.phy_type, settings.phy_type, ChangeFlags::RESTART);
|
|
// ETH has changed, so we need to check the ethernet pins. Only if ETH is being used.
|
|
if (settings.phy_type != PHY_type::PHY_TYPE_NONE) {
|
|
check_flag(original_settings.eth_power, settings.eth_power, ChangeFlags::RESTART);
|
|
check_flag(original_settings.eth_clock_mode, settings.eth_clock_mode, ChangeFlags::RESTART);
|
|
if (settings.eth_power != -1) { // Ethernet Power -1 means disabled
|
|
EMSESP::system_.remove_gpio(settings.eth_power, true);
|
|
}
|
|
|
|
// remove the ethernet pins from valid list, regardless of whether the GPIOs are valid or not
|
|
EMSESP::system_.remove_gpio(23, true); // MDC
|
|
EMSESP::system_.remove_gpio(18, true); // MDIO
|
|
EMSESP::system_.remove_gpio(19, true); // TXD0
|
|
EMSESP::system_.remove_gpio(22, true); // TXD1
|
|
EMSESP::system_.remove_gpio(21, true); // TXEN
|
|
EMSESP::system_.remove_gpio(25, true); // RXD0
|
|
EMSESP::system_.remove_gpio(26, true); // RXD1
|
|
EMSESP::system_.remove_gpio(27, true); // CRS
|
|
|
|
if (settings.eth_clock_mode < 2) {
|
|
EMSESP::system_.remove_gpio(0, true); // ETH.clock input
|
|
} else if (settings.eth_clock_mode == 2) {
|
|
EMSESP::system_.remove_gpio(16, true); // ETH.clock output
|
|
} else if (settings.eth_clock_mode == 3) {
|
|
EMSESP::system_.remove_gpio(17, true); // ETH.clock output
|
|
}
|
|
}
|
|
|
|
// if any of the GPIOs have changed and re-validate them
|
|
bool have_valid_gpios = true;
|
|
|
|
// Helper lambda for optional GPIOs (can be 0 to disable)
|
|
auto add_optional_gpio = [&have_valid_gpios](uint8_t & gpio, const char * name) {
|
|
if (gpio != 0 && !EMSESP::system_.add_gpio(gpio, name)) {
|
|
gpio = 0; // 0 means disabled
|
|
have_valid_gpios = false;
|
|
}
|
|
};
|
|
|
|
// add new gpio assignment
|
|
check_flag(original_settings.rx_gpio, settings.rx_gpio, ChangeFlags::UART);
|
|
have_valid_gpios &= EMSESP::system_.add_gpio(settings.rx_gpio, "UART Rx");
|
|
|
|
check_flag(original_settings.tx_gpio, settings.tx_gpio, ChangeFlags::UART);
|
|
have_valid_gpios &= EMSESP::system_.add_gpio(settings.tx_gpio, "UART Tx");
|
|
|
|
check_flag(original_settings.led_gpio, settings.led_gpio, ChangeFlags::LED);
|
|
add_optional_gpio(settings.led_gpio, "LED");
|
|
|
|
check_flag(original_settings.dallas_gpio, settings.dallas_gpio, ChangeFlags::TEMPERATURE_SENSOR);
|
|
add_optional_gpio(settings.dallas_gpio, "Dallas");
|
|
|
|
check_flag(original_settings.pbutton_gpio, settings.pbutton_gpio, ChangeFlags::BUTTON);
|
|
have_valid_gpios &= EMSESP::system_.add_gpio(settings.pbutton_gpio, "Button");
|
|
|
|
// check if the LED type, eth_phy_addr or eth_clock_mode have changed
|
|
check_flag(original_settings.led_type, settings.led_type, ChangeFlags::LED);
|
|
check_flag(original_settings.eth_phy_addr, settings.eth_phy_addr, ChangeFlags::RESTART);
|
|
check_flag(original_settings.eth_clock_mode, settings.eth_clock_mode, ChangeFlags::RESTART);
|
|
|
|
// tx_mode
|
|
settings.tx_mode = root["tx_mode"] | EMSESP_DEFAULT_TX_MODE;
|
|
check_flag(original_settings.tx_mode, settings.tx_mode, ChangeFlags::UART);
|
|
|
|
// syslog
|
|
settings.syslog_enabled = root["syslog_enabled"] | EMSESP_DEFAULT_SYSLOG_ENABLED;
|
|
check_flag(original_settings.syslog_enabled, settings.syslog_enabled, ChangeFlags::SYSLOG);
|
|
settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
|
|
check_flag(original_settings.syslog_level, settings.syslog_level, ChangeFlags::SYSLOG);
|
|
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
|
check_flag(original_settings.syslog_mark_interval, settings.syslog_mark_interval, ChangeFlags::SYSLOG);
|
|
settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT;
|
|
check_flag(original_settings.syslog_port, settings.syslog_port, ChangeFlags::SYSLOG);
|
|
|
|
#ifndef EMSESP_STANDALONE
|
|
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
|
if (original_settings.syslog_host != settings.syslog_host) {
|
|
add_flags(ChangeFlags::SYSLOG);
|
|
}
|
|
#endif
|
|
|
|
// temperature sensor
|
|
settings.dallas_parasite = root["dallas_parasite"] | EMSESP_DEFAULT_DALLAS_PARASITE;
|
|
check_flag(original_settings.dallas_parasite, settings.dallas_parasite, ChangeFlags::TEMPERATURE_SENSOR);
|
|
|
|
// shower
|
|
settings.shower_timer = root["shower_timer"] | EMSESP_DEFAULT_SHOWER_TIMER;
|
|
check_flag(original_settings.shower_timer, settings.shower_timer, ChangeFlags::SHOWER);
|
|
settings.shower_alert = root["shower_alert"] | EMSESP_DEFAULT_SHOWER_ALERT;
|
|
check_flag(original_settings.shower_alert, settings.shower_alert, ChangeFlags::SHOWER);
|
|
settings.shower_alert_trigger = root["shower_alert_trigger"] | EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER;
|
|
check_flag(original_settings.shower_alert_trigger, settings.shower_alert_trigger, ChangeFlags::SHOWER);
|
|
settings.shower_min_duration = root["shower_min_duration"] | EMSESP_DEFAULT_SHOWER_MIN_DURATION;
|
|
check_flag(original_settings.shower_min_duration, settings.shower_min_duration, ChangeFlags::SHOWER);
|
|
settings.shower_alert_coldshot = root["shower_alert_coldshot"] | EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT;
|
|
check_flag(original_settings.shower_alert_coldshot, settings.shower_alert_coldshot, ChangeFlags::SHOWER);
|
|
|
|
// LED
|
|
settings.hide_led = root["hide_led"] | EMSESP_DEFAULT_HIDE_LED;
|
|
check_flag(original_settings.hide_led, settings.hide_led, ChangeFlags::LED);
|
|
|
|
// adc
|
|
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
|
|
check_flag(original_settings.analog_enabled, settings.analog_enabled, ChangeFlags::ANALOG_SENSOR);
|
|
|
|
// telnet, ems bus id and low clock
|
|
settings.telnet_enabled = root["telnet_enabled"] | EMSESP_DEFAULT_TELNET_ENABLED;
|
|
check_flag(original_settings.telnet_enabled, settings.telnet_enabled, ChangeFlags::RESTART);
|
|
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
|
check_flag(original_settings.ems_bus_id, settings.ems_bus_id, ChangeFlags::RESTART);
|
|
settings.low_clock = root["low_clock"];
|
|
check_flag(original_settings.low_clock, settings.low_clock, ChangeFlags::RESTART);
|
|
|
|
// Modbus settings
|
|
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
|
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::MODBUS);
|
|
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
|
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::MODBUS);
|
|
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
|
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::MODBUS);
|
|
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
|
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::MODBUS);
|
|
|
|
//
|
|
// these may need mqtt restart to rebuild HA discovery topics
|
|
//
|
|
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
|
EMSESP::system_.bool_format(settings.bool_format);
|
|
if (Mqtt::ha_enabled()) {
|
|
check_flag(original_settings.bool_format, settings.bool_format, ChangeFlags::MQTT);
|
|
}
|
|
|
|
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
|
|
EMSESP::system_.enum_format(settings.enum_format);
|
|
if (Mqtt::ha_enabled()) {
|
|
check_flag(original_settings.enum_format, settings.enum_format, ChangeFlags::MQTT);
|
|
}
|
|
|
|
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
|
|
EMSESP::system_.locale(settings.locale);
|
|
if (Mqtt::ha_enabled() && original_settings.locale != settings.locale) {
|
|
add_flags(ChangeFlags::MQTT);
|
|
}
|
|
|
|
//
|
|
// without checks or necessary restarts...
|
|
//
|
|
settings.system_name = root["system_name"] | EMSESP_DEFAULT_SYSTEM_NAME;
|
|
|
|
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
|
EMSESP::trace_raw(settings.trace_raw);
|
|
|
|
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
|
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
|
settings.boiler_heatingoff = root["boiler_heatingoff"] | EMSESP_DEFAULT_BOILER_HEATINGOFF;
|
|
settings.remote_timeout = root["remote_timeout"] | EMSESP_DEFAULT_REMOTE_TIMEOUT;
|
|
settings.remote_timeout_enabled = root["remote_timeout_en"] | EMSESP_DEFAULT_REMOTE_TIMEOUT_EN;
|
|
Roomctrl::set_timeout(settings.remote_timeout_enabled ? settings.remote_timeout : 0);
|
|
|
|
settings.fahrenheit = root["fahrenheit"];
|
|
EMSESP::system_.fahrenheit(settings.fahrenheit);
|
|
|
|
settings.readonly_mode = root["readonly_mode"];
|
|
EMSESP::system_.readonly_mode(settings.readonly_mode);
|
|
|
|
settings.developer_mode = root["developer_mode"];
|
|
EMSESP::system_.developer_mode(settings.developer_mode);
|
|
|
|
settings.disable_reset = root["disable_reset"];
|
|
EMSESP::system_.disable_reset(settings.disable_reset);
|
|
|
|
settings.auto_fw_check = root["auto_fw_check"] | EMSESP_DEFAULT_AUTO_FW_CHECK;
|
|
EMSESP::system_.auto_fw_check(settings.auto_fw_check);
|
|
if (settings.auto_fw_check && !original_settings.auto_fw_check) {
|
|
EMSESP::webStatusService.schedule_versions_refresh(); // just switched on, so look straight away
|
|
}
|
|
|
|
settings.bool_dashboard = root["bool_dashboard"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
|
EMSESP::system_.bool_dashboard(settings.bool_dashboard);
|
|
|
|
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
|
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
|
|
|
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
|
|
// Off is 0, so `| FACTORY_EMAIL_SECURITY` would replace it with STARTTLS (2)
|
|
settings.email_security = root["email_security"].isNull() ? FACTORY_EMAIL_SECURITY : root["email_security"].as<uint8_t>();
|
|
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
|
|
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
|
|
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
|
|
settings.email_pass = root["email_pass"] | FACTORY_EMAIL_PASSWORD;
|
|
settings.email_sender = root["email_sender"] | FACTORY_EMAIL_FROM;
|
|
settings.email_recp = root["email_recp"] | FACTORY_EMAIL_TO;
|
|
settings.email_subject = root["email_subject"] | FACTORY_EMAIL_SUBJECT;
|
|
|
|
// if no psram limit weblog buffer to 25 messages
|
|
const uint16_t weblog_buffer_default = (EMSESP::system_.PSram() > 0) ? EMSESP_DEFAULT_WEBLOG_BUFFER : 25;
|
|
settings.weblog_buffer = root["weblog_buffer"] | weblog_buffer_default;
|
|
// a stored 0 is a valid uint16 and passes the | default above, but would make
|
|
// limit_log_messages_ 0 in WebLogService
|
|
if (settings.weblog_buffer == 0) {
|
|
settings.weblog_buffer = weblog_buffer_default;
|
|
}
|
|
|
|
// save the settings if changed from the webUI
|
|
// if we encountered an invalid GPIO, rollback changes and don't save settings,
|
|
// and report the error to WebUI without a restart
|
|
if (!have_valid_gpios) {
|
|
// replace settings with original settings
|
|
settings = original_settings;
|
|
EMSESP::system_.restore_snapshot_gpios(used_gpios, system_gpios);
|
|
|
|
// report the error to WebUI
|
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO);
|
|
return StateUpdateResult::ERROR; // don't save the settings if the GPIOs are invalid
|
|
}
|
|
|
|
// save the setting internally, for reference later
|
|
EMSESP::system_.store_settings(settings);
|
|
|
|
// and finally always write to the settings file
|
|
if (has_flags(ChangeFlags::RESTART)) {
|
|
return StateUpdateResult::CHANGED_RESTART;
|
|
}
|
|
|
|
return StateUpdateResult::CHANGED;
|
|
}
|
|
|
|
// this is called after any of the settings have been persisted to the filesystem
|
|
// either via the Web UI or via the Console
|
|
void WebSettingsService::onUpdate() {
|
|
// skip if we're restarting anyway
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::RESTART)) {
|
|
return;
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::SHOWER)) {
|
|
EMSESP::shower_.start();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::TEMPERATURE_SENSOR)) {
|
|
EMSESP::temperaturesensor_.start();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::UART)) {
|
|
EMSESP::system_.uart_init();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) {
|
|
EMSESP::system_.syslog_init(); // re-start (or stop)
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::ANALOG_SENSOR)) {
|
|
EMSESP::analogsensor_.start();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::BUTTON)) {
|
|
EMSESP::system_.button_init();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::LED)) {
|
|
EMSESP::led_.init();
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::MQTT)) {
|
|
Mqtt::reset_mqtt(); // reload MQTT, init HA etc
|
|
}
|
|
|
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::MODBUS)) {
|
|
EMSESP::system_.modbus_init();
|
|
}
|
|
|
|
WebSettings::reset_flags();
|
|
EMSESP::system_.reset_unused_gpios();
|
|
}
|
|
|
|
void WebSettingsService::begin() {
|
|
_fsPersistence.readFromFS();
|
|
}
|
|
|
|
void WebSettingsService::save() {
|
|
_fsPersistence.writeToFS();
|
|
}
|
|
|
|
// send the board profile as JSON
|
|
void WebSettingsService::board_profile(AsyncWebServerRequest * request) {
|
|
if (request->hasParam("boardProfile")) {
|
|
std::string board_profile = request->getParam("boardProfile")->value().c_str();
|
|
|
|
auto * response = new PsramAsyncJsonResponse(false);
|
|
JsonObject root = response->getRoot();
|
|
|
|
// 0=led, 1=dallas, 2=rx, 3=tx, 4=button, 5=phy_type, 6=eth_power, 7=eth_phy_addr, 8=eth_clock_mode, 9=led_type
|
|
std::vector<int8_t> data;
|
|
(void)System::load_board_profile(data, board_profile);
|
|
root["board_profile"] = board_profile;
|
|
root["led_gpio"] = data[0];
|
|
root["dallas_gpio"] = data[1];
|
|
root["rx_gpio"] = data[2];
|
|
root["tx_gpio"] = data[3];
|
|
root["pbutton_gpio"] = data[4];
|
|
root["phy_type"] = data[5];
|
|
root["eth_power"] = data[6];
|
|
root["eth_phy_addr"] = data[7];
|
|
root["eth_clock_mode"] = data[8];
|
|
root["led_type"] = data[9];
|
|
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
AsyncWebServerResponse * response = request->beginResponse(200);
|
|
request->send(response);
|
|
}
|
|
|
|
// loads the board profile to set the gpios
|
|
// if the board profile is not found, or default, it will try to autodetect the board profile
|
|
void WebSettings::set_board_profile(WebSettings & settings) {
|
|
// The optional NVS boot value has priority and overrides any board_profile setting.
|
|
// This is only done for BBQKees boards
|
|
// Note 1: we never set the NVS boot value in the code - this is done on initial pre-loading
|
|
// Note 2: The board profile is dynamically changed for the session, but the value in the settings file on the FS remains untouched
|
|
if (EMSESP::system_.getBBQKeesGatewayDetails(FUSE_VALUE::MFG).startsWith("BBQKees")) {
|
|
String bbq_board = EMSESP::system_.getBBQKeesGatewayDetails(FUSE_VALUE::BOARD);
|
|
if (!bbq_board.isEmpty() && settings.board_profile != "CUSTOM") {
|
|
#if defined(EMSESP_DEBUG)
|
|
EMSESP::logger().debug("Overriding board profile with eFuse value %s", bbq_board.c_str());
|
|
#endif
|
|
settings.board_profile = bbq_board;
|
|
}
|
|
}
|
|
|
|
// if it's CUSTOM no need to load the board profile from the settings
|
|
// as it's already set
|
|
if (settings.board_profile == "CUSTOM") {
|
|
EMSESP::logger().info("Using CUSTOM board profile");
|
|
EMSESP::system_.set_valid_system_gpios();
|
|
return;
|
|
}
|
|
|
|
// load the board profile into the data vector
|
|
// 0=led, 1=dallas, 2=rx, 3=tx, 4=button, 5=phy_type, 6=eth_power, 7=eth_phy_addr, 8=eth_clock_mode, 9=led_type
|
|
std::vector<int8_t> data(10, 99); // initialize with 99 for all values, just as a safe guard to catch bad gpios
|
|
if (settings.board_profile != "default") {
|
|
if (!System::load_board_profile(data, settings.board_profile.c_str())) {
|
|
#if defined(EMSESP_DEBUG)
|
|
EMSESP::logger().debug("Unable to identify board profile %s", settings.board_profile.c_str());
|
|
#endif
|
|
settings.board_profile = "default"; // can't find profile, fallback to "default"
|
|
}
|
|
}
|
|
|
|
// we still don't have a valid board profile. Let's see if we can determine one from the build config or hardware
|
|
if (settings.board_profile == "default") {
|
|
EMSESP::logger().info("Autodetecting board profile");
|
|
#if CONFIG_IDF_TARGET_ESP32
|
|
// check for no PSRAM, could be a E32 or S32?
|
|
if (!ESP.getPsramSize()) {
|
|
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
|
|
settings.board_profile = "E32"; // Ethernet without PSRAM
|
|
} else {
|
|
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
|
}
|
|
} else {
|
|
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
|
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
|
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
|
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
|
} else {
|
|
settings.board_profile = "E32V2"; // Ethernet and PSRAM
|
|
}
|
|
} else {
|
|
settings.board_profile = "S32"; // ESP32 standard WiFi with PSRAM
|
|
}
|
|
}
|
|
|
|
// override if we know the target from the build config like C3, S2, S3 etc..
|
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
|
settings.board_profile = "C3MINI";
|
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
|
settings.board_profile = "S2MINI";
|
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
|
settings.board_profile = "S32S3"; // BBQKees Gateway S3
|
|
#elif CONFIG_IDF_TARGET_ESP32C6
|
|
// there is no C6 board profile, so start as CUSTOM with pins that suit the Seeed XIAO ESP32-C6 (the c6_xiao_4M build).
|
|
// load_board_profile() has no entry for CUSTOM, so without these the GPIOs stay at 99, every settings update
|
|
// fails the GPIO check and the settings fall back to empty values.
|
|
// Rx D9 (GPIO20), Tx D8 (GPIO19), button BOOT (GPIO9), no LED (the onboard LED on GPIO15 is active low), no Dallas
|
|
settings.board_profile = "CUSTOM";
|
|
data = {0, 0, 20, 19, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0};
|
|
#endif
|
|
// apply the new board profile setting
|
|
System::load_board_profile(data, settings.board_profile.c_str());
|
|
}
|
|
|
|
// log board profile and PSRAM info
|
|
#ifndef EMSESP_STANDALONE
|
|
uint32_t psram_size = ESP.getPsramSize() / 1024; // in KB
|
|
if (psram_size > 0) {
|
|
EMSESP::logger().info("Applied board profile %s (PSRAM: %lu KB)", settings.board_profile.c_str(), psram_size);
|
|
} else {
|
|
EMSESP::logger().info("Applied board profile %s (PSRAM: not available)", settings.board_profile.c_str());
|
|
}
|
|
#endif
|
|
|
|
// apply the new board profile settings
|
|
// 0=led, 1=dallas, 2=rx, 3=tx, 4=button, 5=phy_type, 6=eth_power, 7=eth_phy_addr, 8=eth_clock_mode, 9=led_type
|
|
settings.led_gpio = data[0]; // LED GPIO
|
|
settings.dallas_gpio = data[1]; // Dallas GPIO
|
|
settings.rx_gpio = data[2]; // UART Rx GPIO
|
|
settings.tx_gpio = data[3]; // UART Tx GPIO
|
|
settings.pbutton_gpio = data[4]; // Button GPIO
|
|
settings.phy_type = data[5]; // PHY Type
|
|
settings.eth_power = data[6]; // Ethernet Power GPIO
|
|
settings.eth_phy_addr = data[7]; // Ethernet PHY Address
|
|
settings.eth_clock_mode = data[8]; // Ethernet Clock Mode
|
|
settings.led_type = data[9]; // LED Type
|
|
}
|
|
|
|
// returns true if the value was changed
|
|
bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
|
|
if (prev_v != new_v) {
|
|
add_flags(flag);
|
|
#if defined(EMSESP_DEBUG)
|
|
// EMSESP::logger().debug("check_flag: flag %d, prev_v=%d, new_v=%d", flag, prev_v, new_v);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void WebSettings::add_flags(uint16_t flags) {
|
|
flags_ |= flags;
|
|
}
|
|
|
|
bool WebSettings::has_flags(uint16_t flags) {
|
|
return (flags_ & flags) == flags;
|
|
}
|
|
|
|
void WebSettings::reset_flags() {
|
|
flags_ = ChangeFlags::NONE;
|
|
}
|
|
|
|
uint16_t WebSettings::get_flags() {
|
|
return flags_;
|
|
}
|
|
|
|
} // namespace emsesp
|