From 7bcf3b627701ec132527094c369abe93df595654 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Thu, 15 Jul 2021 18:56:37 +0200 Subject: [PATCH] config can now be changed from webpage --- components/cpputils | 2 +- components/espcpputils | 2 +- main/CMakeLists.txt | 1 + main/configwrapper.h | 138 ++++--------------- main/myconfig.cpp | 129 +++++++----------- main/myconfig.h | 45 ++++++- main/nvswrappers.h | 104 ++++++++++++++ main/webserver.cpp | 298 +++++++++++++++++++++++++++++++++++++---- 8 files changed, 496 insertions(+), 223 deletions(-) create mode 100644 main/nvswrappers.h diff --git a/components/cpputils b/components/cpputils index a33ed96..89bff1a 160000 --- a/components/cpputils +++ b/components/cpputils @@ -1 +1 @@ -Subproject commit a33ed96bc52af827cb0f2d109b351b95a380039f +Subproject commit 89bff1a8dcac2f951ac3f2c3d7415691ce16ca12 diff --git a/components/espcpputils b/components/espcpputils index a504262..3dc1efd 160000 --- a/components/espcpputils +++ b/components/espcpputils @@ -1 +1 @@ -Subproject commit a504262f3949b267c6f79e97f448ecf1f8fb1a45 +Subproject commit 3dc1efd38dcea8946eb684a1bebf4ff79d0c4112 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 5a4e5bf..100782b 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -13,6 +13,7 @@ set(headers mymdns.h mymqtt.h mywifi.h + nvswrappers.h webserver.h ) diff --git a/main/configwrapper.h b/main/configwrapper.h index 14cac4c..602e618 100644 --- a/main/configwrapper.h +++ b/main/configwrapper.h @@ -2,149 +2,65 @@ // system includes #include -#include -#include - -// esp-idf includes -#include -#include // 3rdparty lib includes #include #include // local includes -#include "espchrono.h" -#include "futurecpp.h" +#include "nvswrappers.h" +#include "refwhenneeded.h" namespace deckenlampe { extern nvs_handle_t nvsHandle; -template esp_err_t nvs_get(nvs_handle handle, const char* key, T* out_value) = delete; -template esp_err_t nvs_set(nvs_handle handle, const char* key, T value) = delete; - template class ConfigWrapper { public: - ConfigWrapper(const char *key, const T &value) : m_key{key}, m_value{value} {} + ConfigWrapper(const char *name, const char *nvsKey, typename cpputils::RefWhenNeeded::T value) : + m_name{name}, + m_nvsKey{nvsKey}, + m_value{value} + {} - const char *key() const { return m_key; } - const T &value() const { return m_value; } - void setValue(const T &value) { m_value = value; } + const char *name() const { return m_name; } + const char *nvsKey() const { return m_nvsKey; } + typename cpputils::RefWhenNeeded::T value() const { return m_value; } + void setValue(typename cpputils::RefWhenNeeded::T value) { m_value = value; } - tl::expected readFromFlash() const; - tl::expected writeToFlash(const T &value); + tl::expected, std::string> readFromFlash() const; + tl::expected writeToFlash(typename cpputils::RefWhenNeeded::T value); private: - const char * const m_key; + const char * const m_name; + const char * const m_nvsKey; T m_value; }; template -tl::expected ConfigWrapper::readFromFlash() const +tl::expected, std::string> ConfigWrapper::readFromFlash() const { T val; - const auto result = nvs_get(nvsHandle, m_key, &val); - //ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_init(): %s", esp_err_to_name(result)); + const auto result = nvs_get(nvsHandle, m_nvsKey, &val); + //ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_get() %s: %s", m_nvsKey, esp_err_to_name(result)); if (result == ESP_OK) return val; + else if (result == ESP_ERR_NVS_NOT_FOUND) + return std::nullopt; else - return tl::make_unexpected(fmt::format("nvs_get() {} failed with {}", m_key, esp_err_to_name(result))); + return tl::make_unexpected(fmt::format("nvs_get() {} failed with {}", m_nvsKey, esp_err_to_name(result))); } template -tl::expected ConfigWrapper::writeToFlash(const T &value) +tl::expected ConfigWrapper::writeToFlash(typename cpputils::RefWhenNeeded::T value) { - return {}; -} - -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, int8_t* out_value) { return nvs_get_i8 (handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint8_t* out_value) { return nvs_get_u8 (handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, int16_t* out_value) { return nvs_get_i16(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint16_t* out_value){ return nvs_get_u16(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, int32_t* out_value) { return nvs_get_i32(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint32_t* out_value){ return nvs_get_u32(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, int64_t* out_value) { return nvs_get_i64(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint64_t* out_value){ return nvs_get_u64(handle, key, out_value); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value) -{ - uint8_t temp; - const auto result = nvs_get(handle, key, &temp); - if (result == ESP_OK && out_value) - *out_value = temp; - return result; -} -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, gpio_num_t* out_value) -{ - std::underlying_type_t temp; - const auto result = nvs_get(handle, key, &temp); - if (result == ESP_OK && out_value) - *out_value = gpio_num_t(temp); - return result; -} -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, float* out_value) -{ - uint32_t temp; - const auto result = nvs_get(handle, key, &temp); + const auto result = nvs_set(nvsHandle, m_nvsKey, value); + //ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_set() %s: %s", m_nvsKey, esp_err_to_name(result)); if (result == ESP_OK) - *out_value = std::bit_cast(temp); - return result; + return {}; + else + return tl::make_unexpected(fmt::format("nvs_set() {} failed with {}", m_nvsKey, esp_err_to_name(result))); } -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, double* out_value) -{ - uint64_t temp; - const auto result = nvs_get(handle, key, &temp); - if (result == ESP_OK) - *out_value = std::bit_cast(temp); - return result; -} -template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value) -{ - size_t length; - if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK) - return result; - - char buf[length]; - if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK) - return result; - - *out_value = buf; - - return ESP_OK; -} - -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, int8_t value) { return nvs_set_i8 (handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint8_t value) { return nvs_set_u8 (handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, int16_t value) { return nvs_set_i16(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint16_t value) { return nvs_set_u16(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, int32_t value) { return nvs_set_i32(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint32_t value) { return nvs_set_u32(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, int64_t value) { return nvs_set_i64(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint64_t value) { return nvs_set_u64(handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, bool value) { return nvs_set_u8 (handle, key, value); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, gpio_num_t value) { return nvs_set (handle, key, std::to_underlying(value)); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, float value) { return nvs_set(handle, key, std::bit_cast(value)); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, double value) { return nvs_set(handle, key, std::bit_cast(value)); } -template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value) { return nvs_set_str(handle, key, value.c_str()); } - - -#define IMPLEMENT_NVS_GET_SET_CHRONO(Name) \ - template<> inline esp_err_t nvs_get(nvs_handle handle, const char* key, Name* out_value) \ - { \ - Name::rep temp; \ - const auto result = nvs_get(handle, key, &temp); \ - if (result == ESP_OK && out_value) \ - *out_value = Name{temp}; \ - return result; \ - } \ - \ - template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, Name value) { return nvs_set(handle, key, Name::rep(value.count())); } - -IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::milliseconds32) -IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::seconds32) -IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::minutes32) -IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::hours32) -#undef IMPLEMENT_NVS_GET_SET_CHRONO } // namespace deckenlampe diff --git a/main/myconfig.cpp b/main/myconfig.cpp index 123a7f4..c48729e 100644 --- a/main/myconfig.cpp +++ b/main/myconfig.cpp @@ -10,57 +10,57 @@ namespace deckenlampe { nvs_handle_t nvsHandle; namespace config { -ConfigWrapper hostname{"hostname", "deckenlampe1"}; +ConfigWrapper hostname{"hostname", "hostname", "deckenlampe1"}; -ConfigWrapper sta_ssid{"sta_ssid", "ScheissAP"}; -ConfigWrapper sta_key{"sta_key", "Passwort_123"}; +ConfigWrapper sta_ssid{"sta_ssid", "sta_ssid", "ScheissAP"}; +ConfigWrapper sta_key{"sta_key", "sta_key", "Passwort_123"}; -ConfigWrapper ap_ssid{"ap_ssid", "deckenlampe1"}; -ConfigWrapper ap_key{"ap_key", "Passwort_123"}; +ConfigWrapper ap_ssid{"ap_ssid", "ap_ssid", "deckenlampe1"}; +ConfigWrapper ap_key{"ap_key", "ap_key", "Passwort_123"}; -ConfigWrapper enable_webserver{"enable_webserver", true}; +ConfigWrapper enable_webserver{"enable_webserver", "enableebserver", true}; -ConfigWrapper enable_mdns{"enable_mdns", true}; +ConfigWrapper enable_mdns{"enable_mdns", "enable_mdns", true}; -ConfigWrapper enable_mqtt{"enable_mqtt", true}; -ConfigWrapper broker_url{"broker_url", "mqtt://192.168.0.2/"}; +ConfigWrapper enable_mqtt{"enable_mqtt", "enable_mqtt", true}; +ConfigWrapper broker_url{"broker_url", "broker_url", "mqtt://192.168.0.2/"}; -ConfigWrapper enable_lamp{"enable_lamp", true}; -ConfigWrapper pins_lamp{"pins_lamp", GPIO_NUM_4}; -ConfigWrapper invertLamp{"invertLamp", true}; -ConfigWrapper topic_lamp_availability{"topic_lamp_availability", "dahoam/wohnzimmer/deckenlicht1/available"}; -ConfigWrapper topic_lamp_status{"topic_lamp_status", "dahoam/wohnzimmer/deckenlicht1/status"}; -ConfigWrapper topic_lamp_set{"topic_lamp_set", "dahoam/wohnzimmer/deckenlicht1/set"}; +ConfigWrapper enable_lamp{"enable_lamp", "enable_lamp", true}; +ConfigWrapper pins_lamp{"pins_lamp", "pins_lamp", GPIO_NUM_4}; +ConfigWrapper invertLamp{"invertLamp", "invertLamp", true}; +ConfigWrapper topic_lamp_availability{"topic_lamp_availability", "topiclampavaila", "dahoam/wohnzimmer/deckenlicht1/available"}; +ConfigWrapper topic_lamp_status{"topic_lamp_status", "topic_lamp_stat", "dahoam/wohnzimmer/deckenlicht1/status"}; +ConfigWrapper topic_lamp_set{"topic_lamp_set", "topic_lamp_set", "dahoam/wohnzimmer/deckenlicht1/set"}; -ConfigWrapper enable_switch{"enable_switch", true}; -ConfigWrapper pins_switch{"pins_switch", GPIO_NUM_35}; -ConfigWrapper invert_switch{"invert_switch", true}; -ConfigWrapper topic_switch_availability{"topic_switch_availability", "dahoam/wohnzimmer/schalter1/available"}; -ConfigWrapper topic_switch_status{"topic_switch_status", "dahoam/wohnzimmer/schalter1/status"}; +ConfigWrapper enable_switch{"enable_switch", "enable_switch", true}; +ConfigWrapper pins_switch{"pins_switch", "pins_switch", GPIO_NUM_35}; +ConfigWrapper invert_switch{"invert_switch", "invert_switch", true}; +ConfigWrapper topic_switch_availability{"topic_switch_availability", "tpcswitchavaila", "dahoam/wohnzimmer/schalter1/available"}; +ConfigWrapper topic_switch_status{"topic_switch_status", "topicswitchstat", "dahoam/wohnzimmer/schalter1/status"}; -ConfigWrapper enable_dht{"enable_dht", true}; -ConfigWrapper pins_dht{"pins_dht", GPIO_NUM_33}; -ConfigWrapper topic_dht11_availability{"topic_dht11_availability", "dahoam/wohnzimmer/dht11_1/available"}; -ConfigWrapper topic_dht11_temperature{"topic_dht11_temperature", "dahoam/wohnzimmer/dht11_1/temperature"}; -ConfigWrapper topic_dht11_humidity{"topic_dht11_humidity", "dahoam/wohnzimmer/dht11_1/humidity"}; +ConfigWrapper enable_dht{"enable_dht", "enable_dht", true}; +ConfigWrapper pins_dht{"pins_dht", "pins_dht", GPIO_NUM_33}; +ConfigWrapper topic_dht11_availability{"topic_dht11_availability", "tpcdht11availab", "dahoam/wohnzimmer/dht11_1/available"}; +ConfigWrapper topic_dht11_temperature{"topic_dht11_temperature", "tpcdht11tempera", "dahoam/wohnzimmer/dht11_1/temperature"}; +ConfigWrapper topic_dht11_humidity{"topic_dht11_humidity", "tpcdht11humidit", "dahoam/wohnzimmer/dht11_1/humidity"}; -ConfigWrapper enable_i2c{"enable_i2c", true}; -ConfigWrapper pins_sda{"pins_sda", GPIO_NUM_16}; -ConfigWrapper pins_scl{"pins_scl", GPIO_NUM_17}; +ConfigWrapper enable_i2c{"enable_i2c", "enable_i2c", true}; +ConfigWrapper pins_sda{"pins_sda", "pins_sda", GPIO_NUM_16}; +ConfigWrapper pins_scl{"pins_scl", "pins_scl", GPIO_NUM_17}; -ConfigWrapper enable_tsl{"enable_tsl", true}; -ConfigWrapper topic_tsl2561_availability{"topic_tsl2561_availability", "dahoam/wohnzimmer/tsl2561_1/available"}; -ConfigWrapper topic_tsl2561_lux{"topic_tsl2561_lux", "dahoam/wohnzimmer/tsl2561_1/lux"}; +ConfigWrapper enable_tsl{"enable_tsl", "enable_tsl", true}; +ConfigWrapper topic_tsl2561_availability{"topic_tsl2561_availability", "tpctsl2561avail", "dahoam/wohnzimmer/tsl2561_1/available"}; +ConfigWrapper topic_tsl2561_lux{"topic_tsl2561_lux", "tpc_tsl2561_lux", "dahoam/wohnzimmer/tsl2561_1/lux"}; -ConfigWrapper enable_bmp{"enable_bmp", true}; -ConfigWrapper topic_bmp085_availability{"topic_bmp085_availability", "dahoam/wohnzimmer/bmp085_1/available"}; -ConfigWrapper topic_bmp085_pressure{"topic_bmp085_pressure", "dahoam/wohnzimmer/bmp085_1/pressure"}; -ConfigWrapper topic_bmp085_temperature{"topic_bmp085_temperature", "dahoam/wohnzimmer/bmp085_1/temperature"}; -ConfigWrapper topic_bmp085_altitude{"topic_bmp085_altitude", "dahoam/wohnzimmer/bmp085_1/altitude"}; +ConfigWrapper enable_bmp{"enable_bmp", "enable_bmp", true}; +ConfigWrapper topic_bmp085_availability{"topic_bmp085_availability", "topicbmp085avai", "dahoam/wohnzimmer/bmp085_1/available"}; +ConfigWrapper topic_bmp085_pressure{"topic_bmp085_pressure", "tpcbmp085pressu", "dahoam/wohnzimmer/bmp085_1/pressure"}; +ConfigWrapper topic_bmp085_temperature{"topic_bmp085_temperature", "tpcbmp085temper", "dahoam/wohnzimmer/bmp085_1/temperature"}; +ConfigWrapper topic_bmp085_altitude{"topic_bmp085_altitude", "tpcbmp085altitu", "dahoam/wohnzimmer/bmp085_1/altitude"}; -ConfigWrapper availableTimeoutTime{"availableTimeoutTime", 1min}; -ConfigWrapper valueUpdateInterval{"valueUpdateInterval", 15s}; +ConfigWrapper availableTimeoutTime{"availableTimeoutTime", "availTimeouTime", 1min}; +ConfigWrapper valueUpdateInterval{"valueUpdateInterval", "valUpdaInterval", 15s}; } // namespace config namespace { @@ -87,44 +87,7 @@ void init_config() return; } - loadParam(config::hostname); - loadParam(config::sta_ssid); - loadParam(config::sta_key); - loadParam(config::ap_ssid); - loadParam(config::ap_key); - loadParam(config::enable_webserver); - loadParam(config::enable_mdns); - loadParam(config::enable_mqtt); - loadParam(config::broker_url); - loadParam(config::enable_lamp); - loadParam(config::pins_lamp); - loadParam(config::invertLamp); - loadParam(config::topic_lamp_availability); - loadParam(config::topic_lamp_status); - loadParam(config::topic_lamp_set); - loadParam(config::enable_switch); - loadParam(config::pins_switch); - loadParam(config::invert_switch); - loadParam(config::topic_switch_availability); - loadParam(config::topic_switch_status); - loadParam(config::enable_dht); - loadParam(config::pins_dht); - loadParam(config::topic_dht11_availability); - loadParam(config::topic_dht11_temperature); - loadParam(config::topic_dht11_humidity); - loadParam(config::enable_i2c); - loadParam(config::pins_sda); - loadParam(config::pins_scl); - loadParam(config::enable_tsl); - loadParam(config::topic_tsl2561_availability); - loadParam(config::topic_tsl2561_lux); - loadParam(config::enable_bmp); - loadParam(config::topic_bmp085_availability); - loadParam(config::topic_bmp085_pressure); - loadParam(config::topic_bmp085_temperature); - loadParam(config::topic_bmp085_altitude); - loadParam(config::availableTimeoutTime); - loadParam(config::valueUpdateInterval); + config::foreachConfig([](auto &config){ loadParam(config); }); } void update_config() @@ -135,10 +98,16 @@ namespace { template void loadParam(ConfigWrapper &config) { - if (const auto value = config.readFromFlash()) - config.setValue(*value); - else - ESP_LOGE(TAG, "error loading param %s: %.*s", config.key(), value.error().size(), value.error().data()); + if (const auto len = std::strlen(config.nvsKey()); len > 15) { + ESP_LOGE(TAG, "%s too long %zd (%zd)", config.nvsKey(), len, len-15); + assert(false); + } + + if (const auto value = config.readFromFlash()) { + if (*value) + config.setValue(**value); + } else + ESP_LOGE(TAG, "error loading config %s: %.*s", config.name(), value.error().size(), value.error().data()); } } // namespace } // namespace deckenlamp diff --git a/main/myconfig.h b/main/myconfig.h index d4ae15c..d0fe192 100644 --- a/main/myconfig.h +++ b/main/myconfig.h @@ -66,7 +66,50 @@ extern ConfigWrapper topic_bmp085_altitude; extern ConfigWrapper availableTimeoutTime; extern ConfigWrapper valueUpdateInterval; -} // namespace configs + +template +void foreachConfig(T &&callback) +{ + callback(hostname); + callback(sta_ssid); + callback(sta_key); + callback(ap_ssid); + callback(ap_key); + callback(enable_webserver); + callback(enable_mdns); + callback(enable_mqtt); + callback(broker_url); + callback(enable_lamp); + callback(pins_lamp); + callback(invertLamp); + callback(topic_lamp_availability); + callback(topic_lamp_status); + callback(topic_lamp_set); + callback(enable_switch); + callback(pins_switch); + callback(invert_switch); + callback(topic_switch_availability); + callback(topic_switch_status); + callback(enable_dht); + callback(pins_dht); + callback(topic_dht11_availability); + callback(topic_dht11_temperature); + callback(topic_dht11_humidity); + callback(enable_i2c); + callback(pins_sda); + callback(pins_scl); + callback(enable_tsl); + callback(topic_tsl2561_availability); + callback(topic_tsl2561_lux); + callback(enable_bmp); + callback(topic_bmp085_availability); + callback(topic_bmp085_pressure); + callback(topic_bmp085_temperature); + callback(topic_bmp085_altitude); + callback(availableTimeoutTime); + callback(valueUpdateInterval); +} +} // namespace config void init_config(); void update_config(); diff --git a/main/nvswrappers.h b/main/nvswrappers.h new file mode 100644 index 0000000..b25a7f9 --- /dev/null +++ b/main/nvswrappers.h @@ -0,0 +1,104 @@ +#pragma once + +// system includes +#include +#include +#include + +// esp-idf includes +#include +#include + +// local includes +#include "futurecpp.h" +#include "espchrono.h" + +namespace deckenlampe { +inline esp_err_t nvs_get(nvs_handle handle, const char* key, int8_t* out_value) { return nvs_get_i8 (handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint8_t* out_value) { return nvs_get_u8 (handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, int16_t* out_value) { return nvs_get_i16(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint16_t* out_value){ return nvs_get_u16(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, int32_t* out_value) { return nvs_get_i32(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint32_t* out_value){ return nvs_get_u32(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, int64_t* out_value) { return nvs_get_i64(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint64_t* out_value){ return nvs_get_u64(handle, key, out_value); } +inline esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value) +{ + uint8_t temp; + const auto result = nvs_get(handle, key, &temp); + if (result == ESP_OK && out_value) + *out_value = temp; + return result; +} +inline esp_err_t nvs_get(nvs_handle handle, const char* key, gpio_num_t* out_value) +{ + std::underlying_type_t temp; + const auto result = nvs_get(handle, key, &temp); + if (result == ESP_OK && out_value) + *out_value = gpio_num_t(temp); + return result; +} +inline esp_err_t nvs_get(nvs_handle handle, const char* key, float* out_value) +{ + uint32_t temp; + const auto result = nvs_get(handle, key, &temp); + if (result == ESP_OK) + *out_value = std::bit_cast(temp); + return result; +} +inline esp_err_t nvs_get(nvs_handle handle, const char* key, double* out_value) +{ + uint64_t temp; + const auto result = nvs_get(handle, key, &temp); + if (result == ESP_OK) + *out_value = std::bit_cast(temp); + return result; +} +inline esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value) +{ + size_t length; + if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK) + return result; + + char buf[length]; + if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK) + return result; + + *out_value = buf; + + return ESP_OK; +} + +inline esp_err_t nvs_set(nvs_handle handle, const char* key, int8_t value) { return nvs_set_i8 (handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint8_t value) { return nvs_set_u8 (handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, int16_t value) { return nvs_set_i16(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint16_t value) { return nvs_set_u16(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, int32_t value) { return nvs_set_i32(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint32_t value) { return nvs_set_u32(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, int64_t value) { return nvs_set_i64(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint64_t value) { return nvs_set_u64(handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, bool value) { return nvs_set_u8 (handle, key, value); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, gpio_num_t value) { return nvs_set (handle, key, std::to_underlying(value)); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, float value) { return nvs_set(handle, key, std::bit_cast(value)); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, double value) { return nvs_set(handle, key, std::bit_cast(value)); } +inline esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value) { return nvs_set_str(handle, key, value.c_str()); } + + +#define IMPLEMENT_NVS_GET_SET_CHRONO(Name) \ + inline esp_err_t nvs_get(nvs_handle handle, const char* key, Name* out_value) \ + { \ + Name::rep temp; \ + const auto result = nvs_get(handle, key, &temp); \ + if (result == ESP_OK && out_value) \ + *out_value = Name{temp}; \ + return result; \ + } \ + \ + inline esp_err_t nvs_set(nvs_handle handle, const char* key, Name value) { return nvs_set(handle, key, Name::rep(value.count())); } + +IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::milliseconds32) +IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::seconds32) +IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::minutes32) +IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::hours32) +#undef IMPLEMENT_NVS_GET_SET_CHRONO +} // namespace deckenlampe diff --git a/main/webserver.cpp b/main/webserver.cpp index fc6f7a2..810bb23 100644 --- a/main/webserver.cpp +++ b/main/webserver.cpp @@ -19,6 +19,11 @@ #include "feature_bmp.h" #include "mymqtt.h" #include "espwifistack.h" +#include "espcppmacros.h" +#include "espstrutils.h" +#include "strutils.h" +#include "espchrono.h" +#include "numberparsing.h" namespace deckenlampe { httpd_handle_t httpdHandle; @@ -73,25 +78,23 @@ void update_webserver() } namespace { -#define CALL_AND_EXIT_ON_ERROR(METHOD, ...) \ - if (const auto result = METHOD(__VA_ARGS__); result != ESP_OK) \ - { \ - ESP_LOGE(TAG, "%s() failed with %s", #METHOD, esp_err_to_name(result)); \ - return result; \ - } - -#define CALL_AND_EXIT(METHOD, ...) \ - if (const auto result = METHOD(__VA_ARGS__); result != ESP_OK) \ - { \ - ESP_LOGE(TAG, "%s() failed with %s", #METHOD, esp_err_to_name(result)); \ - return result; \ - } \ - else \ - return result; +esp_err_t webserver_dht_display(httpd_req_t *req, std::string &body); +esp_err_t webserver_tsl_display(httpd_req_t *req, std::string &body); +esp_err_t webserver_bmp_display(httpd_req_t *req, std::string &body); +esp_err_t webserver_wifi_display(httpd_req_t *req, std::string &body); +esp_err_t webserver_mqtt_display(httpd_req_t *req, std::string &body); +esp_err_t webserver_config_display(httpd_req_t *req, std::string &body, std::string_view query); esp_err_t webserver_root_handler(httpd_req_t *req) { - std::string body = "this is work in progress...
\n"; + std::string query; + + if (const size_t queryLength = httpd_req_get_url_query_len(req)) { + query.resize(queryLength); + CALL_AND_EXIT_ON_ERROR(httpd_req_get_url_query_str, req, query.data(), query.size() + 1) + } + + std::string body; if (config::enable_lamp.value()) { body += "on
\n" @@ -108,6 +111,34 @@ esp_err_t webserver_root_handler(httpd_req_t *req) if (config::enable_switch.value()) body += fmt::format("Switch: {}
\n", switchState ? "ON" : "OFF"); + if (const auto result = webserver_dht_display(req, body); result != ESP_OK) + return result; + + if (const auto result = webserver_tsl_display(req, body); result != ESP_OK) + return result; + + if (const auto result = webserver_bmp_display(req, body); result != ESP_OK) + return result; + + body += "
\n"; + + if (const auto result = webserver_wifi_display(req, body); result != ESP_OK) + return result; + + if (const auto result = webserver_mqtt_display(req, body); result != ESP_OK) + return result; + + if (const auto result = webserver_config_display(req, body, query); result != ESP_OK) + return result; + + CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") + CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) + + return ESP_OK; +} + +esp_err_t webserver_dht_display(httpd_req_t *req, std::string &body) +{ if (config::enable_dht.value()) { if (lastDhtValue) { body += fmt::format("DHT11 Temperature: {:.1f} C
\n", lastDhtValue->temperature); @@ -116,6 +147,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req) body += "DHT11 not available at the moment
\n"; } + return ESP_OK; +} + +esp_err_t webserver_tsl_display(httpd_req_t *req, std::string &body) +{ if (config::enable_i2c.value() && config::enable_tsl.value()) { if (lastTslValue) { body += fmt::format("TSL2561 Brightness: {:.1f} lux
\n", lastTslValue->lux); @@ -123,6 +159,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req) body += "TSL2561 not available at the moment
\n"; } + return ESP_OK; +} + +esp_err_t webserver_bmp_display(httpd_req_t *req, std::string &body) +{ if (config::enable_i2c.value() && config::enable_bmp.value()) { if (lastBmpValue) { body += fmt::format("BMP085 Pressure: {:.1f} lux
\n", lastBmpValue->pressure); @@ -132,8 +173,12 @@ esp_err_t webserver_root_handler(httpd_req_t *req) body += "BMP085 not available at the moment
\n"; } - body += "
\n" - "

wifi

\n" + return ESP_OK; +} + +esp_err_t webserver_wifi_display(httpd_req_t *req, std::string &body) +{ + body += "

wifi

\n" "\n"; body += fmt::format("\n", wifi_stack::toString(wifi_stack::wifiStateMachineState)); @@ -182,15 +227,15 @@ esp_err_t webserver_root_handler(httpd_req_t *req) for (const auto &entry : scanResult->entries) { body += fmt::format( - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n", + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n", htmlentities(entry.ssid), wifi_stack::toString(entry.authmode), wifi_stack::toString(entry.pairwise_cipher), @@ -206,6 +251,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req) body += "no wifi scan result at the moment!
\n"; } + return ESP_OK; +} + +esp_err_t webserver_mqtt_display(httpd_req_t *req, std::string &body) +{ if (config::enable_mqtt.value()) { body += "
\n" "

MQTT

\n" @@ -219,8 +269,198 @@ esp_err_t webserver_root_handler(httpd_req_t *req) body += "
state machine state{}
{}{}{}{}{}{}{}
{}{}{}{}{}{}{}
\n"; } - CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") - CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) + return ESP_OK; +} + +template +std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper &config, std::string_view query) +{ + return "form not implemented"; +} + +template<> +std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper &config, std::string_view query) +{ + std::string str; + + { + char valueBufEncoded[256]; + if (const auto result = httpd_query_key_value(query.data(), config.nvsKey(), valueBufEncoded, 256); result == ESP_OK) { + char valueBuf[257]; + espcpputils::urldecode(valueBuf, valueBufEncoded); + + std::string_view value{valueBuf}; + + if (value == "true" || value == "false") { + if (const auto result = config.writeToFlash(value == "true")) + str += "Successfully saved"; + else + str += fmt::format("Error while saving: {}", htmlentities(result.error())); + } else { + str += fmt::format("Error while saving: Invalid value \"{}\"", htmlentities(value)); + } + } else if (result != ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "httpd_query_key_value() %s failed with %s", config.nvsKey(), esp_err_to_name(result)); + str += fmt::format("Error while saving: httpd_query_key_value() failed with {}", esp_err_to_name(result)); + } + } + + str += fmt::format("
" + "" + "" + "" + "
", + htmlentities(config.nvsKey()), + htmlentities(config.nvsKey()), + config.value() ? " checked" : ""); + + return str; +} + +template<> +std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper &config, std::string_view query) +{ + std::string str; + + { + char valueBufEncoded[256]; + if (const auto result = httpd_query_key_value(query.data(), config.nvsKey(), valueBufEncoded, 256); result == ESP_OK) { + char valueBuf[257]; + espcpputils::urldecode(valueBuf, valueBufEncoded); + + if (const auto result = config.writeToFlash(valueBuf)) + str += "Successfully saved"; + else + str += fmt::format("Error while saving: {}", htmlentities(result.error())); + } else if (result != ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "httpd_query_key_value() %s failed with %s", config.nvsKey(), esp_err_to_name(result)); + str += fmt::format("Error while saving: httpd_query_key_value() failed with {}", esp_err_to_name(result)); + } + } + + str += fmt::format("
" + "" + "" + "
", + htmlentities(config.nvsKey()), + htmlentities(config.value())); + + return str; +} + +template<> +std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper &config, std::string_view query) +{ + std::string str; + + { + char valueBufEncoded[256]; + if (const auto result = httpd_query_key_value(query.data(), config.nvsKey(), valueBufEncoded, 256); result == ESP_OK) { + char valueBuf[257]; + espcpputils::urldecode(valueBuf, valueBufEncoded); + + std::string_view value{valueBuf}; + + if (auto parsed = cpputils::fromString>(value)) { + if (const auto result = config.writeToFlash(gpio_num_t(*parsed))) + str += "Successfully saved"; + else + str += fmt::format("Error while saving: {}", htmlentities(result.error())); + } else { + str += fmt::format("Error while saving: Invalid value \"{}\"", htmlentities(value)); + } + } else if (result != ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "httpd_query_key_value() %s failed with %s", config.nvsKey(), esp_err_to_name(result)); + } + } + + str += fmt::format("
" + "" + "" + "
", + htmlentities(config.nvsKey()), + config.value()); + + return str; +} + +template<> +std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper &config, std::string_view query) +{ + std::string str; + + { + char valueBufEncoded[256]; + if (const auto result = httpd_query_key_value(query.data(), config.nvsKey(), valueBufEncoded, 256); result == ESP_OK) { + char valueBuf[257]; + espcpputils::urldecode(valueBuf, valueBufEncoded); + + std::string_view value{valueBuf}; + + if (auto parsed = cpputils::fromString(value)) { + if (const auto result = config.writeToFlash(espchrono::seconds32(*parsed))) + str += "Successfully saved"; + else + str += fmt::format("Error while saving: {}", htmlentities(result.error())); + } else { + str += fmt::format("Error while saving: Invalid value \"{}\"", htmlentities(value)); + } + } else if (result != ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "httpd_query_key_value() %s failed with %s", config.nvsKey(), esp_err_to_name(result)); + } + } + + str += fmt::format("
" + "" + "" + "
", + htmlentities(config.nvsKey()), + config.value().count()); + + return str; +} + +esp_err_t webserver_config_display(httpd_req_t *req, std::string &body, std::string_view query) +{ + body += "
\n" + "

config

\n" + "\n" + "\n" + "\n" + "\n" + //"\n" + "\n" + "\n" + "\n" + "\n" + "\n"; + + config::foreachConfig([&](auto &config){ + using cpputils::toString; + using espchrono::toString; + + body += fmt::format("\n" + "\n" + //"\n" + "\n" + "\n" + "\n", + htmlentities(config.name()), + //htmlentities(toString(config.value())), + webserver_form_for_config(req, config, query), + [&]() -> std::string { + if (const auto result = config.readFromFlash()) { + if (*result) + return htmlentities(toString(**result)); + else + return "not set"; + } else + return fmt::format("{}", htmlentities(result.error())); + }()); + }); + + body += "\n" + "
namecurrent valueset new valuevalue in flash
{}{}{}{}
\n"; return ESP_OK; }