diff --git a/.gitmodules b/.gitmodules index 92477d9..b016723 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,9 @@ [submodule "components/esphttpdutils"] path = components/esphttpdutils url = git@github.com:0xFEEDC0DE64/esphttpdutils.git +[submodule "components/espconfiglib"] + path = components/espconfiglib + url = git@github.com:0xFEEDC0DE64/espconfiglib.git +[submodule "components/ArduinoJson"] + path = components/ArduinoJson + url = git@github.com:0xFEEDC0DE64/ArduinoJson.git diff --git a/components/ArduinoJson b/components/ArduinoJson new file mode 160000 index 0000000..475a650 --- /dev/null +++ b/components/ArduinoJson @@ -0,0 +1 @@ +Subproject commit 475a650703d203293e9037a9d0cfcbd3de17084f diff --git a/components/arduino-esp32 b/components/arduino-esp32 index b0223be..c9ca6f1 160000 --- a/components/arduino-esp32 +++ b/components/arduino-esp32 @@ -1 +1 @@ -Subproject commit b0223be116f6176d4ef2eae405d776ba044aaf27 +Subproject commit c9ca6f199bb734a74eb70aad3b40f2e4328d5599 diff --git a/components/espconfiglib b/components/espconfiglib new file mode 160000 index 0000000..8af9f03 --- /dev/null +++ b/components/espconfiglib @@ -0,0 +1 @@ +Subproject commit 8af9f032ccf95d3fd657b7d5dc69df431ab03e2e diff --git a/components/espwifistack b/components/espwifistack index dc31bac..a483ae1 160000 --- a/components/espwifistack +++ b/components/espwifistack @@ -1 +1 @@ -Subproject commit dc31bacf984170245d8e523a3d329e8762fa04db +Subproject commit a483ae1533a7b072305a851e64b17bbf86a12b94 diff --git a/export.sh b/export.sh index 6074efa..dc71ac8 100644 --- a/export.sh +++ b/export.sh @@ -1,57 +1,10 @@ -if [[ $_ == $0 ]] && [[ "$1" != "--skip-source-check" ]] -then - echo "export.sh has to be sourced, not run in a subshell" - echo ". export.sh" - exit 1 -fi +DECKENLAMPE_ROOT="$(dirname "$BASH_SOURCE")" -GOE_ROOT="$(dirname "$BASH_SOURCE")" - -if [[ ! -f "${GOE_ROOT}/esp-idf/export.sh" ]] +if [[ ! -f "${DECKENLAMPE_ROOT}/esp-idf/export.sh" ]] then echo "esp-idf is missing, please check out all needed submodules!" echo "git submodule update --init --recursive" return fi -. ${GOE_ROOT}/esp-idf/export.sh - -complete -W "$(./switchconf.sh --list)" ./switchconf.sh - -GOE_INIT_FAILED= - -if [[ -e "build" ]] && [[ ! -L "build" ]] -then - echo "ERROR: build folder exists but isnt a symlink!" - GOE_INIT_FAILED=1 -fi - -if [[ -e "sdkconfig" ]] -then - if [[ ! -L "sdkconfig" ]] - then - echo "ERROR: sdkconfig exists but isnt a symlink!" - GOE_INIT_FAILED=1 - fi -else - echo "ERROR: sdkconfig does not exist" - GOE_INIT_FAILED=1 -fi - -if [[ -e "config.cmake" ]] -then - if [[ ! -L "config.cmake" ]] - then - echo "ERROR: config.cmake exists but isnt a symlink!" - GOE_INIT_FAILED=1 - fi -else - echo "ERROR: config.cmake does not exist" - GOE_INIT_FAILED=1 -fi - -if [[ ! -z "$GOE_INIT_FAILED" ]] -then - echo "run ./switchconf.sh to fix all listed issues" - return -fi +. ${DECKENLAMPE_ROOT}/esp-idf/export.sh diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index bd63976..6567a15 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -6,6 +6,7 @@ set(headers feature_bmp.h feature_dht.h feature_lamp.h + feature_pms.h feature_switch.h feature_tsl.h myble.h @@ -23,6 +24,7 @@ set(sources feature_bmp.cpp feature_dht.cpp feature_lamp.cpp + feature_pms.cpp feature_switch.cpp feature_tsl.cpp main.cpp @@ -38,8 +40,8 @@ set(sources set(dependencies freertos nvs_flash esp_http_server esp_https_ota mdns app_update esp_system mqtt arduino-esp32 date esp-nimble-cpp expected fmt - Adafruit_BMP085_Unified Adafruit_TSL2561 DHT-sensor-library - cpputils espasyncota espchrono espcpputils esphttpdutils espwifistack + airgradient Adafruit_BMP085_Unified Adafruit_TSL2561 DHT-sensor-library + cpputils espasyncota espchrono espconfiglib espcpputils esphttpdutils espwifistack ) idf_component_register( diff --git a/main/feature_pms.cpp b/main/feature_pms.cpp new file mode 100644 index 0000000..25948e9 --- /dev/null +++ b/main/feature_pms.cpp @@ -0,0 +1,196 @@ +#include "feature_pms.h" + +// esp-idf includes +#include + +// Arduino includes +#include +#include + +// 3rdparty lib includes +#include +#include + +// local includes +#include "myconfig.h" +#include "mymqtt.h" + +using namespace std::chrono_literals; + +namespace deckenlampe { +namespace { +constexpr const char * const TAG = "PMS"; + +espchrono::millis_clock::time_point lastRequest; + +struct DATA { + // Standard Particles, CF=1 + uint16_t PM_SP_UG_1_0; + uint16_t PM_SP_UG_2_5; + uint16_t PM_SP_UG_10_0; + + // Atmospheric environment + uint16_t PM_AE_UG_1_0; + uint16_t PM_AE_UG_2_5; + uint16_t PM_AE_UG_10_0; +} pms_data; + +uint8_t pms_payload[12]; +uint8_t pms_index = 0; +uint16_t pms_frameLen; +uint16_t pms_checksum; +uint16_t pms_calculatedChecksum; +} + +void init_pms() +{ + if (!config::enable_pms.value()) + return; + + ESP_LOGI(TAG, "starting Serial1 for PMS %i %i", config::pins_pms_rx.value(), config::pins_pms_tx.value()); + + Serial1.begin(9600, SERIAL_8N1, config::pins_pms_rx.value(), config::pins_pms_tx.value()); + + { + ESP_LOGI(TAG, "setting into active mode..."); + uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; + Serial1.write({command, sizeof(command)}); + } +} + +void update_pms() +{ + if (!config::enable_pms.value()) + return; + + if (espchrono::ago(lastRequest) >= 1s) + { + lastRequest = espchrono::millis_clock::now(); + + if (false) + { + ESP_LOGI(TAG, "setting into active mode..."); + uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; + Serial1.write({command, sizeof(command)}); + } + + if (false) + { + ESP_LOGI(TAG, "setting into passive mode..."); + uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 }; + Serial1.write({command, sizeof(command)}); + } + + if (false) + { + ESP_LOGI(TAG, "requesting read..."); + uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 }; + Serial1.write({command, sizeof(command)}); + } + } + + if (false) + { + uint8_t buf[256]; + if (const auto read = Serial1.read(buf, sizeof(buf))) + { + ESP_LOGI(TAG, "received %zd %s", read, espcpputils::toHexString({buf, read}).c_str()); + } + } + + if (Serial1.available()) + { + uint8_t ch = Serial1.read(); + + switch (pms_index) + { + case 0: + if (ch != 0x42) + return; + pms_calculatedChecksum = ch; + break; + case 1: + if (ch != 0x4D) + { + pms_index = 0; + return; + } + pms_calculatedChecksum += ch; + break; + case 2: + pms_calculatedChecksum += ch; + pms_frameLen = ch << 8; + break; + case 3: + pms_frameLen |= ch; + // Unsupported sensor, different frame length, transmission error e.t.c. + if (pms_frameLen != 2 * 9 + 2 && pms_frameLen != 2 * 13 + 2) + { + pms_index = 0; + return; + } + pms_calculatedChecksum += ch; + break; + default: + if (pms_index == pms_frameLen + 2) + { + pms_checksum = ch << 8; + } + else if (pms_index == pms_frameLen + 2 + 1) + { + pms_checksum |= ch; + + if (pms_calculatedChecksum == pms_checksum) + { + //_PMSstatus = STATUS_OK; + + // Standard Particles, CF=1. + pms_data.PM_SP_UG_1_0 = makeWord(pms_payload[0], pms_payload[1]); + pms_data.PM_SP_UG_2_5 = makeWord(pms_payload[2], pms_payload[3]); + pms_data.PM_SP_UG_10_0 = makeWord(pms_payload[4], pms_payload[5]); + + // Atmospheric Environment. + pms_data.PM_AE_UG_1_0 = makeWord(pms_payload[6], pms_payload[7]); + pms_data.PM_AE_UG_2_5 = makeWord(pms_payload[8], pms_payload[9]); + pms_data.PM_AE_UG_10_0 = makeWord(pms_payload[10], pms_payload[11]); + + ESP_LOGI(TAG, "PM_SP_UG_1_0 %hu", pms_data.PM_SP_UG_1_0); + ESP_LOGI(TAG, "PM_SP_UG_2_5 %hu", pms_data.PM_SP_UG_2_5); + ESP_LOGI(TAG, "PM_SP_UG_10_0 %hu", pms_data.PM_SP_UG_10_0); + ESP_LOGI(TAG, "PM_AE_UG_1_0 %hu", pms_data.PM_AE_UG_1_0); + ESP_LOGI(TAG, "PM_AE_UG_2_5 %hu", pms_data.PM_AE_UG_2_5); + ESP_LOGI(TAG, "PM_AE_UG_10_0 %hu", pms_data.PM_AE_UG_10_0); + + if (mqttConnected) + { + mqttVerbosePub(config::topic_pms_st_1_0.value(), std::to_string(pms_data.PM_SP_UG_1_0), 0, 1); + mqttVerbosePub(config::topic_pms_st_2_5.value(), std::to_string(pms_data.PM_SP_UG_2_5), 0, 1); + mqttVerbosePub(config::topic_pms_st_10_0.value(), std::to_string(pms_data.PM_SP_UG_10_0), 0, 1); + mqttVerbosePub(config::topic_pms_ae_1_0.value(), std::to_string(pms_data.PM_AE_UG_1_0), 0, 1); + mqttVerbosePub(config::topic_pms_ae_2_5.value(), std::to_string(pms_data.PM_AE_UG_2_5), 0, 1); + mqttVerbosePub(config::topic_pms_ae_10_0.value(), std::to_string(pms_data.PM_AE_UG_10_0), 0, 1); + } + } + + pms_index = 0; + return; + } + else + { + pms_calculatedChecksum += ch; + uint8_t payloadIndex = pms_index - 4; + + // Payload is common to all sensors (first 2x6 bytes). + if (payloadIndex < sizeof(pms_payload)) + { + pms_payload[payloadIndex] = ch; + } + } + break; + } + + pms_index++; + } +} + +} // namespace deckenlampe diff --git a/main/feature_pms.h b/main/feature_pms.h new file mode 100644 index 0000000..4ad83e0 --- /dev/null +++ b/main/feature_pms.h @@ -0,0 +1,8 @@ +#pragma once + +namespace deckenlampe { + +void init_pms(); +void update_pms(); + +} // namespace deckenlampe diff --git a/main/main.cpp b/main/main.cpp index bceb6df..e04a844 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -29,6 +29,7 @@ #include "feature_dht.h" #include "feature_tsl.h" #include "feature_bmp.h" +#include "feature_pms.h" #include "espchrono.h" using namespace std::chrono_literals; @@ -108,6 +109,8 @@ extern "C" void app_main() init_bmp(); + init_pms(); + while (true) { #if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) @@ -145,6 +148,9 @@ extern "C" void app_main() update_bmp(); vPortYield(); + update_pms(); + vPortYield(); + update_switch(); vPortYield(); diff --git a/main/myconfig.cpp b/main/myconfig.cpp index ff9c5c4..d71cf1a 100644 --- a/main/myconfig.cpp +++ b/main/myconfig.cpp @@ -63,6 +63,16 @@ ConfigWrapper topic_bmp085_pressure{"topic_bmp085_pressure", "tpcbm ConfigWrapper topic_bmp085_temperature{"topic_bmp085_temperature", "tpcbmp085temper", "dahoam/wohnzimmer/bmp085_1/temperature"}; ConfigWrapper topic_bmp085_altitude{"topic_bmp085_altitude", "tpcbmp085altitu", "dahoam/wohnzimmer/bmp085_1/altitude"}; +ConfigWrapper enable_pms{"enable_pms", "enable_pms", false}; +ConfigWrapper pins_pms_rx{"pins_pms_rx", "pins_pms_rx", GPIO_NUM_25}; +ConfigWrapper pins_pms_tx{"pins_pms_tx", "pins_pms_tx", GPIO_NUM_26}; +ConfigWrapper topic_pms_st_1_0{"topic_pms_st_1_0", "tpcpmsst1_0", "dahoam/wohnzimmer/pms/st/1_0"}; +ConfigWrapper topic_pms_st_2_5{"topic_pms_st_2_5", "tpcpmsst2_5", "dahoam/wohnzimmer/pms/st/2_5"}; +ConfigWrapper topic_pms_st_10_0{"topic_pms_st_10_0", "tpcpmsst10_0", "dahoam/wohnzimmer/pms/st/10_0"}; +ConfigWrapper topic_pms_ae_1_0{"topic_pms_ae_1_0", "tpcpmsae1_0", "dahoam/wohnzimmer/pms/ae/1_0"}; +ConfigWrapper topic_pms_ae_2_5{"topic_pms_ae_2_5", "tpcpmsae2_5", "dahoam/wohnzimmer/pms/ae/2_5"}; +ConfigWrapper topic_pms_ae_10_0{"topic_pms_ae_10_0", "tpcpmsae10_0", "dahoam/wohnzimmer/pms/ae/10_0"}; + ConfigWrapper availableTimeoutTime{"availableTimeoutTime", "availTimeouTime", 1min}; ConfigWrapper valueUpdateInterval{"valueUpdateInterval", "valUpdaInterval", 15s}; } // namespace config diff --git a/main/myconfig.h b/main/myconfig.h index d533d93..fc7e56c 100644 --- a/main/myconfig.h +++ b/main/myconfig.h @@ -68,6 +68,16 @@ extern ConfigWrapper topic_bmp085_pressure; extern ConfigWrapper topic_bmp085_temperature; extern ConfigWrapper topic_bmp085_altitude; +extern ConfigWrapper enable_pms; +extern ConfigWrapper pins_pms_rx; +extern ConfigWrapper pins_pms_tx; +extern ConfigWrapper topic_pms_st_1_0; +extern ConfigWrapper topic_pms_st_2_5; +extern ConfigWrapper topic_pms_st_10_0; +extern ConfigWrapper topic_pms_ae_1_0; +extern ConfigWrapper topic_pms_ae_2_5; +extern ConfigWrapper topic_pms_ae_10_0; + extern ConfigWrapper availableTimeoutTime; extern ConfigWrapper valueUpdateInterval; @@ -113,6 +123,15 @@ void foreachConfig(T &&callback) callback(topic_bmp085_pressure); callback(topic_bmp085_temperature); callback(topic_bmp085_altitude); + callback(enable_pms); + callback(pins_pms_rx); + callback(pins_pms_tx); + callback(topic_pms_st_1_0); + callback(topic_pms_st_2_5); + callback(topic_pms_st_10_0); + callback(topic_pms_ae_1_0); + callback(topic_pms_ae_2_5); + callback(topic_pms_ae_10_0); callback(availableTimeoutTime); callback(valueUpdateInterval); } diff --git a/sdkconfig b/sdkconfig index 25c7b78..364bf70 100644 --- a/sdkconfig +++ b/sdkconfig @@ -156,6 +156,19 @@ CONFIG_ARDUINO_SELECTIVE_COMPILATION=y CONFIG_ARDUINO_SELECTIVE_Wire=y # end of Arduino Configuration +# +# ESP Config lib settings +# +# CONFIG_LOG_LOCAL_LEVEL_CONFIG_NONE is not set +# CONFIG_LOG_LOCAL_LEVEL_CONFIG_ERROR is not set +# CONFIG_LOG_LOCAL_LEVEL_CONFIG_WARN is not set +CONFIG_LOG_LOCAL_LEVEL_CONFIG_INFO=y +# CONFIG_LOG_LOCAL_LEVEL_CONFIG_DEBUG is not set +# CONFIG_LOG_LOCAL_LEVEL_CONFIG_VERBOSE is not set +CONFIG_LOG_LOCAL_LEVEL_CONFIG=3 +# CONFIG_SEPARATE_FACTORY_NVS_PARTITION is not set +# end of ESP Config lib settings + # # espcpputils settings #