From c04c4138a3061f86d7d669830c4d39a1894e387c Mon Sep 17 00:00:00 2001 From: admar Date: Sun, 28 Jul 2024 17:24:50 +0200 Subject: [PATCH 01/13] Setup PlatformIO project using WIFIMANAGER-ESP32 and dESPatch --- .clang-format | 4 + .gitignore | 1 + .vscode/c_cpp_properties.json | 517 ++++++++++++++++ .vscode/extensions.json | 10 + .vscode/launch.json | 44 ++ Claire.ino | 1061 --------------------------------- include/README | 39 ++ include/WifiHandler.hpp | 14 + include/root_ca.h | 37 ++ lib/README | 46 ++ platformio.ini | 18 + src/WifiHandler.cpp | 68 +++ src/main.cpp | 28 + test/README | 11 + 14 files changed, 837 insertions(+), 1061 deletions(-) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json delete mode 100644 Claire.ino create mode 100644 include/README create mode 100644 include/WifiHandler.hpp create mode 100644 include/root_ca.h create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/WifiHandler.cpp create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b914e15 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 120 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..4aa958b --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,517 @@ +// +// !!! WARNING !!! AUTO-GENERATED FILE! +// PLEASE DO NOT MODIFY IT AND USE "platformio.ini": +// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags +// +{ + "configurations": [ + { + "name": "PlatformIO", + "includePath": [ + "/home/admar/Documents/Arduino/buienradarklok/Claire/include", + "/home/admar/Documents/Arduino/buienradarklok/Claire/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/ArduinoJson/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/WifiManager-esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/heap/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/log/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps/sntp", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/lwip/src/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include/arch", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/platform_port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/soc", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/public_compat", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_pm/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ringbuf/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/vfs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_wifi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_event/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_netif/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_eth/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcpip_adapter/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ipc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_trace/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_timer/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/mbedtls/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_update/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spi_flash/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bootloader_support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nvs_flash/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/pthread/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/xtensa", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include/port/xtensa", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ieee802154/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/console", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/asio/asio/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/osi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/include/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/api/include/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cbor/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/unity/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cmock/CMock/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/libcoap/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls/esp-tls-crypto", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_adc_cal/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hid/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcp_transport/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_ota/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/interface", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protobuf-c/protobuf-c", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/common", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/security", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/transports", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mdns/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_local_ctrl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/sdmmc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_serial_slave_link/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_websocket_client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/expat/expat/lib", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wear_levelling/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/diskio", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/vfs", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freemodbus/freemodbus/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/jsmn/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json/cJSON", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/port_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mqtt/esp-mqtt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/openssl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/perfmon/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spiffs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ulp/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wifi_provisioning/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rmaker_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_diagnostics/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rtc_store/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_insights/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_generator/upstream", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_schedule/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rainmaker/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/gpio_button/button/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/qrcode/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ws2812_led", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_littlefs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/tool", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/typedef", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/image", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/math", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/nn", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/layer", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/detect", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/model_zoo", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/driver/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/conversions/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/mem/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/add/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/addc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mulc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/sub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/test/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fb_gfx/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/cores/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/variants/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdateServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/I2S/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Insights/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", + "" + ], + "browse": { + "limitSymbolsToIncludedHeaders": true, + "path": [ + "/home/admar/Documents/Arduino/buienradarklok/Claire/include", + "/home/admar/Documents/Arduino/buienradarklok/Claire/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/ArduinoJson/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/WifiManager-esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/heap/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/log/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps/sntp", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/lwip/src/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include/arch", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/platform_port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/soc", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/public_compat", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_pm/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ringbuf/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/vfs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_wifi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_event/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_netif/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_eth/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcpip_adapter/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ipc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_trace/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_timer/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/mbedtls/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_update/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spi_flash/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bootloader_support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nvs_flash/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/pthread/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/xtensa", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include/port/xtensa", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ieee802154/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/console", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/asio/asio/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/osi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/include/esp32/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/api/include/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cbor/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/unity/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cmock/CMock/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/libcoap/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls/esp-tls-crypto", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_adc_cal/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hid/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcp_transport/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_ota/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_server/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/interface", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protobuf-c/protobuf-c", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/common", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/security", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/transports", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mdns/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_local_ctrl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/sdmmc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_serial_slave_link/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_websocket_client/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/expat/expat/lib", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/port/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wear_levelling/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/diskio", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/vfs", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freemodbus/freemodbus/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/jsmn/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json/cJSON", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/port_include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mqtt/esp-mqtt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/openssl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/perfmon/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spiffs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ulp/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wifi_provisioning/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rmaker_common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_diagnostics/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rtc_store/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_insights/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_generator/upstream", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_schedule/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rainmaker/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/gpio_button/button/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/qrcode/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ws2812_led", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_littlefs/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/tool", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/typedef", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/image", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/math", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/nn", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/layer", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/detect", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/model_zoo", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/driver/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/conversions/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/mem/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/add/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/addc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mulc/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/sub/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/test/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fb_gfx/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include", + "/home/admar/.platformio/packages/framework-arduinoespressif32/cores/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/variants/esp32", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdateServer/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/I2S/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Insights/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", + "" + ] + }, + "defines": [ + "PLATFORMIO=60115", + "ARDUINO_ESP32_DEV", + "HAVE_CONFIG_H", + "MBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"", + "UNITY_INCLUDE_CONFIG_H", + "WITH_POSIX", + "_GNU_SOURCE", + "IDF_VER=\"v4.4.7-dirty\"", + "ESP_PLATFORM", + "_POSIX_READER_WRITER_LOCKS", + "ARDUINO_ARCH_ESP32", + "ESP32", + "F_CPU=240000000L", + "ARDUINO=10812", + "ARDUINO_VARIANT=\"esp32\"", + "ARDUINO_BOARD=\"Espressif ESP32 Dev Module\"", + "ARDUINO_PARTITION_default", + "" + ], + "cStandard": "gnu99", + "cppStandard": "gnu++11", + "compilerPath": "/home/admar/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc", + "compilerArgs": [ + "-mlongcalls", + "" + ] + } + ], + "version": 4 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f510696 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,44 @@ +// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY +// +// PlatformIO Debugging Solution +// +// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html +// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html + +{ + "version": "0.2.0", + "configurations": [ + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug", + "executable": "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/build/esp32dev/firmware.elf", + "projectEnvName": "esp32dev", + "toolchainBinDir": "/home/admar/.platformio/packages/toolchain-xtensa-esp32/bin", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": { + "type": "PlatformIO", + "task": "Pre-Debug" + } + }, + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug (skip Pre-Debug)", + "executable": "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/build/esp32dev/firmware.elf", + "projectEnvName": "esp32dev", + "toolchainBinDir": "/home/admar/.platformio/packages/toolchain-xtensa-esp32/bin", + "internalConsoleOptions": "openOnSessionStart" + }, + { + "type": "platformio-debug", + "request": "launch", + "name": "PIO Debug (without uploading)", + "executable": "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/build/esp32dev/firmware.elf", + "projectEnvName": "esp32dev", + "toolchainBinDir": "/home/admar/.platformio/packages/toolchain-xtensa-esp32/bin", + "internalConsoleOptions": "openOnSessionStart", + "loadMode": "manual" + } + ] +} diff --git a/Claire.ino b/Claire.ino deleted file mode 100644 index d298742..0000000 --- a/Claire.ino +++ /dev/null @@ -1,1061 +0,0 @@ -//#define DISABLE_WIFI -#define HW_PROTO_PAPER 1 -#define HW_PROTO_V1 2 - -#ifndef LED_BUILTIN -#define LED_BUILTIN 2 -#endif - -#ifndef BUTTON_BUILTIN -#define BUTTON_BUILTIN 0 -#endif - -#include // https://github.com/admarschoonen/WiFiManager -#include - -#include -#include -#include - -#include -#include "bsec.h" - -#include "soc/soc.h" -#include "soc/rtc_cntl_reg.h" - -#ifndef LED_BUILTIN -#define LED_BUILTIN 2 -#endif - -#define NUM_LEDS_PROTO_PAPER 93 -#define DATA_PIN_PROTO_PAPER 5 -#define CONNECT_SW_PIN_PROTO_PAPER BUTTON_BUILTIN -#define NUM_LEDS_PROTO_V1 29 -#define DATA_PIN_PROTO_V1 14 -#define CONNECT_SW_PIN_PROTO_V1 21 - -static int BUIENRADAR_START_LED = 0; -static int BUIENRADAR_SKIP_LED = 1; -static int BUIENRADAR_NUM_LEDS = 12; -static int PAQI_START_LED = 0; -static int PAQI_SKIP_LED = 0; -static int PAQI_NUM_LEDS = 0; -static int UVI_LED = 0; -static int AQI_LED = 0; -static int POLLEN_LED = 0; -static int IAQI_LED = 0; -static int CONNECT_SW_PIN = 0; -static int LIGHT_PIN = 0; - -#define SHOW_AQI_LED -#define SHOW_POLLEN_LED -#define SHOW_UVI_LED - -#define MIN(x, y) (((x) <= (y)) ? (x) : (y)) - -Adafruit_NeoPixel leds_rgb_proto_paper(NUM_LEDS_PROTO_PAPER, DATA_PIN_PROTO_PAPER, NEO_GRB + NEO_KHZ800); -Adafruit_NeoPixel leds_rgbw_proto_v1(NUM_LEDS_PROTO_V1, DATA_PIN_PROTO_V1, NEO_RGBW + NEO_KHZ800); - -const uint8_t bsec_config_iaq[] = { - #include "config/generic_33v_3s_4d/bsec_iaq.txt" -}; - -static int ledMapPrecipitation[12] = {0}; -static int ledMapPAQI[12] = {0}; -static int ledMapUVI = 0; -static int ledMapPollen = 0; -static int ledMapIAQI = 0; -static int ledMapAQI = 0; - -#define SEALEVELPRESSURE_HPA (1013.25) -#define BME_CS 5 -String output; -Bsec iaqSensor; - -static HTTPClient http; -static const String baseUrlAQI = "https://sinoptik.luon.net/forecast?metrics=PAQI&metrics=AQI&metrics=pollen&metrics=UVI"; -static const String baseUrlPrecipitation = "https://sinoptik.luon.net/forecast?metrics=precipitation"; - -static int hw_variant = 0; - -WiFiManager wifiManager; -DESPatch dESPatch; -const char* root_ca = \ - "-----BEGIN CERTIFICATE-----\n" \ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" \ - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" \ - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" \ - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" \ - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" \ - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" \ - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" \ - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" \ - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" \ - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" \ - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" \ - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" \ - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" \ - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" \ - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" \ - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" \ - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" \ - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" \ - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" \ - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" \ - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" \ - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" \ - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" \ - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" \ - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" \ - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" \ - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" \ - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" \ - "-----END CERTIFICATE-----\n"; - - - -const float location[] = {51.445466493287434, 5.515445691496135}; // Telefoonstraat, Eindhoven -const String address = "Telefoonstraat 18, Eindhoven"; - -//const float location[] = {51.44083, 5.47778}; // Eindhoven -//const float location[] = {52.09083, 5.12222}; // Utrecht -//const float location[] = {51.8425, 5.85278}; // Nijmegen -//const float location[] = {52.37403, 4.88969}; // Amsterdam -//const float location[] = {52.15833, 4.49306}; // Leiden - -typedef struct RainPickerDataArray24 { - const int len = 24; - long time[24]; - float value[24]; -} RainPickerDataArray24; - -typedef struct RainPickerDataArray5 { - const int len = 5; - long time[5]; - float value[5]; -} RainPickerDataArray5; - -typedef struct RainpickerData { - float lat; - float lon; - long time; - - RainPickerDataArray24 PAQI; - RainPickerDataArray24 AQI; - RainPickerDataArray24 pollen; - RainPickerDataArray5 UVI; - RainPickerDataArray24 precipitation; -} RainpickerData; - -RainpickerData rainpickerData; - -StaticJsonDocument<6144> doc; - -static bool initDone = false; - -void parseJson(String * payload) -{ - doc.clear(); - - DeserializationError error = deserializeJson(doc, * payload); - - if (error) { - Serial.print(F("deserializeJson() failed: ")); - Serial.println(error.f_str()); - return; - } - - rainpickerData.lat = doc["lat"]; - rainpickerData.lon = doc["lon"]; - rainpickerData.time = doc["time"]; - - int n = 0; - for (JsonObject elem : doc["PAQI"].as()) { - if (n >= rainpickerData.PAQI.len) { - break; - } - rainpickerData.PAQI.time[n] = elem["time"]; - rainpickerData.PAQI.value[n] = elem["value"]; - n = n + 1; - } - - n = 0; - for (JsonObject elem : doc["AQI"].as()) { - if (n >= rainpickerData.AQI.len) { - break; - } - rainpickerData.AQI.time[n] = elem["time"]; - rainpickerData.AQI.value[n] = elem["value"]; - n = n + 1; - } - - n = 0; - for (JsonObject elem : doc["pollen"].as()) { - if (n >= rainpickerData.pollen.len) { - break; - } - rainpickerData.pollen.time[n] = elem["time"]; - rainpickerData.pollen.value[n] = elem["value"]; - n = n + 1; - } - - n = 0; - for (JsonObject elem : doc["UVI"].as()) { - if (n >= rainpickerData.UVI.len) { - break; - } - rainpickerData.UVI.time[n] = elem["time"]; - rainpickerData.UVI.value[n] = elem["value"]; - n = n + 1; - } - - n = 0; - for (JsonObject elem : doc["precipitation"].as()) { - if (n >= rainpickerData.precipitation.len) { - break; - } - rainpickerData.precipitation.time[n] = elem["time"]; - rainpickerData.precipitation.value[n] = elem["value"]; - n = n + 1; - } -} - -static void setup_pins_proto_paper(void) -{ - int n = 0; - - BUIENRADAR_START_LED = 32; - BUIENRADAR_SKIP_LED = 1; - BUIENRADAR_NUM_LEDS = 12; - - PAQI_START_LED = 72; - PAQI_SKIP_LED = 0; - PAQI_NUM_LEDS = 12; - - // #define UVI_LED 92 // LED in center - UVI_LED = 84; // LED above center - AQI_LED = 86; // LED to the right - POLLEN_LED = 90; // LED to the left - IAQI_LED = 88; // LED below center - - CONNECT_SW_PIN = CONNECT_SW_PIN_PROTO_PAPER; - - n = 0; - while (n < BUIENRADAR_NUM_LEDS) { - ledMapPrecipitation[n] = BUIENRADAR_START_LED + n * (BUIENRADAR_SKIP_LED + 1); - n = n + 1; - } - - n = 0; - while (n < PAQI_NUM_LEDS) { - ledMapPAQI[n] = PAQI_START_LED + n * (PAQI_SKIP_LED + 1); - n = n + 1; - } - - ledMapAQI = AQI_LED; - ledMapUVI = UVI_LED; - ledMapPollen = POLLEN_LED; - ledMapIAQI = IAQI_LED; -} - -static void setup_pins_proto_v1(void) -{ - int n = 0; - - BUIENRADAR_START_LED = 0; - BUIENRADAR_SKIP_LED = 0; - BUIENRADAR_NUM_LEDS = 12; - - PAQI_START_LED = 12; - PAQI_SKIP_LED = 0; - PAQI_NUM_LEDS = 12; - - UVI_LED = 27; // LED above center - AQI_LED = 24; // LED to the right - POLLEN_LED = 26; // LED to the left - IAQI_LED = 25; // LED below center - - CONNECT_SW_PIN = CONNECT_SW_PIN_PROTO_V1; - - ledMapPrecipitation[0] = 11; - n = 1; - while (n < BUIENRADAR_NUM_LEDS) { - ledMapPrecipitation[n] = BUIENRADAR_START_LED + (n - 1) * (BUIENRADAR_SKIP_LED + 1); - n = n + 1; - } - - ledMapPAQI[0] = 23; - n = 1; - while (n < PAQI_NUM_LEDS) { - ledMapPAQI[n] = PAQI_START_LED + (n - 1) * (PAQI_SKIP_LED + 1); - n = n + 1; - } - - ledMapAQI = AQI_LED; - ledMapUVI = UVI_LED; - ledMapPollen = POLLEN_LED; - ledMapIAQI = IAQI_LED; - - LIGHT_PIN = 34; - analogSetPinAttenuation(LIGHT_PIN, ADC_0db); - analogReadResolution(12); -} - -void leds_clear(void) { - if (hw_variant == HW_PROTO_PAPER) { - leds_rgb_proto_paper.clear(); - leds_rgb_proto_paper.show(); - } else if (hw_variant == HW_PROTO_V1) { - leds_rgbw_proto_v1.clear(); - leds_rgbw_proto_v1.show(); - } -} - -int checkIaqSensorStatus(void) -{ - if (iaqSensor.bsecStatus != BSEC_OK) { - if (iaqSensor.bsecStatus < BSEC_OK) { - output = "BSEC error code : " + String(iaqSensor.bsecStatus); - Serial.println(output); - //for (;;); - } else { - output = "BSEC warning code : " + String(iaqSensor.bsecStatus); - Serial.println(output); - } - } - - if (iaqSensor.bme68xStatus != BME68X_OK) { - if (iaqSensor.bme68xStatus < BME68X_OK) { - output = "BME680 error code : " + String(iaqSensor.bme68xStatus); - Serial.println(output); - //for (;;); - } else { - output = "BME680 warning code : " + String(iaqSensor.bme68xStatus); - Serial.println(output); - } - } - - return iaqSensor.bme68xStatus; -} - -void readSensors(void) { - static bool readingInProgress = false; - static unsigned long endTime = 0; - - if (hw_variant == HW_PROTO_PAPER) { - return; - } - - unsigned long time_trigger = millis(); - if (iaqSensor.run()) { // If new data is available - output = String(time_trigger); - output += " Sensor data: Traw: " + String(iaqSensor.rawTemperature); - output += " *C , P: " + String(iaqSensor.pressure); - output += " hPa, RHraw: " + String(iaqSensor.rawHumidity); - output += " %, Rgas: " + String(iaqSensor.gasResistance); - output += " Ohm, IAQ: " + String(iaqSensor.iaq); - output += ", accuracy: " + String(iaqSensor.iaqAccuracy); - output += ", Tcorr: " + String(iaqSensor.temperature); - output += " *C, RHcorr: " + String(iaqSensor.humidity); - output += " %, IAQstatic: " + String(iaqSensor.staticIaq); - output += ", eCO2: " + String(iaqSensor.co2Equivalent); - output += " ppm, eVOC: " + String(iaqSensor.breathVocEquivalent); - output += " ppm"; - - int light = 0; - for (int n = 0; n < 256; n++) { - light += analogRead(LIGHT_PIN); - } - light = (light + 128) >> 8; - output += ", light: " + String(light); - Serial.println(output); - } else { - checkIaqSensorStatus(); - } -} - -void taskBME680( void * parameter ) -{ - while (initDone == false) { - delay(100); - } - - while (true) { - readSensors(); - delay(100); - } - - //Serial.println("Ending task 1"); - //vTaskDelete( NULL ); -} - -void setup() { - const char url[] = "https://apikey:cqprlgiafadnidsgeqozcpldkaeqimqw@despatch.luon.net/files/4/despatch.json"; - unsigned long interval = 60; // By default check for updates every 60 seconds - int x; - - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable brownout; needed for some dev kits - - // sanity check delay - allows reprogramming if accidently blowing power w/leds - delay(2000); - - Serial.begin(115200); - Serial.println("buienradarklok starting"); - - http.setReuse(false); - - SPI.begin(); - iaqSensor.begin(BME_CS, SPI); - String output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix); - Serial.println(output); - x = checkIaqSensorStatus(); - - if (x != BME68X_OK) { - Serial.println(F("Could not find a valid BME680 sensor; assuming hw variant \"paper prototype\"!")); - hw_variant = HW_PROTO_PAPER; - setup_pins_proto_paper(); - leds_rgb_proto_paper.begin(); // INITIALIZE NeoPixel strip object - } else { - Serial.println(F("BME680 found; assuming hw variant \"proto v1\"")); - - iaqSensor.setConfig(bsec_config_iaq); - iaqSensor.setTemperatureOffset(13.5f); - checkIaqSensorStatus(); - - hw_variant = HW_PROTO_V1; - setup_pins_proto_v1(); - leds_rgbw_proto_v1.begin(); // INITIALIZE NeoPixel strip object - } - - bsec_virtual_sensor_t sensorList[10] = { - BSEC_OUTPUT_RAW_TEMPERATURE, - BSEC_OUTPUT_RAW_PRESSURE, - BSEC_OUTPUT_RAW_HUMIDITY, - BSEC_OUTPUT_RAW_GAS, - BSEC_OUTPUT_IAQ, - BSEC_OUTPUT_STATIC_IAQ, - BSEC_OUTPUT_CO2_EQUIVALENT, - BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, - }; - - iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP); - checkIaqSensorStatus(); - - // Print the header - output = "Timestamp [ms], raw temperature [°C], pressure [hPa], raw relative humidity [%], gas [Ohm], IAQ, IAQ accuracy, temperature [°C], relative humidity [%], Static IAQ, CO2 equivalent, breath VOC equivalent"; - Serial.println(output); - - leds_clear(); - -#ifndef DISABLE_WIFI - //wifiManager.resetSettings(); - wifiManager.configure("Claire-", true, LED_BUILTIN, true, CONNECT_SW_PIN, false); - - //fetches ssid and pass and tries to connect - //if it does not connect it starts an access point - //and goes into a blocking loop awaiting configuration - if (!wifiManager.autoConnect()) { - Serial.println("failed to connect and hit timeout"); - //reset and try again, or maybe put it to deep sleep - ESP.restart(); - delay(1000); - } - - //if you get here you have connected to the WiFi - Serial.print("connected with address: "); - Serial.println(WiFi.localIP()); - - if (hw_variant == HW_PROTO_PAPER) { - //keep LED on - digitalWrite(LED_BUILTIN, LED_ON_VALUE_DEFAULT); - } - - x = dESPatch.configure(url, true, false, interval, false, root_ca); - Serial.print("dESPatch.configure() returned with code "); - Serial.println(x); -#endif - initDone = true; - - xTaskCreate(taskBME680, "taskBME680", 10000, NULL, 1, NULL); -} - -// From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ -String urlencode(String str) -{ - String encodedString=""; - char c; - char code0; - char code1; - char code2; - for (int i =0; i < str.length(); i++){ - c=str.charAt(i); - if (c == ' '){ - encodedString+= '+'; - } else if (isalnum(c)){ - encodedString+=c; - } else{ - code1=(c & 0xf)+'0'; - if ((c & 0xf) >9){ - code1=(c & 0xf) - 10 + 'A'; - } - c=(c>>4)&0xf; - code0=c+'0'; - if (c > 9){ - code0=c - 10 + 'A'; - } - code2='\0'; - encodedString+='%'; - encodedString+=code0; - encodedString+=code1; - //encodedString+=code2; - } - yield(); - } - - return encodedString; -} - -void getPrecipitation(const float * location) { - int httpCode; - //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); - String url, latS, lonS; - int tmp1, tmp2; - String payload = ""; - - tmp1 = int(location[0]); - tmp2 = int((location[0] - tmp1) * 1000000); - latS = String(tmp1) + "." + String(tmp2); - tmp1 = int(location[1]); - tmp2 = int((location[1] - tmp1) * 1000000); - lonS = String(tmp1) + "." + String(tmp2); - - url = baseUrlPrecipitation + "&lat=" + latS + "&lon=" + lonS; - - Serial.print(millis()); - Serial.print(" Retrieving precipitation from "); - Serial.println(url); - - http.begin(url, root_ca); - httpCode = http.GET(); - - if (httpCode > 0) { - payload = http.getString(); - parseJson(&payload); - } else { - Serial.print(millis()); - Serial.print(" Got http code "); - Serial.println(httpCode); - } - http.end(); -} - -void getPrecipitation(const String address) { - int httpCode; - //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); - String url, a; - String payload = ""; - - a = urlencode(address); - - url = baseUrlPrecipitation + "&address=" + a; - - Serial.print(millis()); - Serial.print(" Retrieving precipitation from "); - Serial.println(url); - - http.begin(url, root_ca); - httpCode = http.GET(); - - if (httpCode > 0) { - payload = http.getString(); - parseJson(&payload); - } else { - Serial.print(millis()); - Serial.print(" Got http code "); - Serial.println(httpCode); - } - http.end(); -} - -void getAQI(const float * location) { - int httpCode; - //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); - String url, latS, lonS; - int tmp1, tmp2; - String payload = ""; - - tmp1 = int(location[0]); - tmp2 = int((location[0] - tmp1) * 1000000); - latS = String(tmp1) + "." + String(tmp2); - tmp1 = int(location[1]); - tmp2 = int((location[1] - tmp1) * 1000000); - lonS = String(tmp1) + "." + String(tmp2); - - url = baseUrlAQI + "&lat=" + latS + "&lon=" + lonS; - - Serial.print(millis()); - Serial.print(" Retrieving AQI from "); - Serial.println(url); - - http.begin(url, root_ca); - httpCode = http.GET(); - - if (httpCode > 0) { - payload = http.getString(); - parseJson(&payload); - } else { - Serial.print(millis()); - Serial.print(" Got http code "); - Serial.println(httpCode); - } - http.end(); -} - -void getAQI(const String address) { - int httpCode; - //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); - String url, a; - String payload = ""; - - a = urlencode(address); - - url = baseUrlAQI + "&address=" + a; - - Serial.print(millis()); - Serial.print(" Retrieving AQI from "); - Serial.println(url); - - http.begin(url, root_ca); - httpCode = http.GET(); - - if (httpCode > 0) { - payload = http.getString(); - parseJson(&payload); - } else { - Serial.print(millis()); - Serial.print(" Got http code "); - Serial.println(httpCode); - } - http.end(); -} - -static int colormap(float x) { - // Input: intensity - // Output: RGB encoded pixel color similar to luchtmeetnet legend - int y = 0; - - if (x < 1.0) { - y = (0 << 16) | (0 << 8) | 0; // black (good) - } else if (x < 2.0) { - // 1.0 <= x < 2.0 - if (hw_variant == HW_PROTO_PAPER) { - y = (0 << 16) | (0 << 8) | 160; // blue (good) - } else if (hw_variant == HW_PROTO_V1) { - y = (0 << 16) | (0 << 8) | 255; // blue (good) - } - } else if (x < 3.0) { - // 2.0 <= x < 3.0 - y = (0 << 16) | (255 << 8) | 255; // cyan (good) - } else if (x < 4.0) { - // 3.0 <= x < 4.0 - y = (255 << 16) | (255 << 8) | 255; // white (mediocre) - } else if (x < 5.0) { - // 4.0 <= x < 5.0 - y = (200 << 16) | (200 << 8) | 32; // light yellow (mediocre) - } else if (x < 6.0) { - // 5.0 <= x < 6.0 - y = (120 << 16) | (120 << 8) | 0; // yellow (mediocre) - } else if (x < 7.0) { - // 6.0 <= x < 7.0 - y = (200 << 16) | (80 << 8) | 0; // orange (inadequate) - } else if (x < 8.0) { - // 7.0 <= x < 8.0 - y = (255 << 16) | (50 << 8) | 0; // red orange (inadequate) - } else if (x < 9.0) { - // 8.0 <= x < 9.0 - y = (180 << 16) | (0 << 8) | 0; // red (bad) - } else if (x < 10.0) { - // 9.0 <= x < 10.0 - y = (200 << 16) | (0 << 8) | 160; // magenta (bad) - } else { - // 10.0 <= x - y = (60 << 16) | (0 << 8) | 210; // purple (terrible) - } - - return y; -} - -float colormapIaqi(void) -{ - float x = 0.0f; - float iaqi = iaqSensor.staticIaq; - - if (iaqi <= 50.0f) { - x = 1.0f; // Excellent - } else if (iaqi <= 100.0f) { - x = 2.0f; // Good - } else if (iaqi <= 125.0f) { - x = 4.0f; - } else if (iaqi <= 150.0f) { - x = 4.0f; // Lightly polluted - } else if (iaqi <= 175.0f) { - x = 5.0f; - } else if (iaqi <= 200.0f) { - x = 6.0f; // Moderately polluted - } else if (iaqi <= 225.0f) { - x = 7.0f; - } else if (iaqi <= 250.0f) { - x = 8.0f; // Heavily polluted - } else if (iaqi <= 300.0f) { - x = 9.0f; - } else if (iaqi <= 350.0f) { - x = 10.0f; // Severely polluted - } else { - x = 11.0f; // Extremely polluted - } - - return colormap(x); -} - -static float buienradarMap(float x) { - // Input: rain intensity (mm/h) - // Output: floating point number which can be mapped on color scale - float y = 0; - - // Use E3 series of preferred numbers to map intensity to color - if (x < 0.1) { - y = 0.5; - } else if (x < 0.22) { - y = 1.5; - } else if (x < 0.47) { - y = 2.5; - } else if (x < 1.0) { - y = 3.5; - } else if (x < 2.2) { - y = 4.5; - } else if (x < 4.7) { - y = 5.5; - } else if (x < 10) { - y = 6.5; - } else if (x < 22) { - y = 7.5; - } else if (x < 47) { - y = 8.5; - } else if (x < 100) { - y = 9.5; - } else { - y = 10.5; - } - - return y; -} - -static void ledsSetPixelColor(uint32_t ledIdx, int r, int g, int b, int w) { - if (hw_variant == HW_PROTO_PAPER) { - leds_rgb_proto_paper.setPixelColor(ledIdx, leds_rgb_proto_paper.Color(r, g, b)); - } else if (hw_variant == HW_PROTO_V1) { - leds_rgbw_proto_v1.setPixelColor(ledIdx, leds_rgbw_proto_v1.Color(g, r, b, w)); - } -} - -static void ledsSetPixelColor(uint32_t ledIdx, int r, int g, int b) { - ledsSetPixelColor(ledIdx, r, g, b, 0); -} - -static void updateLedsNoWifi(void) { - uint32_t ledIdx; - uint32_t ledCount; - uint32_t color; - int r, g, b, w, n; - - if (hw_variant == HW_PROTO_PAPER) { - // Start by setting all leds to value of the first datapoint - ledIdx = 11; - ledCount = 0; - for (ledCount = 0; ledCount < 11; ledCount++) { - color = colormap(ledCount); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(ledIdx, r, g, b); - ledIdx = ledIdx + PAQI_SKIP_LED + 1; - } - } else if (hw_variant == HW_PROTO_V1) { - // Start by setting all leds to value of the first datapoint - ledIdx = 0; - ledCount = 0; - for (ledCount = 0; ledCount < 11; ledCount++) { - color = colormap(ledCount); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - w = (color & 0xFF000000) >> 24; - - ledsSetPixelColor(ledIdx, r, g, b, w); - ledIdx = ledIdx + PAQI_SKIP_LED + 1; - } - } -} - -static void updateLeds(void) { - uint32_t ledIdx; - uint32_t ledCount; - uint32_t color; - int r, g, b, w, n; - - // Start by setting all leds to value of the first datapoint - ledIdx = PAQI_START_LED; - ledCount = 0; - for (ledCount = 0; ledCount < PAQI_NUM_LEDS; ledCount++) { - color = colormap(rainpickerData.PAQI.value[0]); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - w = (color & 0xFF000000) >> 24; - - ledsSetPixelColor(ledMapPAQI[ledCount], r, g, b); - ledIdx = ledIdx + PAQI_SKIP_LED + 1; - } - - ledIdx = PAQI_START_LED; - ledCount = 0; - for (n = 0; n < rainpickerData.PAQI.len; n++) { - if (rainpickerData.time < rainpickerData.PAQI.time[n]) { - ledIdx = ledIdx + PAQI_SKIP_LED + 1; - ledCount = ledCount + 1; - } - if (ledCount >= PAQI_NUM_LEDS) { - break; - } - - color = colormap(rainpickerData.PAQI.value[n]); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - w = (color & 0xFF000000) >> 24; - - ledsSetPixelColor(ledMapPAQI[ledCount], r, g, b); - } - - #ifdef SHOW_AQI_LED - // calculate AQI max value for next 12 hours - uint32_t AQI_max_value = 0; - //Serial.println("AQI:"); - for (n = 0; n < rainpickerData.AQI.len; n++) { - /*Serial.print(" "); - Serial.print(rainpickerData.AQI.time[n]); - Serial.print(": "); - Serial.println(rainpickerData.AQI.value[n]);*/ - if (rainpickerData.time - rainpickerData.AQI.time[n] >= 60 * 60) { - continue; - } - if (rainpickerData.AQI.time[n] >= rainpickerData.time + PAQI_NUM_LEDS * 60 * 60) { - break; - } - if (rainpickerData.AQI.value[n] > AQI_max_value) { - AQI_max_value = rainpickerData.AQI.value[n]; - } - } - //Serial.print("AQI max value: "); - //Serial.println(AQI_max_value); - color = colormap(AQI_max_value); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(AQI_LED, r, g, b); - #endif - - #ifdef SHOW_POLLEN_LED - // calculate pollen max value for next 12 hours - uint32_t pollen_max_value = rainpickerData.pollen.value[0]; - for (n = 0; n < rainpickerData.pollen.len; n++) { - if (rainpickerData.time - rainpickerData.pollen.time[n] >= 60 * 60) { - continue; - } - if (rainpickerData.pollen.time[n] >= rainpickerData.time + PAQI_NUM_LEDS * 60 * 60) { - break; - } - if (rainpickerData.pollen.value[n] > pollen_max_value) { - pollen_max_value = rainpickerData.pollen.value[n]; - } - } - - color = colormap(pollen_max_value); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(POLLEN_LED, r, g, b); - #endif - - #ifdef SHOW_UVI_LED - // calculate UVI led value - // UVI data is daily based --> search for first timestamp in the future and select value of the timestamp just before that - uint32_t UVI_value = rainpickerData.UVI.value[0]; - - for (n = 0; n < rainpickerData.UVI.len; n++) { - if (rainpickerData.UVI.time[n] >= rainpickerData.time) { - break; - } - UVI_value = rainpickerData.UVI.value[n]; - } - color = colormap(UVI_value); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(UVI_LED, r, g, b); - #endif - - // Start by setting all leds to value of the first datapoint - ledIdx = BUIENRADAR_START_LED; - for (ledCount = 0; ledCount < BUIENRADAR_NUM_LEDS; ledCount++) { - color = colormap(rainpickerData.precipitation.value[0]); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(ledMapPrecipitation[ledCount], r, g, b); - ledIdx = ledIdx + BUIENRADAR_SKIP_LED + 1; - } - - ledIdx = BUIENRADAR_START_LED; - ledCount = 0; - for (n = 0; n < rainpickerData.precipitation.len; n++) { - if (rainpickerData.time < rainpickerData.precipitation.time[n]) { - ledIdx = ledIdx + BUIENRADAR_SKIP_LED + 1; - ledCount = ledCount + 1; - } - if (ledCount >= BUIENRADAR_NUM_LEDS) { - break; - } - - color = colormap(buienradarMap(rainpickerData.precipitation.value[n])); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - ledsSetPixelColor(ledMapPrecipitation[ledCount], r, g, b); - } - - ledsSetPixelColor(IAQI_LED, r, g, b); - - if (hw_variant == HW_PROTO_V1) { - color = colormapIaqi(); - - r = (color & 0xFF0000) >> 16; - g = (color & 0x00FF00) >> 8; - b = (color & 0x0000FF); - - ledsSetPixelColor(IAQI_LED, r, g, b); - } -} - -void readConnectButton(void) { - static int reset_wifi_timer = millis(); - static bool reset_blocked = true; - - if (digitalRead(CONNECT_SW_PIN) == HIGH) { - // button released - reset_wifi_timer = millis(); - reset_blocked = false; - digitalWrite(LED_BUILTIN, LOW); - } else { - // button pressed - if (reset_blocked == false) { - int delta = millis() - reset_wifi_timer; - if (delta < 10000) { - if (delta % 1000 < 500) { - digitalWrite(LED_BUILTIN, HIGH); - } else { - digitalWrite(LED_BUILTIN, LOW); - } - } else { - leds_clear(); - - wifiManager.resetSettings(); - - // block reset so user must release button before we can reset again - reset_blocked = true; - digitalWrite(LED_BUILTIN, LOW); - ESP.restart(); - } - } - } -} - -void loop(void) { - static unsigned long t_prev = 0; - static bool ledOn = false; - unsigned long t_now = millis(); - const int buttonPin = 0; - static int rainColor[12] = {0}; - static int AQIColor[12] = {0}; - static bool firstTime = true; - static int counter = 0; - - if (firstTime || (t_now - t_prev >= 60000)) { - t_prev = t_now; - firstTime = false; - -#ifndef DISABLE_WIFI - if (WiFi.status() != WL_CONNECTED) { - Serial.print(millis()); - Serial.print(" wifi status error: "); - Serial.print(WiFi.status()); - Serial.print("; expected: "); - Serial.println(WL_CONNECTED); - bool result = WiFi.reconnect(); - Serial.print(millis()); - Serial.print(" reconnecting result: "); - Serial.print(result); - Serial.print("; expected: "); - Serial.println(ESP_OK); - } - getPrecipitation(address); - - if (counter == 0) { - getAQI(address); - } - - counter = counter + 1; - - if (counter == 5) { - // AQI should only be retreived every 5 minutes - counter = 0; - } -#endif - } - -#ifdef DISABLE_WIFI - updateLedsNoWifi(); -#else - updateLeds(); -#endif - - if (hw_variant == HW_PROTO_PAPER) { - leds_rgb_proto_paper.show(); - } else if (hw_variant == HW_PROTO_V1) { - leds_rgbw_proto_v1.show(); - } - - readConnectButton(); - - delay(100); -#ifndef DISABLE_WIFI - dESPatch.checkForUpdate(true); -#endif -} diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/include/WifiHandler.hpp b/include/WifiHandler.hpp new file mode 100644 index 0000000..9f3fe93 --- /dev/null +++ b/include/WifiHandler.hpp @@ -0,0 +1,14 @@ +#ifndef __WIFIHANDLER_HPP__ +#define __WIFIHANDLER_HPP__ + +#include // https://github.com/admarschoonen/WiFiManager + +#define TICKER_RATE_CONNECTING 0.6 +#define TICKER_RATE_CONFIG 0.2 +#define TICKER_RATE_ERASE 0.05 + +void wifiManagerCb(WiFiManager::Status status); + +void connect(); + +#endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/include/root_ca.h b/include/root_ca.h new file mode 100644 index 0000000..96c6030 --- /dev/null +++ b/include/root_ca.h @@ -0,0 +1,37 @@ +#ifndef __ROOT_CA_H__ +#define __ROOT_CA_H__ + +const char* root_ca = + "-----BEGIN CERTIFICATE-----\n" + "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" + "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" + "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" + "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" + "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" + "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" + "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" + "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" + "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" + "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" + "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" + "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" + "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" + "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" + "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" + "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" + "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" + "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" + "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" + "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" + "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" + "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" + "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" + "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" + "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" + "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" + "-----END CERTIFICATE-----\n"; + +#endif // __ROOT_CA_H__ \ No newline at end of file diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4a9bc9f --- /dev/null +++ b/platformio.ini @@ -0,0 +1,18 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + https://github.com/admarschoonen/WIFIMANAGER-ESP32.git#v0.99.11 + https://github.com/admarschoonen/dESPatch.git#v0.9.4 + bblanchon/ArduinoJson @ ^7.1.0 diff --git a/src/WifiHandler.cpp b/src/WifiHandler.cpp new file mode 100644 index 0000000..71957a8 --- /dev/null +++ b/src/WifiHandler.cpp @@ -0,0 +1,68 @@ +#include +#include // https://github.com/admarschoonen/WiFiManager + +#include "../include/WifiHandler.hpp" + +WiFiManager wifiManager; +Ticker ticker; + +static void tick(void) { + int state = 0; + + if (LED_BUILTIN >= 0) { + state = digitalRead(LED_BUILTIN); + digitalWrite(LED_BUILTIN, !state); + } +} + +void wifiManagerCb(WiFiManager::Status status) { + ticker.detach(); + + if (status.mode == WiFiManager::Mode::CONNECTING) { + ticker.attach(TICKER_RATE_CONNECTING, tick); + Serial.println("CONNECTING"); + } else if (status.mode == WiFiManager::Mode::SCANNING) { + ticker.attach(TICKER_RATE_CONNECTING, tick); + Serial.println("SCANNING"); + } else if (status.mode == WiFiManager::Mode::PORTAL) { + ticker.attach(TICKER_RATE_CONFIG, tick); + Serial.println("PORTAL"); + } else if (status.mode == WiFiManager::Mode::ERASING) { + ticker.attach(TICKER_RATE_ERASE, tick); + Serial.println("ERASING"); + } else if (status.mode == WiFiManager::Mode::CONNECTED) { + ticker.detach(); + digitalWrite(LED_BUILTIN, HIGH); + Serial.println("CONNECTED"); + } else if (status.mode == WiFiManager::Mode::DISCONNECTED) { + ticker.detach(); + digitalWrite(LED_BUILTIN, LOW); + Serial.println("DISCONNECTED"); + } +} + +void connect() { + Serial.begin(115200); + + wifiManager.resetSettings(); + // wifiManager.configure("esp32-", true, LED_BUILTIN, true, BUTTON_BUILTIN, + // false); + wifiManager.configure("esp32-", true, wifiManagerCb, BUTTON_BUILTIN, false); + + // fetches ssid and pass and tries to connect + // if it does not connect it starts an access point + // and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect()) { + Serial.println("failed to connect and hit timeout"); + // reset and try again, or maybe put it to deep sleep + ESP.restart(); + delay(1000); + } + + // if you get here you have connected to the WiFi + Serial.print("connected with address: "); + Serial.println(WiFi.localIP()); + + // keep LED on + digitalWrite(LED_BUILTIN, LED_ON_VALUE_DEFAULT); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..01d66f0 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,28 @@ +#include + +#include "../include/WifiHandler.hpp" +#include "../include/root_ca.h" + +DESPatch dESPatch; + +void setup() { + Serial.begin(115200); + connect(); + + const char url[] = + "https://apikey:cqprlgiafadnidsgeqozcpldkaeqimqw@despatch.luon.net/" + "files/4/despatch.json"; + unsigned long interval = 60; // By default check for updates every 60 seconds + + int x = dESPatch.configure(url, true, false, interval, false, root_ca); + + Serial.print("dESPatch.configure() returned with code "); + Serial.println(x); + Serial.println(":-)"); +} + +void loop(void) { + Serial.println("hello :-)"); + dESPatch.checkForUpdate(true); + delay(500); +} diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html From c60c3ecb2d5532f10ed655bb075c0d7dba119a97 Mon Sep 17 00:00:00 2001 From: admar Date: Thu, 8 Aug 2024 22:54:33 +0200 Subject: [PATCH 02/13] Detach ticker in right places, do not call reset settings for WifiManager --- src/WifiHandler.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WifiHandler.cpp b/src/WifiHandler.cpp index 71957a8..7faaefb 100644 --- a/src/WifiHandler.cpp +++ b/src/WifiHandler.cpp @@ -16,7 +16,9 @@ static void tick(void) { } void wifiManagerCb(WiFiManager::Status status) { - ticker.detach(); + if (ticker.active()) { + ticker.detach(); + } if (status.mode == WiFiManager::Mode::CONNECTING) { ticker.attach(TICKER_RATE_CONNECTING, tick); @@ -31,11 +33,9 @@ void wifiManagerCb(WiFiManager::Status status) { ticker.attach(TICKER_RATE_ERASE, tick); Serial.println("ERASING"); } else if (status.mode == WiFiManager::Mode::CONNECTED) { - ticker.detach(); digitalWrite(LED_BUILTIN, HIGH); Serial.println("CONNECTED"); } else if (status.mode == WiFiManager::Mode::DISCONNECTED) { - ticker.detach(); digitalWrite(LED_BUILTIN, LOW); Serial.println("DISCONNECTED"); } @@ -44,10 +44,10 @@ void wifiManagerCb(WiFiManager::Status status) { void connect() { Serial.begin(115200); - wifiManager.resetSettings(); + // wifiManager.resetSettings(); // wifiManager.configure("esp32-", true, LED_BUILTIN, true, BUTTON_BUILTIN, // false); - wifiManager.configure("esp32-", true, wifiManagerCb, BUTTON_BUILTIN, false); + wifiManager.configure("Claire", true, wifiManagerCb, BUTTON_BUILTIN, false); // fetches ssid and pass and tries to connect // if it does not connect it starts an access point From 93d5240443d3a41f766d4293596cd96c028c6daf Mon Sep 17 00:00:00 2001 From: admar Date: Fri, 16 Aug 2024 21:29:50 +0200 Subject: [PATCH 03/13] Use LED_BUITIN and BUTTON_BUILTIN for user input/output + reset settings if user presses button within 3 seconds --- include/WifiHandler.hpp | 13 ++++++++++++ src/WifiHandler.cpp | 45 ++++++++++++++++++++++++++--------------- src/main.cpp | 18 +++++++++++++++-- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/include/WifiHandler.hpp b/include/WifiHandler.hpp index 9f3fe93..01f5edd 100644 --- a/include/WifiHandler.hpp +++ b/include/WifiHandler.hpp @@ -7,8 +7,21 @@ #define TICKER_RATE_CONFIG 0.2 #define TICKER_RATE_ERASE 0.05 +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + +#ifndef BUTTON_BUILTIN +#define BUTTON_BUILTIN 0 +#endif + +#define CONNECT_BUTTON BUTTON_BUILTIN + void wifiManagerCb(WiFiManager::Status status); +void set_blink_rate(float interval); + void connect(); +void resetSettings(); #endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/src/WifiHandler.cpp b/src/WifiHandler.cpp index 7faaefb..c0c1b5c 100644 --- a/src/WifiHandler.cpp +++ b/src/WifiHandler.cpp @@ -15,39 +15,38 @@ static void tick(void) { } } -void wifiManagerCb(WiFiManager::Status status) { +void set_blink_rate(float interval) { if (ticker.active()) { ticker.detach(); } + if (interval > 0.0f) { + ticker.attach(interval, tick); + } +} + +void wifiManagerCb(WiFiManager::Status status) { if (status.mode == WiFiManager::Mode::CONNECTING) { - ticker.attach(TICKER_RATE_CONNECTING, tick); - Serial.println("CONNECTING"); + set_blink_rate(TICKER_RATE_CONNECTING); } else if (status.mode == WiFiManager::Mode::SCANNING) { - ticker.attach(TICKER_RATE_CONNECTING, tick); - Serial.println("SCANNING"); + set_blink_rate(TICKER_RATE_CONNECTING); } else if (status.mode == WiFiManager::Mode::PORTAL) { - ticker.attach(TICKER_RATE_CONFIG, tick); - Serial.println("PORTAL"); + set_blink_rate(TICKER_RATE_CONFIG); } else if (status.mode == WiFiManager::Mode::ERASING) { - ticker.attach(TICKER_RATE_ERASE, tick); - Serial.println("ERASING"); + set_blink_rate(TICKER_RATE_ERASE); } else if (status.mode == WiFiManager::Mode::CONNECTED) { + set_blink_rate(0.0f); digitalWrite(LED_BUILTIN, HIGH); - Serial.println("CONNECTED"); } else if (status.mode == WiFiManager::Mode::DISCONNECTED) { + set_blink_rate(0.0f); digitalWrite(LED_BUILTIN, LOW); - Serial.println("DISCONNECTED"); } } void connect() { Serial.begin(115200); - // wifiManager.resetSettings(); - // wifiManager.configure("esp32-", true, LED_BUILTIN, true, BUTTON_BUILTIN, - // false); - wifiManager.configure("Claire", true, wifiManagerCb, BUTTON_BUILTIN, false); + wifiManager.configure("Claire", wifiManagerCb); // fetches ssid and pass and tries to connect // if it does not connect it starts an access point @@ -64,5 +63,19 @@ void connect() { Serial.println(WiFi.localIP()); // keep LED on - digitalWrite(LED_BUILTIN, LED_ON_VALUE_DEFAULT); + digitalWrite(LED_BUILTIN, HIGH); +} + +void resetSettings() { + WiFiManager::Status status = {.wifi_status = 0U, .mode = WiFiManager::Mode::ERASING}; + + Serial.println("resetting wifi settings..."); + + wifiManagerCb(status); + wifiManager.resetSettings(); + delay(1000); + status.mode = WiFiManager::Mode::DISCONNECTED; + wifiManagerCb(status); + + return; } diff --git a/src/main.cpp b/src/main.cpp index 01d66f0..c29cd5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,21 @@ DESPatch dESPatch; void setup() { Serial.begin(115200); + + pinMode(LED_BUILTIN, OUTPUT); + set_blink_rate(TICKER_RATE_CONNECTING); + + pinMode(CONNECT_BUTTON, INPUT); + + if (CONNECT_BUTTON >= 0) { + // Give user 3 second chance to press button and reset settings + Serial.println("Waiting 3 seconds to check if user presses the button"); + delay(3000); + if (not digitalRead(CONNECT_BUTTON)) { + resetSettings(); + } + } + connect(); const char url[] = @@ -18,11 +33,10 @@ void setup() { Serial.print("dESPatch.configure() returned with code "); Serial.println(x); - Serial.println(":-)"); } void loop(void) { Serial.println("hello :-)"); dESPatch.checkForUpdate(true); - delay(500); + delay(5000); } From 48e071ef90f3d5eb04e4950635b4c991abf8513f Mon Sep 17 00:00:00 2001 From: admar Date: Sat, 17 Aug 2024 17:27:35 +0200 Subject: [PATCH 04/13] Update WIFIMANAGER-ESP32 and dESPatch libraries --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4a9bc9f..621d03b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,6 @@ platform = espressif32 board = esp32dev framework = arduino lib_deps = - https://github.com/admarschoonen/WIFIMANAGER-ESP32.git#v0.99.11 - https://github.com/admarschoonen/dESPatch.git#v0.9.4 + https://github.com/admarschoonen/WIFIMANAGER-ESP32.git#v0.100.0 + https://github.com/admarschoonen/dESPatch.git#v0.9.5 bblanchon/ArduinoJson @ ^7.1.0 From a0dc2e257ad12cc096668e5083aa73cdfadfb95a Mon Sep 17 00:00:00 2001 From: admar Date: Sat, 17 Aug 2024 22:46:10 +0200 Subject: [PATCH 05/13] Get config from server --- include/WifiHandler.hpp | 2 + include/backend.hpp | 21 +++++ include/root_ca.hpp | 6 ++ src/WifiHandler.cpp | 11 ++- src/backend.cpp | 131 +++++++++++++++++++++++++++ src/main.cpp | 18 ++-- include/root_ca.h => src/root_ca.cpp | 5 - 7 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 include/backend.hpp create mode 100644 include/root_ca.hpp create mode 100644 src/backend.cpp rename include/root_ca.h => src/root_ca.cpp (96%) diff --git a/include/WifiHandler.hpp b/include/WifiHandler.hpp index 01f5edd..506bf17 100644 --- a/include/WifiHandler.hpp +++ b/include/WifiHandler.hpp @@ -24,4 +24,6 @@ void set_blink_rate(float interval); void connect(); void resetSettings(); +String getMac(bool insertColons); + #endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/include/backend.hpp b/include/backend.hpp new file mode 100644 index 0000000..e31ee4d --- /dev/null +++ b/include/backend.hpp @@ -0,0 +1,21 @@ +#ifndef __BACKEND_HPP__ +#define __BACKEND_HPP__ + +#include +#include + +#include "../include/root_ca.hpp" + +extern JsonDocument config; +extern bool isConfigValid; + +extern JsonDocument weatherData; + +bool parseJsonConfig(String& payload); +bool getConfig(); + +String urlencode(String str); + +bool getWeatherData(); + +#endif // __BACKEND_HPP__ \ No newline at end of file diff --git a/include/root_ca.hpp b/include/root_ca.hpp new file mode 100644 index 0000000..a24e7e5 --- /dev/null +++ b/include/root_ca.hpp @@ -0,0 +1,6 @@ +#ifndef __ROOT_CA_H__ +#define __ROOT_CA_H__ + +extern const char* root_ca; + +#endif // __ROOT_CA_H__ \ No newline at end of file diff --git a/src/WifiHandler.cpp b/src/WifiHandler.cpp index c0c1b5c..03abd61 100644 --- a/src/WifiHandler.cpp +++ b/src/WifiHandler.cpp @@ -52,14 +52,14 @@ void connect() { // if it does not connect it starts an access point // and goes into a blocking loop awaiting configuration if (!wifiManager.autoConnect()) { - Serial.println("failed to connect and hit timeout"); + Serial.println(String(millis()) + " Failed to connect and hit timeout"); // reset and try again, or maybe put it to deep sleep ESP.restart(); delay(1000); } // if you get here you have connected to the WiFi - Serial.print("connected with address: "); + Serial.print(String(millis()) + " Connected with address: "); Serial.println(WiFi.localIP()); // keep LED on @@ -69,7 +69,7 @@ void connect() { void resetSettings() { WiFiManager::Status status = {.wifi_status = 0U, .mode = WiFiManager::Mode::ERASING}; - Serial.println("resetting wifi settings..."); + Serial.println(String(millis()) + " Resetting wifi settings..."); wifiManagerCb(status); wifiManager.resetSettings(); @@ -79,3 +79,8 @@ void resetSettings() { return; } + +String getMac(bool insertColons) { + String mac = wifiManager.getMacAsString(insertColons); + return mac; +} \ No newline at end of file diff --git a/src/backend.cpp b/src/backend.cpp new file mode 100644 index 0000000..d969cc4 --- /dev/null +++ b/src/backend.cpp @@ -0,0 +1,131 @@ +#include + +#include "../include/WifiHandler.hpp" +#include "../include/backend.hpp" + +JsonDocument config; +bool isConfigValid = false; +String configTimestamp = ""; + +static HTTPClient http; + +JsonDocument weatherData; + +bool parseJsonConfig(String& payload) { + bool isSuccess = false; + + config.clear(); + + DeserializationError error = deserializeJson(config, payload); + + if (error) { + Serial.print(String(millis()) + " DeserializeJson() failed: " + String(error.f_str())); + } else { + if (config["config version"] != "0.0.1") { + Serial.println(String(millis()) + " Unsupported config version"); + } else { + isSuccess = true; + } + } + + return isSuccess; +} + +bool getConfig() { + String mac = getMac(false); + String url = "https://target.luon.net/~admar/Claire/" + mac + "/config.json"; + String payload = ""; + bool isSuccess = false; + + http.begin(url, root_ca); + if (isConfigValid) { + http.addHeader("If-Modified-Since", configTimestamp); + } + + const char* headerKeys[] = {"Last-Modified"}; + const size_t headerKeysCount = sizeof(headerKeys) / sizeof(headerKeys[0]); + http.collectHeaders(headerKeys, headerKeysCount); + + int httpCode = http.GET(); + + if (httpCode == 200) { + // Content is in payload + payload = http.getString(); + isSuccess = parseJsonConfig(payload); + + if (isSuccess) { + configTimestamp = http.header("Last-Modified"); + Serial.println(String(millis()) + " ConfigTimestamp: " + configTimestamp); + } + } else if (httpCode == 304) { + // Content did not change + isSuccess = true; + } else { + Serial.println(String(millis()) + " Got http code " + httpCode); + } + http.end(); + + return isSuccess; +} + +// From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ +String urlencode(String str) { + String encodedString = ""; + char c; + char code0; + char code1; + char code2; + for (int i = 0; i < str.length(); i++) { + c = str.charAt(i); + if (c == ' ') { + encodedString += '+'; + } else if (isalnum(c)) { + encodedString += c; + } else { + code1 = (c & 0xf) + '0'; + if ((c & 0xf) > 9) { + code1 = (c & 0xf) - 10 + 'A'; + } + c = (c >> 4) & 0xf; + code0 = c + '0'; + if (c > 9) { + code0 = c - 10 + 'A'; + } + code2 = '\0'; + encodedString += '%'; + encodedString += code0; + encodedString += code1; + // encodedString+=code2; + } + yield(); + } + + return encodedString; +} + +bool getWeatherData() { + String encodedAddress = urlencode(config["address"]); + String url = + "https://sinoptik.luon.net/" + "forecast?address=" + + encodedAddress + + "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=" + "pollen&metrics=PAQI"; + + String payload = ""; + bool isSuccess = false; + + http.begin(url, root_ca); + + int httpCode = http.GET(); + + if (httpCode > 0) { + payload = http.getString(); + // isSuccess = parseJsonConfig(payload); + } else { + Serial.println(String(millis()) + " Got http code " + httpCode); + } + http.end(); + + return isSuccess; +} diff --git a/src/main.cpp b/src/main.cpp index c29cd5c..605071f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include "../include/WifiHandler.hpp" -#include "../include/root_ca.h" +#include "../include/backend.hpp" DESPatch dESPatch; @@ -15,7 +15,7 @@ void setup() { if (CONNECT_BUTTON >= 0) { // Give user 3 second chance to press button and reset settings - Serial.println("Waiting 3 seconds to check if user presses the button"); + Serial.println(String(millis()) + " Waiting 3 seconds to check if user presses the button"); delay(3000); if (not digitalRead(CONNECT_BUTTON)) { resetSettings(); @@ -31,12 +31,18 @@ void setup() { int x = dESPatch.configure(url, true, false, interval, false, root_ca); - Serial.print("dESPatch.configure() returned with code "); - Serial.println(x); + Serial.println(String(millis()) + " dESPatch.configure() returned with code " + String(x)); } void loop(void) { - Serial.println("hello :-)"); + isConfigValid = getConfig(); + + if (not isConfigValid) { + Serial.println(String(millis()) + " Invalid config"); + } else { + Serial.println(String(millis()) + " Address: " + String(static_cast(config["address"]))); + // getWeatherData(); + } dESPatch.checkForUpdate(true); - delay(5000); + delay(2000); } diff --git a/include/root_ca.h b/src/root_ca.cpp similarity index 96% rename from include/root_ca.h rename to src/root_ca.cpp index 96c6030..c981279 100644 --- a/include/root_ca.h +++ b/src/root_ca.cpp @@ -1,6 +1,3 @@ -#ifndef __ROOT_CA_H__ -#define __ROOT_CA_H__ - const char* root_ca = "-----BEGIN CERTIFICATE-----\n" "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" @@ -33,5 +30,3 @@ const char* root_ca = "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" "-----END CERTIFICATE-----\n"; - -#endif // __ROOT_CA_H__ \ No newline at end of file From 388bd0a84c8ab6a5daa9bd344b50f00c8ac13b84 Mon Sep 17 00:00:00 2001 From: admar Date: Sun, 18 Aug 2024 11:19:41 +0200 Subject: [PATCH 06/13] Added classes for WifiHandler and BackendCommunication --- include/WifiHandler.hpp | 29 ------ include/backend.hpp | 21 ----- include/backend_communication.hpp | 38 ++++++++ include/wifi_handler.hpp | 33 +++++++ src/WifiHandler.cpp | 86 ------------------ ...{backend.cpp => backend_communication.cpp} | 76 ++++++++-------- src/main.cpp | 31 +++++-- src/wifi_handler.cpp | 90 +++++++++++++++++++ 8 files changed, 221 insertions(+), 183 deletions(-) delete mode 100644 include/WifiHandler.hpp delete mode 100644 include/backend.hpp create mode 100644 include/backend_communication.hpp create mode 100644 include/wifi_handler.hpp delete mode 100644 src/WifiHandler.cpp rename src/{backend.cpp => backend_communication.cpp} (71%) create mode 100644 src/wifi_handler.cpp diff --git a/include/WifiHandler.hpp b/include/WifiHandler.hpp deleted file mode 100644 index 506bf17..0000000 --- a/include/WifiHandler.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __WIFIHANDLER_HPP__ -#define __WIFIHANDLER_HPP__ - -#include // https://github.com/admarschoonen/WiFiManager - -#define TICKER_RATE_CONNECTING 0.6 -#define TICKER_RATE_CONFIG 0.2 -#define TICKER_RATE_ERASE 0.05 - -#ifndef LED_BUILTIN -#define LED_BUILTIN 2 -#endif - -#ifndef BUTTON_BUILTIN -#define BUTTON_BUILTIN 0 -#endif - -#define CONNECT_BUTTON BUTTON_BUILTIN - -void wifiManagerCb(WiFiManager::Status status); - -void set_blink_rate(float interval); - -void connect(); -void resetSettings(); - -String getMac(bool insertColons); - -#endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/include/backend.hpp b/include/backend.hpp deleted file mode 100644 index e31ee4d..0000000 --- a/include/backend.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __BACKEND_HPP__ -#define __BACKEND_HPP__ - -#include -#include - -#include "../include/root_ca.hpp" - -extern JsonDocument config; -extern bool isConfigValid; - -extern JsonDocument weatherData; - -bool parseJsonConfig(String& payload); -bool getConfig(); - -String urlencode(String str); - -bool getWeatherData(); - -#endif // __BACKEND_HPP__ \ No newline at end of file diff --git a/include/backend_communication.hpp b/include/backend_communication.hpp new file mode 100644 index 0000000..45c14bc --- /dev/null +++ b/include/backend_communication.hpp @@ -0,0 +1,38 @@ +#ifndef __BACKEND_HPP__ +#define __BACKEND_HPP__ + +#include +#include + +#include "../include/root_ca.hpp" +#include "../include/wifi_handler.hpp" + +class BackendCommunication { + public: + // Constructor + BackendCommunication(WifiHandler& p_wifi_handler); + + public: + // Public methods + bool parseJsonConfig(String& payload); + bool getConfig(); + + bool getWeatherData(); + + private: + // Private methods + String urlencode(String str); + + public: + // Public member objects + JsonDocument m_config; + bool m_is_config_valid; + + JsonDocument m_weather_data; + + private: + // Private member objects + WifiHandler& m_wifi_handler; +}; + +#endif // __BACKEND_HPP__ \ No newline at end of file diff --git a/include/wifi_handler.hpp b/include/wifi_handler.hpp new file mode 100644 index 0000000..bac7c12 --- /dev/null +++ b/include/wifi_handler.hpp @@ -0,0 +1,33 @@ +#ifndef __WIFIHANDLER_HPP__ +#define __WIFIHANDLER_HPP__ + +#include // https://github.com/admarschoonen/WiFiManager + +class WifiHandler { + public: + // Constructor + WifiHandler(uint8_t p_led_pin); + + public: + // Public static methods + static void setBlinkRate(float p_interval); + static void wifiManagerCb(WiFiManager::Status p_status); + + public: + // Public methods + void connect(); + void resetSettings(); + String getMac(bool insertColons); + + public: + // Public static objects + constexpr static float BLINK_RATE_CONNECTING = 0.6f; + constexpr static float BLINK_RATE_CONFIG = 0.2f; + constexpr static float BLINK_RATE_ERASE = 0.05f; + + private: + // Private objects + WiFiManager m_wifiManager; +}; + +#endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/src/WifiHandler.cpp b/src/WifiHandler.cpp deleted file mode 100644 index 03abd61..0000000 --- a/src/WifiHandler.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include // https://github.com/admarschoonen/WiFiManager - -#include "../include/WifiHandler.hpp" - -WiFiManager wifiManager; -Ticker ticker; - -static void tick(void) { - int state = 0; - - if (LED_BUILTIN >= 0) { - state = digitalRead(LED_BUILTIN); - digitalWrite(LED_BUILTIN, !state); - } -} - -void set_blink_rate(float interval) { - if (ticker.active()) { - ticker.detach(); - } - - if (interval > 0.0f) { - ticker.attach(interval, tick); - } -} - -void wifiManagerCb(WiFiManager::Status status) { - if (status.mode == WiFiManager::Mode::CONNECTING) { - set_blink_rate(TICKER_RATE_CONNECTING); - } else if (status.mode == WiFiManager::Mode::SCANNING) { - set_blink_rate(TICKER_RATE_CONNECTING); - } else if (status.mode == WiFiManager::Mode::PORTAL) { - set_blink_rate(TICKER_RATE_CONFIG); - } else if (status.mode == WiFiManager::Mode::ERASING) { - set_blink_rate(TICKER_RATE_ERASE); - } else if (status.mode == WiFiManager::Mode::CONNECTED) { - set_blink_rate(0.0f); - digitalWrite(LED_BUILTIN, HIGH); - } else if (status.mode == WiFiManager::Mode::DISCONNECTED) { - set_blink_rate(0.0f); - digitalWrite(LED_BUILTIN, LOW); - } -} - -void connect() { - Serial.begin(115200); - - wifiManager.configure("Claire", wifiManagerCb); - - // fetches ssid and pass and tries to connect - // if it does not connect it starts an access point - // and goes into a blocking loop awaiting configuration - if (!wifiManager.autoConnect()) { - Serial.println(String(millis()) + " Failed to connect and hit timeout"); - // reset and try again, or maybe put it to deep sleep - ESP.restart(); - delay(1000); - } - - // if you get here you have connected to the WiFi - Serial.print(String(millis()) + " Connected with address: "); - Serial.println(WiFi.localIP()); - - // keep LED on - digitalWrite(LED_BUILTIN, HIGH); -} - -void resetSettings() { - WiFiManager::Status status = {.wifi_status = 0U, .mode = WiFiManager::Mode::ERASING}; - - Serial.println(String(millis()) + " Resetting wifi settings..."); - - wifiManagerCb(status); - wifiManager.resetSettings(); - delay(1000); - status.mode = WiFiManager::Mode::DISCONNECTED; - wifiManagerCb(status); - - return; -} - -String getMac(bool insertColons) { - String mac = wifiManager.getMacAsString(insertColons); - return mac; -} \ No newline at end of file diff --git a/src/backend.cpp b/src/backend_communication.cpp similarity index 71% rename from src/backend.cpp rename to src/backend_communication.cpp index d969cc4..d15084d 100644 --- a/src/backend.cpp +++ b/src/backend_communication.cpp @@ -1,9 +1,8 @@ #include -#include "../include/WifiHandler.hpp" -#include "../include/backend.hpp" +#include "../include/backend_communication.hpp" +#include "../include/wifi_handler.hpp" -JsonDocument config; bool isConfigValid = false; String configTimestamp = ""; @@ -11,19 +10,22 @@ static HTTPClient http; JsonDocument weatherData; -bool parseJsonConfig(String& payload) { +BackendCommunication::BackendCommunication(WifiHandler& p_wifi_handler) : m_wifi_handler(p_wifi_handler) { return; } + +bool BackendCommunication::parseJsonConfig(String& payload) { bool isSuccess = false; - config.clear(); + m_config.clear(); - DeserializationError error = deserializeJson(config, payload); + DeserializationError error = deserializeJson(m_config, payload); if (error) { Serial.print(String(millis()) + " DeserializeJson() failed: " + String(error.f_str())); } else { - if (config["config version"] != "0.0.1") { + if (m_config["config version"] != "0.0.1") { Serial.println(String(millis()) + " Unsupported config version"); } else { + m_is_config_valid = true; isSuccess = true; } } @@ -31,9 +33,10 @@ bool parseJsonConfig(String& payload) { return isSuccess; } -bool getConfig() { - String mac = getMac(false); - String url = "https://target.luon.net/~admar/Claire/" + mac + "/config.json"; +bool BackendCommunication::getConfig() { + String mac = m_wifi_handler.getMac(false); + String url = "https://target.luon.net/~admar/Claire/" + urlencode(mac) + "/config.json"; + String payload = ""; bool isSuccess = false; @@ -68,8 +71,30 @@ bool getConfig() { return isSuccess; } -// From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ -String urlencode(String str) { +bool BackendCommunication::getWeatherData() { + String url = "https://sinoptik.luon.net/forecast?address=" + urlencode(m_config["address"]) + + "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=pollen&metrics=PAQI"; + + String payload = ""; + bool isSuccess = false; + + http.begin(url, root_ca); + + int httpCode = http.GET(); + + if (httpCode > 0) { + payload = http.getString(); + // isSuccess = parseJsonConfig(payload); + } else { + Serial.println(String(millis()) + " Got http code " + httpCode); + } + http.end(); + + return isSuccess; +} + +String BackendCommunication::urlencode(String str) { + // From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ String encodedString = ""; char c; char code0; @@ -102,30 +127,3 @@ String urlencode(String str) { return encodedString; } - -bool getWeatherData() { - String encodedAddress = urlencode(config["address"]); - String url = - "https://sinoptik.luon.net/" - "forecast?address=" + - encodedAddress + - "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=" - "pollen&metrics=PAQI"; - - String payload = ""; - bool isSuccess = false; - - http.begin(url, root_ca); - - int httpCode = http.GET(); - - if (httpCode > 0) { - payload = http.getString(); - // isSuccess = parseJsonConfig(payload); - } else { - Serial.println(String(millis()) + " Got http code " + httpCode); - } - http.end(); - - return isSuccess; -} diff --git a/src/main.cpp b/src/main.cpp index 605071f..c95c894 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,29 @@ #include -#include "../include/WifiHandler.hpp" -#include "../include/backend.hpp" +#include "../include/backend_communication.hpp" +#include "../include/wifi_handler.hpp" + +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + +#ifndef BUTTON_BUILTIN +#define BUTTON_BUILTIN 0 +#endif + +#define CONNECT_BUTTON BUTTON_BUILTIN DESPatch dESPatch; +WifiHandler wifi_handler(LED_BUILTIN); + +BackendCommunication backend_communication(wifi_handler); void setup() { Serial.begin(115200); pinMode(LED_BUILTIN, OUTPUT); - set_blink_rate(TICKER_RATE_CONNECTING); + + wifi_handler.setBlinkRate(WifiHandler::BLINK_RATE_CONNECTING); pinMode(CONNECT_BUTTON, INPUT); @@ -18,11 +32,11 @@ void setup() { Serial.println(String(millis()) + " Waiting 3 seconds to check if user presses the button"); delay(3000); if (not digitalRead(CONNECT_BUTTON)) { - resetSettings(); + wifi_handler.resetSettings(); } } - connect(); + wifi_handler.connect(); const char url[] = "https://apikey:cqprlgiafadnidsgeqozcpldkaeqimqw@despatch.luon.net/" @@ -35,12 +49,13 @@ void setup() { } void loop(void) { - isConfigValid = getConfig(); + backend_communication.getConfig(); - if (not isConfigValid) { + if (not backend_communication.m_is_config_valid) { Serial.println(String(millis()) + " Invalid config"); } else { - Serial.println(String(millis()) + " Address: " + String(static_cast(config["address"]))); + Serial.println(String(millis()) + + " Address: " + String(static_cast(backend_communication.m_config["address"]))); // getWeatherData(); } dESPatch.checkForUpdate(true); diff --git a/src/wifi_handler.cpp b/src/wifi_handler.cpp new file mode 100644 index 0000000..cf2bbf1 --- /dev/null +++ b/src/wifi_handler.cpp @@ -0,0 +1,90 @@ +#include + +#include "../include/wifi_handler.hpp" + +static uint8_t g_led_pin; +static Ticker g_ticker; + +WifiHandler::WifiHandler(uint8_t p_led_pin) { + g_led_pin = p_led_pin; + return; +} + +static void tick() { + int state = 0; + + if (g_led_pin >= 0) { + state = digitalRead(g_led_pin); + digitalWrite(g_led_pin, !state); + } +} + +void WifiHandler::setBlinkRate(float p_interval) { + if (g_ticker.active()) { + g_ticker.detach(); + } + + if (p_interval > 0.0f) { + g_ticker.attach(p_interval, tick); + } +} + +void WifiHandler::wifiManagerCb(WiFiManager::Status p_status) { + if (p_status.mode == WiFiManager::Mode::CONNECTING) { + WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_CONNECTING); + } else if (p_status.mode == WiFiManager::Mode::SCANNING) { + WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_CONNECTING); + } else if (p_status.mode == WiFiManager::Mode::PORTAL) { + WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_CONFIG); + } else if (p_status.mode == WiFiManager::Mode::ERASING) { + WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_ERASE); + } else if (p_status.mode == WiFiManager::Mode::CONNECTED) { + WifiHandler::setBlinkRate(0.0f); + digitalWrite(g_led_pin, HIGH); + } else if (p_status.mode == WiFiManager::Mode::DISCONNECTED) { + WifiHandler::setBlinkRate(0.0f); + digitalWrite(g_led_pin, LOW); + } +} + +void WifiHandler::connect() { + Serial.begin(115200); + + m_wifiManager.configure("Claire", WifiHandler::wifiManagerCb); + + // fetches ssid and pass and tries to connect + // if it does not connect it starts an access point + // and goes into a blocking loop awaiting configuration + if (!m_wifiManager.autoConnect()) { + Serial.println(String(millis()) + " Failed to connect and hit timeout"); + // reset and try again, or maybe put it to deep sleep + ESP.restart(); + delay(1000); + } + + // if you get here you have connected to the WiFi + Serial.print(String(millis()) + " Connected with address: "); + Serial.println(WiFi.localIP()); + + // keep LED on + digitalWrite(g_led_pin, HIGH); +} + +void WifiHandler::resetSettings() { + WiFiManager::Status status = {.wifi_status = 0U, .mode = WiFiManager::Mode::ERASING}; + + Serial.println(String(millis()) + " Resetting wifi settings..."); + + wifiManagerCb(status); + m_wifiManager.resetSettings(); + delay(1000); + status.mode = WiFiManager::Mode::DISCONNECTED; + wifiManagerCb(status); + + return; +} + +String WifiHandler::getMac(bool p_insertColons) { + String mac = m_wifiManager.getMacAsString(p_insertColons); + return mac; +} \ No newline at end of file From 3aecd54880c0cc6550e61db94f858a4b00dd7805 Mon Sep 17 00:00:00 2001 From: admar Date: Fri, 30 Aug 2024 21:08:34 +0200 Subject: [PATCH 07/13] Added Display class; Neopixel is disabled for now --- .vscode/c_cpp_properties.json | 4 +- include/backend_communication.hpp | 11 +- include/display.hpp | 48 ++++++++ include/wifi_handler.hpp | 5 +- platformio.ini | 5 +- src/backend_communication.cpp | 64 +++++++---- src/display.cpp | 185 ++++++++++++++++++++++++++++++ src/main.cpp | 36 +++++- src/wifi_handler.cpp | 2 +- 9 files changed, 326 insertions(+), 34 deletions(-) create mode 100644 include/display.hpp create mode 100644 src/display.cpp diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 4aa958b..ec0e346 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -11,6 +11,7 @@ "/home/admar/Documents/Arduino/buienradarklok/Claire/include", "/home/admar/Documents/Arduino/buienradarklok/Claire/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/Adafruit NeoPixel", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", @@ -250,6 +251,7 @@ "/home/admar/Documents/Arduino/buienradarklok/Claire/include", "/home/admar/Documents/Arduino/buienradarklok/Claire/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/Adafruit NeoPixel", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", @@ -505,7 +507,7 @@ "" ], "cStandard": "gnu99", - "cppStandard": "gnu++11", + "cppStandard": "gnu++20", "compilerPath": "/home/admar/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc", "compilerArgs": [ "-mlongcalls", diff --git a/include/backend_communication.hpp b/include/backend_communication.hpp index 45c14bc..322b73c 100644 --- a/include/backend_communication.hpp +++ b/include/backend_communication.hpp @@ -1,5 +1,5 @@ -#ifndef __BACKEND_HPP__ -#define __BACKEND_HPP__ +#ifndef __BACKEND_COMMUNICATION_HPP__ +#define __BACKEND_COMMUNICATION_HPP__ #include #include @@ -14,13 +14,16 @@ class BackendCommunication { public: // Public methods - bool parseJsonConfig(String& payload); bool getConfig(); bool getWeatherData(); private: // Private methods + bool parseConfig(String& p_payload); + + bool parseWeatherData(String& p_payload); + String urlencode(String str); public: @@ -35,4 +38,4 @@ class BackendCommunication { WifiHandler& m_wifi_handler; }; -#endif // __BACKEND_HPP__ \ No newline at end of file +#endif // __BACKEND_COMMUNICATION_HPP__ \ No newline at end of file diff --git a/include/display.hpp b/include/display.hpp new file mode 100644 index 0000000..6e844e6 --- /dev/null +++ b/include/display.hpp @@ -0,0 +1,48 @@ +#ifndef __DISPLAY_HPP__ +#define __DISPLAY_HPP__ + +#include +#include +#include + +class Display { + public: + // Constructor + Display(); + + public: + // Public methods + void setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_aqi, + ArduinoJson::V710PB22::JsonArray p_pollen); + void setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation); + void setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_uvi); + void setLocalAirQualityLed(); + void setWifiStatusLed(bool p_enable); + + private: + // Private methods + float findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array); + std::vector mergeJsonArraysFromTimestamp(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array_a, + ArduinoJson::V710PB22::JsonArray p_array_b); + std::tuple mapValueToColorRGB(float p_value); + std::tuple mapValueToColorRGBW(float p_value); + void setPixelToColorMappedValue(uint8_t index, float p_value); + + private: + // Private static members + constexpr static uint16_t m_NUM_LEDS_PROTO_V1 = 29U; + constexpr static int16_t m_DATA_PIN_PROTO_V1 = 14; + + private: + // Private members + Adafruit_NeoPixel m_leds; + std::array m_precipitation_forecast_map; + std::array m_air_quality_forecast_map; + uint8_t m_max_pollen_map; + uint8_t m_max_air_qualilty_map; + uint8_t m_uvi_map; + uint8_t m_local_air_quality_map; + uint8_t m_wifi_status_map; +}; + +#endif // __DISPLAY_HPP__ \ No newline at end of file diff --git a/include/wifi_handler.hpp b/include/wifi_handler.hpp index bac7c12..b12f8a7 100644 --- a/include/wifi_handler.hpp +++ b/include/wifi_handler.hpp @@ -3,10 +3,12 @@ #include // https://github.com/admarschoonen/WiFiManager +#include "display.hpp" + class WifiHandler { public: // Constructor - WifiHandler(uint8_t p_led_pin); + WifiHandler(uint8_t p_led_pin, Display p_display); public: // Public static methods @@ -28,6 +30,7 @@ class WifiHandler { private: // Private objects WiFiManager m_wifiManager; + Display& m_display; }; #endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 621d03b..cea889a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,10 @@ platform = espressif32 board = esp32dev framework = arduino +build_unflags = -std=gnu++11 +build_flags = -std=gnu++2a lib_deps = https://github.com/admarschoonen/WIFIMANAGER-ESP32.git#v0.100.0 https://github.com/admarschoonen/dESPatch.git#v0.9.5 - bblanchon/ArduinoJson @ ^7.1.0 + bblanchon/ArduinoJson@^7.1.0 + adafruit/Adafruit NeoPixel@^1.12.3 diff --git a/src/backend_communication.cpp b/src/backend_communication.cpp index d15084d..9c4ba1c 100644 --- a/src/backend_communication.cpp +++ b/src/backend_communication.cpp @@ -12,27 +12,6 @@ JsonDocument weatherData; BackendCommunication::BackendCommunication(WifiHandler& p_wifi_handler) : m_wifi_handler(p_wifi_handler) { return; } -bool BackendCommunication::parseJsonConfig(String& payload) { - bool isSuccess = false; - - m_config.clear(); - - DeserializationError error = deserializeJson(m_config, payload); - - if (error) { - Serial.print(String(millis()) + " DeserializeJson() failed: " + String(error.f_str())); - } else { - if (m_config["config version"] != "0.0.1") { - Serial.println(String(millis()) + " Unsupported config version"); - } else { - m_is_config_valid = true; - isSuccess = true; - } - } - - return isSuccess; -} - bool BackendCommunication::getConfig() { String mac = m_wifi_handler.getMac(false); String url = "https://target.luon.net/~admar/Claire/" + urlencode(mac) + "/config.json"; @@ -54,11 +33,10 @@ bool BackendCommunication::getConfig() { if (httpCode == 200) { // Content is in payload payload = http.getString(); - isSuccess = parseJsonConfig(payload); + isSuccess = parseConfig(payload); if (isSuccess) { configTimestamp = http.header("Last-Modified"); - Serial.println(String(millis()) + " ConfigTimestamp: " + configTimestamp); } } else if (httpCode == 304) { // Content did not change @@ -84,7 +62,7 @@ bool BackendCommunication::getWeatherData() { if (httpCode > 0) { payload = http.getString(); - // isSuccess = parseJsonConfig(payload); + isSuccess = parseWeatherData(payload); } else { Serial.println(String(millis()) + " Got http code " + httpCode); } @@ -93,6 +71,44 @@ bool BackendCommunication::getWeatherData() { return isSuccess; } +bool BackendCommunication::parseConfig(String& payload) { + bool isSuccess = false; + + m_config.clear(); + + DeserializationError error = deserializeJson(m_config, payload); + + if (error) { + Serial.print(String(millis()) + " DeserializeJson() failed: " + String(error.f_str())); + } else { + if (m_config["config version"] != "0.0.1") { + Serial.println(String(millis()) + " Unsupported config version"); + } else { + m_is_config_valid = true; + isSuccess = true; + } + } + + return isSuccess; +} + +bool BackendCommunication::parseWeatherData(String& payload) { + bool isSuccess = false; + + m_weather_data.clear(); + + DeserializationError error = deserializeJson(m_weather_data, payload); + + if (error) { + Serial.print(String(millis()) + " DeserializeJson() failed: " + String(error.f_str())); + } else { + m_is_config_valid = true; + isSuccess = true; + } + + return isSuccess; +} + String BackendCommunication::urlencode(String str) { // From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ String encodedString = ""; diff --git a/src/display.cpp b/src/display.cpp new file mode 100644 index 0000000..4c4511d --- /dev/null +++ b/src/display.cpp @@ -0,0 +1,185 @@ +#include "../include/display.hpp" + +Display::Display() { + // m_leds = Adafruit_NeoPixel(m_NUM_LEDS_PROTO_V1, m_DATA_PIN_PROTO_V1, NEO_RGBW + NEO_KHZ800); + + m_precipitation_forecast_map = {11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + m_air_quality_forecast_map = {23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; + m_max_pollen_map = 26; + m_max_air_qualilty_map = 24; + m_uvi_map = 27; + m_local_air_quality_map = 25; + m_wifi_status_map = 28; + return; +} + +void Display::setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_aqi, + ArduinoJson::V710PB22::JsonArray p_pollen) { + uint32_t first_aqi_time = p_pollen[0]["aqi"].as(); + uint32_t first_pollen_time = p_pollen[0]["time"].as(); + + std::vector merged_array = {}; + + if (first_aqi_time <= first_pollen_time) { + merged_array = mergeJsonArraysFromTimestamp(p_current_time, p_aqi, p_pollen); + } else { + merged_array = mergeJsonArraysFromTimestamp(p_current_time, p_pollen, p_aqi); + } + + uint32_t index = 0; + for (float value : merged_array) { + setPixelToColorMappedValue(m_air_quality_forecast_map.at(index), value); + index++; + } + + float max_aqi = findMaxValueInTwelveHourInterval(p_current_time, p_aqi); + setPixelToColorMappedValue(m_max_air_qualilty_map, max_aqi); + + float max_pollen = findMaxValueInTwelveHourInterval(p_current_time, p_pollen); + setPixelToColorMappedValue(m_max_pollen_map, max_pollen); +} + +void Display::setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation) { + float previous_value = 0.0f; + + uint32_t index = 0U; + for (JsonObject elem : p_precipitation) { + uint32_t time = elem["time"].as(); + float value = elem["value"].as(); + + if ((index == 0U) and (time > p_current_time)) { + setPixelToColorMappedValue(m_precipitation_forecast_map.at(index), previous_value); + index = 1U; + } + + if (index > 0U) { + setPixelToColorMappedValue(m_precipitation_forecast_map.at(index), value); + index += 1U; + } + + previous_value = value; + } +} + +void Display::setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_uvi) { + for (JsonObject elem : p_uvi) { + uint32_t time = elem["time"].as(); + float value = elem["value"].as(); + + if (time > p_current_time) { + setPixelToColorMappedValue(m_uvi_map, value); + break; + } + } +} + +void Display::setLocalAirQualityLed() {} + +void Display::setWifiStatusLed(bool p_enable) {} + +float Display::findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array) { + float max_value = 0.0f; + + for (auto elem : p_array) { + uint32_t time = elem["time"].as(); + constexpr uint32_t TWELVE_HOURS_IN_SECONDS = 12U * 60U * 60U; + if ((time >= p_time_stamp) and (time < p_time_stamp + TWELVE_HOURS_IN_SECONDS)) { + max_value = std::max(max_value, elem["value"].as()); + } + } + + return max_value; +} + +std::vector Display::mergeJsonArraysFromTimestamp(uint32_t p_time_stamp, + ArduinoJson::V710PB22::JsonArray p_array_a, + ArduinoJson::V710PB22::JsonArray p_array_b) { + // Assumption: timestamp in p_array_a <= p_array_b + + std::vector merged_vector = {}; + size_t start_point_b = 0; + + for (auto elem_a : p_array_a) { + uint32_t time_a = elem_a["time"].as(); + + if (time_a >= p_time_stamp) { + float merged_value = elem_a["value"].as(); + + for (size_t index_b = start_point_b; index_b < p_array_b.size(); index_b++) { + uint32_t time_b = p_array_b[index_b]["time"].as(); + + if (time_b >= time_a) { + float value_b = p_array_b[index_b]["value"].as(); + merged_value = std::max(merged_value, value_b); + merged_vector.push_back(merged_value); + start_point_b = index_b + 1U; + break; + } + } + } + } + + for (size_t index_b = start_point_b; index_b < p_array_b.size(); index_b++) { + float value_b = p_array_b[index_b]["value"].as(); + merged_vector.push_back(value_b); + } + + return merged_vector; +} + +std::tuple Display::mapValueToColorRGB(float p_value) { + if (p_value < 1.0) { + return {0, 0, 0}; // black (good) + } else if (p_value < 2.0) { + return {0, 0, 160}; // blue (good) + } else if (p_value < 3.0) { + return {0, 255, 255}; // cyan (good) + } else if (p_value < 4.0) { + return {255, 255, 255}; // white (mediocre) + } else if (p_value < 5.0) { + return {200, 200, 32}; // light yellow (mediocre) + } else if (p_value < 6.0) { + return {120, 120, 0}; // yellow (mediocre) + } else if (p_value < 7.0) { + return {200, 80, 0}; // orange (inadequate) + } else if (p_value < 8.0) { + return {255, 50, 0}; // red-orange (inadequate) + } else if (p_value < 9.0) { + return {180, 0, 0}; // red (bad) + } else if (p_value < 10.0) { + return {200, 0, 160}; // magenta (bad) + } else { + return {60, 0, 210}; // purple (terrible) + } +} + +std::tuple Display::mapValueToColorRGBW(float p_value) { + if (p_value < 1.0) { + return {0, 0, 0, 0}; // black (good) + } else if (p_value < 2.0) { + return {0, 0, 255, 0}; // blue (good) + } else if (p_value < 3.0) { + return {0, 255, 255, 0}; // cyan (good) + } else if (p_value < 4.0) { + return {255, 255, 255, 0}; // white (mediocre) + } else if (p_value < 5.0) { + return {200, 200, 32, 0}; // light yellow (mediocre) + } else if (p_value < 6.0) { + return {120, 120, 0, 0}; // yellow (mediocre) + } else if (p_value < 7.0) { + return {200, 80, 0, 0}; // orange (inadequate) + } else if (p_value < 8.0) { + return {255, 50, 0, 0}; // red-orange (inadequate) + } else if (p_value < 9.0) { + return {180, 0, 0, 0}; // red (bad) + } else if (p_value < 10.0) { + return {200, 0, 160, 0}; // magenta (bad) + } else { + return {60, 0, 210, 0}; // purple (terrible) + } +} + +void Display::setPixelToColorMappedValue(uint8_t p_index, float p_value) { + auto [red, green, blue, white] = mapValueToColorRGBW(p_value); + m_leds.setPixelColor(p_index, m_leds.Color(red, green, blue, white)); +} diff --git a/src/main.cpp b/src/main.cpp index c95c894..a680931 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #include "../include/backend_communication.hpp" +#include "../include/display.hpp" #include "../include/wifi_handler.hpp" #ifndef LED_BUILTIN @@ -14,7 +15,8 @@ #define CONNECT_BUTTON BUTTON_BUILTIN DESPatch dESPatch; -WifiHandler wifi_handler(LED_BUILTIN); +Display display; +WifiHandler wifi_handler(LED_BUILTIN, display); BackendCommunication backend_communication(wifi_handler); @@ -56,7 +58,37 @@ void loop(void) { } else { Serial.println(String(millis()) + " Address: " + String(static_cast(backend_communication.m_config["address"]))); - // getWeatherData(); + + backend_communication.getWeatherData(); + + String text = String(millis()) + " Precipitation:"; + for (JsonObject elem : backend_communication.m_weather_data["precipitation"].as()) { + text += " " + String(elem["value"].as()); + } + Serial.println(text); + + text = String(millis()) + " AQI:"; + for (JsonObject elem : backend_communication.m_weather_data["AQI"].as()) { + text += " " + String(elem["value"].as()); + } + Serial.println(text); + + text = String(millis()) + " Pollen:"; + for (JsonObject elem : backend_communication.m_weather_data["pollen"].as()) { + text += " " + String(elem["value"].as()); + } + Serial.println(text); + text = String(millis()) + " UVI:"; + for (JsonObject elem : backend_communication.m_weather_data["UVI"].as()) { + text += " " + String(elem["value"].as()); + } + Serial.println(text); + + uint32_t time = backend_communication.m_weather_data["time"].as(); + text = String(millis()) + " time: " + std::to_string(time).c_str(); + Serial.println(text); + + // display.setUVIForecastLed(time, backend_communication.m_weather_data["UVI"].as()); } dESPatch.checkForUpdate(true); delay(2000); diff --git a/src/wifi_handler.cpp b/src/wifi_handler.cpp index cf2bbf1..4cf9e70 100644 --- a/src/wifi_handler.cpp +++ b/src/wifi_handler.cpp @@ -5,7 +5,7 @@ static uint8_t g_led_pin; static Ticker g_ticker; -WifiHandler::WifiHandler(uint8_t p_led_pin) { +WifiHandler::WifiHandler(uint8_t p_led_pin, Display p_display) : m_display(p_display) { g_led_pin = p_led_pin; return; } From 60d91a5deff9ccd514b448db085349209229484c Mon Sep 17 00:00:00 2001 From: admar Date: Sat, 31 Aug 2024 17:32:34 +0200 Subject: [PATCH 08/13] Pass reference to NeoPixels to Display class. --- include/display.hpp | 11 +++++++---- src/display.cpp | 22 +++++++++++++++++----- src/main.cpp | 16 ++++++++++++++-- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/include/display.hpp b/include/display.hpp index 6e844e6..5610350 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -8,16 +8,19 @@ class Display { public: // Constructor - Display(); + Display(Adafruit_NeoPixel& p_leds); public: // Public methods - void setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_aqi, - ArduinoJson::V710PB22::JsonArray p_pollen); + void setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_pollen, + ArduinoJson::V710PB22::JsonArray p_aqi); void setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation); void setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_uvi); void setLocalAirQualityLed(); void setWifiStatusLed(bool p_enable); + void setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white); + void clear(); + void show(); private: // Private methods @@ -35,7 +38,7 @@ class Display { private: // Private members - Adafruit_NeoPixel m_leds; + Adafruit_NeoPixel& m_leds; std::array m_precipitation_forecast_map; std::array m_air_quality_forecast_map; uint8_t m_max_pollen_map; diff --git a/src/display.cpp b/src/display.cpp index 4c4511d..bb2bd02 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,8 +1,6 @@ #include "../include/display.hpp" -Display::Display() { - // m_leds = Adafruit_NeoPixel(m_NUM_LEDS_PROTO_V1, m_DATA_PIN_PROTO_V1, NEO_RGBW + NEO_KHZ800); - +Display::Display(Adafruit_NeoPixel& p_leds) : m_leds(p_leds) { m_precipitation_forecast_map = {11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; m_air_quality_forecast_map = {23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; m_max_pollen_map = 26; @@ -13,8 +11,8 @@ Display::Display() { return; } -void Display::setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_aqi, - ArduinoJson::V710PB22::JsonArray p_pollen) { +void Display::setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_pollen, + ArduinoJson::V710PB22::JsonArray p_aqi) { uint32_t first_aqi_time = p_pollen[0]["aqi"].as(); uint32_t first_pollen_time = p_pollen[0]["time"].as(); @@ -77,6 +75,20 @@ void Display::setLocalAirQualityLed() {} void Display::setWifiStatusLed(bool p_enable) {} +void Display::setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white) { + m_leds.setPixelColor(p_index, m_leds.Color(p_red, p_green, p_blue, p_white)); +} + +void Display::clear() { + m_leds.clear(); + return; +} + +void Display::show() { + m_leds.show(); + return; +} + float Display::findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array) { float max_value = 0.0f; diff --git a/src/main.cpp b/src/main.cpp index a680931..d2db1e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include #include #include "../include/backend_communication.hpp" @@ -15,12 +16,18 @@ #define CONNECT_BUTTON BUTTON_BUILTIN DESPatch dESPatch; -Display display; + +Adafruit_NeoPixel leds = Adafruit_NeoPixel(29, 14, NEO_GRBW + NEO_KHZ800); +Display display(leds); + WifiHandler wifi_handler(LED_BUILTIN, display); BackendCommunication backend_communication(wifi_handler); void setup() { + display.clear(); + display.show(); + Serial.begin(115200); pinMode(LED_BUILTIN, OUTPUT); @@ -88,7 +95,12 @@ void loop(void) { text = String(millis()) + " time: " + std::to_string(time).c_str(); Serial.println(text); - // display.setUVIForecastLed(time, backend_communication.m_weather_data["UVI"].as()); + // display.setPollenPlusAQIForecastLeds(time, backend_communication.m_weather_data["pollen"].as(), + // backend_communication.m_weather_data["AQI"].as()); + // display.setPrecipitationForecastLeds(time, + // backend_communication.m_weather_data["precipitation"].as()); + display.setUVIForecastLed(time, backend_communication.m_weather_data["UVI"].as()); + display.show(); } dESPatch.checkForUpdate(true); delay(2000); From 5cba98368f53f546ee0e80efa77c98933c447da3 Mon Sep 17 00:00:00 2001 From: admar Date: Sun, 1 Sep 2024 13:24:40 +0200 Subject: [PATCH 09/13] Fix merging pollen + AQI --- include/display.hpp | 3 ++ src/backend_communication.cpp | 2 +- src/display.cpp | 82 +++++++++++++++++++++++++---------- src/main.cpp | 58 +++++++++++-------------- 4 files changed, 88 insertions(+), 57 deletions(-) diff --git a/include/display.hpp b/include/display.hpp index 5610350..d0b5a03 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -27,6 +27,9 @@ class Display { float findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array); std::vector mergeJsonArraysFromTimestamp(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array_a, ArduinoJson::V710PB22::JsonArray p_array_b); + std::vector mergeJsonArraysFromTimestampHelper(uint32_t p_time_stamp, + ArduinoJson::V710PB22::JsonArray p_array_a, + ArduinoJson::V710PB22::JsonArray p_array_b); std::tuple mapValueToColorRGB(float p_value); std::tuple mapValueToColorRGBW(float p_value); void setPixelToColorMappedValue(uint8_t index, float p_value); diff --git a/src/backend_communication.cpp b/src/backend_communication.cpp index 9c4ba1c..0fd2061 100644 --- a/src/backend_communication.cpp +++ b/src/backend_communication.cpp @@ -51,7 +51,7 @@ bool BackendCommunication::getConfig() { bool BackendCommunication::getWeatherData() { String url = "https://sinoptik.luon.net/forecast?address=" + urlencode(m_config["address"]) + - "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=pollen&metrics=PAQI"; + "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=pollen"; String payload = ""; bool isSuccess = false; diff --git a/src/display.cpp b/src/display.cpp index bb2bd02..43faa25 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -13,15 +13,12 @@ Display::Display(Adafruit_NeoPixel& p_leds) : m_leds(p_leds) { void Display::setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_pollen, ArduinoJson::V710PB22::JsonArray p_aqi) { - uint32_t first_aqi_time = p_pollen[0]["aqi"].as(); - uint32_t first_pollen_time = p_pollen[0]["time"].as(); + std::vector merged_array = mergeJsonArraysFromTimestamp(p_current_time, p_aqi, p_pollen); - std::vector merged_array = {}; + if (merged_array.size() > m_air_quality_forecast_map.size()) { + uint32_t number_of_elements_to_erase = merged_array.size() - m_air_quality_forecast_map.size(); - if (first_aqi_time <= first_pollen_time) { - merged_array = mergeJsonArraysFromTimestamp(p_current_time, p_aqi, p_pollen); - } else { - merged_array = mergeJsonArraysFromTimestamp(p_current_time, p_pollen, p_aqi); + merged_array.erase(merged_array.end() - number_of_elements_to_erase, merged_array.end()); } uint32_t index = 0; @@ -56,6 +53,10 @@ void Display::setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson: } previous_value = value; + + if (index >= m_precipitation_forecast_map.size()) { + break; + } } } @@ -106,28 +107,63 @@ float Display::findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJs std::vector Display::mergeJsonArraysFromTimestamp(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array_a, ArduinoJson::V710PB22::JsonArray p_array_b) { - // Assumption: timestamp in p_array_a <= p_array_b + uint32_t start_time_a = p_array_a[0]["time"].as(); + uint32_t start_time_b = p_array_b[0]["time"].as(); + + std::vector merged_array = {}; + + if (start_time_a <= start_time_b) { + merged_array = mergeJsonArraysFromTimestampHelper(p_time_stamp, p_array_a, p_array_b); + } else { + merged_array = mergeJsonArraysFromTimestampHelper(p_time_stamp, p_array_b, p_array_a); + } + + return merged_array; +} + +std::vector Display::mergeJsonArraysFromTimestampHelper(uint32_t p_time_stamp, + ArduinoJson::V710PB22::JsonArray p_array_a, + ArduinoJson::V710PB22::JsonArray p_array_b) { + // Assumption: time stamp in first element of p_array_a <= p_array_b std::vector merged_vector = {}; + size_t start_point_a = 0; size_t start_point_b = 0; - for (auto elem_a : p_array_a) { - uint32_t time_a = elem_a["time"].as(); + for (size_t index_a = start_point_a; index_a < p_array_a.size(); index_a++) { + uint32_t time_a = p_array_a[index_a]["time"].as(); - if (time_a >= p_time_stamp) { - float merged_value = elem_a["value"].as(); - - for (size_t index_b = start_point_b; index_b < p_array_b.size(); index_b++) { - uint32_t time_b = p_array_b[index_b]["time"].as(); - - if (time_b >= time_a) { - float value_b = p_array_b[index_b]["value"].as(); - merged_value = std::max(merged_value, value_b); - merged_vector.push_back(merged_value); - start_point_b = index_b + 1U; - break; - } + if (time_a > p_time_stamp) { + if (index_a > 0) { + start_point_a = index_a - 1; + } else { + start_point_a = 0; } + break; + } + } + + for (size_t index_a = start_point_a; index_a < p_array_a.size(); index_a++) { + uint32_t time_a = p_array_a[index_a]["time"].as(); + float value_a = p_array_a[index_a]["value"].as(); + + bool is_merged = false; + for (size_t index_b = start_point_b; index_b < p_array_b.size(); index_b++) { + uint32_t time_b = p_array_b[index_b]["time"].as(); + + if (time_b == time_a) { + float value_b = p_array_b[index_b]["value"].as(); + float merged_value = std::max(value_a, value_b); + + merged_vector.push_back(merged_value); + is_merged = true; + start_point_b = index_b + 1U; + break; + } + } + + if (not is_merged) { + merged_vector.push_back(value_a); } } diff --git a/src/main.cpp b/src/main.cpp index d2db1e8..13a69a4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,7 @@ void setup() { } wifi_handler.connect(); + digitalWrite(LED_BUILTIN, LOW); const char url[] = "https://apikey:cqprlgiafadnidsgeqozcpldkaeqimqw@despatch.luon.net/" @@ -58,50 +59,41 @@ void setup() { } void loop(void) { + uint32_t time_start = millis(); + backend_communication.getConfig(); if (not backend_communication.m_is_config_valid) { - Serial.println(String(millis()) + " Invalid config"); + Serial.println(String("Uptime: " + String(millis())) + " Invalid config"); } else { - Serial.println(String(millis()) + - " Address: " + String(static_cast(backend_communication.m_config["address"]))); - backend_communication.getWeatherData(); - String text = String(millis()) + " Precipitation:"; - for (JsonObject elem : backend_communication.m_weather_data["precipitation"].as()) { - text += " " + String(elem["value"].as()); - } - Serial.println(text); + uint32_t utc_time = backend_communication.m_weather_data["time"].as(); + Serial.println("Uptime: " + String(millis()) + ", UTC time: " + String(utc_time)); - text = String(millis()) + " AQI:"; - for (JsonObject elem : backend_communication.m_weather_data["AQI"].as()) { - text += " " + String(elem["value"].as()); - } - Serial.println(text); + display.setPollenPlusAQIForecastLeds(utc_time, backend_communication.m_weather_data["pollen"].as(), + backend_communication.m_weather_data["AQI"].as()); - text = String(millis()) + " Pollen:"; - for (JsonObject elem : backend_communication.m_weather_data["pollen"].as()) { - text += " " + String(elem["value"].as()); - } - Serial.println(text); - text = String(millis()) + " UVI:"; - for (JsonObject elem : backend_communication.m_weather_data["UVI"].as()) { - text += " " + String(elem["value"].as()); - } - Serial.println(text); + display.setPrecipitationForecastLeds(utc_time, + backend_communication.m_weather_data["precipitation"].as()); - uint32_t time = backend_communication.m_weather_data["time"].as(); - text = String(millis()) + " time: " + std::to_string(time).c_str(); - Serial.println(text); + display.setUVIForecastLed(utc_time, backend_communication.m_weather_data["UVI"].as()); - // display.setPollenPlusAQIForecastLeds(time, backend_communication.m_weather_data["pollen"].as(), - // backend_communication.m_weather_data["AQI"].as()); - // display.setPrecipitationForecastLeds(time, - // backend_communication.m_weather_data["precipitation"].as()); - display.setUVIForecastLed(time, backend_communication.m_weather_data["UVI"].as()); display.show(); } + dESPatch.checkForUpdate(true); - delay(2000); + + uint32_t time_end = millis(); + uint32_t time_delta = time_end - time_start; + + uint32_t delay_time = 0U; + constexpr uint32_t ONE_SECOND_IN_MILLISECONDS = 1000U; + constexpr uint32_t INTERVAL = 60U * ONE_SECOND_IN_MILLISECONDS; + + if (time_delta < INTERVAL) { + delay_time = INTERVAL - time_delta; + } + + delay(delay_time); } From 8dc11493aa2eb04b97dfa0a7d7e024be8e9e9a67 Mon Sep 17 00:00:00 2001 From: admar Date: Sun, 1 Sep 2024 16:50:16 +0200 Subject: [PATCH 10/13] Use NeoPixel led for wifi status --- include/display.hpp | 3 ++- include/wifi_handler.hpp | 5 +---- src/display.cpp | 16 +++++++++++++++- src/main.cpp | 27 ++++++++++++++++++++++++--- src/wifi_handler.cpp | 25 ++++++++++--------------- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/include/display.hpp b/include/display.hpp index d0b5a03..eafc80c 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -17,7 +17,8 @@ class Display { void setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation); void setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_uvi); void setLocalAirQualityLed(); - void setWifiStatusLed(bool p_enable); + void setWifiStatusLed(bool p_is_connected); + void setErrorLed(bool p_is_error); void setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white); void clear(); void show(); diff --git a/include/wifi_handler.hpp b/include/wifi_handler.hpp index b12f8a7..5141a46 100644 --- a/include/wifi_handler.hpp +++ b/include/wifi_handler.hpp @@ -3,12 +3,10 @@ #include // https://github.com/admarschoonen/WiFiManager -#include "display.hpp" - class WifiHandler { public: // Constructor - WifiHandler(uint8_t p_led_pin, Display p_display); + WifiHandler(std::function p_tickerCb); public: // Public static methods @@ -30,7 +28,6 @@ class WifiHandler { private: // Private objects WiFiManager m_wifiManager; - Display& m_display; }; #endif // __WIFIHANDLER_HPP__ \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index 43faa25..c7d78e7 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -74,7 +74,21 @@ void Display::setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22:: void Display::setLocalAirQualityLed() {} -void Display::setWifiStatusLed(bool p_enable) {} +void Display::setWifiStatusLed(bool p_is_connected) { + if (p_is_connected) { + m_leds.setPixelColor(m_wifi_status_map, m_leds.Color(0U, 0U, 255U, 255U)); + } else { + m_leds.setPixelColor(m_wifi_status_map, m_leds.Color(0U, 0U, 0U, 0U)); + } +} + +void Display::setErrorLed(bool p_is_error) { + if (p_is_error) { + m_leds.setPixelColor(m_wifi_status_map, m_leds.Color(255U, 0U, 0U, 0U)); + } else { + m_leds.setPixelColor(m_wifi_status_map, m_leds.Color(0U, 0U, 0U, 0U)); + } +} void Display::setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white) { m_leds.setPixelColor(p_index, m_leds.Color(p_red, p_green, p_blue, p_white)); diff --git a/src/main.cpp b/src/main.cpp index 13a69a4..e9efc3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,15 +15,34 @@ #define CONNECT_BUTTON BUTTON_BUILTIN +// Forward function declarations +void wifiHandlerFeedbackCb(WiFiManager::Status p_wifi_status); + +// Global objects DESPatch dESPatch; Adafruit_NeoPixel leds = Adafruit_NeoPixel(29, 14, NEO_GRBW + NEO_KHZ800); Display display(leds); -WifiHandler wifi_handler(LED_BUILTIN, display); +WifiHandler wifi_handler(wifiHandlerFeedbackCb); BackendCommunication backend_communication(wifi_handler); +void wifiHandlerFeedbackCb(WiFiManager::Status p_wifi_status) { + static bool state = false; + + if (p_wifi_status.mode == WiFiManager::Mode::CONNECTED) { + state = true; + } else if (p_wifi_status.mode == WiFiManager::Mode::DISCONNECTED) { + state = false; + } else { + state = !state; + } + + display.setWifiStatusLed(state); + display.show(); +} + void setup() { display.clear(); display.show(); @@ -64,7 +83,8 @@ void loop(void) { backend_communication.getConfig(); if (not backend_communication.m_is_config_valid) { - Serial.println(String("Uptime: " + String(millis())) + " Invalid config"); + display.setErrorLed(true); + Serial.println(String("Uptime: " + String(millis())) + ". Error: invalid config"); } else { backend_communication.getWeatherData(); @@ -79,9 +99,10 @@ void loop(void) { display.setUVIForecastLed(utc_time, backend_communication.m_weather_data["UVI"].as()); - display.show(); + display.setWifiStatusLed(false); } + display.show(); dESPatch.checkForUpdate(true); uint32_t time_end = millis(); diff --git a/src/wifi_handler.cpp b/src/wifi_handler.cpp index 4cf9e70..11b9ff0 100644 --- a/src/wifi_handler.cpp +++ b/src/wifi_handler.cpp @@ -2,20 +2,18 @@ #include "../include/wifi_handler.hpp" -static uint8_t g_led_pin; static Ticker g_ticker; +static WiFiManager::Status g_wifi_status; +static std::function g_tickerCb = nullptr; -WifiHandler::WifiHandler(uint8_t p_led_pin, Display p_display) : m_display(p_display) { - g_led_pin = p_led_pin; +WifiHandler::WifiHandler(std::function p_tickerCb) { + g_tickerCb = p_tickerCb; return; } static void tick() { - int state = 0; - - if (g_led_pin >= 0) { - state = digitalRead(g_led_pin); - digitalWrite(g_led_pin, !state); + if (g_tickerCb != nullptr) { + g_tickerCb(g_wifi_status); } } @@ -30,6 +28,8 @@ void WifiHandler::setBlinkRate(float p_interval) { } void WifiHandler::wifiManagerCb(WiFiManager::Status p_status) { + g_wifi_status = p_status; + if (p_status.mode == WiFiManager::Mode::CONNECTING) { WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_CONNECTING); } else if (p_status.mode == WiFiManager::Mode::SCANNING) { @@ -40,10 +40,10 @@ void WifiHandler::wifiManagerCb(WiFiManager::Status p_status) { WifiHandler::setBlinkRate(WifiHandler::BLINK_RATE_ERASE); } else if (p_status.mode == WiFiManager::Mode::CONNECTED) { WifiHandler::setBlinkRate(0.0f); - digitalWrite(g_led_pin, HIGH); + g_tickerCb(g_wifi_status); } else if (p_status.mode == WiFiManager::Mode::DISCONNECTED) { WifiHandler::setBlinkRate(0.0f); - digitalWrite(g_led_pin, LOW); + g_tickerCb(g_wifi_status); } } @@ -65,9 +65,6 @@ void WifiHandler::connect() { // if you get here you have connected to the WiFi Serial.print(String(millis()) + " Connected with address: "); Serial.println(WiFi.localIP()); - - // keep LED on - digitalWrite(g_led_pin, HIGH); } void WifiHandler::resetSettings() { @@ -80,8 +77,6 @@ void WifiHandler::resetSettings() { delay(1000); status.mode = WiFiManager::Mode::DISCONNECTED; wifiManagerCb(status); - - return; } String WifiHandler::getMac(bool p_insertColons) { From 09dc9805bc38af06be522a2d81b89439bf7dc47b Mon Sep 17 00:00:00 2001 From: admar Date: Sun, 1 Sep 2024 22:10:53 +0200 Subject: [PATCH 11/13] Added BME680 sensor --- .vscode/c_cpp_properties.json | 10 ++-- .vscode/settings.json | 5 ++ include/display.hpp | 2 +- include/sensor_manager.hpp | 55 +++++++++++++++++++ include/wifi_handler.hpp | 6 +- platformio.ini | 1 + src/display.cpp | 30 +++++++++- src/main.cpp | 9 +++ src/sensor_manager.cpp | 100 ++++++++++++++++++++++++++++++++++ 9 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 include/sensor_manager.hpp create mode 100644 src/sensor_manager.cpp diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index ec0e346..e0a9f3a 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -11,6 +11,9 @@ "/home/admar/Documents/Arduino/buienradarklok/Claire/include", "/home/admar/Documents/Arduino/buienradarklok/Claire/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/BSEC Software Library/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/Adafruit NeoPixel", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", @@ -237,12 +240,10 @@ "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", - "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", - "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", "" ], "browse": { @@ -251,6 +252,9 @@ "/home/admar/Documents/Arduino/buienradarklok/Claire/include", "/home/admar/Documents/Arduino/buienradarklok/Claire/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", + "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/BSEC Software Library/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", + "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/Adafruit NeoPixel", "/home/admar/Documents/Arduino/buienradarklok/Claire/.pio/libdeps/esp32dev/dESPatch", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", @@ -477,12 +481,10 @@ "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", - "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", - "/home/admar/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", "" ] }, diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a478c1f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "string_view": "cpp" + } +} \ No newline at end of file diff --git a/include/display.hpp b/include/display.hpp index eafc80c..8b2d0f6 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -16,7 +16,7 @@ class Display { ArduinoJson::V710PB22::JsonArray p_aqi); void setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation); void setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_uvi); - void setLocalAirQualityLed(); + void setLocalAirQualityLed(float p_iaq); void setWifiStatusLed(bool p_is_connected); void setErrorLed(bool p_is_error); void setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white); diff --git a/include/sensor_manager.hpp b/include/sensor_manager.hpp new file mode 100644 index 0000000..d1c39ed --- /dev/null +++ b/include/sensor_manager.hpp @@ -0,0 +1,55 @@ +#ifndef __SENSOR_MANAGER_HPP__ +#define __SENSOR_MANAGER_HPP__ + +#include "bsec.h" + +class SensorManager { + public: + // Public types + struct SensorData { + __typeof__(Bsec::iaq) iaq; + __typeof__(Bsec::iaqAccuracy) iaqAccuracy; + __typeof__(Bsec::staticIaq) staticIaq; + __typeof__(Bsec::co2Equivalent) co2Equivalent; + __typeof__(Bsec::breathVocEquivalent) breathVocEquivalent; + __typeof__(Bsec::rawTemperature) rawTemperature; + __typeof__(Bsec::pressure) pressure; + __typeof__(Bsec::rawHumidity) rawHumidity; + __typeof__(Bsec::gasResistance) gasResistance; + __typeof__(Bsec::stabStatus) stabStatus; + __typeof__(Bsec::runInStatus) runInStatus; + __typeof__(Bsec::temperature) temperature; + __typeof__(Bsec::humidity) humidity; + __typeof__(Bsec::gasPercentage) gasPercentage; + }; + + public: + // Constructor + SensorManager(); + + bool setup(); + + bool read(); + + public: + // Public members + SensorData data; + + private: + bool checkIaqSensorStatus(); + + private: + // Private static members + constexpr static uint16_t m_NUM_LEDS_PROTO_V1 = 29U; + constexpr static int16_t m_DATA_PIN_PROTO_V1 = 14; + constexpr static uint8_t m_BME_CS = 5U; + constexpr static uint8_t m_BSEC_CONFIG_IAQ[] = { +#include "config/generic_33v_3s_4d/bsec_iaq.txt" + }; + + private: + // Private members + Bsec m_iaqSensor; +}; + +#endif // __SENSOR_MANAGER_HPP__ diff --git a/include/wifi_handler.hpp b/include/wifi_handler.hpp index 5141a46..fcb03af 100644 --- a/include/wifi_handler.hpp +++ b/include/wifi_handler.hpp @@ -1,5 +1,5 @@ -#ifndef __WIFIHANDLER_HPP__ -#define __WIFIHANDLER_HPP__ +#ifndef __WIFI_HANDLER_HPP__ +#define __WIFI_HANDLER_HPP__ #include // https://github.com/admarschoonen/WiFiManager @@ -30,4 +30,4 @@ class WifiHandler { WiFiManager m_wifiManager; }; -#endif // __WIFIHANDLER_HPP__ \ No newline at end of file +#endif // __WIFI_HANDLER_HPP__ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index cea889a..26f5450 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,3 +19,4 @@ lib_deps = https://github.com/admarschoonen/dESPatch.git#v0.9.5 bblanchon/ArduinoJson@^7.1.0 adafruit/Adafruit NeoPixel@^1.12.3 + boschsensortec/BSEC Software Library@^1.8.1492 diff --git a/src/display.cpp b/src/display.cpp index c7d78e7..f1daa10 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -72,7 +72,35 @@ void Display::setUVIForecastLed(uint32_t p_current_time, ArduinoJson::V710PB22:: } } -void Display::setLocalAirQualityLed() {} +void Display::setLocalAirQualityLed(float p_iaq) { + float value = 0.0f; + + if (p_iaq <= 50.0f) { + value = 1.0f; // Excellent + } else if (p_iaq <= 100.0f) { + value = 2.0f; // Good + } else if (p_iaq <= 125.0f) { + value = 4.0f; + } else if (p_iaq <= 150.0f) { + value = 4.0f; // Lightly polluted + } else if (p_iaq <= 175.0f) { + value = 5.0f; + } else if (p_iaq <= 200.0f) { + value = 6.0f; // Moderately polluted + } else if (p_iaq <= 225.0f) { + value = 7.0f; + } else if (p_iaq <= 250.0f) { + value = 8.0f; // Heavily polluted + } else if (p_iaq <= 300.0f) { + value = 9.0f; + } else if (p_iaq <= 350.0f) { + value = 10.0f; // Severely polluted + } else { + value = 11.0f; // Extremely polluted + } + + setPixelToColorMappedValue(m_local_air_quality_map, value); +} void Display::setWifiStatusLed(bool p_is_connected) { if (p_is_connected) { diff --git a/src/main.cpp b/src/main.cpp index e9efc3d..867ebd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "../include/backend_communication.hpp" #include "../include/display.hpp" +#include "../include/sensor_manager.hpp" #include "../include/wifi_handler.hpp" #ifndef LED_BUILTIN @@ -28,6 +29,8 @@ WifiHandler wifi_handler(wifiHandlerFeedbackCb); BackendCommunication backend_communication(wifi_handler); +SensorManager sensor_manager; + void wifiHandlerFeedbackCb(WiFiManager::Status p_wifi_status) { static bool state = false; @@ -75,6 +78,8 @@ void setup() { int x = dESPatch.configure(url, true, false, interval, false, root_ca); Serial.println(String(millis()) + " dESPatch.configure() returned with code " + String(x)); + + sensor_manager.setup(); } void loop(void) { @@ -99,9 +104,13 @@ void loop(void) { display.setUVIForecastLed(utc_time, backend_communication.m_weather_data["UVI"].as()); + display.setLocalAirQualityLed(sensor_manager.data.staticIaq); + display.setWifiStatusLed(false); } + sensor_manager.read(); + display.show(); dESPatch.checkForUpdate(true); diff --git a/src/sensor_manager.cpp b/src/sensor_manager.cpp new file mode 100644 index 0000000..3025630 --- /dev/null +++ b/src/sensor_manager.cpp @@ -0,0 +1,100 @@ +#include + +#include "../include/sensor_manager.hpp" + +SensorManager::SensorManager() { + data = {}; + return; +} + +bool SensorManager::setup() { + SPI.begin(); + m_iaqSensor.begin(m_BME_CS, SPI); + String output = "BSEC library version " + String(m_iaqSensor.version.major) + "." + + String(m_iaqSensor.version.minor) + "." + String(m_iaqSensor.version.major_bugfix) + "." + + String(m_iaqSensor.version.minor_bugfix); + Serial.println(output); + + if (not checkIaqSensorStatus()) { + return false; + } + + m_iaqSensor.setConfig(m_BSEC_CONFIG_IAQ); + m_iaqSensor.setTemperatureOffset(13.5f); + + if (not checkIaqSensorStatus()) { + return false; + } + + bsec_virtual_sensor_t sensorList[13] = {BSEC_OUTPUT_IAQ, + BSEC_OUTPUT_STATIC_IAQ, + BSEC_OUTPUT_CO2_EQUIVALENT, + BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, + BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_HUMIDITY, + BSEC_OUTPUT_RAW_GAS, + BSEC_OUTPUT_STABILIZATION_STATUS, + BSEC_OUTPUT_RUN_IN_STATUS, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + BSEC_OUTPUT_GAS_PERCENTAGE}; + + m_iaqSensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP); + + if (checkIaqSensorStatus()) { + return false; + } + + return true; +} + +bool SensorManager::read() { + if (m_iaqSensor.run()) { // If new data is available + data.iaq = m_iaqSensor.iaq; + data.iaqAccuracy = m_iaqSensor.iaqAccuracy; + data.staticIaq = m_iaqSensor.staticIaq; + data.co2Equivalent = m_iaqSensor.co2Equivalent; + data.breathVocEquivalent = m_iaqSensor.breathVocEquivalent; + data.rawTemperature = m_iaqSensor.rawTemperature; + data.pressure = m_iaqSensor.pressure; + data.rawHumidity = m_iaqSensor.rawHumidity; + data.gasResistance = m_iaqSensor.gasResistance; + data.stabStatus = m_iaqSensor.stabStatus; + data.runInStatus = m_iaqSensor.runInStatus; + data.temperature = m_iaqSensor.temperature; + data.humidity = m_iaqSensor.humidity; + data.gasPercentage = m_iaqSensor.gasPercentage; + } else { + Serial.println("sensor status: " + String(checkIaqSensorStatus())); + } + return true; +} + +bool SensorManager::checkIaqSensorStatus() { + if (m_iaqSensor.bsecStatus != BSEC_OK) { + if (m_iaqSensor.bsecStatus < BSEC_OK) { + Serial.println("BSEC error code: " + String(m_iaqSensor.bsecStatus)); + + return false; + } else { + Serial.println("BSEC warning code: " + String(m_iaqSensor.bsecStatus)); + + return false; + } + } + + if (m_iaqSensor.bme68xStatus != BME68X_OK) { + if (m_iaqSensor.bme68xStatus < BME68X_OK) { + Serial.println("BME68X error code: " + String(m_iaqSensor.bme68xStatus)); + + return false; + } else { + Serial.println("BME68X warning code: " + String(m_iaqSensor.bme68xStatus)); + + return false; + } + } + + return true; +} \ No newline at end of file From 7094d20ecad8efd273d5870dfb7a8b844c7a0080 Mon Sep 17 00:00:00 2001 From: admar Date: Sat, 7 Sep 2024 00:03:53 +0200 Subject: [PATCH 12/13] Several improvements: * Fix color mapping of precipitation * Fix bug in calculation of max value of pollen / AQI * Added update() method to backend which will only update after interval period has passed --- include/backend_communication.hpp | 12 +++++ include/display.hpp | 5 +- include/sensor_manager.hpp | 29 ++++++------ src/backend_communication.cpp | 66 ++++++++++++++++---------- src/display.cpp | 47 ++++++++++++++++-- src/main.cpp | 29 +++--------- src/sensor_manager.cpp | 79 ++++++++++++++++++------------- 7 files changed, 165 insertions(+), 102 deletions(-) diff --git a/include/backend_communication.hpp b/include/backend_communication.hpp index 322b73c..8103dbd 100644 --- a/include/backend_communication.hpp +++ b/include/backend_communication.hpp @@ -8,6 +8,10 @@ #include "../include/wifi_handler.hpp" class BackendCommunication { + public: + // Public enums + enum State { ERROR, STILL_VALID, UPDATED }; + public: // Constructor BackendCommunication(WifiHandler& p_wifi_handler); @@ -18,6 +22,8 @@ class BackendCommunication { bool getWeatherData(); + State update(); + private: // Private methods bool parseConfig(String& p_payload); @@ -30,8 +36,14 @@ class BackendCommunication { // Public member objects JsonDocument m_config; bool m_is_config_valid; + String m_config_timestamp; JsonDocument m_weather_data; + int32_t m_next_call; + + private: + // Private static members + constexpr static uint32_t INTERVAL_MS = 60U * 1000U; private: // Private member objects diff --git a/include/display.hpp b/include/display.hpp index 8b2d0f6..afe153c 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -31,14 +31,15 @@ class Display { std::vector mergeJsonArraysFromTimestampHelper(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array_a, ArduinoJson::V710PB22::JsonArray p_array_b); + float mapPrecipitationToColorIndex(const float p_rain_intensity); std::tuple mapValueToColorRGB(float p_value); std::tuple mapValueToColorRGBW(float p_value); void setPixelToColorMappedValue(uint8_t index, float p_value); private: // Private static members - constexpr static uint16_t m_NUM_LEDS_PROTO_V1 = 29U; - constexpr static int16_t m_DATA_PIN_PROTO_V1 = 14; + constexpr static uint16_t NUM_LEDS_PROTO_V1 = 29U; + constexpr static int16_t DATA_PIN_PROTO_V1 = 14; private: // Private members diff --git a/include/sensor_manager.hpp b/include/sensor_manager.hpp index d1c39ed..bd1fd43 100644 --- a/include/sensor_manager.hpp +++ b/include/sensor_manager.hpp @@ -8,19 +8,19 @@ class SensorManager { // Public types struct SensorData { __typeof__(Bsec::iaq) iaq; - __typeof__(Bsec::iaqAccuracy) iaqAccuracy; - __typeof__(Bsec::staticIaq) staticIaq; - __typeof__(Bsec::co2Equivalent) co2Equivalent; - __typeof__(Bsec::breathVocEquivalent) breathVocEquivalent; - __typeof__(Bsec::rawTemperature) rawTemperature; + __typeof__(Bsec::iaqAccuracy) iaq_accuracy; + __typeof__(Bsec::staticIaq) static_iaq; + __typeof__(Bsec::co2Equivalent) co2_equivalent; + __typeof__(Bsec::breathVocEquivalent) breath_voc_equivalent; + __typeof__(Bsec::rawTemperature) raw_temperature; __typeof__(Bsec::pressure) pressure; - __typeof__(Bsec::rawHumidity) rawHumidity; - __typeof__(Bsec::gasResistance) gasResistance; - __typeof__(Bsec::stabStatus) stabStatus; - __typeof__(Bsec::runInStatus) runInStatus; + __typeof__(Bsec::rawHumidity) raw_humidity; + __typeof__(Bsec::gasResistance) gas_resistance; + __typeof__(Bsec::stabStatus) stab_status; + __typeof__(Bsec::runInStatus) run_in_status; __typeof__(Bsec::temperature) temperature; __typeof__(Bsec::humidity) humidity; - __typeof__(Bsec::gasPercentage) gasPercentage; + __typeof__(Bsec::gasPercentage) gas_percentage; }; public: @@ -40,16 +40,15 @@ class SensorManager { private: // Private static members - constexpr static uint16_t m_NUM_LEDS_PROTO_V1 = 29U; - constexpr static int16_t m_DATA_PIN_PROTO_V1 = 14; - constexpr static uint8_t m_BME_CS = 5U; - constexpr static uint8_t m_BSEC_CONFIG_IAQ[] = { + constexpr static uint8_t BME_CS = 5U; + constexpr static uint8_t BSEC_CONFIG_IAQ[] = { #include "config/generic_33v_3s_4d/bsec_iaq.txt" }; private: // Private members - Bsec m_iaqSensor; + Bsec m_iaq_sensor; + uint32_t m_next_call; }; #endif // __SENSOR_MANAGER_HPP__ diff --git a/src/backend_communication.cpp b/src/backend_communication.cpp index 0fd2061..ac4097e 100644 --- a/src/backend_communication.cpp +++ b/src/backend_communication.cpp @@ -3,25 +3,20 @@ #include "../include/backend_communication.hpp" #include "../include/wifi_handler.hpp" -bool isConfigValid = false; -String configTimestamp = ""; - -static HTTPClient http; - -JsonDocument weatherData; - BackendCommunication::BackendCommunication(WifiHandler& p_wifi_handler) : m_wifi_handler(p_wifi_handler) { return; } bool BackendCommunication::getConfig() { + HTTPClient http; + String mac = m_wifi_handler.getMac(false); String url = "https://target.luon.net/~admar/Claire/" + urlencode(mac) + "/config.json"; String payload = ""; - bool isSuccess = false; + bool is_success = false; http.begin(url, root_ca); - if (isConfigValid) { - http.addHeader("If-Modified-Since", configTimestamp); + if (m_is_config_valid) { + http.addHeader("If-Modified-Since", m_config_timestamp); } const char* headerKeys[] = {"Last-Modified"}; @@ -33,46 +28,69 @@ bool BackendCommunication::getConfig() { if (httpCode == 200) { // Content is in payload payload = http.getString(); - isSuccess = parseConfig(payload); + is_success = parseConfig(payload); - if (isSuccess) { - configTimestamp = http.header("Last-Modified"); + if (is_success) { + m_config_timestamp = http.header("Last-Modified"); } } else if (httpCode == 304) { // Content did not change - isSuccess = true; + is_success = true; } else { Serial.println(String(millis()) + " Got http code " + httpCode); } http.end(); - return isSuccess; + return is_success; } bool BackendCommunication::getWeatherData() { + HTTPClient http; + String url = "https://sinoptik.luon.net/forecast?address=" + urlencode(m_config["address"]) + "&metrics=precipitation&metrics=UVI&metrics=AQI&metrics=pollen"; String payload = ""; - bool isSuccess = false; + bool is_success = false; http.begin(url, root_ca); - int httpCode = http.GET(); + int http_code = http.GET(); - if (httpCode > 0) { + if (http_code > 0) { payload = http.getString(); - isSuccess = parseWeatherData(payload); + is_success = parseWeatherData(payload); } else { - Serial.println(String(millis()) + " Got http code " + httpCode); + Serial.println(String(millis()) + " Got http code " + http_code); } http.end(); - return isSuccess; + return is_success; +} + +BackendCommunication::State BackendCommunication::update() { + int32_t time = static_cast(millis() & 0x7FFFFFFF); + int32_t time_delta = m_next_call - time; + + if (time_delta > 0) { + return STILL_VALID; + } + + m_next_call = (time + INTERVAL_MS) & 0x7FFFFFFF; + + if (not getConfig()) { + return ERROR; + } + + if (not getWeatherData()) { + return ERROR; + } + + return UPDATED; } bool BackendCommunication::parseConfig(String& payload) { - bool isSuccess = false; + bool is_success = false; m_config.clear(); @@ -85,11 +103,11 @@ bool BackendCommunication::parseConfig(String& payload) { Serial.println(String(millis()) + " Unsupported config version"); } else { m_is_config_valid = true; - isSuccess = true; + is_success = true; } } - return isSuccess; + return is_success; } bool BackendCommunication::parseWeatherData(String& payload) { diff --git a/src/display.cpp b/src/display.cpp index f1daa10..b82d483 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -35,12 +35,12 @@ void Display::setPollenPlusAQIForecastLeds(uint32_t p_current_time, ArduinoJson: } void Display::setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson::V710PB22::JsonArray p_precipitation) { - float previous_value = 0.0f; + float previous_value = mapPrecipitationToColorIndex(0.0f); uint32_t index = 0U; for (JsonObject elem : p_precipitation) { uint32_t time = elem["time"].as(); - float value = elem["value"].as(); + float color_index = mapPrecipitationToColorIndex(elem["value"].as()); if ((index == 0U) and (time > p_current_time)) { setPixelToColorMappedValue(m_precipitation_forecast_map.at(index), previous_value); @@ -48,11 +48,11 @@ void Display::setPrecipitationForecastLeds(uint32_t p_current_time, ArduinoJson: } if (index > 0U) { - setPixelToColorMappedValue(m_precipitation_forecast_map.at(index), value); + setPixelToColorMappedValue(m_precipitation_forecast_map.at(index), color_index); index += 1U; } - previous_value = value; + previous_value = color_index; if (index >= m_precipitation_forecast_map.size()) { break; @@ -134,13 +134,18 @@ void Display::show() { float Display::findMaxValueInTwelveHourInterval(uint32_t p_time_stamp, ArduinoJson::V710PB22::JsonArray p_array) { float max_value = 0.0f; + float previous_value = 0.0f; for (auto elem : p_array) { uint32_t time = elem["time"].as(); + float value = elem["value"].as(); constexpr uint32_t TWELVE_HOURS_IN_SECONDS = 12U * 60U * 60U; + if ((time >= p_time_stamp) and (time < p_time_stamp + TWELVE_HOURS_IN_SECONDS)) { - max_value = std::max(max_value, elem["value"].as()); + max_value = std::max(max_value, previous_value); } + + previous_value = value; } return max_value; @@ -217,6 +222,38 @@ std::vector Display::mergeJsonArraysFromTimestampHelper(uint32_t p_time_s return merged_vector; } +float Display::mapPrecipitationToColorIndex(const float p_rain_intensity) { + float color_index = 0.0f; + + if (p_rain_intensity < 0.1f) { + color_index = 0.0f; + } else if (p_rain_intensity < 0.22f) { + color_index = 1.0f; + } else if (p_rain_intensity < 0.47f) { + color_index = 2.0f; + } else if (p_rain_intensity < 1.0f) { + color_index = 3.0f; + } else if (p_rain_intensity < 2.2f) { + color_index = 4.0f; + } else if (p_rain_intensity < 4.7f) { + color_index = 5.0f; + } else if (p_rain_intensity < 10.0f) { + color_index = 6.0f; + } else if (p_rain_intensity < 22.0f) { + color_index = 7.0f; + } else if (p_rain_intensity < 47.0f) { + color_index = 8.0f; + } else if (p_rain_intensity < 100.0f) { + color_index = 9.0f; + } else if (p_rain_intensity < 220.0f) { + color_index = 10.0f; + } else { + color_index = 11.0f; + } + + return color_index; +} + std::tuple Display::mapValueToColorRGB(float p_value) { if (p_value < 1.0) { return {0, 0, 0}; // black (good) diff --git a/src/main.cpp b/src/main.cpp index 867ebd7..5e71dd3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,16 +83,13 @@ void setup() { } void loop(void) { - uint32_t time_start = millis(); + sensor_manager.read(); + display.setLocalAirQualityLed(sensor_manager.data.static_iaq); - backend_communication.getConfig(); - - if (not backend_communication.m_is_config_valid) { + if (backend_communication.update() == BackendCommunication::State::ERROR) { display.setErrorLed(true); - Serial.println(String("Uptime: " + String(millis())) + ". Error: invalid config"); + Serial.println("Uptime: " + String(millis()) + ", backend error"); } else { - backend_communication.getWeatherData(); - uint32_t utc_time = backend_communication.m_weather_data["time"].as(); Serial.println("Uptime: " + String(millis()) + ", UTC time: " + String(utc_time)); @@ -104,26 +101,12 @@ void loop(void) { display.setUVIForecastLed(utc_time, backend_communication.m_weather_data["UVI"].as()); - display.setLocalAirQualityLed(sensor_manager.data.staticIaq); - display.setWifiStatusLed(false); } - sensor_manager.read(); - display.show(); + dESPatch.checkForUpdate(true); - uint32_t time_end = millis(); - uint32_t time_delta = time_end - time_start; - - uint32_t delay_time = 0U; - constexpr uint32_t ONE_SECOND_IN_MILLISECONDS = 1000U; - constexpr uint32_t INTERVAL = 60U * ONE_SECOND_IN_MILLISECONDS; - - if (time_delta < INTERVAL) { - delay_time = INTERVAL - time_delta; - } - - delay(delay_time); + delay(1000U); } diff --git a/src/sensor_manager.cpp b/src/sensor_manager.cpp index 3025630..0740c25 100644 --- a/src/sensor_manager.cpp +++ b/src/sensor_manager.cpp @@ -9,18 +9,18 @@ SensorManager::SensorManager() { bool SensorManager::setup() { SPI.begin(); - m_iaqSensor.begin(m_BME_CS, SPI); - String output = "BSEC library version " + String(m_iaqSensor.version.major) + "." + - String(m_iaqSensor.version.minor) + "." + String(m_iaqSensor.version.major_bugfix) + "." + - String(m_iaqSensor.version.minor_bugfix); + m_iaq_sensor.begin(BME_CS, SPI); + String output = "BSEC library version " + String(m_iaq_sensor.version.major) + "." + + String(m_iaq_sensor.version.minor) + "." + String(m_iaq_sensor.version.major_bugfix) + "." + + String(m_iaq_sensor.version.minor_bugfix); Serial.println(output); if (not checkIaqSensorStatus()) { return false; } - m_iaqSensor.setConfig(m_BSEC_CONFIG_IAQ); - m_iaqSensor.setTemperatureOffset(13.5f); + m_iaq_sensor.setConfig(BSEC_CONFIG_IAQ); + m_iaq_sensor.setTemperatureOffset(13.5f); if (not checkIaqSensorStatus()) { return false; @@ -40,7 +40,7 @@ bool SensorManager::setup() { BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, BSEC_OUTPUT_GAS_PERCENTAGE}; - m_iaqSensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP); + m_iaq_sensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP); if (checkIaqSensorStatus()) { return false; @@ -50,47 +50,60 @@ bool SensorManager::setup() { } bool SensorManager::read() { - if (m_iaqSensor.run()) { // If new data is available - data.iaq = m_iaqSensor.iaq; - data.iaqAccuracy = m_iaqSensor.iaqAccuracy; - data.staticIaq = m_iaqSensor.staticIaq; - data.co2Equivalent = m_iaqSensor.co2Equivalent; - data.breathVocEquivalent = m_iaqSensor.breathVocEquivalent; - data.rawTemperature = m_iaqSensor.rawTemperature; - data.pressure = m_iaqSensor.pressure; - data.rawHumidity = m_iaqSensor.rawHumidity; - data.gasResistance = m_iaqSensor.gasResistance; - data.stabStatus = m_iaqSensor.stabStatus; - data.runInStatus = m_iaqSensor.runInStatus; - data.temperature = m_iaqSensor.temperature; - data.humidity = m_iaqSensor.humidity; - data.gasPercentage = m_iaqSensor.gasPercentage; - } else { - Serial.println("sensor status: " + String(checkIaqSensorStatus())); + uint32_t time = millis(); + + if ((m_next_call == 0) or (time < m_next_call)) { + return false; + } + + bool is_run_result_valid = m_iaq_sensor.run(); + bool is_sensor_status_valid = checkIaqSensorStatus(); + + if (is_run_result_valid and is_sensor_status_valid) { // If new data is available + data.iaq = m_iaq_sensor.iaq; + data.iaq_accuracy = m_iaq_sensor.iaqAccuracy; + data.static_iaq = m_iaq_sensor.staticIaq; + data.co2_equivalent = m_iaq_sensor.co2Equivalent; + data.breath_voc_equivalent = m_iaq_sensor.breathVocEquivalent; + data.raw_temperature = m_iaq_sensor.rawTemperature; + data.pressure = m_iaq_sensor.pressure; + data.raw_humidity = m_iaq_sensor.rawHumidity; + data.gas_resistance = m_iaq_sensor.gasResistance; + data.stab_status = m_iaq_sensor.stabStatus; + data.run_in_status = m_iaq_sensor.runInStatus; + data.temperature = m_iaq_sensor.temperature; + data.humidity = m_iaq_sensor.humidity; + data.gas_percentage = m_iaq_sensor.gasPercentage; + + m_next_call = static_cast(m_iaq_sensor.nextCall & 0xFFFFFFFF); + + Serial.println("Next call: " + String(m_next_call)); + return true; + } else { + return false; } - return true; } bool SensorManager::checkIaqSensorStatus() { - if (m_iaqSensor.bsecStatus != BSEC_OK) { - if (m_iaqSensor.bsecStatus < BSEC_OK) { - Serial.println("BSEC error code: " + String(m_iaqSensor.bsecStatus)); + if (m_iaq_sensor.bsecStatus != BSEC_OK) { + if (m_iaq_sensor.bsecStatus < BSEC_OK) { + Serial.println("BSEC error code: " + String(m_iaq_sensor.bsecStatus)); return false; } else { - Serial.println("BSEC warning code: " + String(m_iaqSensor.bsecStatus)); + Serial.println("BSEC warning code: " + String(m_iaq_sensor.bsecStatus)); return false; } } - if (m_iaqSensor.bme68xStatus != BME68X_OK) { - if (m_iaqSensor.bme68xStatus < BME68X_OK) { - Serial.println("BME68X error code: " + String(m_iaqSensor.bme68xStatus)); + if (m_iaq_sensor.bme68xStatus != BME68X_OK) { + if (m_iaq_sensor.bme68xStatus < BME68X_OK) { + Serial.println("BME68X error code: " + String(m_iaq_sensor.bme68xStatus)); return false; } else { - Serial.println("BME68X warning code: " + String(m_iaqSensor.bme68xStatus)); + Serial.println("BME68X warning code: " + String(m_iaq_sensor.bme68xStatus)); return false; } From f22d4c3d809f727b694d920ff39fb06696073dd4 Mon Sep 17 00:00:00 2001 From: admar Date: Tue, 24 Sep 2024 11:27:59 +0200 Subject: [PATCH 13/13] Added color-preserving dimming of display --- .vscode/settings.json | 49 ++++- include/color_conversion.hpp | 24 +++ include/display.hpp | 2 + src/color_conversion.cpp | 248 ++++++++++++++++++++++++++ src/display.cpp | 18 +- src/main.cpp | 2 + test/cieluv.cpp | 335 +++++++++++++++++++++++++++++++++++ 7 files changed, 676 insertions(+), 2 deletions(-) create mode 100644 include/color_conversion.hpp create mode 100644 src/color_conversion.cpp create mode 100644 test/cieluv.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index a478c1f..b72cb1c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,52 @@ { "files.associations": { - "string_view": "cpp" + "string_view": "cpp", + "array": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/include/color_conversion.hpp b/include/color_conversion.hpp new file mode 100644 index 0000000..d8a2e38 --- /dev/null +++ b/include/color_conversion.hpp @@ -0,0 +1,24 @@ +#ifndef __COLOR_CONVERSION_HPP__ +#define __COLOR_CONVERSION_HPP__ + +#include +#include + +std::tuple rgb_to_xyz(uint8_t p_red, uint8_t p_green, uint8_t p_blue); +std::tuple xyz_to_rgb(float p_x, float p_y, float p_z); + +std::tuple xyz_to_cieluv(float p_x, float p_y, float p_z); +std::tuple cieluv_to_xyz(float p_l_star, float p_u_star, float p_v_star); + +std::tuple cieluv_to_cielchuv(float p_l_star, float p_u_star, float p_v_star); +std::tuple cielchuv_to_cieluv(float p_l_star, float p_c_star, float p_h_star); + +bool is_xyz_in_rgb_range(float p_x, float p_y, float p_z); +bool is_cieluv_in_rgb_range(float p_l_star, float p_u_star, float p_v_star); +bool is_cielchuv_in_rgb_range(float p_l_star, float p_c_star, float p_h_star); + +std::tuple map_cielchuv_to_visible_color(float p_l_star, float p_c_star, float p_h_star); + +std::tuple rgb_to_cielchuv(uint8_t p_red, uint8_t p_green, uint8_t p_blue); +std::tuple cielchuv_to_rgb(float p_l_star, float p_c_star, float p_h_star); +#endif // __COLOR_CONVERSION_HPP__ \ No newline at end of file diff --git a/include/display.hpp b/include/display.hpp index afe153c..c6c259b 100644 --- a/include/display.hpp +++ b/include/display.hpp @@ -20,6 +20,7 @@ class Display { void setWifiStatusLed(bool p_is_connected); void setErrorLed(bool p_is_error); void setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, uint8_t p_blue, uint8_t p_white); + void setBrightness(float p_brightness); void clear(); void show(); @@ -51,6 +52,7 @@ class Display { uint8_t m_uvi_map; uint8_t m_local_air_quality_map; uint8_t m_wifi_status_map; + float m_brightness; }; #endif // __DISPLAY_HPP__ \ No newline at end of file diff --git a/src/color_conversion.cpp b/src/color_conversion.cpp new file mode 100644 index 0000000..25d9227 --- /dev/null +++ b/src/color_conversion.cpp @@ -0,0 +1,248 @@ +#include +#include + +#include "../include/color_conversion.hpp" + +std::tuple rgb_to_xyz(const uint8_t p_red, const uint8_t p_green, const uint8_t p_blue) { + auto normalize_uint8_t = [](const uint8_t p_value) -> float { + return std::min(static_cast(p_value) / 255.0F, 1.0F); + }; + + auto gamma_correction = [](const float p_value) -> float { + float gamma_corrected_value = 0.0F; + + if (p_value <= 0.0405F) { + gamma_corrected_value = p_value / 12.92F; + } else { + gamma_corrected_value = std::pow((p_value + 0.055F) / 1.055F, 2.4F); + } + + return gamma_corrected_value; + }; + + float red = gamma_correction(normalize_uint8_t(p_red)); + float green = gamma_correction(normalize_uint8_t(p_green)); + float blue = gamma_correction(normalize_uint8_t(p_blue)); + + float x = 0.4124564F * red + 0.3575761F * green + 0.1804375F * blue; + float y = 0.2126729F * red + 0.7151522F * green + 0.0721750F * blue; + float z = 0.0193339F * red + 0.1191920F * green + 0.9503041F * blue; + + return {x * 100.0F, y * 100.0F, z * 100.0F}; +} + +std::tuple xyz_to_rgb_float(float p_x, float p_y, float p_z) { + auto reverse_gamma_correction = [](const float p_value) -> float { + float reverse_gamma_corrected_value = 0.0F; + + if (p_value <= 0.0031308F) { + reverse_gamma_corrected_value = p_value * 12.92F; + } else { + reverse_gamma_corrected_value = std::pow(p_value, 1.0F / 2.4F) * 1.055F - 0.055F; + } + + return reverse_gamma_corrected_value; + }; + + auto clamp_if_within_error_margin = [](const float p_value) -> float { + float value = p_value; + constexpr float MARGIN = 0.001F; + + if ((p_value >= -MARGIN) and (p_value < 0.0F)) { + value = 0.0F; + } else if ((p_value <= 1.0F + MARGIN) and (p_value > 1.0F)) { + value = 1.0F; + } + + return value; + }; + + float red = 3.2404542F * p_x / 100.0F - 1.5371385F * p_y / 100.0F - 0.4985314F * p_z / 100.0F; + float green = -0.9692660F * p_x / 100.0F + 1.8760108F * p_y / 100.0F + 0.0415560F * p_z / 100.0F; + float blue = 0.0556434F * p_x / 100.0F - 0.2040259F * p_y / 100.0F + 1.0572252F * p_z / 100.0F; + + red = clamp_if_within_error_margin(reverse_gamma_correction(red)); + green = clamp_if_within_error_margin(reverse_gamma_correction(green)); + blue = clamp_if_within_error_margin(reverse_gamma_correction(blue)); + + return {red, green, blue}; +} + +std::tuple xyz_to_rgb(float p_x, float p_y, float p_z) { + auto uint8_t_scale = [](const float p_value) -> uint8_t { + return static_cast(std::clamp(p_value, 0.0F, 1.0F) * 255.0F + 0.5F); + }; + + auto [red, green, blue] = xyz_to_rgb_float(p_x, p_y, p_z); + + uint8_t red_uint8_t = uint8_t_scale(red); + uint8_t green_uint8_t = uint8_t_scale(green); + uint8_t blue_uint8_t = uint8_t_scale(blue); + + return {red_uint8_t, green_uint8_t, blue_uint8_t}; +} + +std::tuple xyz_to_uv(const float p_x, const float p_y, const float p_z) { + float denominator = (p_x + 15.0F * p_y + 3.0F * p_z); + + if (denominator == 0.0F) { + denominator = 1.0F; + } + + float u = (4.0F * p_x) / denominator; + float v = (9.0F * p_y) / denominator; + + return {u, v}; +} + +std::tuple uv_to_xy(const float p_u, const float p_v) { + float denominator = 6.0F * p_u - 16.0F * p_v + 12.0F; + + if (denominator == 0.0F) { + denominator = 1.0F; + } + + float x = 9.0F * p_u / denominator; + float y = 4.0F * p_v / denominator; + + return {x, y}; +} + +std::tuple xyz_d65_reference_white_point() { + // Standard D64 reference white point values + constexpr float X_N = 95.047F; + constexpr float Y_N = 100.0F; + constexpr float Z_N = 108.833F; + + return {X_N, Y_N, Z_N}; +} + +std::tuple xyz_to_cieluv(const float p_x, const float p_y, const float p_z) { + auto [X_N, Y_N, Z_N] = xyz_d65_reference_white_point(); + auto [U_N, V_N] = xyz_to_uv(X_N, Y_N, Z_N); + + auto [u, v] = xyz_to_uv(p_x, p_y, p_z); + + float l_star = 0.0F; + + if (p_y == 0.0F) { + l_star = 0.0F; + } else if ((p_y / Y_N) > 0.0088565F) { + l_star = 116.0F * std::cbrt(p_y / Y_N) - 16.0F; + } else { + l_star = p_y / Y_N * 903.3F; + } + + float u_star = 13.0F * l_star * (u - U_N); + float v_star = 13.0F * l_star * (v - V_N); + + return {l_star, u_star, v_star}; +} + +std::tuple cieluv_to_xyz(float p_l_star, float p_u_star, float p_v_star) { + auto [X_N, Y_N, Z_N] = xyz_d65_reference_white_point(); + auto [U_N, V_N] = xyz_to_uv(X_N, Y_N, Z_N); + + float denominator = 13.0F * p_l_star; + + if (denominator == 0.0F) { + denominator = 13.0F; + } + + float u = p_u_star / denominator + U_N; + float v = p_v_star / denominator + V_N; + + float y = 0.0F; + if (p_l_star <= 8.0F) { + y = Y_N * p_l_star * 0.0011071F; + } else { + y = Y_N * std::pow((p_l_star + 16.F) / 116.0F, 3.0F); + } + + if (v == 0.0F) { + denominator = 4.0F; + } else { + denominator = 4.0F * v; + } + float x = y * 9.0F * u / denominator; + float z = y * (12.0F - 3.0F * u - 20.0F * v) / denominator; + + return {x, y, z}; +} + +std::tuple cieluv_to_cielchuv(float p_l_star, float p_u_star, float p_v_star) { + float c_star = std::sqrt(p_u_star * p_u_star + p_v_star * p_v_star); + float h_star = std::atan2(p_v_star, p_u_star); + + if (h_star < 0.0F) { + h_star += 2.0F * M_PI; + } + + return {p_l_star, c_star, h_star}; +} + +std::tuple cielchuv_to_cieluv(float p_l_star, float p_c_star, float p_h_star) { + float u_star = p_c_star * std::cos(p_h_star); + float v_star = p_c_star * std::sin(p_h_star); + + return {p_l_star, u_star, v_star}; +} + +bool is_xyz_in_rgb_range(float p_x, float p_y, float p_z) { + auto is_in_range_inclusive = [](const float p_value, const float p_min, const float p_max) -> bool { + return (p_value >= p_min and p_value <= p_max); + }; + + auto [red, green, blue] = xyz_to_rgb_float(p_x, p_y, p_z); + + bool is_red_in_range = is_in_range_inclusive(red, 0.0F, 1.0F); + bool is_green_in_range = is_in_range_inclusive(green, 0.0F, 1.0F); + bool is_blue_in_range = is_in_range_inclusive(blue, 0.0F, 1.0F); + + return is_red_in_range and is_green_in_range and is_blue_in_range; +} + +bool is_cieluv_in_rgb_range(float p_l_star, float p_u_star, float p_v_star) { + auto [x, y, z] = cieluv_to_xyz(p_l_star, p_u_star, p_v_star); + + return is_xyz_in_rgb_range(x, y, z); +} + +bool is_cielchuv_in_rgb_range(float p_l_star, float p_c_star, float p_h_star) { + auto [l, u, v] = cielchuv_to_cieluv(p_l_star, p_c_star, p_h_star); + + return is_cieluv_in_rgb_range(l, u, v); +} + +std::tuple map_cielchuv_to_visible_color(float p_l_star, float p_c_star, float p_h_star) { + float c_star = p_c_star; + uint32_t count = 0U; + + while (not is_cielchuv_in_rgb_range(p_l_star, c_star, p_h_star)) { + c_star = c_star * 0.99F; + count = count + 1U; + + if (count > 1000U) { + c_star = 0.0F; + break; + } + } + + return {p_l_star, c_star, p_h_star}; +} + +std::tuple rgb_to_cielchuv(uint8_t p_red, uint8_t p_green, uint8_t p_blue) { + auto [x, y, z] = rgb_to_xyz(p_red, p_green, p_blue); + auto [l, u, v] = xyz_to_cieluv(x, y, z); + auto [l_star, c_star, h_star] = cieluv_to_cielchuv(l, u, v); + + return {l_star, c_star, h_star}; +} + +std::tuple cielchuv_to_rgb(float p_l_star, float p_c_star, float p_h_star) { + auto [l, u, v] = cielchuv_to_cieluv(p_l_star, p_c_star, p_h_star); + auto [x, y, z] = cieluv_to_xyz(l, u, v); + auto [red, green, blue] = xyz_to_rgb(x, y, z); + + return {red, green, blue}; +} diff --git a/src/display.cpp b/src/display.cpp index b82d483..a3b5fb5 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,3 +1,4 @@ +#include "../include/color_conversion.hpp" #include "../include/display.hpp" Display::Display(Adafruit_NeoPixel& p_leds) : m_leds(p_leds) { @@ -8,6 +9,7 @@ Display::Display(Adafruit_NeoPixel& p_leds) : m_leds(p_leds) { m_uvi_map = 27; m_local_air_quality_map = 25; m_wifi_status_map = 28; + m_brightness = 0.2F; return; } @@ -122,6 +124,11 @@ void Display::setPixelColor(uint32_t p_index, uint8_t p_red, uint8_t p_green, ui m_leds.setPixelColor(p_index, m_leds.Color(p_red, p_green, p_blue, p_white)); } +void Display::setBrightness(float p_brightness) { + m_brightness = p_brightness; + return; +} + void Display::clear() { m_leds.clear(); return; @@ -308,5 +315,14 @@ std::tuple Display::mapValueToColorRGBW(floa void Display::setPixelToColorMappedValue(uint8_t p_index, float p_value) { auto [red, green, blue, white] = mapValueToColorRGBW(p_value); - m_leds.setPixelColor(p_index, m_leds.Color(red, green, blue, white)); + + auto [l_star, c_star, h_star] = rgb_to_cielchuv(red, green, blue); + + l_star = l_star * m_brightness; + + auto [l_star_dimmed, c_star_dimmed, h_star_dimmed] = map_cielchuv_to_visible_color(l_star, c_star, h_star); + + auto [red_dimmed, green_dimmed, blue_dimmed] = cielchuv_to_rgb(l_star_dimmed, c_star_dimmed, h_star_dimmed); + + m_leds.setPixelColor(p_index, m_leds.Color(red_dimmed, green_dimmed, blue_dimmed, white)); } diff --git a/src/main.cpp b/src/main.cpp index 5e71dd3..b2a6929 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -102,6 +102,8 @@ void loop(void) { display.setUVIForecastLed(utc_time, backend_communication.m_weather_data["UVI"].as()); display.setWifiStatusLed(false); + + display.setBrightness(backend_communication.m_config["brightness"].as()); } display.show(); diff --git a/test/cieluv.cpp b/test/cieluv.cpp new file mode 100644 index 0000000..b4b2d4b --- /dev/null +++ b/test/cieluv.cpp @@ -0,0 +1,335 @@ +#include +#include +#include +#include +#include +#include + +std::tuple rgb_to_xyz(const uint8_t p_red, const uint8_t p_green, const uint8_t p_blue) { + auto normalize_uint8_t = [](const uint8_t p_value) -> float { + return std::min(static_cast(p_value) / 255.0F, 1.0F); + }; + + auto gamma_correction = [](const float p_value) -> float { + float gamma_corrected_value = 0.0F; + + if (p_value <= 0.0405F) { + gamma_corrected_value = p_value / 12.92F; + } else { + gamma_corrected_value = std::pow((p_value + 0.055F) / 1.055F, 2.4F); + } + + return gamma_corrected_value; + }; + + float red = gamma_correction(normalize_uint8_t(p_red)); + float green = gamma_correction(normalize_uint8_t(p_green)); + float blue = gamma_correction(normalize_uint8_t(p_blue)); + + float x = 0.4124564F * red + 0.3575761F * green + 0.1804375F * blue; + float y = 0.2126729F * red + 0.7151522F * green + 0.0721750F * blue; + float z = 0.0193339F * red + 0.1191920F * green + 0.9503041F * blue; + + return {x * 100.0F, y * 100.0F, z * 100.0F}; +} + +std::tuple xyz_to_rgb_float(float p_x, float p_y, float p_z) { + auto reverse_gamma_correction = [](const float p_value) -> float { + float reverse_gamma_corrected_value = 0.0F; + + if (p_value <= 0.0031308F) { + reverse_gamma_corrected_value = p_value * 12.92F; + } else { + reverse_gamma_corrected_value = std::pow(p_value, 1.0F / 2.4F) * 1.055F - 0.055F; + } + + return reverse_gamma_corrected_value; + }; + + auto clamp_if_within_error_margin = [](const float p_value) -> float { + float value = p_value; + constexpr float MARGIN = 0.001F; + + if ((p_value >= -MARGIN) and (p_value < 0.0F)) { + value = 0.0F; + } else if ((p_value <= 1.0F + MARGIN) and (p_value > 1.0F)) { + value = 1.0F; + } + + return value; + }; + + float red = 3.2404542F * p_x / 100.0F - 1.5371385F * p_y / 100.0F - 0.4985314F * p_z / 100.0F; + float green = -0.9692660F * p_x / 100.0F + 1.8760108F * p_y / 100.0F + 0.0415560F * p_z / 100.0F; + float blue = 0.0556434F * p_x / 100.0F - 0.2040259F * p_y / 100.0F + 1.0572252F * p_z / 100.0F; + + red = clamp_if_within_error_margin(reverse_gamma_correction(red)); + green = clamp_if_within_error_margin(reverse_gamma_correction(green)); + blue = clamp_if_within_error_margin(reverse_gamma_correction(blue)); + + return {red, green, blue}; +} + +std::tuple xyz_to_rgb(float p_x, float p_y, float p_z) { + auto uint8_t_scale = [](const float p_value) -> uint8_t { + return static_cast(std::clamp(p_value, 0.0F, 1.0F) * 255.0F + 0.5F); + }; + + auto [red, green, blue] = xyz_to_rgb_float(p_x, p_y, p_z); + + uint8_t red_uint8_t = uint8_t_scale(red); + uint8_t green_uint8_t = uint8_t_scale(green); + uint8_t blue_uint8_t = uint8_t_scale(blue); + + return {red_uint8_t, green_uint8_t, blue_uint8_t}; +} + +std::tuple xyz_to_uv(const float p_x, const float p_y, const float p_z) { + float denominator = (p_x + 15.0F * p_y + 3.0F * p_z); + + if (denominator == 0.0F) { + denominator = 1.0F; + } + + float u = (4.0F * p_x) / denominator; + float v = (9.0F * p_y) / denominator; + + return {u, v}; +} + +std::tuple uv_to_xy(const float p_u, const float p_v) { + float denominator = 6.0F * p_u - 16.0F * p_v + 12.0F; + + if (denominator == 0.0F) { + denominator = 1.0F; + } + + float x = 9.0F * p_u / denominator; + float y = 4.0F * p_v / denominator; + + return {x, y}; +} + +std::tuple xyz_d65_reference_white_point() { + // Standard D64 reference white point values + constexpr float X_N = 95.047F; + constexpr float Y_N = 100.0F; + constexpr float Z_N = 108.833F; + + return {X_N, Y_N, Z_N}; +} + +std::tuple xyz_to_cieluv(const float p_x, const float p_y, const float p_z) { + auto [X_N, Y_N, Z_N] = xyz_d65_reference_white_point(); + auto [U_N, V_N] = xyz_to_uv(X_N, Y_N, Z_N); + + auto [u, v] = xyz_to_uv(p_x, p_y, p_z); + + float l_star = 0.0F; + + if (p_y == 0.0F) { + l_star = 0.0F; + } else if ((p_y / Y_N) > 0.0088565F) { + l_star = 116.0F * std::cbrt(p_y / Y_N) - 16.0F; + } else { + l_star = p_y / Y_N * 903.3F; + } + + float u_star = 13.0F * l_star * (u - U_N); + float v_star = 13.0F * l_star * (v - V_N); + + return {l_star, u_star, v_star}; +} + +std::tuple cieluv_to_xyz(float p_l_star, float p_u_star, float p_v_star) { + auto [X_N, Y_N, Z_N] = xyz_d65_reference_white_point(); + auto [U_N, V_N] = xyz_to_uv(X_N, Y_N, Z_N); + + float denominator = 13.0F * p_l_star; + + if (denominator == 0.0F) { + denominator = 13.0F; + } + + float u = p_u_star / denominator + U_N; + float v = p_v_star / denominator + V_N; + + // auto [x, y] = uv_to_xy(u, v); + + float y = 0.0F; + if (p_l_star <= 8.0F) { + y = Y_N * p_l_star * 0.0011071F; + } else { + y = Y_N * std::pow((p_l_star + 16.F) / 116.0F, 3.0F); + } + + if (v == 0.0F) { + denominator = 4.0F; + } else { + denominator = 4.0F * v; + } + float x = y * 9.0F * u / denominator; + float z = y * (12.0F - 3.0F * u - 20.0F * v) / denominator; + + return {x, y, z}; +} + +std::tuple cieluv_to_cielchuv(float p_l_star, float p_u_star, float p_v_star) { + float c_star = std::sqrt(p_u_star * p_u_star + p_v_star * p_v_star); + float h_star = std::atan2(p_v_star, p_u_star); + + if (h_star < 0.0F) { + h_star += 2.0F * M_PI; + } + + return {p_l_star, c_star, h_star}; +} + +std::tuple cielchuv_to_cieluv(float p_l_star, float p_c_star, float p_h_star) { + float u_star = p_c_star * std::cos(p_h_star); + float v_star = p_c_star * std::sin(p_h_star); + + return {p_l_star, u_star, v_star}; +} + +bool is_xyz_in_rgb_range(float p_x, float p_y, float p_z) { + auto is_in_range_inclusive = [](const float p_value, const float p_min, const float p_max) -> bool { + return (p_value >= p_min and p_value <= p_max); + }; + + auto [red, green, blue] = xyz_to_rgb_float(p_x, p_y, p_z); + + bool is_red_in_range = is_in_range_inclusive(red, 0.0F, 1.0F); + bool is_green_in_range = is_in_range_inclusive(green, 0.0F, 1.0F); + bool is_blue_in_range = is_in_range_inclusive(blue, 0.0F, 1.0F); + + return is_red_in_range and is_green_in_range and is_blue_in_range; +} + +bool is_cieluv_in_rgb_range(float p_l_star, float p_u_star, float p_v_star) { + auto [x, y, z] = cieluv_to_xyz(p_l_star, p_u_star, p_v_star); + + return is_xyz_in_rgb_range(x, y, z); +} + +bool is_cielchuv_in_rgb_range(float p_l_star, float p_c_star, float p_h_star) { + auto [l, u, v] = cielchuv_to_cieluv(p_l_star, p_c_star, p_h_star); + + return is_cieluv_in_rgb_range(l, u, v); +} + +std::tuple map_cielchuv_to_visible_color(float p_l_star, float p_c_star, float p_h_star) { + float c_star = p_c_star; + uint32_t count = 0U; + + while (not is_cielchuv_in_rgb_range(p_l_star, c_star, p_h_star)) { + c_star = c_star * 0.99F; + count = count + 1U; + + if (count > 1000U) { + c_star = 0.0F; + std::cout << "breaky breaky!" << std::endl; + break; + } + } + + return {p_l_star, c_star, p_h_star}; +} + +std::tuple rgb_to_cielchuv(uint8_t p_red, uint8_t p_green, uint8_t p_blue) { + auto [x, y, z] = rgb_to_xyz(p_red, p_green, p_blue); + auto [l, u, v] = xyz_to_cieluv(x, y, z); + auto [l_star, c_star, h_star] = cieluv_to_cielchuv(l, u, v); + + return {l_star, c_star, h_star}; +} + +std::tuple cielchuv_to_rgb(float p_l_star, float p_c_star, float p_h_star) { + auto [l, u, v] = cielchuv_to_cieluv(p_l_star, p_c_star, p_h_star); + auto [x, y, z] = cieluv_to_xyz(l, u, v); + auto [red, green, blue] = xyz_to_rgb(x, y, z); + + return {red, green, blue}; +} + +int main(void) { + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + + for (r = 0; r < 192; r = r + 64) { + for (g = 0; g < 192; g = g + 64) { + for (b = 0; b < 192; b = b + 64) { + auto [x, y, z] = rgb_to_xyz(r, g, b); + auto [l, u, v] = xyz_to_cieluv(x, y, z); + auto [x2, y2, z2] = cieluv_to_xyz(l, u, v); + auto [r2, g2, b2] = xyz_to_rgb(x2, y2, z2); + + std::cout << "RGB: [" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + "]" + + " --> XYZ: [" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + "]" + + " --> LUV: [" + std::to_string(l) + ", " + std::to_string(u) + ", " + std::to_string(v) + "]" + + " --> XYZ: [" + std::to_string(x2) + ", " + std::to_string(y2) + ", " + std::to_string(z2) + "]" + + " --> RGB: [" + std::to_string(r2) + ", " + std::to_string(g2) + ", " + std::to_string(b2) + "]" + + " --> E: [" + std::to_string(r - r2) + ", " + std::to_string(g - g2) + ", " + std::to_string(b - b2) + "]" << std::endl; + } + } + } + + std::cout << std::endl; + + for (r = 0; r < 192; r = r + 64) { + for (g = 0; g < 192; g = g + 64) { + for (b = 0; b < 192; b = b + 64) { + auto [x, y, z] = rgb_to_xyz(r, g, b); + auto [l, u, v] = xyz_to_cieluv(x, y, z); + auto [l_, c, h] = cieluv_to_cielchuv(l, u, v); + + auto [l2, c2, h2] = map_cielchuv_to_visible_color(l / 2.0F, c, h); + + auto [l2_, u2, v2] = cielchuv_to_cieluv(l2, c2, h2); + auto [x2, y2, z2] = cieluv_to_xyz(l2, u2, v2); + auto [r2_f, g2_f, b2_f] = xyz_to_rgb_float(x2, y2, z2); + auto [r2, g2, b2] = xyz_to_rgb(x2, y2, z2); + + std::cout << "RGB: [" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + "]" + + " --> XYZ: [" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + "]" + + " --> LUV: [" + std::to_string(l) + ", " + std::to_string(u) + ", " + std::to_string(v) + "]" + + " --> LCH: [" + std::to_string(l) + ", " + std::to_string(c) + ", " + std::to_string(h) + "]" + + " --> / 2: [" + std::to_string(l2) + ", " + std::to_string(c2) + ", " + std::to_string(h2) + "]" + + "; visible: " + std::to_string(is_cielchuv_in_rgb_range(l / 2.0F, c, h)) + + " / " + std::to_string(is_cielchuv_in_rgb_range(l2, c2, h2)) + + " --> LUV: [" + std::to_string(l2) + ", " + std::to_string(u2) + ", " + std::to_string(v2) + "]" + + " --> XYZ: [" + std::to_string(x2) + ", " + std::to_string(y2) + ", " + std::to_string(z2) + "]" + + " --> RGB: [" + std::to_string(r2_f) + ", " + std::to_string(g2_f) + ", " + std::to_string(b2_f) + "]" + + " --> RGB: [" + std::to_string(r2) + ", " + std::to_string(g2) + ", " + std::to_string(b2) + "]" << std::endl; + } + } + } + + // std::cout << std::endl; + + // float x = 0.925094F; + // float y = 0.370037F; + // float z = 4.872159F; + // auto [red, green, blue] = xyz_to_rgb_float(x, y, z); + // std::cout << std::to_string(red) + " " + std::to_string(green) + " " + std::to_string(blue) + " " + std::to_string(is_xyz_in_rgb_range(x, y, z)) << std::endl; + + // float x2 = 0.740333F; + // float y2 = 0.185027F; + // float z2 = 0.031254F; + // auto [red2, green2, blue2] = xyz_to_rgb_float(x2, y2, z2); + // std::cout << std::to_string(red2) + " " + std::to_string(green2) + " " + std::to_string(blue2) + " " + std::to_string(is_xyz_in_rgb_range(x2, y2, z2)) << std::endl; + + // // float L = 3.342548F; + // float L = 1.671274F; + // float C = 13.526361F; + // float H = 4.640314F; + // + // auto [L_, U, V] = cielchuv_to_cieluv(L, C, H); + // auto [X, Y, Z] = cieluv_to_xyz(L_, U, V); + // auto [R, G, B] = xyz_to_rgb_float(X, Y, Z); + // std::cout << "RGB: " << std::to_string(R) + " " + std::to_string(G) + " " + std::to_string(B) + + // ", XYZ: " + std::to_string(X) + " " + std::to_string(Y) + " " + std::to_string(Z) + + // ", LUV: " + std::to_string(L_) + " " + std::to_string(U) + " " + std::to_string(V) + + // " " + std::to_string(is_xyz_in_rgb_range(X, Y, Z)) << std::endl; +}