From f74e16d44395a9367b32efeeedc97f4ab106e765 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Thu, 15 Jul 2021 13:17:07 +0200 Subject: [PATCH] Code cleanups --- components/Adafruit_TSL2561 | 2 +- components/DHT-sensor-library | 2 +- main/CMakeLists.txt | 20 + main/feature_bmp.cpp | 118 +++++ main/feature_bmp.h | 25 + main/feature_dht.cpp | 91 ++++ main/feature_dht.h | 23 + main/feature_lamp.cpp | 33 ++ main/feature_lamp.h | 13 + main/feature_switch.cpp | 66 +++ main/feature_switch.h | 11 + main/feature_tsl.cpp | 120 +++++ main/feature_tsl.h | 21 + main/main.cpp | 855 ++-------------------------------- main/myconfig.cpp | 36 ++ main/myconfig.h | 64 +++ main/mymdns.cpp | 50 ++ main/mymdns.h | 6 + main/mymqtt.cpp | 194 ++++++++ main/mymqtt.h | 18 + main/mywifi.cpp | 65 +++ main/mywifi.h | 6 + main/webserver.cpp | 202 ++++++++ main/webserver.h | 11 + 24 files changed, 1230 insertions(+), 822 deletions(-) create mode 100644 main/feature_bmp.cpp create mode 100644 main/feature_bmp.h create mode 100644 main/feature_dht.cpp create mode 100644 main/feature_dht.h create mode 100644 main/feature_lamp.cpp create mode 100644 main/feature_lamp.h create mode 100644 main/feature_switch.cpp create mode 100644 main/feature_switch.h create mode 100644 main/feature_tsl.cpp create mode 100644 main/feature_tsl.h create mode 100644 main/myconfig.cpp create mode 100644 main/myconfig.h create mode 100644 main/mymdns.cpp create mode 100644 main/mymdns.h create mode 100644 main/mymqtt.cpp create mode 100644 main/mymqtt.h create mode 100644 main/mywifi.cpp create mode 100644 main/mywifi.h create mode 100644 main/webserver.cpp create mode 100644 main/webserver.h diff --git a/components/Adafruit_TSL2561 b/components/Adafruit_TSL2561 index 8737563..98cf7b8 160000 --- a/components/Adafruit_TSL2561 +++ b/components/Adafruit_TSL2561 @@ -1 +1 @@ -Subproject commit 8737563fa6b83e4670d955e3418240ad4cad79e9 +Subproject commit 98cf7b89b9c34ae99714f701aa53eea9c605f145 diff --git a/components/DHT-sensor-library b/components/DHT-sensor-library index 845a39f..5b9e875 160000 --- a/components/DHT-sensor-library +++ b/components/DHT-sensor-library @@ -1 +1 @@ -Subproject commit 845a39f12b5e4045c52c8d5d102beebb7d3c9a76 +Subproject commit 5b9e8755b68f642e296fab1fa7b0c214bf9a08bd diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4487b3f..6f4a7af 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,10 +2,30 @@ idf_build_get_property(project_dir PROJECT_DIR) message(STATUS "The project dir is: ${project_dir}") set(headers + feature_bmp.h + feature_dht.h + feature_lamp.h + feature_switch.h + feature_tsl.h + myconfig.h + mymdns.h + mymqtt.h + mywifi.h + webserver.h ) set(sources + feature_bmp.cpp + feature_dht.cpp + feature_lamp.cpp + feature_switch.cpp + feature_tsl.cpp main.cpp + myconfig.cpp + mymdns.cpp + mymqtt.cpp + mywifi.cpp + webserver.cpp ) set(dependencies diff --git a/main/feature_bmp.cpp b/main/feature_bmp.cpp new file mode 100644 index 0000000..c350cb6 --- /dev/null +++ b/main/feature_bmp.cpp @@ -0,0 +1,118 @@ +#include "feature_bmp.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include + +// local includes +#include "myconfig.h" +#include "mymqtt.h" +#include "delayedconstruction.h" + +using namespace std::chrono_literals; + +namespace deckenlampe { +std::optional lastBmpValue; +espchrono::millis_clock::time_point last_bmp085_pressure_pub; +espchrono::millis_clock::time_point last_bmp085_temperature_pub; +espchrono::millis_clock::time_point last_bmp085_altitude_pub; +bool bmpInitialized{}; + +namespace { +constexpr const char * const TAG = "BMP"; + +cpputils::DelayedConstruction bmp; +espchrono::millis_clock::time_point last_bmp085_readout; +} // namespace + +void init_bmp() +{ + if (!config::enable_bmp) + return; + + bmp.construct(10085); + + ESP_LOGI(TAG, "calling bmp.begin()..."); + bmpInitialized = bmp->begin(); + ESP_LOGI(TAG, "finished with %s", bmpInitialized ? "true" : "false"); + + if (bmpInitialized) { + sensor_t sensor = bmp->getSensor(); + ESP_LOGI(TAG, "------------------------------------"); + ESP_LOGI(TAG, "Sensor: %s", sensor.name); + ESP_LOGI(TAG, "Driver Ver: %i", sensor.version); + ESP_LOGI(TAG, "Unique ID: %i", sensor.sensor_id); + ESP_LOGI(TAG, "Max Value: %.1f hPa", sensor.max_value); + ESP_LOGI(TAG, "Min Value: %.1f hPa", sensor.min_value); + ESP_LOGI(TAG, "Resolution: %.1f hPa", sensor.resolution); + ESP_LOGI(TAG, "------------------------------------"); + ESP_LOGI(TAG, ""); + } +} + +void update_bmp() +{ + if (!config::enable_bmp) + return; + + if (bmpInitialized) { + if (espchrono::ago(last_bmp085_readout) < 5s) + return; + + last_bmp085_readout = espchrono::millis_clock::now(); + + if (std::optional values = bmp->getTemperatureAndPressure()) { + if (values->temperature && values->pressure) { + BmpValue bmpValue { + .timestamp = espchrono::millis_clock::now(), + .pressure = values->pressure, + .temperature = values->temperature}; + ESP_LOGI(TAG, "read bmp Pressure: %.1f hPa", bmpValue.pressure); + ESP_LOGI(TAG, "read bmp Temperature: %.1f C", bmpValue.temperature); + + /* Then convert the atmospheric pressure, and SLP to altitude */ + /* Update this next line with the current SLP for better results */ + constexpr const float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; + bmpValue.altitude = bmp->pressureToAltitude(seaLevelPressure, bmpValue.pressure); + ESP_LOGI(TAG, "read bmp Altitude: %.1f m", bmpValue.altitude); + + if (mqttClient && mqttConnected) { + if (!lastBmpValue) + mqttVerbosePub(config::topic_bmp085_availability, "online", 0, 1); + if (!lastBmpValue || espchrono::ago(last_bmp085_pressure_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_bmp085_pressure, fmt::format("{:.1f}", bmpValue.pressure), 0, 1) >= 0) + last_bmp085_pressure_pub = espchrono::millis_clock::now(); + } + if (!lastBmpValue || espchrono::ago(last_bmp085_temperature_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_bmp085_temperature, fmt::format("{:.1f}", bmpValue.temperature), 0, 1) >= 0) + last_bmp085_temperature_pub = espchrono::millis_clock::now(); + } + if (!lastBmpValue || espchrono::ago(last_bmp085_altitude_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_bmp085_altitude, fmt::format("{:.1f}", bmpValue.altitude), 0, 1) >= 0) + last_bmp085_altitude_pub = espchrono::millis_clock::now(); + } + } + + lastBmpValue = bmpValue; + } else { + ESP_LOGW(TAG, "bmp sensor error"); + goto bmpOffline; + } + } else { + ESP_LOGW(TAG, "bmp failed"); + goto bmpOffline; + } + } else { + bmpOffline: + if (lastBmpValue && espchrono::ago(lastBmpValue->timestamp) >= config::availableTimeoutTime) { + ESP_LOGW(TAG, "bmp timeouted"); + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_bmp085_availability, "offline", 0, 1); + lastBmpValue = std::nullopt; + } + } +} +} // namespace deckenlampe diff --git a/main/feature_bmp.h b/main/feature_bmp.h new file mode 100644 index 0000000..174d8ca --- /dev/null +++ b/main/feature_bmp.h @@ -0,0 +1,25 @@ +#pragma once + +// system includes +#include + +// local includes +#include "espchrono.h" + +namespace deckenlampe { +struct BmpValue +{ + espchrono::millis_clock::time_point timestamp; + float pressure; + float temperature; + float altitude; +}; +extern std::optional lastBmpValue; +extern espchrono::millis_clock::time_point last_bmp085_pressure_pub; +extern espchrono::millis_clock::time_point last_bmp085_temperature_pub; +extern espchrono::millis_clock::time_point last_bmp085_altitude_pub; +extern bool bmpInitialized; + +void init_bmp(); +void update_bmp(); +} // namespace deckenlampe diff --git a/main/feature_dht.cpp b/main/feature_dht.cpp new file mode 100644 index 0000000..e99afdb --- /dev/null +++ b/main/feature_dht.cpp @@ -0,0 +1,91 @@ +#include "feature_dht.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include + +// local includes +#include "myconfig.h" +#include "mymqtt.h" +#include "delayedconstruction.h" + +using namespace std::chrono_literals; + +namespace deckenlampe { +std::optional lastDhtValue; +espchrono::millis_clock::time_point last_dht11_temperature_pub; +espchrono::millis_clock::time_point last_dht11_humidity_pub; +bool dhtInitialized{}; + +namespace { +constexpr const char * const TAG = "DHT"; + +cpputils::DelayedConstruction dht; +espchrono::millis_clock::time_point last_dht11_readout; +} // namespace + +void init_dht() +{ + if (!config::enable_dht) + return; + + dht.construct(config::pins_dht, DHT11); + + ESP_LOGI(TAG, "calling dht.begin()..."); + dhtInitialized = dht->begin(); + ESP_LOGI(TAG, "finished with %s", dhtInitialized ? "true" : "false"); +} + +void update_dht() +{ + if (!config::enable_dht) + return; + + if (dhtInitialized) { + if (espchrono::ago(last_dht11_readout) < 5s) + return; + + last_dht11_readout = espchrono::millis_clock::now(); + + if (const auto data = dht->read()) { + DhtValue dhtValue { + .timestamp = espchrono::millis_clock::now(), + .temperature = dht->readTemperature(*data), + .humidity = dht->readHumidity(*data) + }; + + ESP_LOGI(TAG, "read dht temperature: %.1f C", dhtValue.temperature); + ESP_LOGI(TAG, "read dht humidity: %.1f %%", dhtValue.humidity); + + if (mqttClient && mqttConnected) { + if (!lastDhtValue) + mqttVerbosePub(config::topic_dht11_availability, "online", 0, 1); + if (!lastDhtValue || espchrono::ago(last_dht11_temperature_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_dht11_temperature, fmt::format("{:.1f}", dhtValue.temperature), 0, 1) >= 0) + last_dht11_temperature_pub = espchrono::millis_clock::now(); + } + if (!lastDhtValue || espchrono::ago(last_dht11_humidity_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_dht11_humidity, fmt::format("{:.1f}", dhtValue.humidity), 0, 1) >= 0) + last_dht11_humidity_pub = espchrono::millis_clock::now(); + } + } + + lastDhtValue = dhtValue; + } else { + ESP_LOGW(TAG, "dht failed"); + goto dhtOffline; + } + } else { + dhtOffline: + if (lastDhtValue && espchrono::ago(lastDhtValue->timestamp) >= config::availableTimeoutTime) { + ESP_LOGW(TAG, "dht timeouted"); + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_dht11_availability, "offline", 0, 1); + lastDhtValue = std::nullopt; + } + } +} +} // namespace deckenlampe diff --git a/main/feature_dht.h b/main/feature_dht.h new file mode 100644 index 0000000..ceadb51 --- /dev/null +++ b/main/feature_dht.h @@ -0,0 +1,23 @@ +#pragma once + +// system includes +#include + +// local includes +#include "espchrono.h" + +namespace deckenlampe { +struct DhtValue +{ + espchrono::millis_clock::time_point timestamp; + float temperature; + float humidity; +}; +extern std::optional lastDhtValue; +extern espchrono::millis_clock::time_point last_dht11_temperature_pub; +extern espchrono::millis_clock::time_point last_dht11_humidity_pub; +extern bool dhtInitialized; + +void init_dht(); +void update_dht(); +} // namespace deckenlampe diff --git a/main/feature_lamp.cpp b/main/feature_lamp.cpp new file mode 100644 index 0000000..bf0b942 --- /dev/null +++ b/main/feature_lamp.cpp @@ -0,0 +1,33 @@ +#include "feature_lamp.h" + +// Arduino includes +#include + +// local includes +#include + +namespace deckenlampe { +std::atomic lampState; + +void init_lamp() +{ + if (!config::enable_lamp) + return; + + pinMode(config::pins_lamp, OUTPUT); + writeLamp(lampState); +} + +void update_lamp() +{ + if (!config::enable_lamp) + return; +} + +void writeLamp(bool state) +{ + if (config::invertLamp) + state = !state; + digitalWrite(config::pins_lamp, state ? HIGH : LOW); +} +} // namespace deckenlampe diff --git a/main/feature_lamp.h b/main/feature_lamp.h new file mode 100644 index 0000000..9b43f45 --- /dev/null +++ b/main/feature_lamp.h @@ -0,0 +1,13 @@ +#pragma once + +// system includes +#include + +namespace deckenlampe { +extern std::atomic lampState; + +void init_lamp(); +void update_lamp(); + +void writeLamp(bool state); +} // namespace deckenlampe diff --git a/main/feature_switch.cpp b/main/feature_switch.cpp new file mode 100644 index 0000000..db2f36d --- /dev/null +++ b/main/feature_switch.cpp @@ -0,0 +1,66 @@ +#include "feature_switch.h" + +// Arduino includes +#include + +// local includes +#include "myconfig.h" +#include "mymqtt.h" +#include "feature_lamp.h" + +namespace deckenlampe { +std::atomic switchState; + +namespace { +uint8_t switchDebounce{}; + +bool readSwitch(); +} // namespace + +void init_switch() +{ + if (!config::enable_switch) + return; + + pinMode(config::pins_switch, INPUT); +} + +void update_switch() +{ + if (!config::enable_switch) + return; + + const auto newState = readSwitch(); + if (newState == switchState.load()) + switchDebounce = 0; + else { + switchDebounce++; + if (switchDebounce >= 10) { + switchDebounce = 0; + + switchState = newState; + + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_switch_status, switchState ? "ON" : "OFF", 0, 1); + + if (config::enable_lamp) { + lampState = !lampState; + writeLamp(lampState); + + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_lamp_status, lampState ? "ON" : "OFF", 0, 1); + } + } + } +} + +namespace { +bool readSwitch() +{ + bool state = digitalRead(config::pins_switch) == HIGH; + if (config::invert_switch) + state = !state; + return state; +} +} // namespace +} // namespace deckenlampe diff --git a/main/feature_switch.h b/main/feature_switch.h new file mode 100644 index 0000000..276a173 --- /dev/null +++ b/main/feature_switch.h @@ -0,0 +1,11 @@ +#pragma once + +// system includes +#include + +namespace deckenlampe { +extern std::atomic switchState; + +void init_switch(); +void update_switch(); +} // namespace deckenlampe diff --git a/main/feature_tsl.cpp b/main/feature_tsl.cpp new file mode 100644 index 0000000..7e9713b --- /dev/null +++ b/main/feature_tsl.cpp @@ -0,0 +1,120 @@ +#include "feature_tsl.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include + +// local includes +#include "myconfig.h" +#include "mymqtt.h" +#include "delayedconstruction.h" + +using namespace std::chrono_literals; + +namespace deckenlampe { +std::optional lastTslValue; +espchrono::millis_clock::time_point last_tsl2561_lux_pub; +bool tslInitialized{}; + +namespace { +constexpr const char * const TAG = "TSL"; + +cpputils::DelayedConstruction tsl; +espchrono::millis_clock::time_point last_tsl2561_readout; +} // namespace + +void init_tsl() +{ + if (!config::enable_tsl) + return; + + tsl.construct(TSL2561_ADDR_FLOAT, 12345); + + ESP_LOGI(TAG, "calling tsl.begin()..."); + tslInitialized = tsl->begin(true); + ESP_LOGI(TAG, "finished with %s", tslInitialized ? "true" : "false"); + + if (tslInitialized) { + sensor_t sensor = tsl->getSensor(); + ESP_LOGI(TAG, "------------------------------------"); + ESP_LOGI(TAG, "Sensor: %s", sensor.name); + ESP_LOGI(TAG, "Driver Ver: %i", sensor.version); + ESP_LOGI(TAG, "Unique ID: %i", sensor.sensor_id); + ESP_LOGI(TAG, "Max Value: %.1f lux", sensor.max_value); + ESP_LOGI(TAG, "Min Value: %.1f lux", sensor.min_value); + ESP_LOGI(TAG, "Resolution: %.1f lux", sensor.resolution); + ESP_LOGI(TAG, "------------------------------------"); + ESP_LOGI(TAG, ""); + + /* You can also manually set the gain or enable auto-gain support */ + // tsl->setGain(TSL2561_GAIN_1X); /* No gain ... use in bright light to avoid sensor saturation */ + // tsl->setGain(TSL2561_GAIN_16X); /* 16x gain ... use in low light to boost sensitivity */ + tsl->enableAutoRange(true); /* Auto-gain ... switches automatically between 1x and 16x */ + + /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */ + tsl->setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS); /* fast but low resolution */ + // tsl->setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS); /* medium resolution and speed */ + // tsl->setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS); /* 16-bit data but slowest conversions */ + + /* Update these values depending on what you've set above! */ + ESP_LOGI(TAG, "------------------------------------"); + ESP_LOGI(TAG, "Gain: Auto"); + ESP_LOGI(TAG, "Timing: 13 ms"); + ESP_LOGI(TAG, "------------------------------------"); + } +} + +void update_tsl() +{ + if (!config::enable_tsl) + return; + + if (tslInitialized) { + if (espchrono::ago(last_tsl2561_readout) < 5s) + return; + + last_tsl2561_readout = espchrono::millis_clock::now(); + + if (std::optional event = tsl->getEvent()) { + /* Display the results (light is measured in lux) */ + if (event->light) { + TslValue tslValue { + .timestamp = espchrono::millis_clock::now(), + .lux = event->light + }; + ESP_LOGI(TAG, "read tsl: %.1f lux", tslValue.lux); + + if (mqttClient && mqttConnected) { + if (!lastTslValue) + mqttVerbosePub(config::topic_tsl2561_availability, "online", 0, 1); + if (!lastTslValue || espchrono::ago(last_tsl2561_lux_pub) >= config::valueUpdateInterval) { + if (mqttVerbosePub(config::topic_tsl2561_lux, fmt::format("{:.1f}", tslValue.lux), 0, 1) >= 0) + last_tsl2561_lux_pub = espchrono::millis_clock::now(); + } + } + + lastTslValue = tslValue; + } else { + /* If event.light = 0 lux the sensor is probably saturated + * and no reliable data could be generated! */ + ESP_LOGW(TAG, "tsl sensor overload %f", event->light); + goto tslOffline; + } + } else { + ESP_LOGW(TAG, "tsl failed"); + goto tslOffline; + } + } else { + tslOffline: + if (lastTslValue && espchrono::ago(lastTslValue->timestamp) >= config::availableTimeoutTime) { + ESP_LOGW(TAG, "tsl timeouted"); + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_tsl2561_availability, "offline", 0, 1); + lastTslValue = std::nullopt; + } + } +} +} // namespace deckenlampe diff --git a/main/feature_tsl.h b/main/feature_tsl.h new file mode 100644 index 0000000..2bfd56a --- /dev/null +++ b/main/feature_tsl.h @@ -0,0 +1,21 @@ +#pragma once + +// system includes +#include + +// local includes +#include "espchrono.h" + +namespace deckenlampe { +struct TslValue +{ + espchrono::millis_clock::time_point timestamp; + float lux; +}; +extern std::optional lastTslValue; +extern espchrono::millis_clock::time_point last_tsl2561_lux_pub; +extern bool tslInitialized; + +void init_tsl(); +void update_tsl(); +} // namespace deckenlampe diff --git a/main/main.cpp b/main/main.cpp index be53472..de78e85 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -14,133 +14,41 @@ #ifdef CONFIG_APP_ROLLBACK_ENABLE #include #endif -#include -#include -#include #include -#include // Arduino includes #include #include // 3rdparty lib includes -#include -#include -#include #include // local includes -#include "espwifistack.h" #include "tickchrono.h" -#include "wrappers/mqtt_client.h" +#include "myconfig.h" +#include "mywifi.h" +#include "webserver.h" +#include "mymdns.h" +#include "mymqtt.h" +#include "feature_lamp.h" +#include "feature_switch.h" +#include "feature_dht.h" +#include "feature_tsl.h" +#include "feature_bmp.h" using namespace std::chrono_literals; +namespace deckenlampe { namespace { constexpr const char * const TAG = "MAIN"; -constexpr const bool invertLamp = true; -constexpr const bool invertSwitch = true; - -constexpr const gpio_num_t pins_lamp = GPIO_NUM_25; -constexpr const gpio_num_t pins_switch = GPIO_NUM_35; -constexpr const gpio_num_t pins_sda = GPIO_NUM_16; -constexpr const gpio_num_t pins_scl = GPIO_NUM_17; -constexpr const gpio_num_t pins_dht = GPIO_NUM_33; - -constexpr const std::string_view broker_url = "mqtt://192.168.0.2/"; - -constexpr const std::string_view topic_lamp_availability = "dahoam/wohnzimmer/deckenlicht1/available"; -constexpr const std::string_view topic_lamp_status = "dahoam/wohnzimmer/deckenlicht1/status"; -constexpr const std::string_view topic_lamp_set = "dahoam/wohnzimmer/deckenlicht1/set"; - -constexpr const std::string_view topic_switch_availability = "dahoam/wohnzimmer/schalter1/available"; -constexpr const std::string_view topic_switch_status = "dahoam/wohnzimmer/schalter1/status"; - -constexpr const std::string_view topic_tsl2561_availability = "dahoam/wohnzimmer/tsl2561_1/available"; -constexpr const std::string_view topic_tsl2561_lux = "dahoam/wohnzimmer/tsl2561_1/lux"; - -constexpr const std::string_view topic_bmp085_availability = "dahoam/wohnzimmer/bmp085_1/available"; -constexpr const std::string_view topic_bmp085_pressure = "dahoam/wohnzimmer/bmp085_1/pressure"; -constexpr const std::string_view topic_bmp085_temperature = "dahoam/wohnzimmer/bmp085_1/temperature"; -constexpr const std::string_view topic_bmp085_altitude = "dahoam/wohnzimmer/bmp085_1/altitude"; - -constexpr const std::string_view topic_dht11_availability = "dahoam/wohnzimmer/dht11_1/available"; -constexpr const std::string_view topic_dht11_temperature = "dahoam/wohnzimmer/dht11_1/temperature"; -constexpr const std::string_view topic_dht11_humidity = "dahoam/wohnzimmer/dht11_1/humidity"; - -constexpr const auto availableTimeoutTime = 1min; -constexpr const auto valueUpdateInterval = 15s; - - - -std::atomic mqttConnected; -espcpputils::mqtt_client mqttClient; - -std::atomic lampState; -std::atomic switchState; -uint8_t switchDebounce{}; - -//espchrono::millis_clock::time_point lastLampToggle; -//espchrono::millis_clock::time_point lastSwitchToggle; - - -Adafruit_TSL2561_Unified tsl{TSL2561_ADDR_FLOAT, 12345}; -struct TslValue -{ - espchrono::millis_clock::time_point timestamp; - float lux; -}; -std::optional lastTslValue; -espchrono::millis_clock::time_point last_tsl2561_lux_pub; - - -Adafruit_BMP085_Unified bmp{10085}; -struct BmpValue -{ - espchrono::millis_clock::time_point timestamp; - float pressure; - float temperature; - float altitude; -}; -std::optional lastBmpValue; -espchrono::millis_clock::time_point last_bmp085_pressure_pub; -espchrono::millis_clock::time_point last_bmp085_temperature_pub; -espchrono::millis_clock::time_point last_bmp085_altitude_pub; - - -DHT dht(pins_dht, DHT11); -struct DhtValue -{ - espchrono::millis_clock::time_point timestamp; - float temperature; - float humidity; -}; -std::optional lastDhtValue; -espchrono::millis_clock::time_point last_dht11_temperature_pub; -espchrono::millis_clock::time_point last_dht11_humidity_pub; - - - -esp_err_t webserver_root_handler(httpd_req_t *req); -esp_err_t webserver_on_handler(httpd_req_t *req); -esp_err_t webserver_off_handler(httpd_req_t *req); -esp_err_t webserver_toggle_handler(httpd_req_t *req); -esp_err_t webserver_reboot_handler(httpd_req_t *req); - -void mqttEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); - -int mqttVerbosePub(std::string_view topic, std::string_view value, int qos, int retain); - -void verboseDigitalWrite(uint8_t pin, uint8_t val); - -void writeLamp(bool state); -bool readSwitch(); } // namespace +} // namespace deckenlamp extern "C" void app_main() { + using namespace deckenlampe; + #if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) { const auto taskHandle = xTaskGetCurrentTaskHandle(); @@ -174,489 +82,53 @@ extern "C" void app_main() } #endif - { - const auto result = nvs_flash_init(); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_init(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } + init_config(); - nvs_handle_t nvsHandle; + init_lamp(); - { - const auto result = nvs_open("deckenlampe", NVS_READWRITE, &nvsHandle); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_open(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - wifi_stack::config wifiConfig { - .wifiEnabled = true, - .hostname = "deckenlampe1", - .wifis = std::array { - wifi_stack::wifi_entry { .ssid = "ScheissAP", .key = "Passwort_123" }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} }, - wifi_stack::wifi_entry { .ssid = {}, .key = {} } - }, - .sta_ip = { - .dhcpEnabled = true, -// .staticIp = {}, -// .staticGateway = {}, -// .staticSubnet = {}, -// .staticDns1 = {}, -// .staticDns2 = {} - }, - .ap = { - { - .ssid = "deckenlampe1", - .key = "Passwort_123" - }, - .channel = 1, - .authmode = WIFI_AUTH_WPA2_PSK, - .ssid_hidden = false, - .max_connection = 4, - .beacon_interval = 100, - .ip{192, 168, 4, 1}, - .subnet{255, 255, 255, 0} - }, - .min_rssi = -90 - }; - - wifi_stack::init(wifiConfig); - - pinMode(pins_lamp, OUTPUT); - writeLamp(lampState); - - pinMode(pins_switch, INPUT); - - httpd_handle_t httpdHandle{}; - - { - httpd_config_t httpConfig HTTPD_DEFAULT_CONFIG(); - - const auto result = httpd_start(&httpdHandle, &httpConfig); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_start(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - httpd_uri_t uri { - .uri = "/", - .method = HTTP_GET, - .handler = webserver_root_handler, - .user_ctx = NULL - }; - - const auto result = httpd_register_uri_handler(httpdHandle, &uri); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", "/", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - httpd_uri_t uri { - .uri = "/on", - .method = HTTP_GET, - .handler = webserver_on_handler, - .user_ctx = NULL - }; - - const auto result = httpd_register_uri_handler(httpdHandle, &uri); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", "/on", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - httpd_uri_t uri { - .uri = "/off", - .method = HTTP_GET, - .handler = webserver_off_handler, - .user_ctx = NULL - }; - - const auto result = httpd_register_uri_handler(httpdHandle, &uri); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", "/off", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - httpd_uri_t uri { - .uri = "/toggle", - .method = HTTP_GET, - .handler = webserver_toggle_handler, - .user_ctx = NULL - }; - - const auto result = httpd_register_uri_handler(httpdHandle, &uri); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", "/toggle", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - httpd_uri_t uri { - .uri = "/reboot", - .method = HTTP_GET, - .handler = webserver_reboot_handler, - .user_ctx = NULL - }; - - const auto result = httpd_register_uri_handler(httpdHandle, &uri); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", "/reboot", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - const auto result = mdns_init(); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_init(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - const auto result = mdns_hostname_set(wifiConfig.hostname.c_str()); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_hostname_set(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - const auto result = mdns_instance_name_set(wifiConfig.hostname.c_str()); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_instance_name_set(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - const auto result = mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_service_add(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - { - esp_mqtt_client_config_t mqtt_cfg = { - .uri = broker_url.data(), - }; - - mqttClient = espcpputils::mqtt_client{&mqtt_cfg}; - - if (!mqttClient) - { - ESP_LOGE(TAG, "error while initializing mqtt client!"); - return; - } - else - { - { - const auto result = mqttClient.register_event(MQTT_EVENT_ANY, mqttEventHandler, NULL); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_mqtt_client_register_event(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - - const auto result = mqttClient.start(); - ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_mqtt_client_start(): %s", esp_err_to_name(result)); - //if (result != ESP_OK) - // return result; - } - } - - espchrono::millis_clock::time_point lastValuesRead; + init_switch(); { ESP_LOGI(TAG, "calling Wire.begin()..."); - const auto result = Wire.begin(pins_sda, pins_scl); + const auto result = Wire.begin(config::pins_sda, config::pins_scl); ESP_LOGI(TAG, "finished with %s", result ? "true" : "false"); } - bool tslInitialized{}; - { - ESP_LOGI(TAG, "calling tsl.begin()..."); - tslInitialized = tsl.begin(true); - ESP_LOGI(TAG, "finished with %s", tslInitialized ? "true" : "false"); + init_wifi(); - if (tslInitialized) - { - sensor_t sensor = tsl.getSensor(); - ESP_LOGI(TAG, "------------------------------------"); - ESP_LOGI(TAG, "Sensor: %s", sensor.name); - ESP_LOGI(TAG, "Driver Ver: %i", sensor.version); - ESP_LOGI(TAG, "Unique ID: %i", sensor.sensor_id); - ESP_LOGI(TAG, "Max Value: %.1f lux", sensor.max_value); - ESP_LOGI(TAG, "Min Value: %.1f lux", sensor.min_value); - ESP_LOGI(TAG, "Resolution: %.1f lux", sensor.resolution); - ESP_LOGI(TAG, "------------------------------------"); - ESP_LOGI(TAG, ""); + init_webserver(); - /* You can also manually set the gain or enable auto-gain support */ - // tsl.setGain(TSL2561_GAIN_1X); /* No gain ... use in bright light to avoid sensor saturation */ - // tsl.setGain(TSL2561_GAIN_16X); /* 16x gain ... use in low light to boost sensitivity */ - tsl.enableAutoRange(true); /* Auto-gain ... switches automatically between 1x and 16x */ + init_mdns(); - /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */ - tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS); /* fast but low resolution */ - // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS); /* medium resolution and speed */ - // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS); /* 16-bit data but slowest conversions */ + init_mqtt(); - /* Update these values depending on what you've set above! */ - ESP_LOGI(TAG, "------------------------------------"); - ESP_LOGI(TAG, "Gain: Auto"); - ESP_LOGI(TAG, "Timing: 13 ms"); - ESP_LOGI(TAG, "------------------------------------"); - } - } + init_dht(); - bool bmpInitialized{}; - { - ESP_LOGI(TAG, "calling bmp.begin()..."); - bmpInitialized = bmp.begin(); - ESP_LOGI(TAG, "finished with %s", bmpInitialized ? "true" : "false"); + init_tsl(); - if (bmpInitialized) - { - sensor_t sensor = bmp.getSensor(); - ESP_LOGI(TAG, "------------------------------------"); - ESP_LOGI(TAG, "Sensor: %s", sensor.name); - ESP_LOGI(TAG, "Driver Ver: %i", sensor.version); - ESP_LOGI(TAG, "Unique ID: %i", sensor.sensor_id); - ESP_LOGI(TAG, "Max Value: %.1f hPa", sensor.max_value); - ESP_LOGI(TAG, "Min Value: %.1f hPa", sensor.min_value); - ESP_LOGI(TAG, "Resolution: %.1f hPa", sensor.resolution); - ESP_LOGI(TAG, "------------------------------------"); - ESP_LOGI(TAG, ""); - } - } - - bool dhtInitialized{}; - { - ESP_LOGI(TAG, "calling dht.begin()..."); - dhtInitialized = dht.begin(); - ESP_LOGI(TAG, "finished with %s", dhtInitialized ? "true" : "false"); - } + init_bmp(); while (true) { - wifi_stack::update(wifiConfig); + update_config(); -// if (espchrono::ago(lastLampToggle) >= 10s) -// { -// lastLampToggle = espchrono::millis_clock::now(); -// lampState = !lampState; -// writeLamp(lampState); + update_lamp(); -// if (mqttClient && mqttConnected) -// mqttVerbosePub(topic_lamp_status, lampState ? "ON" : "OFF", 0, 1); -// } + update_wifi(); -// if (espchrono::ago(lastSwitchToggle) >= 30s) -// { -// lastSwitchToggle = espchrono::millis_clock::now(); -// switchState = !switchState; + update_webserver(); -// if (mqttClient && mqttConnected) -// mqttVerbosePub(topic_switch_status, switchState ? "ON" : "OFF", 0, 1); -// } + update_mdns(); - if (espchrono::ago(lastValuesRead) >= 2s) { - lastValuesRead = espchrono::millis_clock::now(); + update_mqtt(); - if (tslInitialized) - { - if (std::optional event = tsl.getEvent()) - { - /* Display the results (light is measured in lux) */ - if (event->light) - { - TslValue tslValue { - .timestamp = espchrono::millis_clock::now(), - .lux = event->light - }; - ESP_LOGI(TAG, "read tsl: %.1f lux", tslValue.lux); + update_dht(); - if (mqttClient && mqttConnected) - { - if (!lastTslValue) - mqttVerbosePub(topic_tsl2561_availability, "online", 0, 1); - if (!lastTslValue || espchrono::ago(last_tsl2561_lux_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_tsl2561_lux, fmt::format("{:.1f}", tslValue.lux), 0, 1) >= 0) - last_tsl2561_lux_pub = espchrono::millis_clock::now(); - } - } + update_tsl(); - lastTslValue = tslValue; - } - else - { - /* If event.light = 0 lux the sensor is probably saturated - * and no reliable data could be generated! */ - ESP_LOGW(TAG, "tsl sensor overload %f", event->light); - goto tslOffline; - } - } - else - { - ESP_LOGW(TAG, "tsl failed"); - goto tslOffline; - } - } - else - { - tslOffline: - if (lastTslValue && espchrono::ago(lastTslValue->timestamp) >= availableTimeoutTime) - { - ESP_LOGW(TAG, "tsl timeouted"); - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_tsl2561_availability, "offline", 0, 1); - lastTslValue = std::nullopt; - } - } + update_bmp(); - if (bmpInitialized) - { - if (std::optional values = bmp.getTemperatureAndPressure()) - { - if (values->temperature && values->pressure) - { - BmpValue bmpValue { - .timestamp = espchrono::millis_clock::now(), - .pressure = values->pressure, - .temperature = values->temperature - }; - ESP_LOGI(TAG, "read bmp Pressure: %.1f hPa", bmpValue.pressure); - ESP_LOGI(TAG, "read bmp Temperature: %.1f C", bmpValue.temperature); - - /* Then convert the atmospheric pressure, and SLP to altitude */ - /* Update this next line with the current SLP for better results */ - constexpr const float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; - bmpValue.altitude = bmp.pressureToAltitude(seaLevelPressure, bmpValue.pressure); - ESP_LOGI(TAG, "read bmp Altitude: %.1f m", bmpValue.altitude); - - if (mqttClient && mqttConnected) - { - if (!lastBmpValue) - mqttVerbosePub(topic_bmp085_availability, "online", 0, 1); - if (!lastBmpValue || espchrono::ago(last_bmp085_pressure_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_bmp085_pressure, fmt::format("{:.1f}", bmpValue.pressure), 0, 1) >= 0) - last_bmp085_pressure_pub = espchrono::millis_clock::now(); - } - if (!lastBmpValue || espchrono::ago(last_bmp085_temperature_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_bmp085_temperature, fmt::format("{:.1f}", bmpValue.temperature), 0, 1) >= 0) - last_bmp085_temperature_pub = espchrono::millis_clock::now(); - } - if (!lastBmpValue || espchrono::ago(last_bmp085_altitude_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_bmp085_altitude, fmt::format("{:.1f}", bmpValue.altitude), 0, 1) >= 0) - last_bmp085_altitude_pub = espchrono::millis_clock::now(); - } - } - - lastBmpValue = bmpValue; - } - else - { - ESP_LOGW(TAG, "bmp sensor error"); - goto bmpOffline; - } - } - else - { - ESP_LOGW(TAG, "bmp failed"); - goto bmpOffline; - } - } - else - { - bmpOffline: - if (lastBmpValue && espchrono::ago(lastBmpValue->timestamp) >= availableTimeoutTime) - { - ESP_LOGW(TAG, "bmp timeouted"); - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_bmp085_availability, "offline", 0, 1); - lastBmpValue = std::nullopt; - } - } - - if (dhtInitialized) { - if (const auto data = dht.read()) - { - DhtValue dhtValue { - .timestamp = espchrono::millis_clock::now(), - .temperature = dht.readTemperature(*data), - .humidity = dht.readHumidity(*data) - }; - - ESP_LOGI(TAG, "read dht temperature: %.1f C", dhtValue.temperature); - ESP_LOGI(TAG, "read dht humidity: %.1f %%", dhtValue.humidity); - - if (mqttClient && mqttConnected) - { - if (!lastDhtValue) - mqttVerbosePub(topic_dht11_availability, "online", 0, 1); - if (!lastDhtValue || espchrono::ago(last_dht11_temperature_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_dht11_temperature, fmt::format("{:.1f}", dhtValue.temperature), 0, 1) >= 0) - last_dht11_temperature_pub = espchrono::millis_clock::now(); - } - if (!lastDhtValue || espchrono::ago(last_dht11_humidity_pub) >= valueUpdateInterval) - { - if (mqttVerbosePub(topic_dht11_humidity, fmt::format("{:.1f}", dhtValue.humidity), 0, 1) >= 0) - last_dht11_humidity_pub = espchrono::millis_clock::now(); - } - } - - lastDhtValue = dhtValue; - } else { - ESP_LOGW(TAG, "dht failed"); - goto dhtOffline; - } - } else { - dhtOffline: - if (lastDhtValue && espchrono::ago(lastDhtValue->timestamp) >= availableTimeoutTime) { - ESP_LOGW(TAG, "dht timeouted"); - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_dht11_availability, "offline", 0, 1); - lastDhtValue = std::nullopt; - } - } - } - - { - const auto newState = readSwitch(); - if (newState == switchState.load()) - switchDebounce = 0; - else { - switchDebounce++; - if (switchDebounce >= 10) { - switchDebounce = 0; - - switchState = newState; - - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_switch_status, switchState ? "ON" : "OFF", 0, 1); - - lampState = !lampState; - writeLamp(lampState); - - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_lamp_status, lampState ? "ON" : "OFF", 0, 1); - } - } - } + update_switch(); #if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) if (const auto result = esp_task_wdt_reset(); result != ESP_OK) @@ -676,263 +148,6 @@ extern "C" void app_main() } } -#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; - -namespace { -esp_err_t webserver_root_handler(httpd_req_t *req) -{ - std::string body = "this is work in progress...
\n" - "on
\n" - "off
\n" - "toggle
\n" - "reboot
\n" - "
\n"; - - body += fmt::format("Lamp: {}
\n", lampState ? "ON" : "OFF"); - body += fmt::format("Switch: {}
\n", switchState ? "ON" : "OFF"); - - if (lastTslValue) - { - body += fmt::format("TSL2561 Brightness: {:.1f} lux
\n", lastTslValue->lux); - } - - if (lastBmpValue) - { - body += fmt::format("BMP085 Pressure: {:.1f} lux
\n", lastBmpValue->pressure); - body += fmt::format("BMP085 Temperature: {:.1f} C
\n", lastBmpValue->temperature); - body += fmt::format("BMP085 Altitude: {:.1f} m
\n", lastBmpValue->altitude); - } - - if (lastDhtValue) - { - body += fmt::format("DHT11 Temperature: {:.1f} C
\n", lastDhtValue->temperature); - body += fmt::format("DHT11 Humidity: {:.1f} %
\n", lastDhtValue->humidity); - } - - 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_on_handler(httpd_req_t *req) -{ - const bool state = (lampState = true); - writeLamp(state); - - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_lamp_status, state ? "ON" : "OFF", 0, 1); - - std::string_view body{"ON called..."}; - 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_off_handler(httpd_req_t *req) -{ - const bool state = (lampState = false); - writeLamp(state); - - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_lamp_status, state ? "ON" : "OFF", 0, 1); - - std::string_view body{"OFF called..."}; - 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_toggle_handler(httpd_req_t *req) -{ - const bool state = (lampState = !lampState); - writeLamp(state); - - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_lamp_status, state ? "ON" : "OFF", 0, 1); - - std::string_view body{"TOGGLE called..."}; - 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_reboot_handler(httpd_req_t *req) -{ - std::string_view body{"REBOOT called..."}; - 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()) - - esp_restart(); - - return ESP_OK; -} - -void mqttEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - CPP_UNUSED(event_handler_arg) - - const esp_mqtt_event_t *data = reinterpret_cast(event_data); - - switch (esp_mqtt_event_id_t(event_id)) - { - case MQTT_EVENT_ERROR: - ESP_LOGE(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_ERROR"); - - // ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); - // if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) - // { - // log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); - // log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); - // log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); - // ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); - - // } - break; - - case MQTT_EVENT_CONNECTED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_CONNECTED"); - - mqttConnected = true; - - mqttVerbosePub(topic_lamp_availability, "online", 0, 1); - mqttVerbosePub(topic_lamp_status, lampState.load() ? "ON" : "OFF", 0, 1); - - mqttVerbosePub(topic_switch_availability, "online", 0, 1); - mqttVerbosePub(topic_switch_status, switchState.load() ? "ON" : "OFF", 0, 1); - - mqttVerbosePub(topic_tsl2561_availability, lastTslValue ? "online" : "offline", 0, 1); - if (lastTslValue) - { - if (mqttVerbosePub(topic_tsl2561_lux, fmt::format("{:.1f}", lastTslValue->lux), 0, 1) >= 0) - last_tsl2561_lux_pub = espchrono::millis_clock::now(); - } - - mqttVerbosePub(topic_bmp085_availability, lastBmpValue ? "online" : "offline", 0, 1); - if (lastBmpValue) - { - if (mqttVerbosePub(topic_bmp085_pressure, fmt::format("{:.1f}", lastBmpValue->pressure), 0, 1) >= 0) - last_bmp085_pressure_pub = espchrono::millis_clock::now(); - if (mqttVerbosePub(topic_bmp085_temperature, fmt::format("{:.1f}", lastBmpValue->temperature), 0, 1) >= 0) - last_bmp085_temperature_pub = espchrono::millis_clock::now(); - if (mqttVerbosePub(topic_bmp085_altitude, fmt::format("{:.1f}", lastBmpValue->altitude), 0, 1) >= 0) - last_bmp085_altitude_pub = espchrono::millis_clock::now(); - } - - mqttVerbosePub(topic_dht11_availability, lastDhtValue ? "online" : "offline", 0, 1); - if (lastDhtValue) - { - if (mqttVerbosePub(topic_dht11_temperature, fmt::format("{:.1f}", lastDhtValue->temperature), 0, 1) >= 0) - last_dht11_temperature_pub = espchrono::millis_clock::now(); - if (mqttVerbosePub(topic_dht11_humidity, fmt::format("{:.1f}", lastDhtValue->humidity), 0, 1) >= 0) - last_dht11_humidity_pub = espchrono::millis_clock::now(); - } - - mqttClient.subscribe(topic_lamp_set, 0); - - break; - - case MQTT_EVENT_DISCONNECTED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_DISCONNECTED"); - - mqttConnected = false; - - break; - - case MQTT_EVENT_SUBSCRIBED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_SUBSCRIBED"); - break; - - case MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_UNSUBSCRIBED"); - break; - - case MQTT_EVENT_PUBLISHED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_PUBLISHED"); - break; - - case MQTT_EVENT_DATA: - { - std::string_view topic{data->topic, (size_t)data->topic_len}; - std::string_view value{data->data, (size_t)data->data_len}; - - ESP_LOGI(TAG, "%s event_id=%s topic=%.*s data=%.*s", event_base, "MQTT_EVENT_DATA", topic.size(), topic.data(), value.size(), value.data()); - - if (topic == topic_lamp_set) - { - bool newState = (lampState = (value == "ON")); - writeLamp(newState); - if (mqttClient && mqttConnected) - mqttVerbosePub(topic_lamp_status, newState ? "ON" : "OFF", 0, 1); - } - - break; - } - case MQTT_EVENT_BEFORE_CONNECT: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_BEFORE_CONNECT"); - break; - - case MQTT_EVENT_DELETED: - ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_DELETED"); - break; - - default: - ESP_LOGW(TAG, "%s unknown event_id %i", event_base, event_id); - break; - } -} - -int mqttVerbosePub(std::string_view topic, std::string_view value, int qos, int retain) -{ - ESP_LOGD(TAG, "topic=\"%.*s\" value=\"%.*s\"", topic.size(), topic.data(), value.size(), value.data()); - const auto pending_msg_id = mqttClient.publish(topic, value, qos, retain); - if (pending_msg_id < 0) - ESP_LOGE(TAG, "topic=\"%.*s\" value=\"%.*s\" failed pending_msg_id=%i", topic.size(), topic.data(), value.size(), value.data(), pending_msg_id); - else - ESP_LOGI(TAG, "topic=\"%.*s\" value=\"%.*s\" succeeded pending_msg_id=%i", topic.size(), topic.data(), value.size(), value.data(), pending_msg_id); - return pending_msg_id; -} - -void verboseDigitalWrite(uint8_t pin, uint8_t val) -{ - ESP_LOGI(TAG, "pin=%hhu val=%hhu", pin, val); - - digitalWrite(pin, val); -} - -void writeLamp(bool state) -{ - if (invertLamp) - state = !state; - verboseDigitalWrite(pins_lamp, state ? HIGH : LOW); -} - -bool readSwitch() -{ - bool state = digitalRead(pins_switch) == HIGH; - if (invertSwitch) - state = !state; - return state; -} -} // namespace - auto espchrono::local_clock::timezone() noexcept -> time_zone { return time_zone{1h, DayLightSavingMode::EuropeanSummerTime}; diff --git a/main/myconfig.cpp b/main/myconfig.cpp new file mode 100644 index 0000000..1d23f37 --- /dev/null +++ b/main/myconfig.cpp @@ -0,0 +1,36 @@ +#include "myconfig.h" + +// esp-idf includes +#include +#include + +namespace deckenlampe { +nvs_handle_t nvsHandle; + +namespace { +constexpr const char * const TAG = "CONFIG"; +} // namespace + +void init_config() +{ + { + const auto result = nvs_flash_init(); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_init(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } + + { + const auto result = nvs_open("deckenlampe", NVS_READWRITE, &nvsHandle); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_open(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } + + // TODO +} + +void update_config() +{ +} +} // namespace deckenlamp diff --git a/main/myconfig.h b/main/myconfig.h new file mode 100644 index 0000000..a3b6462 --- /dev/null +++ b/main/myconfig.h @@ -0,0 +1,64 @@ +#pragma once + +// system includes +#include +#include + +// esp-idf includes +#include +#include + +namespace deckenlampe { +extern nvs_handle_t nvsHandle; + +namespace config { +constexpr const std::string_view hostname = "deckenlampe1"; + +constexpr const std::string_view sta_ssid = "ScheissAP"; +constexpr const std::string_view sta_key = "Passwort_123"; + +constexpr const std::string_view ap_ssid = "deckenlampe1"; +constexpr const std::string_view ap_key = "Passwort_123"; + +constexpr const std::string_view broker_url = "mqtt://192.168.0.2/"; + +constexpr const bool enable_lamp = true; +constexpr const gpio_num_t pins_lamp = GPIO_NUM_25; +constexpr const bool invertLamp = true; +constexpr const std::string_view topic_lamp_availability = "dahoam/wohnzimmer/deckenlicht1/available"; +constexpr const std::string_view topic_lamp_status = "dahoam/wohnzimmer/deckenlicht1/status"; +constexpr const std::string_view topic_lamp_set = "dahoam/wohnzimmer/deckenlicht1/set"; + + +constexpr const bool enable_switch = true; +constexpr const gpio_num_t pins_switch = GPIO_NUM_35; +constexpr const bool invert_switch = true; +constexpr const std::string_view topic_switch_availability = "dahoam/wohnzimmer/schalter1/available"; +constexpr const std::string_view topic_switch_status = "dahoam/wohnzimmer/schalter1/status"; + +constexpr const bool enable_dht = true; +constexpr const gpio_num_t pins_dht = GPIO_NUM_33; +constexpr const std::string_view topic_dht11_availability = "dahoam/wohnzimmer/dht11_1/available"; +constexpr const std::string_view topic_dht11_temperature = "dahoam/wohnzimmer/dht11_1/temperature"; +constexpr const std::string_view topic_dht11_humidity = "dahoam/wohnzimmer/dht11_1/humidity"; + +constexpr const gpio_num_t pins_sda = GPIO_NUM_16; +constexpr const gpio_num_t pins_scl = GPIO_NUM_17; + +constexpr const bool enable_tsl = true; +constexpr const std::string_view topic_tsl2561_availability = "dahoam/wohnzimmer/tsl2561_1/available"; +constexpr const std::string_view topic_tsl2561_lux = "dahoam/wohnzimmer/tsl2561_1/lux"; + +constexpr const bool enable_bmp = true; +constexpr const std::string_view topic_bmp085_availability = "dahoam/wohnzimmer/bmp085_1/available"; +constexpr const std::string_view topic_bmp085_pressure = "dahoam/wohnzimmer/bmp085_1/pressure"; +constexpr const std::string_view topic_bmp085_temperature = "dahoam/wohnzimmer/bmp085_1/temperature"; +constexpr const std::string_view topic_bmp085_altitude = "dahoam/wohnzimmer/bmp085_1/altitude"; + +constexpr const std::chrono::minutes availableTimeoutTime{1}; +constexpr const std::chrono::seconds valueUpdateInterval{15}; +} // namespace configs + +void init_config(); +void update_config(); +} // namespace deckenlampe diff --git a/main/mymdns.cpp b/main/mymdns.cpp new file mode 100644 index 0000000..220d291 --- /dev/null +++ b/main/mymdns.cpp @@ -0,0 +1,50 @@ +#include "mymdns.h" + +// esp-idf includes +#include +#include + +// local includes +#include "myconfig.h" + +namespace deckenlampe { +namespace { +constexpr const char * const TAG = "MDNS"; +} // namespace + +void init_mdns() +{ + { + const auto result = mdns_init(); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_init(): %s", esp_err_to_name(result)); + if (result != ESP_OK) + return; + } + + { + const auto result = mdns_hostname_set(config::hostname.data()); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_hostname_set(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } + + { + const auto result = mdns_instance_name_set(config::hostname.data()); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_instance_name_set(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } + + { + const auto result = mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "mdns_service_add(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } +} + +void update_mdns() +{ + +} +} // namespace deckenlampe diff --git a/main/mymdns.h b/main/mymdns.h new file mode 100644 index 0000000..0e8e299 --- /dev/null +++ b/main/mymdns.h @@ -0,0 +1,6 @@ +#pragma once + +namespace deckenlampe { +void init_mdns(); +void update_mdns(); +} // namespace deckenlampe diff --git a/main/mymqtt.cpp b/main/mymqtt.cpp new file mode 100644 index 0000000..77fd7b6 --- /dev/null +++ b/main/mymqtt.cpp @@ -0,0 +1,194 @@ +#include "mymqtt.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "myconfig.h" +#include "feature_lamp.h" +#include "feature_switch.h" +#include "feature_dht.h" +#include "feature_tsl.h" +#include "feature_bmp.h" +#include "cppmacros.h" + +namespace deckenlampe { +std::atomic mqttConnected; +espcpputils::mqtt_client mqttClient; + +namespace { +constexpr const char * const TAG = "MQTT"; + +void mqttEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); +} // namespace + +void init_mqtt() +{ + esp_mqtt_client_config_t mqtt_cfg = { + .uri = config::broker_url.data(), + }; + + mqttClient = espcpputils::mqtt_client{&mqtt_cfg}; + + if (!mqttClient) + { + ESP_LOGE(TAG, "error while initializing mqtt client!"); + return; + } + + { + const auto result = mqttClient.register_event(MQTT_EVENT_ANY, mqttEventHandler, NULL); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_mqtt_client_register_event(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } + + const auto result = mqttClient.start(); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_mqtt_client_start(): %s", esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; +} + +void update_mqtt() +{ +} + +int mqttVerbosePub(std::string_view topic, std::string_view value, int qos, int retain) +{ + ESP_LOGD(TAG, "topic=\"%.*s\" value=\"%.*s\"", topic.size(), topic.data(), value.size(), value.data()); + const auto pending_msg_id = mqttClient.publish(topic, value, qos, retain); + if (pending_msg_id < 0) + ESP_LOGE(TAG, "topic=\"%.*s\" value=\"%.*s\" failed pending_msg_id=%i", topic.size(), topic.data(), value.size(), value.data(), pending_msg_id); + else + ESP_LOGI(TAG, "topic=\"%.*s\" value=\"%.*s\" succeeded pending_msg_id=%i", topic.size(), topic.data(), value.size(), value.data(), pending_msg_id); + return pending_msg_id; +} + +namespace { +void mqttEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + CPP_UNUSED(event_handler_arg) + + const esp_mqtt_event_t *data = reinterpret_cast(event_data); + + switch (esp_mqtt_event_id_t(event_id)) { + case MQTT_EVENT_ERROR: + ESP_LOGE(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_ERROR"); + + // ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + // if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) + // { + // log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); + // log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); + // log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); + // ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); + + // } + break; + + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_CONNECTED"); + + mqttConnected = true; + + if (config::enable_lamp) { + mqttVerbosePub(config::topic_lamp_availability, "online", 0, 1); + mqttVerbosePub(config::topic_lamp_status, lampState.load() ? "ON" : "OFF", 0, 1); + mqttClient.subscribe(config::topic_lamp_set, 0); + } + + if (config::enable_switch) { + mqttVerbosePub(config::topic_switch_availability, "online", 0, 1); + mqttVerbosePub(config::topic_switch_status, switchState.load() ? "ON" : "OFF", 0, 1); + } + + if (config::enable_dht) { + mqttVerbosePub(config::topic_dht11_availability, lastDhtValue ? "online" : "offline", 0, 1); + if (lastDhtValue) { + if (mqttVerbosePub(config::topic_dht11_temperature, fmt::format("{:.1f}", lastDhtValue->temperature), 0, 1) >= 0) + last_dht11_temperature_pub = espchrono::millis_clock::now(); + if (mqttVerbosePub(config::topic_dht11_humidity, fmt::format("{:.1f}", lastDhtValue->humidity), 0, 1) >= 0) + last_dht11_humidity_pub = espchrono::millis_clock::now(); + } + } + + if (config::enable_tsl) { + mqttVerbosePub(config::topic_tsl2561_availability, lastTslValue ? "online" : "offline", 0, 1); + if (lastTslValue) { + if (mqttVerbosePub(config::topic_tsl2561_lux, fmt::format("{:.1f}", lastTslValue->lux), 0, 1) >= 0) + last_tsl2561_lux_pub = espchrono::millis_clock::now(); + } + } + + if (config::enable_bmp) { + mqttVerbosePub(config::topic_bmp085_availability, lastBmpValue ? "online" : "offline", 0, 1); + if (lastBmpValue) { + if (mqttVerbosePub(config::topic_bmp085_pressure, fmt::format("{:.1f}", lastBmpValue->pressure), 0, 1) >= 0) + last_bmp085_pressure_pub = espchrono::millis_clock::now(); + if (mqttVerbosePub(config::topic_bmp085_temperature, fmt::format("{:.1f}", lastBmpValue->temperature), 0, 1) >= 0) + last_bmp085_temperature_pub = espchrono::millis_clock::now(); + if (mqttVerbosePub(config::topic_bmp085_altitude, fmt::format("{:.1f}", lastBmpValue->altitude), 0, 1) >= 0) + last_bmp085_altitude_pub = espchrono::millis_clock::now(); + } + } + + break; + + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_DISCONNECTED"); + + mqttConnected = false; + + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_SUBSCRIBED"); + break; + + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_UNSUBSCRIBED"); + break; + + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_PUBLISHED"); + break; + + case MQTT_EVENT_DATA: { + std::string_view topic{data->topic, (size_t)data->topic_len}; + std::string_view value{data->data, (size_t)data->data_len}; + + ESP_LOGI(TAG, "%s event_id=%s topic=%.*s data=%.*s", event_base, "MQTT_EVENT_DATA", topic.size(), topic.data(), value.size(), value.data()); + + if (topic == config::topic_lamp_set) { + if (config::enable_lamp) { + bool newState = (lampState = (value == "ON")); + writeLamp(newState); + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_lamp_status, newState ? "ON" : "OFF", 0, 1); + } else { + ESP_LOGW(TAG, "received lamp set without lamp support enabled!"); + } + } else { + ESP_LOGW(TAG, "received unknown data topic=%.*s data=%.*s", topic.size(), topic.data(), value.size(), value.data()); + } + + break; + } + case MQTT_EVENT_BEFORE_CONNECT: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_BEFORE_CONNECT"); + break; + + case MQTT_EVENT_DELETED: + ESP_LOGI(TAG, "%s event_id=%s", event_base, "MQTT_EVENT_DELETED"); + break; + + default: + ESP_LOGW(TAG, "%s unknown event_id %i", event_base, event_id); + break; + } +} +} // namespace +} // namespace deckenlampe diff --git a/main/mymqtt.h b/main/mymqtt.h new file mode 100644 index 0000000..9962d5a --- /dev/null +++ b/main/mymqtt.h @@ -0,0 +1,18 @@ +#pragma once + +// system includes +#include +#include + +// local includes +#include "wrappers/mqtt_client.h" + +namespace deckenlampe { +extern std::atomic mqttConnected; +extern espcpputils::mqtt_client mqttClient; + +void init_mqtt(); +void update_mqtt(); + +int mqttVerbosePub(std::string_view topic, std::string_view value, int qos, int retain); +} // namespace deckenlampe diff --git a/main/mywifi.cpp b/main/mywifi.cpp new file mode 100644 index 0000000..40d74ef --- /dev/null +++ b/main/mywifi.cpp @@ -0,0 +1,65 @@ +#include "mywifi.h" + +// local includes +#include "espwifistack.h" +#include "myconfig.h" + +namespace deckenlampe { +namespace { +wifi_stack::config makeWifiConfig(); +} // namespace + +void init_wifi() +{ + wifi_stack::init(makeWifiConfig()); +} + +void update_wifi() +{ + wifi_stack::update(makeWifiConfig()); +} + +namespace { +wifi_stack::config makeWifiConfig() +{ + return wifi_stack::config { + .wifiEnabled = true, + .hostname = std::string{config::hostname}, + .wifis = std::array { + wifi_stack::wifi_entry { .ssid = std::string{config::sta_ssid}, .key = std::string{config::sta_key} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} }, + wifi_stack::wifi_entry { .ssid = {}, .key = {} } + }, + .sta_ip = { + .dhcpEnabled = true, +// .staticIp = {}, +// .staticGateway = {}, +// .staticSubnet = {}, +// .staticDns1 = {}, +// .staticDns2 = {} + }, + .ap = { + { + .ssid = std::string{config::ap_ssid}, + .key = std::string{config::ap_key} + }, + .channel = 1, + .authmode = WIFI_AUTH_WPA2_PSK, + .ssid_hidden = false, + .max_connection = 4, + .beacon_interval = 100, + .ip{192, 168, 4, 1}, + .subnet{255, 255, 255, 0} + }, + .min_rssi = -90 + }; +} +} // namespace +} // namespace deckenlampe diff --git a/main/mywifi.h b/main/mywifi.h new file mode 100644 index 0000000..5f1aea4 --- /dev/null +++ b/main/mywifi.h @@ -0,0 +1,6 @@ +#pragma once + +namespace deckenlampe { +void init_wifi(); +void update_wifi(); +} // namespace deckenlampe diff --git a/main/webserver.cpp b/main/webserver.cpp new file mode 100644 index 0000000..0a54cfe --- /dev/null +++ b/main/webserver.cpp @@ -0,0 +1,202 @@ +#include "webserver.h" + +// system includes +#include +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "myconfig.h" +#include "feature_lamp.h" +#include "feature_switch.h" +#include "feature_dht.h" +#include "feature_tsl.h" +#include "feature_bmp.h" +#include "mymqtt.h" + +namespace deckenlampe { +httpd_handle_t httpdHandle; + +namespace { +constexpr const char * const TAG = "WEBSERVER"; + +esp_err_t webserver_root_handler(httpd_req_t *req); +esp_err_t webserver_on_handler(httpd_req_t *req); +esp_err_t webserver_off_handler(httpd_req_t *req); +esp_err_t webserver_toggle_handler(httpd_req_t *req); +esp_err_t webserver_reboot_handler(httpd_req_t *req); +} // namespace + +void init_webserver() +{ + { + httpd_config_t httpConfig HTTPD_DEFAULT_CONFIG(); + + const auto result = httpd_start(&httpdHandle, &httpConfig); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_start(): %s", esp_err_to_name(result)); + if (result != ESP_OK) + return; + } + + for (const httpd_uri_t &uri : { + httpd_uri_t { .uri = "/", .method = HTTP_GET, .handler = webserver_root_handler, .user_ctx = NULL }, + httpd_uri_t { .uri = "/on", .method = HTTP_GET, .handler = webserver_on_handler, .user_ctx = NULL }, + httpd_uri_t { .uri = "/off", .method = HTTP_GET, .handler = webserver_off_handler, .user_ctx = NULL }, + httpd_uri_t { .uri = "/toggle", .method = HTTP_GET, .handler = webserver_toggle_handler, .user_ctx = NULL }, + httpd_uri_t { .uri = "/reboot", .method = HTTP_GET, .handler = webserver_reboot_handler, .user_ctx = NULL } + }) + { + const auto result = httpd_register_uri_handler(httpdHandle, &uri); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", uri.uri, esp_err_to_name(result)); + //if (result != ESP_OK) + // return result; + } +} + +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_root_handler(httpd_req_t *req) +{ + std::string body = "this is work in progress...
\n"; + + if (config::enable_lamp) { + body += "on
\n" + "off
\n" + "toggle
\n"; + } + + body += "reboot
\n" + "
\n"; + + if (config::enable_lamp) + body += fmt::format("Lamp: {}
\n", lampState ? "ON" : "OFF"); + + if (config::enable_switch) + body += fmt::format("Switch: {}
\n", switchState ? "ON" : "OFF"); + + if (config::enable_dht) { + if (lastDhtValue) { + body += fmt::format("DHT11 Temperature: {:.1f} C
\n", lastDhtValue->temperature); + body += fmt::format("DHT11 Humidity: {:.1f} %
\n", lastDhtValue->humidity); + } else + body += "DHT11 not available at the moment
\n"; + } + + if (config::enable_tsl) { + if (lastTslValue) { + body += fmt::format("TSL2561 Brightness: {:.1f} lux
\n", lastTslValue->lux); + } else + body += "TSL2561 not available at the moment
\n"; + } + + if (config::enable_bmp) { + if (lastBmpValue) { + body += fmt::format("BMP085 Pressure: {:.1f} lux
\n", lastBmpValue->pressure); + body += fmt::format("BMP085 Temperature: {:.1f} C
\n", lastBmpValue->temperature); + body += fmt::format("BMP085 Altitude: {:.1f} m
\n", lastBmpValue->altitude); + } else + body += "BMP085 not available at the moment
\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; +} + +esp_err_t webserver_on_handler(httpd_req_t *req) +{ + if (!config::enable_lamp) { + ESP_LOGW(TAG, "lamp support not enabled!"); + CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") + } + + const bool state = (lampState = true); + writeLamp(state); + + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); + + std::string_view body{"ON called..."}; + 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_off_handler(httpd_req_t *req) +{ + if (!config::enable_lamp) { + ESP_LOGW(TAG, "lamp support not enabled!"); + CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") + } + + const bool state = (lampState = false); + writeLamp(state); + + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); + + std::string_view body{"OFF called..."}; + 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_toggle_handler(httpd_req_t *req) +{ + if (!config::enable_lamp) { + ESP_LOGW(TAG, "lamp support not enabled!"); + CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") + } + + const bool state = (lampState = !lampState); + writeLamp(state); + + if (mqttClient && mqttConnected) + mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); + + std::string_view body{"TOGGLE called..."}; + 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_reboot_handler(httpd_req_t *req) +{ + std::string_view body{"REBOOT called..."}; + 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()) + + esp_restart(); + + return ESP_OK; +} +} // namespace +} // namespace deckenlampe diff --git a/main/webserver.h b/main/webserver.h new file mode 100644 index 0000000..f03762f --- /dev/null +++ b/main/webserver.h @@ -0,0 +1,11 @@ +#pragma once + +// esp-idf includes +#include + +namespace deckenlampe { +extern httpd_handle_t httpdHandle; + +void init_webserver(); +void update_webserver(); +} // namespace deckenlampe