config can now be changed from webpage

This commit is contained in:
2021-07-15 18:56:37 +02:00
parent 2cd82d3413
commit 7bcf3b6277
8 changed files with 496 additions and 223 deletions

View File

@@ -13,6 +13,7 @@ set(headers
mymdns.h
mymqtt.h
mywifi.h
nvswrappers.h
webserver.h
)

View File

@@ -2,149 +2,65 @@
// system includes
#include <string>
#include <cstdint>
#include <utility>
// esp-idf includes
#include <nvs.h>
#include <hal/gpio_types.h>
// 3rdparty lib includes
#include <tl/expected.hpp>
#include <fmt/core.h>
// local includes
#include "espchrono.h"
#include "futurecpp.h"
#include "nvswrappers.h"
#include "refwhenneeded.h"
namespace deckenlampe {
extern nvs_handle_t nvsHandle;
template<typename T> esp_err_t nvs_get(nvs_handle handle, const char* key, T* out_value) = delete;
template<typename T> esp_err_t nvs_set(nvs_handle handle, const char* key, T value) = delete;
template<typename T>
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>::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>::T value() const { return m_value; }
void setValue(typename cpputils::RefWhenNeeded<T>::T value) { m_value = value; }
tl::expected<T, std::string> readFromFlash() const;
tl::expected<void, std::string> writeToFlash(const T &value);
tl::expected<std::optional<T>, std::string> readFromFlash() const;
tl::expected<void, std::string> writeToFlash(typename cpputils::RefWhenNeeded<T>::T value);
private:
const char * const m_key;
const char * const m_name;
const char * const m_nvsKey;
T m_value;
};
template<typename T>
tl::expected<T, std::string> ConfigWrapper<T>::readFromFlash() const
tl::expected<std::optional<T>, std::string> ConfigWrapper<T>::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<typename T>
tl::expected<void, std::string> ConfigWrapper<T>::writeToFlash(const T &value)
tl::expected<void, std::string> ConfigWrapper<T>::writeToFlash(typename cpputils::RefWhenNeeded<T>::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<gpio_num_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<float>(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<double>(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<uint32_t>(value)); }
template<> inline esp_err_t nvs_set(nvs_handle handle, const char* key, double value) { return nvs_set(handle, key, std::bit_cast<uint64_t>(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

View File

@@ -10,57 +10,57 @@ namespace deckenlampe {
nvs_handle_t nvsHandle;
namespace config {
ConfigWrapper<std::string> hostname{"hostname", "deckenlampe1"};
ConfigWrapper<std::string> hostname{"hostname", "hostname", "deckenlampe1"};
ConfigWrapper<std::string> sta_ssid{"sta_ssid", "ScheissAP"};
ConfigWrapper<std::string> sta_key{"sta_key", "Passwort_123"};
ConfigWrapper<std::string> sta_ssid{"sta_ssid", "sta_ssid", "ScheissAP"};
ConfigWrapper<std::string> sta_key{"sta_key", "sta_key", "Passwort_123"};
ConfigWrapper<std::string> ap_ssid{"ap_ssid", "deckenlampe1"};
ConfigWrapper<std::string> ap_key{"ap_key", "Passwort_123"};
ConfigWrapper<std::string> ap_ssid{"ap_ssid", "ap_ssid", "deckenlampe1"};
ConfigWrapper<std::string> ap_key{"ap_key", "ap_key", "Passwort_123"};
ConfigWrapper<bool> enable_webserver{"enable_webserver", true};
ConfigWrapper<bool> enable_webserver{"enable_webserver", "enableebserver", true};
ConfigWrapper<bool> enable_mdns{"enable_mdns", true};
ConfigWrapper<bool> enable_mdns{"enable_mdns", "enable_mdns", true};
ConfigWrapper<bool> enable_mqtt{"enable_mqtt", true};
ConfigWrapper<std::string> broker_url{"broker_url", "mqtt://192.168.0.2/"};
ConfigWrapper<bool> enable_mqtt{"enable_mqtt", "enable_mqtt", true};
ConfigWrapper<std::string> broker_url{"broker_url", "broker_url", "mqtt://192.168.0.2/"};
ConfigWrapper<bool> enable_lamp{"enable_lamp", true};
ConfigWrapper<gpio_num_t> pins_lamp{"pins_lamp", GPIO_NUM_4};
ConfigWrapper<bool> invertLamp{"invertLamp", true};
ConfigWrapper<std::string> topic_lamp_availability{"topic_lamp_availability", "dahoam/wohnzimmer/deckenlicht1/available"};
ConfigWrapper<std::string> topic_lamp_status{"topic_lamp_status", "dahoam/wohnzimmer/deckenlicht1/status"};
ConfigWrapper<std::string> topic_lamp_set{"topic_lamp_set", "dahoam/wohnzimmer/deckenlicht1/set"};
ConfigWrapper<bool> enable_lamp{"enable_lamp", "enable_lamp", true};
ConfigWrapper<gpio_num_t> pins_lamp{"pins_lamp", "pins_lamp", GPIO_NUM_4};
ConfigWrapper<bool> invertLamp{"invertLamp", "invertLamp", true};
ConfigWrapper<std::string> topic_lamp_availability{"topic_lamp_availability", "topiclampavaila", "dahoam/wohnzimmer/deckenlicht1/available"};
ConfigWrapper<std::string> topic_lamp_status{"topic_lamp_status", "topic_lamp_stat", "dahoam/wohnzimmer/deckenlicht1/status"};
ConfigWrapper<std::string> topic_lamp_set{"topic_lamp_set", "topic_lamp_set", "dahoam/wohnzimmer/deckenlicht1/set"};
ConfigWrapper<bool> enable_switch{"enable_switch", true};
ConfigWrapper<gpio_num_t> pins_switch{"pins_switch", GPIO_NUM_35};
ConfigWrapper<bool> invert_switch{"invert_switch", true};
ConfigWrapper<std::string> topic_switch_availability{"topic_switch_availability", "dahoam/wohnzimmer/schalter1/available"};
ConfigWrapper<std::string> topic_switch_status{"topic_switch_status", "dahoam/wohnzimmer/schalter1/status"};
ConfigWrapper<bool> enable_switch{"enable_switch", "enable_switch", true};
ConfigWrapper<gpio_num_t> pins_switch{"pins_switch", "pins_switch", GPIO_NUM_35};
ConfigWrapper<bool> invert_switch{"invert_switch", "invert_switch", true};
ConfigWrapper<std::string> topic_switch_availability{"topic_switch_availability", "tpcswitchavaila", "dahoam/wohnzimmer/schalter1/available"};
ConfigWrapper<std::string> topic_switch_status{"topic_switch_status", "topicswitchstat", "dahoam/wohnzimmer/schalter1/status"};
ConfigWrapper<bool> enable_dht{"enable_dht", true};
ConfigWrapper<gpio_num_t> pins_dht{"pins_dht", GPIO_NUM_33};
ConfigWrapper<std::string> topic_dht11_availability{"topic_dht11_availability", "dahoam/wohnzimmer/dht11_1/available"};
ConfigWrapper<std::string> topic_dht11_temperature{"topic_dht11_temperature", "dahoam/wohnzimmer/dht11_1/temperature"};
ConfigWrapper<std::string> topic_dht11_humidity{"topic_dht11_humidity", "dahoam/wohnzimmer/dht11_1/humidity"};
ConfigWrapper<bool> enable_dht{"enable_dht", "enable_dht", true};
ConfigWrapper<gpio_num_t> pins_dht{"pins_dht", "pins_dht", GPIO_NUM_33};
ConfigWrapper<std::string> topic_dht11_availability{"topic_dht11_availability", "tpcdht11availab", "dahoam/wohnzimmer/dht11_1/available"};
ConfigWrapper<std::string> topic_dht11_temperature{"topic_dht11_temperature", "tpcdht11tempera", "dahoam/wohnzimmer/dht11_1/temperature"};
ConfigWrapper<std::string> topic_dht11_humidity{"topic_dht11_humidity", "tpcdht11humidit", "dahoam/wohnzimmer/dht11_1/humidity"};
ConfigWrapper<bool> enable_i2c{"enable_i2c", true};
ConfigWrapper<gpio_num_t> pins_sda{"pins_sda", GPIO_NUM_16};
ConfigWrapper<gpio_num_t> pins_scl{"pins_scl", GPIO_NUM_17};
ConfigWrapper<bool> enable_i2c{"enable_i2c", "enable_i2c", true};
ConfigWrapper<gpio_num_t> pins_sda{"pins_sda", "pins_sda", GPIO_NUM_16};
ConfigWrapper<gpio_num_t> pins_scl{"pins_scl", "pins_scl", GPIO_NUM_17};
ConfigWrapper<bool> enable_tsl{"enable_tsl", true};
ConfigWrapper<std::string> topic_tsl2561_availability{"topic_tsl2561_availability", "dahoam/wohnzimmer/tsl2561_1/available"};
ConfigWrapper<std::string> topic_tsl2561_lux{"topic_tsl2561_lux", "dahoam/wohnzimmer/tsl2561_1/lux"};
ConfigWrapper<bool> enable_tsl{"enable_tsl", "enable_tsl", true};
ConfigWrapper<std::string> topic_tsl2561_availability{"topic_tsl2561_availability", "tpctsl2561avail", "dahoam/wohnzimmer/tsl2561_1/available"};
ConfigWrapper<std::string> topic_tsl2561_lux{"topic_tsl2561_lux", "tpc_tsl2561_lux", "dahoam/wohnzimmer/tsl2561_1/lux"};
ConfigWrapper<bool> enable_bmp{"enable_bmp", true};
ConfigWrapper<std::string> topic_bmp085_availability{"topic_bmp085_availability", "dahoam/wohnzimmer/bmp085_1/available"};
ConfigWrapper<std::string> topic_bmp085_pressure{"topic_bmp085_pressure", "dahoam/wohnzimmer/bmp085_1/pressure"};
ConfigWrapper<std::string> topic_bmp085_temperature{"topic_bmp085_temperature", "dahoam/wohnzimmer/bmp085_1/temperature"};
ConfigWrapper<std::string> topic_bmp085_altitude{"topic_bmp085_altitude", "dahoam/wohnzimmer/bmp085_1/altitude"};
ConfigWrapper<bool> enable_bmp{"enable_bmp", "enable_bmp", true};
ConfigWrapper<std::string> topic_bmp085_availability{"topic_bmp085_availability", "topicbmp085avai", "dahoam/wohnzimmer/bmp085_1/available"};
ConfigWrapper<std::string> topic_bmp085_pressure{"topic_bmp085_pressure", "tpcbmp085pressu", "dahoam/wohnzimmer/bmp085_1/pressure"};
ConfigWrapper<std::string> topic_bmp085_temperature{"topic_bmp085_temperature", "tpcbmp085temper", "dahoam/wohnzimmer/bmp085_1/temperature"};
ConfigWrapper<std::string> topic_bmp085_altitude{"topic_bmp085_altitude", "tpcbmp085altitu", "dahoam/wohnzimmer/bmp085_1/altitude"};
ConfigWrapper<espchrono::seconds32> availableTimeoutTime{"availableTimeoutTime", 1min};
ConfigWrapper<espchrono::seconds32> valueUpdateInterval{"valueUpdateInterval", 15s};
ConfigWrapper<espchrono::seconds32> availableTimeoutTime{"availableTimeoutTime", "availTimeouTime", 1min};
ConfigWrapper<espchrono::seconds32> 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<typename T>
void loadParam(ConfigWrapper<T> &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

View File

@@ -66,7 +66,50 @@ extern ConfigWrapper<std::string> topic_bmp085_altitude;
extern ConfigWrapper<espchrono::seconds32> availableTimeoutTime;
extern ConfigWrapper<espchrono::seconds32> valueUpdateInterval;
} // namespace configs
template<typename T>
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();

104
main/nvswrappers.h Normal file
View File

@@ -0,0 +1,104 @@
#pragma once
// system includes
#include <string>
#include <cstdint>
#include <utility>
// esp-idf includes
#include <nvs.h>
#include <hal/gpio_types.h>
// 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<gpio_num_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<float>(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<double>(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<uint32_t>(value)); }
inline esp_err_t nvs_set(nvs_handle handle, const char* key, double value) { return nvs_set(handle, key, std::bit_cast<uint64_t>(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

View File

@@ -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...<br/>\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 += "<a href=\"/on\">on</a><br/>\n"
@@ -108,6 +111,34 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
if (config::enable_switch.value())
body += fmt::format("Switch: {}<br/>\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 += "<br/>\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<br/>\n", lastDhtValue->temperature);
@@ -116,6 +147,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
body += "DHT11 not available at the moment<br/>\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<br/>\n", lastTslValue->lux);
@@ -123,6 +159,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
body += "TSL2561 not available at the moment<br/>\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<br/>\n", lastBmpValue->pressure);
@@ -132,8 +173,12 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
body += "BMP085 not available at the moment<br/>\n";
}
body += "<br/>\n"
"<h2>wifi</h2>\n"
return ESP_OK;
}
esp_err_t webserver_wifi_display(httpd_req_t *req, std::string &body)
{
body += "<h2>wifi</h2>\n"
"<table border=\"1\">\n";
body += fmt::format("<tr><th>state machine state</th><td>{}</td></tr>\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(
"<tr>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"</tr>\n",
"<tr>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"</tr>\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 += "<span style=\"color: red;\">no wifi scan result at the moment!</span><br/>\n";
}
return ESP_OK;
}
esp_err_t webserver_mqtt_display(httpd_req_t *req, std::string &body)
{
if (config::enable_mqtt.value()) {
body += "<br/>\n"
"<h2>MQTT</h2>\n"
@@ -219,8 +269,198 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
body += "</table>\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<typename T>
std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper<T> &config, std::string_view query)
{
return "<span style=\"color: red;\">form not implemented</span>";
}
template<>
std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper<bool> &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 += "<span style=\"color: green;\">Successfully saved</span>";
else
str += fmt::format("<span style=\"color: red;\">Error while saving: {}</span>", htmlentities(result.error()));
} else {
str += fmt::format("<span style=\"color: red;\">Error while saving: Invalid value \"{}\"</span>", 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("<span style=\"color: red;\">Error while saving: httpd_query_key_value() failed with {}</span>", esp_err_to_name(result));
}
}
str += fmt::format("<form>"
"<input type=\"hidden\" name=\"{}\" value=\"false\" />"
"<input type=\"checkbox\" name=\"{}\" value=\"true\"{} />"
"<button type=\"submit\">Save</button>"
"</form>",
htmlentities(config.nvsKey()),
htmlentities(config.nvsKey()),
config.value() ? " checked" : "");
return str;
}
template<>
std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper<std::string> &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 += "<span style=\"color: green;\">Successfully saved</span>";
else
str += fmt::format("<span style=\"color: red;\">Error while saving: {}</span>", 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("<span style=\"color: red;\">Error while saving: httpd_query_key_value() failed with {}</span>", esp_err_to_name(result));
}
}
str += fmt::format("<form>"
"<input type=\"text\" name=\"{}\" value=\"{}\" />"
"<button type=\"submit\">Save</button>"
"</form>",
htmlentities(config.nvsKey()),
htmlentities(config.value()));
return str;
}
template<>
std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper<gpio_num_t> &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<std::underlying_type_t<gpio_num_t>>(value)) {
if (const auto result = config.writeToFlash(gpio_num_t(*parsed)))
str += "<span style=\"color: green;\">Successfully saved</span>";
else
str += fmt::format("<span style=\"color: red;\">Error while saving: {}</span>", htmlentities(result.error()));
} else {
str += fmt::format("<span style=\"color: red;\">Error while saving: Invalid value \"{}\"</span>", 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("<form>"
"<input type=\"number\" name=\"{}\" value=\"{}\" />"
"<button type=\"submit\">Save</button>"
"</form>",
htmlentities(config.nvsKey()),
config.value());
return str;
}
template<>
std::string webserver_form_for_config(httpd_req_t *req, ConfigWrapper<espchrono::seconds32> &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<espchrono::seconds32::rep>(value)) {
if (const auto result = config.writeToFlash(espchrono::seconds32(*parsed)))
str += "<span style=\"color: green;\">Successfully saved</span>";
else
str += fmt::format("<span style=\"color: red;\">Error while saving: {}</span>", htmlentities(result.error()));
} else {
str += fmt::format("<span style=\"color: red;\">Error while saving: Invalid value \"{}\"</span>", 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("<form>"
"<input type=\"number\" name=\"{}\" value=\"{}\" />"
"<button type=\"submit\">Save</button>"
"</form>",
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 += "<br/>\n"
"<h2>config</h2>\n"
"<table border=\"1\">\n"
"<thead>\n"
"<tr>\n"
"<th>name</th>\n"
//"<th>current value</th>\n"
"<th>set new value</th>\n"
"<th>value in flash</th>\n"
"</tr>\n"
"</thead>\n"
"<tbody>\n";
config::foreachConfig([&](auto &config){
using cpputils::toString;
using espchrono::toString;
body += fmt::format("<tr>\n"
"<th>{}</th>\n"
//"<td>{}</td>\n"
"<td>{}</td>\n"
"<td>{}</td>\n"
"</tr>\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 "<span style=\"color: grey;\">not set</span>";
} else
return fmt::format("<span style=\"color: red\">{}</span>", htmlentities(result.error()));
}());
});
body += "</tbody>\n"
"</table>\n";
return ESP_OK;
}