Merge pull request #3066 from MichaelDvP/dev

fix modbus start #3064
This commit is contained in:
Proddy
2026-05-07 18:05:48 +02:00
committed by GitHub
12 changed files with 467 additions and 421 deletions

View File

@@ -20,6 +20,7 @@ For more details go to [emsesp.org](https://emsesp.org/).
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960) - SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015) - missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
- custom entities check fetch length - custom entities check fetch length
- modbus initialization [#3064](https://github.com/emsesp/EMS-ESP32/issues/3064)
## Changed ## Changed

View File

@@ -26,8 +26,8 @@
"@alova/adapter-xhr": "2.3.1", "@alova/adapter-xhr": "2.3.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/icons-material": "^9.0.0", "@mui/icons-material": "^9.0.1",
"@mui/material": "^9.0.0", "@mui/material": "^9.0.1",
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@table-library/react-table-library": "4.1.15", "@table-library/react-table-library": "4.1.15",
"alova": "3.5.1", "alova": "3.5.1",
@@ -38,10 +38,10 @@
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"mime-types": "^3.0.2", "mime-types": "^3.0.2",
"preact": "^10.29.1", "preact": "^10.29.1",
"react": "^19.2.5", "react": "^19.2.6",
"react-dom": "^19.2.5", "react-dom": "^19.2.6",
"react-icons": "^5.6.0", "react-icons": "^5.6.0",
"react-router": "^7.14.2", "react-router": "^7.15.0",
"react-toastify": "^11.1.0", "react-toastify": "^11.1.0",
"typesafe-i18n": "^5.27.1", "typesafe-i18n": "^5.27.1",
"typescript": "^6.0.3" "typescript": "^6.0.3"
@@ -55,17 +55,17 @@
"@types/node": "^25.6.0", "@types/node": "^25.6.0",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"axe-core": "^4.11.3", "axe-core": "^4.11.4",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"eslint": "^10.2.1", "eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.3", "prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.46.1", "terser": "^5.47.0",
"typescript-eslint": "^8.59.0", "typescript-eslint": "^8.59.2",
"vite": "^8.0.9", "vite": "^8.0.11",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.1.1" "vite-tsconfig-paths": "^6.1.1"
}, },
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820" "packageManager": "pnpm@10.33.4"
} }

730
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -59,7 +59,7 @@ framework = arduino
board_build.partitions = partitions/esp32_partition_16M.csv board_build.partitions = partitions/esp32_partition_16M.csv
board_upload.flash_size = 16MB board_upload.flash_size = 16MB
board_build.app_partition_name = app0 board_build.app_partition_name = app0
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7 platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
; 32MB Flash variants ; 32MB Flash variants
[espressif32_base_32M] [espressif32_base_32M]
@@ -67,7 +67,7 @@ framework = arduino
board_build.partitions = partitions/esp32_partition_32M.csv board_build.partitions = partitions/esp32_partition_32M.csv
board_upload.flash_size = 32MB board_upload.flash_size = 32MB
board_build.app_partition_name = app0 board_build.app_partition_name = app0
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7 platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
; use Tasmota's library for 4MB Flash variants. ; use Tasmota's library for 4MB Flash variants.
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap ; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
@@ -105,7 +105,7 @@ board_build.filesystem = littlefs
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.4.3 bblanchon/ArduinoJson @ 7.4.3
ESP32Async/AsyncTCP @ 3.4.10 ESP32Async/AsyncTCP @ 3.4.10
ESP32Async/ESPAsyncWebServer @ 3.10.3 ESP32Async/ESPAsyncWebServer @ 3.11.0
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
; builds the web interface only, not the firmware ; builds the web interface only, not the firmware

View File

@@ -185,10 +185,14 @@ bool MqttSettingsService::configureMqtt() {
#ifndef TASMOTA_SDK #ifndef TASMOTA_SDK
if (_state.enableTLS) { if (_state.enableTLS) {
if (_state.rootCA == "insecure") { if (_state.rootCA == "insecure") {
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger().debug("Start insecure MQTT"); emsesp::EMSESP::logger().debug("Start insecure MQTT");
#endif
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure(); static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
} else { } else {
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA"); emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA");
#endif
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n"; String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str()); static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str());
} }

View File

@@ -526,6 +526,29 @@ void System::syslog_init() {
#endif #endif
} }
// start or reconfigure modbus
void System::modbus_init() {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
if (settings.modbus_enabled) {
if (EMSESP::modbus_ == nullptr) {
EMSESP::modbus_ = new Modbus;
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
EMSESP::modbus_->stop();
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
}
} else if (EMSESP::modbus_ != nullptr) {
EMSESP::modbus_->stop();
delete EMSESP::modbus_;
EMSESP::modbus_ = nullptr;
}
modbus_enabled_ = settings.modbus_enabled;
modbus_port_ = settings.modbus_port;
modbus_max_clients_ = settings.modbus_max_clients;
modbus_timeout_ = settings.modbus_timeout;
});
}
// read specific major system settings to store locally for faster access // read specific major system settings to store locally for faster access
void System::store_settings(WebSettings & settings) { void System::store_settings(WebSettings & settings) {
version_ = settings.version; version_ = settings.version;
@@ -563,25 +586,6 @@ void System::store_settings(WebSettings & settings) {
locale_ = settings.locale; locale_ = settings.locale;
developer_mode_ = settings.developer_mode; developer_mode_ = settings.developer_mode;
// start services
if (settings.modbus_enabled) {
if (EMSESP::modbus_ == nullptr) {
EMSESP::modbus_ = new Modbus;
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
EMSESP::modbus_->stop();
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
}
} else if (EMSESP::modbus_ != nullptr) {
EMSESP::modbus_->stop();
delete EMSESP::modbus_;
EMSESP::modbus_ = nullptr;
}
modbus_enabled_ = settings.modbus_enabled;
modbus_port_ = settings.modbus_port;
modbus_max_clients_ = settings.modbus_max_clients;
modbus_timeout_ = settings.modbus_timeout;
} }
// Starts up core services // Starts up core services
@@ -631,6 +635,7 @@ void System::start() {
network_init(); // network network_init(); // network
uart_init(); // start UART uart_init(); // start UART
syslog_init(); // start syslog syslog_init(); // start syslog
modbus_init(); // start modbus
} }
// button single click // button single click
@@ -1713,7 +1718,7 @@ void System::exportSystemBackup(JsonObject output) {
output["version"] = EMSESP_APP_VERSION; // add the version to the output output["version"] = EMSESP_APP_VERSION; // add the version to the output
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
// add date/time if NTP enabled and active // add date/time if NTP enabled and active
if ((esp_sntp_enabled()) && (EMSESP::system_.ntp_connected())) { if ((esp_sntp_enabled()) && (EMSESP::system_.ntp_connected())) {
time_t now = time(nullptr); time_t now = time(nullptr);
if (now > 1500000000L) { if (now > 1500000000L) {
@@ -3266,7 +3271,7 @@ void System::set_valid_system_gpios() {
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19"); valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO26 - GPIO32 = SPI flash and PSRAM // GPIO26 - GPIO32 = SPI flash and PSRAM
// GPIO45 - GPIO46 = strapping pins // GPIO45 - GPIO46 = strapping pins
@@ -3279,7 +3284,7 @@ void System::set_valid_system_gpios() {
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25"); valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO3, GPIO45 - GPIO46 = strapping pins // GPIO3, GPIO45 - GPIO46 = strapping pins
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended // GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
@@ -3298,7 +3303,7 @@ void System::set_valid_system_gpios() {
} }
#elif CONFIG_IDF_TARGET_ESP32 #elif CONFIG_IDF_TARGET_ESP32
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11) // GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist // GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
@@ -3378,6 +3383,24 @@ void System::remove_gpio(uint8_t pin, bool also_system) {
} }
} }
// remove a gpio that has 0 for disable
void System::remove_optional_gpio(uint8_t pin) {
if (pin) {
remove_gpio(pin, false);
}
}
// set unused gpios to default state input high-Z
void System::reset_unused_gpios() {
for (const auto & pin : valid_system_gpios_) {
auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; });
if (it == used_gpios_.end()) {
LOG_DEBUG("reset pin %d", pin);
pinMode(pin, INPUT);
}
}
}
// return a list of GPIO's available for use // return a list of GPIO's available for use
std::vector<uint8_t> System::available_gpios() { std::vector<uint8_t> System::available_gpios() {
std::vector<uint8_t> gpios; std::vector<uint8_t> gpios;

View File

@@ -122,6 +122,7 @@ class System {
void show_mem(const char * note); void show_mem(const char * note);
void store_settings(class WebSettings & settings); void store_settings(class WebSettings & settings);
void syslog_init(); void syslog_init();
void modbus_init();
bool check_upgrade(); bool check_upgrade();
bool check_restore(); bool check_restore();
void heartbeat_json(JsonObject output); void heartbeat_json(JsonObject output);
@@ -376,6 +377,8 @@ class System {
#endif #endif
static void remove_gpio(uint8_t pin, bool also_system = false); // remove a gpio from both valid (optional) and used lists static void remove_gpio(uint8_t pin, bool also_system = false); // remove a gpio from both valid (optional) and used lists
static void remove_optional_gpio(uint8_t pin);
static void reset_unused_gpios();
// Partition info map: partition name -> {version, size, install_date} // Partition info map: partition name -> {version, size, install_date}
std::map<std::string, PartitionInfo, std::less<>, AllocatorPSRAM<std::pair<const std::string, PartitionInfo>>> partition_info_; std::map<std::string, PartitionInfo, std::less<>, AllocatorPSRAM<std::pair<const std::string, PartitionInfo>>> partition_info_;

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.8.2-dev.20" #define EMSESP_APP_VERSION "3.8.2-dev.21"

View File

@@ -166,7 +166,9 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) { bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) {
// don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes // don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes
if (!strlen(value)) { if (!strlen(value)) {
#if defined(EMSESP_DEBUG)
EMSESP::logger().debug("can't set empty value!"); EMSESP::logger().debug("can't set empty value!");
#endif
return false; return false;
} }

View File

@@ -465,7 +465,9 @@ void WebSchedulerService::condition() {
} else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) { } else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) {
scheduleItem.retry_cnt = 0xFF; scheduleItem.retry_cnt = 0xFF;
} else if (match.length() != 1) { // the match is not boolean } else if (match.length() != 1) { // the match is not boolean
#if defined(EMSESP_DEBUG)
EMSESP::logger().debug("condition result: %s", match.c_str()); EMSESP::logger().debug("condition result: %s", match.c_str());
#endif
} }
} }
} }

View File

@@ -20,7 +20,7 @@
namespace emsesp { namespace emsesp {
uint8_t WebSettings::flags_ = 0; uint16_t WebSettings::flags_ = 0;
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
@@ -114,11 +114,11 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
reset_flags(); reset_flags();
// before loading new board profile free old gpios from used list to allow remapping // before loading new board profile free old gpios from used list to allow remapping
EMSESP::system_.remove_gpio(original_settings.led_gpio); EMSESP::system_.remove_optional_gpio(original_settings.led_gpio);
EMSESP::system_.remove_gpio(original_settings.dallas_gpio); EMSESP::system_.remove_optional_gpio(original_settings.dallas_gpio);
EMSESP::system_.remove_gpio(original_settings.pbutton_gpio); EMSESP::system_.remove_gpio(original_settings.pbutton_gpio);
EMSESP::system_.remove_gpio(original_settings.rx_gpio); EMSESP::system_.remove_optional_gpio(original_settings.rx_gpio);
EMSESP::system_.remove_gpio(original_settings.tx_gpio); EMSESP::system_.remove_optional_gpio(original_settings.tx_gpio);
// see if the user has changed the board profile // 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 set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type
@@ -243,13 +243,13 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
// Modbus settings // Modbus settings
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED; settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::RESTART); check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::MODBUS);
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT; settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::RESTART); check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::MODBUS);
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS; 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::RESTART); check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::MODBUS);
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT; settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::RESTART); check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::MODBUS);
// //
// these may need mqtt restart to rebuild HA discovery topics // these may need mqtt restart to rebuild HA discovery topics
@@ -372,7 +372,11 @@ void WebSettingsService::onUpdate() {
Mqtt::reset_mqtt(); // reload MQTT, init HA etc Mqtt::reset_mqtt(); // reload MQTT, init HA etc
} }
if (WebSettings::has_flags(WebSettings::ChangeFlags::MODBUS)) {
EMSESP::system_.modbus_init();
}
WebSettings::reset_flags(); WebSettings::reset_flags();
EMSESP::system_.reset_unused_gpios();
} }
void WebSettingsService::begin() { void WebSettingsService::begin() {
@@ -523,7 +527,7 @@ void WebSettings::set_board_profile(WebSettings & settings) {
} }
// returns true if the value was changed // returns true if the value was changed
bool WebSettings::check_flag(int prev_v, int new_v, uint8_t flag) { bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
if (prev_v != new_v) { if (prev_v != new_v) {
add_flags(flag); add_flags(flag);
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
@@ -534,11 +538,11 @@ bool WebSettings::check_flag(int prev_v, int new_v, uint8_t flag) {
return false; return false;
} }
void WebSettings::add_flags(uint8_t flags) { void WebSettings::add_flags(uint16_t flags) {
flags_ |= flags; flags_ |= flags;
} }
bool WebSettings::has_flags(uint8_t flags) { bool WebSettings::has_flags(uint16_t flags) {
return (flags_ & flags) == flags; return (flags_ & flags) == flags;
} }
@@ -546,7 +550,7 @@ void WebSettings::reset_flags() {
flags_ = ChangeFlags::NONE; flags_ = ChangeFlags::NONE;
} }
uint8_t WebSettings::get_flags() { uint16_t WebSettings::get_flags() {
return flags_; return flags_;
} }

View File

@@ -88,7 +88,7 @@ class WebSettings {
static void read(WebSettings & settings, JsonObject root); static void read(WebSettings & settings, JsonObject root);
static StateUpdateResult update(JsonObject root, WebSettings & settings); static StateUpdateResult update(JsonObject root, WebSettings & settings);
enum ChangeFlags : uint8_t { enum ChangeFlags : uint16_t {
NONE = 0, NONE = 0,
UART = (1 << 0), // 1 - uart UART = (1 << 0), // 1 - uart
SYSLOG = (1 << 1), // 2 - syslog SYSLOG = (1 << 1), // 2 - syslog
@@ -98,19 +98,20 @@ class WebSettings {
LED = (1 << 5), // 32 - led LED = (1 << 5), // 32 - led
BUTTON = (1 << 6), // 64 - button BUTTON = (1 << 6), // 64 - button
MQTT = (1 << 7), // 128 - mqtt MQTT = (1 << 7), // 128 - mqtt
RESTART = 0xFF // 255 - restart request (all changes) MODBUS = (1 << 8), // 256 - modbus
RESTART = 0xFFFF // restart request (all changes)
}; };
static bool check_flag(int prev_v, int new_v, uint8_t flag); static bool check_flag(int prev_v, int new_v, uint16_t flag);
static void add_flags(uint8_t flags); static void add_flags(uint16_t flags);
static bool has_flags(uint8_t flags); static bool has_flags(uint16_t flags);
static void reset_flags(); static void reset_flags();
static uint8_t get_flags(); static uint16_t get_flags();
private: private:
static void set_board_profile(WebSettings & settings); static void set_board_profile(WebSettings & settings);
static uint8_t flags_; static uint16_t flags_;
}; };
class WebSettingsService : public StatefulService<WebSettings> { class WebSettingsService : public StatefulService<WebSettings> {