From 607116727cbaa3f89c43b04b4f3830d4e7134610 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Thu, 16 May 2024 18:33:04 +0200 Subject: [PATCH 01/81] Added 1-Wire temperature sensor function (#31) (#32) --- src/AppLayer.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++- src/AppLayer.h | 28 +++++++++++++++++----------- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/AppLayer.cpp b/src/AppLayer.cpp index 18bfb83..f482679 100644 --- a/src/AppLayer.cpp +++ b/src/AppLayer.cpp @@ -50,6 +50,7 @@ // 20240427 Added BLE configuration/status via LoRaWAN // 20240507 Added configuration of max_sensors/rx_flags via LoRaWAN // 20240508 Added configuration of en_decoders via LoRaWAN +// 20240515 Added getOneWireTemperature() // // // ToDo: @@ -59,6 +60,46 @@ #include "AppLayer.h" +#ifdef ONEWIRE_EN + // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) + static OneWire oneWire(PIN_ONEWIRE_BUS); //!< OneWire bus + + // Pass our oneWire reference to Dallas Temperature. + static DallasTemperature owTempSensors(&oneWire); //!< Dallas temperature sensors connected to OneWire bus +#endif + +#ifdef ONEWIRE_EN + /*! + * \brief Get temperature from Maxim OneWire Sensor + * + * \param index sensor index + * + * \returns temperature in degrees Celsius or DEVICE_DISCONNECTED_C + */ + float + AppLayer::getOneWireTemperature(uint8_t index) + { + // Call sensors.requestTemperatures() to issue a global temperature + // request to all devices on the bus + owTempSensors.requestTemperatures(); + + // Get temperature by index + float tempC = owTempSensors.getTempCByIndex(index); + + // Check if reading was successful + if (tempC != DEVICE_DISCONNECTED_C) + { + log_d("Temperature = %.2f°C", tempC); + } + else + { + log_d("Error: Could not read temperature data"); + } + + return tempC; + }; +#endif + uint8_t AppLayer::decodeDownlink(uint8_t port, uint8_t *payload, size_t size) { @@ -395,6 +436,8 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) #endif #ifdef ONEWIRE_EN + float water_temp_c = getOneWireTemperature(); + // Debug output for auxiliary sensors/voltages if (water_temp_c != DEVICE_DISCONNECTED_C) { @@ -732,4 +775,4 @@ std::vector AppLayer::getBleAddr(void) return bleAddr; } -#endif \ No newline at end of file +#endif diff --git a/src/AppLayer.h b/src/AppLayer.h index 5c8855e..e0c6bec 100644 --- a/src/AppLayer.h +++ b/src/AppLayer.h @@ -38,6 +38,7 @@ // 20240424 Fixed BLE address initialization from Preferences, added begin() // 20240426 Moved bleAddrInit() out of begin() // 20240504 Added BresserWeatherSensorLWCmd.h +// 20240515 Added getOneWireTemperature() // // ToDo: // - @@ -121,14 +122,6 @@ class AppLayer Lightning lightningProc; #endif -#ifdef ONEWIRE_EN - // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) - OneWire oneWire(PIN_ONEWIRE_BUS); //!< OneWire bus - - // Pass our oneWire reference to Dallas Temperature. - DallasTemperature temp_sensors(&oneWire); //!< Dallas temperature sensors connected to OneWire bus -#endif - #ifdef DISTANCESENSOR_EN #if defined(ESP32) /// Ultrasonic distance sensor @@ -176,7 +169,7 @@ class AppLayer * * If available, addresses from Preferences are used, otherwise defaults from * BresserWeatherSensorLWCfg.h. - * + * * BleSensors() requires Preferences, which uses the Flash FS, * which is not available before the sketches' begin() is called - * thus the following cannot be handled by the constructor! @@ -191,11 +184,13 @@ class AppLayer // No addresses stored in Preferences, use default knownBLEAddresses = knownBLEAddressesDef; log_d("Using BLE addresses from BresserWeatherSensorLWCfg.h:"); - } else { + } + else + { log_d("Using BLE addresses from Preferences:"); } bleSensors = BleSensors(knownBLEAddresses); - + for (const std::string &s : knownBLEAddresses) { (void)s; @@ -204,6 +199,17 @@ class AppLayer #endif }; +#ifdef ONEWIRE_EN + /*! + * \brief Get temperature from Maxim OneWire Sensor + * + * \param index sensor index + * + * \returns temperature in degrees Celsius or DEVICE_DISCONNECTED_C + */ + float + getOneWireTemperature(uint8_t index = 0); +#endif /*! * \brief Decode app layer specific downlink messages From be853aa35a2e7a456d8e9f83707f9a4f354bfb45 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 19:39:09 +0200 Subject: [PATCH 02/81] Added CMD_GET_APP_PAYLOAD_CFG --- scripts/uplink_formatter.js | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/scripts/uplink_formatter.js b/scripts/uplink_formatter.js index 0a449db..3ae00df 100644 --- a/scripts/uplink_formatter.js +++ b/scripts/uplink_formatter.js @@ -1,4 +1,3 @@ - /////////////////////////////////////////////////////////////////////////////// // uplink_formatter.js // @@ -30,7 +29,7 @@ // port = CMD_SET_BLE_ADDR, {"ble_addr": [, ..., ]} // port = CMD_GET_BLE_CONFIG, {"cmd": "CMD_GET_BLE_CONFIG"} / payload = 0x00 // port = CMD_SET_BLE_CONFIG, {"ble_active": , "ble_scantime": } - +// port = CMD_GET_APP_PAYLOAD_CFG, {"cmd": "CMD_GET_APP_PAYLOAD_CFG"} / payload = 0x00 // // Responses: // ----------- @@ -52,6 +51,8 @@ // // CMD_GET_BLE_CONFIG {"ble_active": , "ble_scantime": } // +// CMD_GET_APP_PAYLOAD_CFG {"bresser": [, , ..., ], "onewire": , "analog": , "digital": } +// // : 0...255 // : 0...65535 // : 0...65535 @@ -66,6 +67,11 @@ // : BLE scan mode - 0: passive / 1: active // : BLE scan time in seconds (0...255) // : e.g. "DE:AD:BE:EF:12:23" +// : Bitmap for enabling Bresser sensors of type N; each bit position corresponds to a channel, e.g. bit 0 controls ch0; +// unused bits can be used to select features +// : Bitmap for enabling 1-Wire sensors; each bit position corresponds to an index +// : Bitmap for enabling analog input channels; each bit positions corresponds to a channel +// : Bitmap for enabling digital input channels in a broad sense — GPIO, SPI, I2C, UART, ... // Based on: // --------- @@ -103,7 +109,8 @@ // renamed from ttn_uplink_formatter.js // 20240427 Added BLE configuration // 20240507 Added CMD_GET_SENSORS_CFG -// 20240608 Added en_decoders to CMD_GET_SENSORS_CFG +// 20240508 Added en_decoders to CMD_GET_SENSORS_CFG +// 20240517 Added CMD_GET_APP_PAYLOAD_CFG // // ToDo: // - @@ -121,6 +128,7 @@ function decoder(bytes, port) { const CMD_GET_SENSORS_CFG = 0xCC; const CMD_GET_BLE_ADDR = 0xC8; const CMD_GET_BLE_CONFIG = 0xCA; + const CMD_GET_APP_PAYLOAD_CFG = 0xCE; const ONEWIRE_EN = 0; @@ -230,6 +238,30 @@ function decoder(bytes, port) { } mac48.BYTES = bytes.length; + var bresser_bitmaps = function (bytes) { + var res = []; + res[0] = "0x" + byte2hex(bytes[0]); + for (var i = 1; i < 16; i++) { + res[i] = "0x" + byte2hex(bytes[i]); + } + return res; + } + bresser_bitmaps.BYTES = 16; + + var hex16 = function (bytes) { + var res; + res = "0x" + byte2hex(bytes[0]) + byte2hex(bytes[1]); + return res; + } + hex16.BYTES = 2; + + var hex32 = function (bytes) { + var res; + res = "0x" + byte2hex(bytes[0]) + byte2hex(bytes[1]) + byte2hex(bytes[2]) + byte2hex(bytes[3]); + return res; + } + hex32.BYTES = 4; + var id32 = function (bytes) { var res = []; var size = bytes.length; @@ -486,6 +518,17 @@ function decoder(bytes, port) { ], ['ble_active', 'ble_scantime'] ); + } else if (port === CMD_GET_APP_PAYLOAD_CFG) { + return decode( + bytes, + [bresser_bitmaps, hex16, hex16, hex32 + ], + ['bresser', 'onewire', 'analog', 'digital'] + //[uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint16, uint16, uint32 + //], + //['type0', 'type1', 'type2', 'type3', 'type4', 'type5', 'type6', 'type7', 'type8', 'type9', 'type10', 'type11', 'type12', 'type13', 'type14', + // 'type15', 'onewire', 'analog', 'digital'] + ); } } From 350cfeeb32b15e70fe0959fa0d019ccb8ee86b1d Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 19:39:55 +0200 Subject: [PATCH 03/81] Added CMD_GET_APP_PAYLOAD_CFG/CMD_GET_APP_PAYLOAD_CFG --- scripts/downlink_formatter.js | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/scripts/downlink_formatter.js b/scripts/downlink_formatter.js index c023e6c..ecf6e56 100644 --- a/scripts/downlink_formatter.js +++ b/scripts/downlink_formatter.js @@ -25,6 +25,8 @@ // port = CMD_SET_SENSORS_INC, {"sensors_inc": [, ..., ]} // port = CMD_GET_SENSORS_EXC, {"cmd": "CMD_GET_SENSORS_EXC"} / payload = 0x00 // port = CMD_SET_SENSORS_EXC, {"sensors_exc": [, ..., ]} +// port = CMD_GET_APP_PAYLOAD_CFG, {"cmd": "CMD_GET_APP_PAYLOAD_CFG"} / payload = 0x00 +// port = CMD_SET_APP_PAYLOAD_CFG, ["bresser": [, ... ], "onewire": , "analog": , "digital": ] // port = CMD_GET_BLE_ADDR, {"cmd": "CMD_GET_BLE_ADDR"} / payload = 0x00 // port = CMD_SET_BLE_ADDR, {"ble_addr": [, ..., ]} // port = CMD_GET_BLE_CONFIG, {"cmd": "CMD_GET_BLE_CONFIG"} / payload = 0x00 @@ -46,6 +48,8 @@ // // CMD_GET_SENSORS_CFG {"max_sensors": , "rx_flags": , "en_decoders": } // +// CMD_GET_APP_PAYLOAD_CFG {"bresser": [, , ..., ], "onewire": , "analog": , "digital": } +// // CMD_GET_BLE_ADDR {"ble_addr": [, ...]} // // CMD_GET_BLE_CONFIG {"ble_active": , "ble_scantime": } @@ -64,6 +68,11 @@ // : BLE scan mode - 0: passive / 1: active // : BLE scan time in seconds (0...255) // : e.g. "DE:AD:BE:EF:12:23" +// : Bitmap for enabling Bresser sensors of type N; each bit position corresponds to a channel, e.g. bit 0 controls ch0; +// unused bits can be used to select features +// : Bitmap for enabling 1-Wire sensors; each bit position corresponds to an index +// : Bitmap for enabling analog input channels; each bit positions corresponds to a channel +// : Bitmap for enabling digital input channels in a broad sense — GPIO, SPI, I2C, UART, ... // // // Based on: @@ -104,6 +113,7 @@ // 20240507 Added CMD_GET_SENSORS_CFG/CMD_SET_SENSORS_CFG // 20240508 Fixed decoding of raw data // Added en_decoders to CMD_GET_SENSORS_CFG/CMD_SET_SENSORS_CFG +// 20240519 Added CMD_GET_APP_PAYLOAD_CFG/CMD_SET_APP_PAYLOAD_CFG // // ToDo: // - @@ -125,6 +135,8 @@ const CMD_GET_SENSORS_EXC = 0xC6; const CMD_SET_SENSORS_EXC = 0xC7; const CMD_GET_SENSORS_CFG = 0xCC; const CMD_SET_SENSORS_CFG = 0xCD; +const CMD_GET_APP_PAYLOAD_CFG = 0xCE; +const CMD_SET_APP_PAYLOAD_CFG = 0xCF; const CMD_GET_BLE_ADDR = 0xC8; const CMD_SET_BLE_ADDR = 0xC9; const CMD_GET_BLE_CONFIG = 0xCA; @@ -257,6 +269,14 @@ function encodeDownlink(input) { errors: [] }; } + else if (input.data.cmd == "CMD_GET_APP_PAYLOAD_CFG") { + return { + bytes: [0], + fPort: CMD_GET_APP_PAYLOAD_CFG, + warnings: [], + errors: [] + }; + } else if (input.data.cmd == "CMD_GET_BLE_ADDR") { return { bytes: [0], @@ -383,6 +403,67 @@ function encodeDownlink(input) { warnings: [], errors: [] }; + } else if (input.data.hasOwnProperty('bresser') && + input.data.hasOwnProperty('onewire') && + input.data.hasOwnProperty('analog') && + input.data.hasOwnProperty('digital')) { + if (input.data.bresser.length != 16) { + return { + bytes: [], + warnings: [], + errors: [": expected 16 bytes, got " + input.data.bresser.length] + }; + } + for (i = 0; i < input.data.bresser.length; i++) { + if (input.data.bresser[i].substr(0, 2) == "0x") { + value = parseInt(input.data.bresser[i].substr(2), 16); + output[i] = value; + } else { + return { + bytes: [], + warnings: [], + errors: ["'bresser': Invalid hex value"] + }; + } + } + if (input.data.onewire.substr(0, 2) == "0x") { + output[16] = parseInt(input.data.onewire.substr(2, 2), 16); + output[17] = parseInt(input.data.onewire.substr(4, 2), 16); + } else { + return { + bytes: [], + warnings: [], + errors: ["'onewire': Invalid hex value"] + }; + } + if (input.data.analog.substr(0, 2) == "0x") { + output[18] = parseInt(input.data.analog.substr(2, 2), 16); + output[19] = parseInt(input.data.analog.substr(4, 2), 16); + } else { + return { + bytes: [], + warnings: [], + errors: ["'analog': Invalid hex value"] + }; + } + if (input.data.digital.substr(0, 2) == "0x") { + output[20] = parseInt(input.data.digital.substr(2, 2), 16); + output[21] = parseInt(input.data.digital.substr(4, 2), 16); + output[22] = parseInt(input.data.digital.substr(6, 2), 16); + output[23] = parseInt(input.data.digital.substr(8, 2), 16); + } else { + return { + bytes: [], + warnings: [], + errors: ["'digital': Invalid hex value"] + }; + } + return { + bytes: output, + fPort: CMD_SET_APP_PAYLOAD_CFG, + warnings: [], + errors: [] + }; } else if (input.data.hasOwnProperty('ble_addr')) { output = []; k = 0; From cd6ff0e274933ccf4b6bd96c3cc17c812299d544 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 19:40:20 +0200 Subject: [PATCH 04/81] Added CMD_GET_APP_PAYLOAD_CFG/CMD_GET_APP_PAYLOAD_CFG --- BresserWeatherSensorLWCmd.h | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/BresserWeatherSensorLWCmd.h b/BresserWeatherSensorLWCmd.h index 9dc6888..174296c 100644 --- a/BresserWeatherSensorLWCmd.h +++ b/BresserWeatherSensorLWCmd.h @@ -308,5 +308,73 @@ // Response: n.a. +// CMD_GET_APP_PAYLOAD_CFG +// ----------------------- +// Port: CMD_GET_APP_PAYLOAD_CFG +#define CMD_GET_APP_PAYLOAD_CFG 0xCE + +// Downlink (command): +// byte0: 0x00 + +// Response: n.a. +// Uplink (command): +// byte00: type00[7:0] +// byte01: type01[7:0] +// byte02: type02[7:0] +// byte03: type03[7:0] +// byte04: type04[7:0] +// byte05: type05[7:0] +// byte06: type06[7:0] +// byte07: type07[7:0] +// byte08: type08[7:0] +// byte09: type09[7:0] +// byte10: type10[7:0] +// byte11: type11[7:0] +// byte12: type12[7:0] +// byte13: type13[7:0] +// byte14: type14[7:0] +// byte15: type15[7:0] +// byte16: onewire[15:8] +// byte17: onewire[7:0] +// byte18: analog[15:8] +// byte19: analog[7:0] +// byte20: digital[31:24] +// byte21: digital[23:16] +// byte22: digital[15:8] +// byte23: digital[7:0] + +// CMD_SET_APP_PAYLOAD_CFG +// Port: CMD_SET_APP_PAYLOAD_CFG +#define CMD_SET_APP_PAYLOAD_CFG 0xCF + +// Uplink (command): +// byte00: type00[7:0] +// byte01: type01[7:0] +// byte02: type02[7:0] +// byte03: type03[7:0] +// byte04: type04[7:0] +// byte05: type05[7:0] +// byte06: type06[7:0] +// byte07: type07[7:0] +// byte08: type08[7:0] +// byte09: type09[7:0] +// byte10: type10[7:0] +// byte11: type11[7:0] +// byte12: type12[7:0] +// byte13: type13[7:0] +// byte14: type14[7:0] +// byte15: type15[7:0] +// byte16: onewire[15:8] +// byte17: onewire[7:0] +// byte18: analog[15:8] +// byte19: analog[7:0] +// byte20: digital[31:24] +// byte21: digital[23:16] +// byte22: digital[15:8] +// byte23: digital[7:0] + +// Response: n.a. + + // =========================== #endif \ No newline at end of file From 2057270b8a677a070835db1d4535048a221db613 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 19:41:03 +0200 Subject: [PATCH 05/81] Added CMD_GET_APP_PAYLOAD_CFG/CMD_GET_APP_PAYLOAD_CFG --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 451d4f6..ba42da5 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,10 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather | | BLE active scan; 1 (active scan) / 0 (passive scan) | | | BLE scan time in seconds; 0...255 | | | BLE sensor MAC addresses; e.g. "DE:AD:BE:EF:12:23" | +| \ | Bitmap for enabling Bresser sensors of type \; each bit position corresponds to a channel,
e.g. bit 0 controls channel 0; unused bits can be used to select features | +| \ | Bitmap for enabling 1-Wire sensors; each bit position corresponds to an index | +| \ | Bitmap for enabling analog input channels; each bit position corresponds to a channel | +| \ | Bitmap for enabling digital input channels in a broader sense — GPIO, SPI, I2C, UART, ... | > [!WARNING] > Confirmed downlinks should not be used! (see [here](https://www.thethingsnetwork.org/forum/t/how-to-purge-a-scheduled-confirmed-downlink/56849/7) for an explanation.) @@ -423,6 +427,8 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather | CMD_SET_BLE_ADDR | 0xC9 (201) | ble_addr0[47:40]
ble_addr0[39:32]
ble_addr0[31:24]
ble_addr0[23:15]
ble_addr0[16:8]
ble_addr0[7:0]
... | n.a. | | CMD_GET_BLE_CONFIG | 0xCA (202) | 0x00 | ble_active[7:0]
ble_scantime[7:0] | | CMD_SET_BLE_CONFIG | 0xCB (203) | ble_active[7:0]
ble_scantime[7:0] | n.a. | +| CMD_GET_APP_PAYLOAD_CFG | 0xCE (206) | 0x00 | type00[7:0]
type01[7:0]
...
type15[7:0]
onewire[15:8]
onewire[7:0]
analog[15:8]
analog[7:0]
digital[31:24]
digital[23:16]
digital[15:8]
digital[7:0] | +| CMD_SET_APP_PAYLOAD_CFG | 0xCF (207) | type00[7:0]
type01[7:0]
...
type15[7:0]
onewire[15:8]
onewire[7:0]
analog[15:8]
analog[7:0]
digital[31:24]
digital[23:16]
digital[15:8]
digital[7:0] | n.a. | #### The Things Network Examples @@ -448,7 +454,7 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather | ----------------------------- | ------------------------------------------------------------------------- | ---------------------------- | | CMD_GET_DATETIME | {"cmd": "CMD_GET_DATETIME"} | {"epoch": \} | | CMD_SET_DATETIME | {"epoch": \} | n.a. | -| CMD_SET_SLEEP_INTERVAL | {"sleep_interval": "} | n.a. | +| CMD_SET_SLEEP_INTERVAL | {"sleep_interval": } | n.a. | | CMD_SET_SLEEP_INTERVAL_LONG | {"sleep_interval_long": } | n.a. | | CMD_GET_LW_CONFIG | {"cmd": "CMD_GET_LW_CONFIG"} | {"sleep_interval": , "sleep_interval_long": } | | CMD_GET_WS_TIMEOUT | {"cmd": "CMD_GET_WS_TIMEOUT"} | {"ws_timeout": } | @@ -464,6 +470,8 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather | CMD_SET_BLE_ADDR | {"ble_addr": [, ..., ]} | n.a. | | CMD_GET_BLE_CONFIG | {"cmd": "CMD_GET_BLE_CONFIG"} | {"ble_active": , "ble_scantime": } | | CMD_SET_BLE_CONFIG | {"ble_active": , "ble_scantime": } | n.a. | +| CMD_GET_APP_PAYLOAD_CFG | {"cmd": "CMD_GET_APP_PAYLOAD_CFG"} | {"bresser": [\, \, ..., \], "onewire": \, "analog": \, "digital": \} | +| CMD_SET_APP_PAYLOAD_CFG | {"bresser": [\, \, ..., \], "onewire": \, "analog": \, "digital": \} | n.a. | #### The Things Network Examples From b2bf46ce4af43dd6b088b41f5574b4a94023ce93 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 19:57:53 +0200 Subject: [PATCH 06/81] Created --- src/PayloadOneWire.cpp | 104 +++++++++++++++++++++++++++++++++++++++++ src/PayloadOneWire.h | 78 +++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/PayloadOneWire.cpp create mode 100644 src/PayloadOneWire.h diff --git a/src/PayloadOneWire.cpp b/src/PayloadOneWire.cpp new file mode 100644 index 0000000..48a29ea --- /dev/null +++ b/src/PayloadOneWire.cpp @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadOneWire.h +// +// Get 1-Wire temperature sensor values and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240520 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#include "PayloadOneWire.h" + +#ifdef ONEWIRE_EN + +// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) +static OneWire oneWire(PIN_ONEWIRE_BUS); //!< OneWire bus + +// Pass our oneWire reference to Dallas Temperature. +static DallasTemperature owTempSensors(&oneWire); //!< Dallas temperature sensors connected to OneWire bus + +// Get temperature from Maxim OneWire Sensor +float PayloadOneWire::getOneWireTemperature(uint8_t index) +{ + // Call sensors.requestTemperatures() to issue a global temperature + // request to all devices on the bus + owTempSensors.requestTemperatures(); + + // Get temperature by index + float tempC = owTempSensors.getTempCByIndex(index); + + // Check if reading was successful + if (tempC != DEVICE_DISCONNECTED_C) + { + log_d("Temperature = %.2f°C", tempC); + } + else + { + log_d("Error: Could not read temperature data"); + } + + return tempC; +}; + +// Encode 1-Wire temperature sensor values for LoRaWAN transmission +void PayloadOneWire::encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder) +{ + + for (int i = APP_PAYLOAD_BYTES_ONEWIRE - 1; i >= 0; i--) + { + unsigned index = (APP_PAYLOAD_BYTES_ONEWIRE * 2) - 1; + for (int bit = 7; bit >= 0; bit--) + { + // Check if sensor with given index is enabled + if ((appPayloadCfg[APP_PAYLOAD_OFFS_ONEWIRE + i] >> bit) & 0x1) + { + // Get temperature by index + float tempC = owTempSensors.getTempCByIndex(index); + + // Check if reading was successful + if (tempC != DEVICE_DISCONNECTED_C) + { + log_d("Temperature[%d] = %.2f°C", index, tempC); + } + else + { + log_d("Error: Could not read temperature[%d] data", index); + } + index--; + encoder.writeTemperature(tempC); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/PayloadOneWire.h b/src/PayloadOneWire.h new file mode 100644 index 0000000..30175a6 --- /dev/null +++ b/src/PayloadOneWire.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadOneWire.h +// +// Get 1-Wire temperature sensor values and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240520 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(_PAYLOAD_ONE_WIRE) +#define _PAYLOAD_ONE_WIRE + +#include "../BresserWeatherSensorLWCfg.h" + +#ifdef ONEWIRE_EN + +// Dallas/Maxim OneWire Temperature Sensor +#include +#include + + +// Dallas/Maxim OneWire Temperature Sensor +#include + +class PayloadOneWire +{ +public: + PayloadOneWire(){}; + + /*! + * \brief Get temperature from Maxim OneWire Sensor + * + * \param index sensor index + * + * \returns temperature in degrees Celsius or DEVICE_DISCONNECTED_C + */ + float getOneWireTemperature(uint8_t index); + + /!* + * \brief Encode 1-Wire temperature sensor values for LoRaWAN transmission + * + * \param appPayloadCfg LoRaWAN payload configuration bitmaps + * \param encoder LoRaWAN payload encoder object + void encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder); +}; +#endif // ONEWIRE_EN +#endif //_PAYLOAD_ONE_WIRE \ No newline at end of file From a8fb89627c66782b8a2cd58aaa707dee575c7c38 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 21:28:06 +0200 Subject: [PATCH 07/81] Fixed encodeOneWire() --- src/PayloadOneWire.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PayloadOneWire.cpp b/src/PayloadOneWire.cpp index 48a29ea..f6f87c5 100644 --- a/src/PayloadOneWire.cpp +++ b/src/PayloadOneWire.cpp @@ -75,9 +75,9 @@ float PayloadOneWire::getOneWireTemperature(uint8_t index) void PayloadOneWire::encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder) { + unsigned index = (APP_PAYLOAD_BYTES_ONEWIRE * 8) - 1; for (int i = APP_PAYLOAD_BYTES_ONEWIRE - 1; i >= 0; i--) { - unsigned index = (APP_PAYLOAD_BYTES_ONEWIRE * 2) - 1; for (int bit = 7; bit >= 0; bit--) { // Check if sensor with given index is enabled @@ -95,9 +95,10 @@ void PayloadOneWire::encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder) { log_d("Error: Could not read temperature[%d] data", index); } - index--; + encoder.writeTemperature(tempC); } + index--; } } } From cc1098d4142f0ad4be5b43755dc3391f5294e05d Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 21:28:32 +0200 Subject: [PATCH 08/81] Added/fixed comments --- src/PayloadOneWire.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/PayloadOneWire.h b/src/PayloadOneWire.h index 30175a6..05e0d3f 100644 --- a/src/PayloadOneWire.h +++ b/src/PayloadOneWire.h @@ -56,6 +56,9 @@ class PayloadOneWire { public: + /*! + * \brief Constructor + */ PayloadOneWire(){}; /*! @@ -67,11 +70,12 @@ class PayloadOneWire */ float getOneWireTemperature(uint8_t index); - /!* - * \brief Encode 1-Wire temperature sensor values for LoRaWAN transmission - * - * \param appPayloadCfg LoRaWAN payload configuration bitmaps - * \param encoder LoRaWAN payload encoder object + /*! + * \brief Encode 1-Wire temperature sensor values for LoRaWAN transmission + * + * \param appPayloadCfg LoRaWAN payload configuration bitmaps + * \param encoder LoRaWAN payload encoder object + */ void encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder); }; #endif // ONEWIRE_EN From f755094abcf9bdfc0a894a090a03f20ab989b3b6 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 21:29:17 +0200 Subject: [PATCH 09/81] Created --- src/PayloadDigital.cpp | 140 +++++++++++++++++++++++++++++++++++++++++ src/PayloadDigital.h | 91 +++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 src/PayloadDigital.cpp create mode 100644 src/PayloadDigital.h diff --git a/src/PayloadDigital.cpp b/src/PayloadDigital.cpp new file mode 100644 index 0000000..5577981 --- /dev/null +++ b/src/PayloadDigital.cpp @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadDigital.cpp +// +// Read digital input channels and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240520 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#include "PayloadDigital.h" + +#ifdef DISTANCESENSOR_EN +#if defined(ESP32) +/// Ultrasonic distance sensor +static DistanceSensor_A02YYUW distanceSensor(&Serial2); +#else +/// Ultrasonic distance sensor +static DistanceSensor_A02YYUW distanceSensor(&Serial1); +#endif +#endif + +void PayloadDigital::begin(void) +{ +#ifdef DISTANCESENSOR_EN + initDistanceSensor(); +#endif +} + +void PayloadDigital::encodeDigital(uint8_t *appPayloadCfg, LoraEncoder &encoder) +{ + unsigned ch = (APP_PAYLOAD_BYTES_DIGITAL * 8) - 1; + for (int i = APP_PAYLOAD_BYTES_DIGITAL - 1; i >= 0; i--) + { + for (int bit = 7; bit >= 0; bit--) + { + if ((appPayloadCfg[APP_PAYLOAD_OFFS_ONEWIRE + i] >> bit) & 0x1) +#ifdef DISTANCESENSOR_EN + // Check if sensor with given index is enabled + if (ch == DISTANCESENSOR_CH) + { + uint16_t distance_mm = readDistanceSensor(); + if (distance_mm > 0) + { + log_i("ch %02u: Distance: %4d mm", ch, distance_mm); + } + else + { + log_i("ch %02u: Distance: ---- mm", ch); + } + encoder.writeUint16(distance_mm); + } +#endif + ch--; + } + } +} + +#ifdef DISTANCESENSOR_EN +void PayloadDigital::initDistanceSensor(void) +{ +#if defined(ESP32) + Serial2.begin(9600, SERIAL_8N1, DISTANCESENSOR_RX, DISTANCESENSOR_TX); + pinMode(DISTANCESENSOR_PWR, OUTPUT); + digitalWrite(DISTANCESENSOR_PWR, LOW); +#elif defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) + Serial1.setRX(DISTANCESENSOR_RX); + Serial1.setTX(DISTANCESENSOR_TX); + Serial1.begin(9600, SERIAL_8N1); + pinMode(DISTANCESENSOR_PWR, OUTPUT_12MA); + digitalWrite(DISTANCESENSOR_PWR, LOW); +#endif +} + +uint16_t PayloadDigital::readDistanceSensor(void) +{ + // Sensor power on + digitalWrite(DISTANCESENSOR_PWR, HIGH); + delay(500); + + int retries = 0; + DistanceSensor_A02YYUW_MEASSUREMENT_STATUS dstStatus; + do + { + dstStatus = distanceSensor.meassure(); + + if (dstStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) + { + log_e("Distance Sensor Error: %d", dstStatus); + } + } while ( + (dstStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) && + (++retries < DISTANCESENSOR_RETRIES)); + + uint16_t distance_mm; + if (dstStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) + { + distance_mm = distanceSensor.getDistance(); + } + else + { + distance_mm = 0; + } + + // Sensor power off + digitalWrite(DISTANCESENSOR_PWR, LOW); + + return distance_mm; +} +#endif diff --git a/src/PayloadDigital.h b/src/PayloadDigital.h new file mode 100644 index 0000000..c4f7b70 --- /dev/null +++ b/src/PayloadDigital.h @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadDigital.h +// +// Read digital input channels and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240520 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(_PAYLOAD_DIGITAL) +#define _PAYLOAD_DIGITAL + +#include "../BresserWeatherSensorLWCfg.h" + +#include + +#ifdef DISTANCESENSOR_EN +// A02YYUW / DFRobot SEN0311 Ultrasonic Distance Sensor +#include +#endif + +class PayloadDigital +{ +public: + /*! + * \brief Constructor + */ + PayloadDigital(){}; + + /*! + * \brief Digital channel startup code + */ + void begin(void); + + /*! + * \brief Encode digital data channels for LoRaWAN transmission + * + * \param appPayloadCfg LoRaWAN payload configuration bitmaps + * \param encoder LoRaWAN payload encoder object + */ + void encodeDigital(uint8_t *appPayloadCfg, LoraEncoder &encoder); + +private: +#ifdef DISTANCESENSOR_EN + /*! + * \brief Initialize ultrasonic distance sensor A02YYUW + * + * Initialize UART and power enable pin + */ + void initDistanceSensor(void); + + /*! + * \brief Read ultrasonic distance sensor (A02YYUW) data + * + * \returns distance in mm (0 if invalid) + */ + uint16_t readDistanceSensor(void) +#endif +}; +#endif //_PAYLOAD_DIGITAL \ No newline at end of file From 629ddee83884023255d9d55c68693ab9f39f334a Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Mon, 20 May 2024 21:32:20 +0200 Subject: [PATCH 10/81] Added definitions for AppLayer payload configuration --- BresserWeatherSensorLWCfg.h | 105 +++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index a227c7e..bc2034a 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -46,6 +46,7 @@ // 20240430 Modified battery voltage measurement // 20240504 PowerFeather: added BATTERY_CAPACITY_MAH // Moved LoRaWAN command interface to BresserWeatherSensorLWCmd.h +// 20240520 Added definitions for AppLayer payload configuration // // Note: // Depending on board package file date, either @@ -61,6 +62,7 @@ #if !defined(_LWCFG_H) #define _LWCFG_H +#include // Enable debug mode (debug messages via serial port) // Arduino IDE: Tools->Core Debug Level: "Debug|Verbose" @@ -129,7 +131,7 @@ #define ADC_EN // Enable OneWire temperature measurement -// #define ONEWIRE_EN +#define ONEWIRE_EN // Enable BLE temperature/humidity measurement // Notes: @@ -152,6 +154,7 @@ // Enable Ultrasonic Distance Sensor #if defined(LORAWAN_NODE) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) // #define DISTANCESENSOR_EN +// #define DISTANCESENSOR_CH 8 #endif @@ -290,4 +293,104 @@ const uint8_t UBATT_SAMPLES = 10; } #endif +/// AppLayer payload configuration size in bytes +#define APP_PAYLOAD_CFG_SIZE 24 + +// Default AppLayer payload configuration +// For each sensor/interface type, there is a set of flags. +// If a flag is set, the "channel" is enabled (according to the flags bit position). + +// -- 868 MHz Sensor Types -- +// 0 - Weather Station; 1 Ch +#define APP_PAYLOAD_CFG_TYPE00 0x00 + +// 1 - Weather Station; 1 Ch +// - Professional Wind Gauge (with T and H); 1 Ch +// Flags: +// 0x10 -> Rain Gauge +// 0x20 -> Light Intensity +// 0x40 -> UV Index +// 0x80 -> Rain Statistics +#define APP_PAYLOAD_CFG_TYPE01 0x01 + +// 2 - Thermo-/Hygro-Sensor; 7 Ch +// Ch: 1 +#define APP_PAYLOAD_CFG_TYPE02 0x02 + +// 3 - Pool / Spa Thermometer; 7 Ch +// Ch: 1 +#define APP_PAYLOAD_CFG_TYPE03 0x02 + +// 4 - Soil Moisture Sensor; 7 Ch +// Ch: 1 +#define APP_PAYLOAD_CFG_TYPE04 0x02 + +// 5 - Water Leakage Sensor; 7 Ch +// Ch: 1 +#define APP_PAYLOAD_CFG_TYPE05 0x02 + +// 6 - reserved +#define APP_PAYLOAD_CFG_TYPE06 0x00 + +// 7 - reserved +#define APP_PAYLOAD_CFG_TYPE07 0x00 + +// 8 - Air Quality Sensor PM2.5/PM10; 4 Ch +#define APP_PAYLOAD_CFG_TYPE08 0x00 + +// 9 - Lightning Sensor; 1 Ch +// - Professional Rain Gauge (with T); 1 Ch +// Ch: 0 +// Flags: +// 0x10 -> Ligning Sensor Raw Data +// 0x20 -> Ligning Preprocessed Data +// 0x80 -> Rain Gauge +#define APP_PAYLOAD_CFG_TYPE09 0x31 + +// 10 - CO2 Sensor; 4 Ch +#define APP_PAYLOAD_CFG_TYPE10 0x00 + +// 11 - HCHO/VCO Sensor; 4 Ch +#define APP_PAYLOAD_CFG_TYPE11 0x00 + +// 12 - reserved +#define APP_PAYLOAD_CFG_TYPE12 0x00 + +// 13 - reserved +#define APP_PAYLOAD_CFG_TYPE13 0x00 + +// 14 - reserved +#define APP_PAYLOAD_CFG_TYPE14 0x00 + +// 15 - reserved +#define APP_PAYLOAD_CFG_TYPE15 0x00 + +// -- 1-Wire Sensors -- +// Index: 0 +#define APP_PAYLOAD_CFG_ONEWIRE1 0x00 // onewire[15:8] +#define APP_PAYLOAD_CFG_ONEWIRE0 0x01 // onewire[7:0] + +// -- Analog Inputs -- +// 0x01: Battery Voltage +// 0x02: Supply Voltage +#define APP_PAYLOAD_CFG_ANALOG1 0x01 // analog[15:8] +#define APP_PAYLOAD_CFG_ANALOG0 0x00 // analog[7:0] + +// -- Digital Inputs -- +// Assign to any type of "channel", +// e.g. GPIO, SPI, I2C, UART, ... +#define APP_PAYLOAD_CFG_DIGITAL3 0x00 // digital[31:24] +#define APP_PAYLOAD_CFG_DIGITAL2 0x00 // digital[23:16] +#define APP_PAYLOAD_CFG_DIGITAL1 0x00 // digital[15:8] +#define APP_PAYLOAD_CFG_DIGITAL0 0x00 // digital[7:0] + +#define APP_PAYLOAD_OFFS_ONEWIRE 16 +#define APP_PAYLOAD_BYTES_ONEWIRE 2 + +#define APP_PAYLOAD_OFFS_ANALOG 18 +#define APP_PAYLOAD_BYTES_ANALOG 2 + +#define APP_PAYLOAD_OFFS_DIGITAL 20 +#define APP_PAYLOAD_BYTES_DIGITAL 4 + #endif // _LWCFG_H From f5b405e755776fb593550b780a442ea4a7c625d3 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Tue, 21 May 2024 18:34:32 +0200 Subject: [PATCH 11/81] Fixes --- src/PayloadDigital.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PayloadDigital.cpp b/src/PayloadDigital.cpp index 5577981..90bb129 100644 --- a/src/PayloadDigital.cpp +++ b/src/PayloadDigital.cpp @@ -64,9 +64,10 @@ void PayloadDigital::encodeDigital(uint8_t *appPayloadCfg, LoraEncoder &encoder) { for (int bit = 7; bit >= 0; bit--) { - if ((appPayloadCfg[APP_PAYLOAD_OFFS_ONEWIRE + i] >> bit) & 0x1) + if ((appPayloadCfg[APP_PAYLOAD_OFFS_DIGITAL + i] >> bit) & 0x1) + { #ifdef DISTANCESENSOR_EN - // Check if sensor with given index is enabled + // Check if channel is enabled if (ch == DISTANCESENSOR_CH) { uint16_t distance_mm = readDistanceSensor(); @@ -81,6 +82,7 @@ void PayloadDigital::encodeDigital(uint8_t *appPayloadCfg, LoraEncoder &encoder) encoder.writeUint16(distance_mm); } #endif + } ch--; } } From 927b774f7dc89effa294e67e3bd3510d12bd54be Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Tue, 21 May 2024 18:36:02 +0200 Subject: [PATCH 12/81] Added UBATT_CH/USUPPLY_CH --- BresserWeatherSensorLWCfg.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index bc2034a..e8fabe4 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -47,6 +47,7 @@ // 20240504 PowerFeather: added BATTERY_CAPACITY_MAH // Moved LoRaWAN command interface to BresserWeatherSensorLWCmd.h // 20240520 Added definitions for AppLayer payload configuration +// 20240521 Added UBATT_CH/USUPPLY_CH // // Note: // Depending on board package file date, either @@ -218,6 +219,9 @@ // Voltage divider R1 / (R1 + R2) -> V_meas = V(R1 + R2); V_adc = V(R1) const float SUPPLY_DIV = 0.5; const uint8_t SUPPLY_SAMPLES = 10; + +// "Channel" in appPayloadCfg +#define USUPPLY_CH 1 #endif #ifdef PIN_ADC1_IN @@ -278,6 +282,9 @@ const float UBATT_DIV = 0.2041; const float UBATT_DIV = 0.5; #endif const uint8_t UBATT_SAMPLES = 10; + +// "Channel" appPayloadCfg +#define UBATT_CH 1 #endif #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) From 9cd92b3a12033353263eeee9667780ea5b6a3190 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Tue, 21 May 2024 18:45:32 +0200 Subject: [PATCH 13/81] Moved USUPPLY_CH --- BresserWeatherSensorLWCfg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index e8fabe4..c5ffb19 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -219,10 +219,10 @@ // Voltage divider R1 / (R1 + R2) -> V_meas = V(R1 + R2); V_adc = V(R1) const float SUPPLY_DIV = 0.5; const uint8_t SUPPLY_SAMPLES = 10; +#endif // "Channel" in appPayloadCfg #define USUPPLY_CH 1 -#endif #ifdef PIN_ADC1_IN // Voltage divider R1 / (R1 + R2) -> V_meas = V(R1 + R2); V_adc = V(R1) From 98460646d78e4fb7b3444238cf27ea4e51b519ef Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Tue, 21 May 2024 18:46:18 +0200 Subject: [PATCH 14/81] Created --- src/PayloadAnalog.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++ src/PayloadAnalog.h | 70 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/PayloadAnalog.cpp create mode 100644 src/PayloadAnalog.h diff --git a/src/PayloadAnalog.cpp b/src/PayloadAnalog.cpp new file mode 100644 index 0000000..6832a9c --- /dev/null +++ b/src/PayloadAnalog.cpp @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadAnalog.cpp +// +// Read analog input channels and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240521 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#include "PayloadAnalog.h" + + +void PayloadAnalog::begin(void) +{ + +} + +void PayloadAnalog::encodeAnalog(uint8_t *appPayloadCfg, LoraEncoder &encoder) +{ + unsigned ch = (APP_PAYLOAD_BYTES_ANALOG * 8) - 1; + for (int i = APP_PAYLOAD_BYTES_ANALOG - 1; i >= 0; i--) + { + for (int bit = 7; bit >= 0; bit--) + { + // Check if channel is enabled + if ((appPayloadCfg[APP_PAYLOAD_OFFS_ANALOG + i] >> bit) & 0x1) { + + if (ch == UBATT_CH) + { + uint16_t uBatt = getBatteryVoltage(); + log_i("ch %02u: U_batt: %04u mv", ch); + encoder.writeUint16(uBatt); + } + + if (ch == USUPPLY_CH) + { + uint16_t uSupply = getSupplyVoltage(); + log_i("ch %02u: U_supply: %04u mv", ch); + encoder.writeUint16(uSupply); + } + } + ch--; + } + } +} + diff --git a/src/PayloadAnalog.h b/src/PayloadAnalog.h new file mode 100644 index 0000000..9cb849e --- /dev/null +++ b/src/PayloadAnalog.h @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadAnalog.h +// +// Read analog input channels and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240521 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(_PAYLOAD_ANALOG) +#define _PAYLOAD_ANALOG + +#include "../BresserWeatherSensorLWCfg.h" +#include "adc/adc.h" +#include + + +class PayloadAnalog +{ +public: + /*! + * \brief Constructor + */ + PayloadAnalog(){}; + + /*! + * \brief Analog channel startup code + */ + void begin(void); + + /*! + * \brief Encode analog data channels for LoRaWAN transmission + * + * \param appPayloadCfg LoRaWAN payload configuration bitmaps + * \param encoder LoRaWAN payload encoder object + */ + void encodeAnalog(uint8_t *appPayloadCfg, LoraEncoder &encoder); +}; +#endif //_PAYLOAD_ANALOG \ No newline at end of file From 612cdb6f5149f466848cecf2b34035830ac480a3 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Wed, 22 May 2024 20:24:52 +0200 Subject: [PATCH 15/81] Created --- src/PayloadBresser.cpp | 333 +++++++++++++++++++++++++++++++++++++++++ src/PayloadBresser.h | 104 +++++++++++++ 2 files changed, 437 insertions(+) create mode 100644 src/PayloadBresser.cpp create mode 100644 src/PayloadBresser.h diff --git a/src/PayloadBresser.cpp b/src/PayloadBresser.cpp new file mode 100644 index 0000000..4be77e8 --- /dev/null +++ b/src/PayloadBresser.cpp @@ -0,0 +1,333 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadBresser.cpp +// +// Read Bresser sensor data and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240521 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#include "PayloadBresser.h" + +void PayloadBresser::begin(void) +{ +} + +void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) +{ + for (int type = 0; type < 16; type++) + { + // Skip if bitmap is zero + if (appPayloadCfg[type] == 0) + continue; + +#ifdef LIGHTNINGSENSOR_EN + if (type == SENSOR_TYPE_LIGHTNING) + { + int idx = -1; + idx = weatherSensor.findType(type, 0); + + encodeLightningSensor(idx, appPayloadCfg[type], encoder); + continue; + } +#endif + + for (int bit = 7; bit >= 0; bit--) + { + // Check if channel is enabled + if (!((appPayloadCfg[type] >> bit) & 0x1)) + continue; + + int idx = -1; + idx = weatherSensor.findType(type, bit); + + if (type == SENSOR_TYPE_THERMO_HYGRO) + { + encodeThermoHygroSensor(idx, encoder); + } + else if (type == SENSOR_TYPE_POOL_THERMO) + { + encodePoolThermometer(idx, encoder); + } + else if (type == SENSOR_TYPE_SOIL) + { + encodeSoilSensor(idx, encoder); + } + else if (type == SENSOR_TYPE_LEAKAGE) + { + encodeLeakageSensor(idx, encoder); + } + else if (type == SENSOR_TYPE_AIR_PM) + { + encodeAirPmSensor(idx, encoder); + } + else if (type == SENSOR_TYPE_WEATHER0) + { + // Note: + // SENSOR_TYPE_RAIN is changed to SENSOR_TYPE_WEATHER0 by the decoder + // to avoid mix-up with SENSOR_TYPE_LIGHTNING (same type code!). + // FIXME + } + else if (type == SENSOR_TYPE_CO2) + { + encodeCo2Sensor(idx, encoder); + } + else if (type == SENSOR_TYPE_HCHO_VOC) + { + encodeHchoVocSensor(idx, encoder); + } + } + } +} + +void PayloadBresser::encodeThermoHygroSensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Thermo/Hygro Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0x7FFF); + encoder.writeUint8(0xFF); + } + else + { + log_i("Temperature: %3.1f °C", weatherSensor.sensor[idx].w.temp_c); + log_i("Humidity: %2d %%", weatherSensor.sensor[idx].w.humidity); + encoder.writeTemperature(weatherSensor.sensor[idx].w.temp_c); + encoder.writeUint8(weatherSensor.sensor[idx].w.humidity); + } +} + +void PayloadBresser::encodePoolThermometer(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Pool Thermometer Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0x7FFF); + } + else + { + log_i("Temperature: %3.1f °C", weatherSensor.sensor[idx].w.temp_c); + encoder.writeTemperature(weatherSensor.sensor[idx].w.temp_c); + } +} + +void PayloadBresser::encodeSoilSensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Soil Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0x7FFF); + encoder.writeUint8(0xFF); + } + else + { + log_i("Soil Temperature: %3.1f °C", weatherSensor.sensor[idx].soil.temp_c); + log_i("Soil Moisture: %2d %%", weatherSensor.sensor[idx].soil.moisture); + encoder.writeTemperature(weatherSensor.sensor[idx].soil.temp_c); + encoder.writeUint8(weatherSensor.sensor[idx].soil.moisture); + } +} + +void PayloadBresser::encodeLeakageSensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Leakage Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint8(0xFF); + } + else + { + log_i("Leakage Alarm: %u", weatherSensor.sensor[idx].leak.alarm); + encoder.writeUint8(weatherSensor.sensor[idx].leak.alarm ? 1 : 0); + } +} + +void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Air Quality (PM) Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0xFFFF); + encoder.writeUint16(0xFFFF); + encoder.writeUint16(0xFFFF); + } + else + { + // Air Quality (Particular Matter) Sensor + if (weatherSensor.sensor[idx].pm.pm_1_0_init) + { + log_i("PM1.0: init"); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("PM1.0: %u µg/m³", weatherSensor.sensor[idx].pm.pm_1_0); + encoder.writeUint16(weatherSensor.sensor[idx].pm.pm_1_0); + } + if (weatherSensor.sensor[idx].pm.pm_2_5_init) + { + log_i("PM2.5: init"); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("PM2.5: %u µg/m³", weatherSensor.sensor[idx].pm.pm_2_5); + encoder.writeUint16(weatherSensor.sensor[idx].pm.pm_2_5); + } + if (weatherSensor.sensor[idx].pm.pm_10_init) + { + log_i("PM10: init"); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("PM10: %u µg/m³", weatherSensor.sensor[idx].pm.pm_10); + encoder.writeUint16(weatherSensor.sensor[idx].pm.pm_10); + } + } +} + +#ifdef LIGHTNINGSENSOR_EN +void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder &encoder) +{ + if (flags & 0x21) + { + // Raw sensor values + if (idx == -1) + { + log_i("-- Lightning Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint8(0xFF); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("Lightning Distance: %2u km", weatherSensor.sensor[idx].lgt.distance_km); + log_i("Lightning Strike Count %4u", weatherSensor.sensor[idx].lgt.strike_count); + encoder.writeUint8(weatherSensor.sensor[idx].lgt.distance_km); + encoder.writeUint16(weatherSensor.sensor[idx].lgt.strike_count); + } + } + + if (flags & 0x20) + { + // Post-processed sensor values + if (lightningProc.lastEvent(lightn_ts, lightn_events, lightn_distance)) + { +#if CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + struct tm timeinfo; + char tbuf[25]; + + localtime_r(&lightn_ts, &timeinfo); + strftime(tbuf, 25, "%Y-%m-%d %H:%M:%S", &timeinfo); +#endif + log_i("Last lightning event @%s: %d events, %d km", tbuf, lightn_events, lightn_distance); + encoder.writeUnixtime(lightn_ts); + encoder.writeUint16(lightn_events); + encoder.writeUint8(lightn_distance); + } + else + { + log_i("-- No Lightning Event Data Available"); + encoder.writeUint32(0xFFFFFFFF); + encoder.writeUint16(0xFFFF); + encoder.writeUint8(0xFF); + } + } +} +#endif + +void PayloadBresser::encodeCo2Sensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- CO2 Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0xFFFF); + } + else + { + // CO2 Sensor + if (weatherSensor.sensor[idx].co2.co2_init) + { + log_i("CO2: init"); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("CO2: %4u", weatherSensor.sensor[idx].co2.co2_ppm); + encoder.writeUint16(weatherSensor.sensor[idx].co2.co2_ppm); + } + } +} + +void PayloadBresser::encodeHchoVocSensor(int idx, LoraEncoder &encoder) +{ + if (idx == -1) + { + log_i("-- Air Quality (HCHO/VOC) Sensor Failure"); + // fill with suspicious dummy values + encoder.writeUint16(0xFFFF); + encoder.writeUint8(0xFF); + } + else + { + if (weatherSensor.sensor[idx].voc.hcho_init) + { + log_i("HCHO: init"); + encoder.writeUint16(0xFFFF); + } + else + { + log_i("HCHO: %u", weatherSensor.sensor[idx].voc.hcho_ppb); + encoder.writeUint16(weatherSensor.sensor[idx].voc.hcho_ppb); + } + + if (weatherSensor.sensor[idx].voc.voc_init) + { + log_i("VOC: init"); + encoder.writeUint8(0xFF); + } + else + { + log_i("VOC: %u", weatherSensor.sensor[idx].voc.voc_level); + encoder.writeUint8(weatherSensor.sensor[idx].voc.voc_level); + } + } +} \ No newline at end of file diff --git a/src/PayloadBresser.h b/src/PayloadBresser.h new file mode 100644 index 0000000..f60f9e0 --- /dev/null +++ b/src/PayloadBresser.h @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////// +// PayloadBresser.h +// +// Read Bresser sensor data and encode as LoRaWAN payload +// +// created: 05/2024 +// +// +// MIT License +// +// Copyright (c) 2024 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20240521 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(_PAYLOAD_BRESSER) +#define _PAYLOAD_BRESSER + +#include "../BresserWeatherSensorLWCfg.h" +#include "WeatherSensorCfg.h" +#include + +#ifdef RAINDATA_EN +#include "RainGauge.h" +#endif +#ifdef LIGHTNINGSENSOR_EN +#include "Lightning.h" +#endif + +#include + +class PayloadBresser +{ +public: + /// Bresser Weather Sensor Receiver + WeatherSensor weatherSensor; + +private: +#ifdef LIGHTNINGSENSOR_EN + time_t lightn_ts; + int lightn_events; + uint8_t lightn_distance; + + /// Lightning sensor post-processing + Lightning lightningProc; +#endif + +public: + /*! + * \brief Constructor + */ + PayloadBresser(){}; + + /*! + * \brief Bresser sensors startup code + */ + void begin(void); + + /*! + * \brief Encode Bresser sensor data for LoRaWAN transmission + * + * \param appPayloadCfg LoRaWAN payload configuration bitmaps + * \param encoder LoRaWAN payload encoder object + */ + void encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder); + +private: + void encodeThermoHygroSensor(int idx, LoraEncoder &encoder); + void encodePoolThermometer(int idx, LoraEncoder &encoder); + void encodeSoilSensor(int idx, LoraEncoder &encoder); + void encodeLeakageSensor(int idx, LoraEncoder &encoder); + void encodeAirPmSensor(int idx, LoraEncoder &encoder); +#ifdef LIGHTNINGSENSOR_EN + void encodeLightningSensor(int idx, uint8_t flags, LoraEncoder &encoder); +#endif + void encodeCo2Sensor(int idx, LoraEncoder &encoder); + void encodeHchoVocSensor(int idx, LoraEncoder &encoder); +}; +#endif //_PAYLOAD_BRESSER \ No newline at end of file From 5d7b9eecd5fddec532fee18f3c5aa4437f90966b Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Thu, 23 May 2024 21:12:36 +0200 Subject: [PATCH 16/81] Added encodeWeatherSensor(), added defines for signalling invalid data --- src/PayloadBresser.cpp | 195 +++++++++++++++++++++++++++++++++-------- src/PayloadBresser.h | 10 +++ 2 files changed, 168 insertions(+), 37 deletions(-) diff --git a/src/PayloadBresser.cpp b/src/PayloadBresser.cpp index 4be77e8..1cd8f78 100644 --- a/src/PayloadBresser.cpp +++ b/src/PayloadBresser.cpp @@ -32,9 +32,13 @@ // History: // // 20240521 Created +// 20240523 Added encodeWeatherSensor(), +// added defines for signalling invalid data // // ToDo: -// - +// - Add handling of Weather Sensor flags +// - Add handling of Professional Rain Gauge +// - Add rain gauge statistics // /////////////////////////////////////////////////////////////////////////////// @@ -46,31 +50,48 @@ void PayloadBresser::begin(void) void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) { - for (int type = 0; type < 16; type++) + // Handle weather sensors first + // SENSOR_TYPE_WEATHER0 and WEATHER_TYPE_WEATHER1 are treated as one. + // Those sensors only have one channel (0). + + uint8_t flags = appPayloadCfg[0] | appPayloadCfg[1]; + if (flags & 1) + { + // Try to find SENSOR_TYPE_WEATHER1 + int idx = weatherSensor.findType(SENSOR_TYPE_WEATHER1); + if (idx < 0) + { + // Try to find SENSOR_TYPE_WEATHER0 + idx = weatherSensor.findType(SENSOR_TYPE_WEATHER0); + } + encodeWeatherSensor(idx, flags, encoder); + } + + for (int type = 2; type < 16; type++) { // Skip if bitmap is zero if (appPayloadCfg[type] == 0) continue; #ifdef LIGHTNINGSENSOR_EN + // Lightning sensor has fixed channel (0) if (type == SENSOR_TYPE_LIGHTNING) { - int idx = -1; - idx = weatherSensor.findType(type, 0); + int idx = weatherSensor.findType(type, 0); encodeLightningSensor(idx, appPayloadCfg[type], encoder); continue; } #endif + // Handle sensors with channel selection for (int bit = 7; bit >= 0; bit--) { // Check if channel is enabled if (!((appPayloadCfg[type] >> bit) & 0x1)) continue; - int idx = -1; - idx = weatherSensor.findType(type, bit); + int idx = weatherSensor.findType(type, bit); if (type == SENSOR_TYPE_THERMO_HYGRO) { @@ -111,14 +132,114 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) } } +void PayloadBresser::encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &encoder) +{ + // Weather Stations Professional 3-in-1 Professional + // 5-in-1 6-in-1 7-in-1 Rain Gauge Wind Gauge + // + // Temperature X X X X X + // Humidity X X X X + // Wind X X X X + // Rain X X X X + // UV X X + // Light Intensity X + + if (idx == -1) + { + log_i("-- Weather Sensor Failure"); + // Invalidate + encoder.writeUint16(INV_TEMP); // Temperature + encoder.writeUint8(INV_UINT8); // Humidity +#ifdef ENCODE_AS_FLOAT + encoder.writeRawFloat(INV_FLOAT); // Wind gust + encoder.writeRawFloat(INV_FLOAT); // Wind avg + encoder.writeRawFloat(INV_FLOAT); // Wind dir +#else + encoder.writeUint16(INV_UINT16); // Wind gust + encoder.writeUint16(INV_UINT16); // Wind avg + encoder.writeUint16(INV_UINT16); // Wind dir +#endif + encoder.writeRawFloat(INV_FLOAT); // Rain + encoder.writeRawFloat(INV_UINT32); // Light + encoder.writeUint8(INV_UINT8); // UV + } + else + { + if (weatherSensor.sensor[idx].w.temp_ok) + { + log_i("Air Temperature: %3.1f °C", weatherSensor.sensor[idx].w.temp_c); + encoder.writeTemperature(weatherSensor.sensor[idx].w.temp_c); + } + else + { + log_i("Air Temperature: --.- °C"); + encoder.writeUint16(INV_UINT16); + } + if (weatherSensor.sensor[idx].w.humidity_ok) + { + log_i("Humidity: %2d %%", weatherSensor.sensor[idx].w.humidity); + } + else + { + log_i("Humidity: -- %%"); + encoder.writeUint8(INV_UINT8); + } + if (weatherSensor.sensor[idx].w.rain_ok) + { + log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[idx].w.rain_mm); + encoder.writeRawFloat(weatherSensor.sensor[idx].w.rain_mm); + } + else + { + log_i("Rain Gauge: ---.- mm"); + encoder.writeRawFloat(INV_FLOAT); + } + if (weatherSensor.sensor[idx].w.wind_ok) + { + log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1 / 10.0); + log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1 / 10.0); + log_i("Wind Direction: %4.1f °", weatherSensor.sensor[idx].w.wind_direction_deg_fp1 / 10.0); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_direction_deg_fp1); + } + else + { + log_i("Wind Speed (avg.): --.- m/s"); + log_i("Wind Speed (max.): --.- m/s"); + log_i("Wind Direction: ---.- °"); + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); + } + if (weatherSensor.sensor[idx].w.uv_ok) + { + log_i("UV Index: %3.1f", weatherSensor.sensor[idx].w.uv); + encoder.writeUint8(static_cast(weatherSensor.sensor[idx].w.uv * 10)); + } + else { + log_i("UV Index: --.-"); + encoder.writeUint8(INV_UINT8); + } + if (weatherSensor.sensor[idx].w.light_ok) { + log_i("Light intensity: %06f lx", weatherSensor.sensor[idx].w.light_lux); + encoder.writeUint32(static_cast(weatherSensor.sensor[idx].w.light_lux)); + } + else { + log_i("Light intensity: ------ lx"); + encoder.writeUint32(INV_UINT32); + } + } +} + void PayloadBresser::encodeThermoHygroSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { log_i("-- Thermo/Hygro Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0x7FFF); - encoder.writeUint8(0xFF); + // Invalidate + encoder.writeUint16(INV_TEMP); + encoder.writeUint8(INV_UINT8); } else { @@ -134,8 +255,8 @@ void PayloadBresser::encodePoolThermometer(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- Pool Thermometer Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0x7FFF); + // Invalidate + encoder.writeUint16(INV_TEMP); } else { @@ -149,9 +270,9 @@ void PayloadBresser::encodeSoilSensor(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- Soil Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0x7FFF); - encoder.writeUint8(0xFF); + // Invalidate + encoder.writeUint16(INV_TEMP); + encoder.writeUint8(INV_UINT8); } else { @@ -167,8 +288,8 @@ void PayloadBresser::encodeLeakageSensor(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- Leakage Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint8(0xFF); + // Invalidate + encoder.writeUint8(INV_UINT8); } else { @@ -182,10 +303,10 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- Air Quality (PM) Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0xFFFF); - encoder.writeUint16(0xFFFF); - encoder.writeUint16(0xFFFF); + // Invalidate + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); } else { @@ -193,7 +314,7 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) if (weatherSensor.sensor[idx].pm.pm_1_0_init) { log_i("PM1.0: init"); - encoder.writeUint16(0xFFFF); + encoder.writeUint16(INV_UINT16); } else { @@ -203,7 +324,7 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) if (weatherSensor.sensor[idx].pm.pm_2_5_init) { log_i("PM2.5: init"); - encoder.writeUint16(0xFFFF); + encoder.writeUint16(INV_UINT16); } else { @@ -213,7 +334,7 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) if (weatherSensor.sensor[idx].pm.pm_10_init) { log_i("PM10: init"); - encoder.writeUint16(0xFFFF); + encoder.writeUint16(INV_UINT16); } else { @@ -232,9 +353,9 @@ void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder & if (idx == -1) { log_i("-- Lightning Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint8(0xFF); - encoder.writeUint16(0xFFFF); + // Invalidate + encoder.writeUint8(INV_UINT8); + encoder.writeUint16(INV_UINT16); } else { @@ -265,9 +386,9 @@ void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder & else { log_i("-- No Lightning Event Data Available"); - encoder.writeUint32(0xFFFFFFFF); - encoder.writeUint16(0xFFFF); - encoder.writeUint8(0xFF); + encoder.writeUint32(INV_UINT32); + encoder.writeUint16(INV_UINT16); + encoder.writeUint8(INV_UINT8); } } } @@ -278,8 +399,8 @@ void PayloadBresser::encodeCo2Sensor(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- CO2 Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0xFFFF); + // Invalidate + encoder.writeUint16(INV_UINT16); } else { @@ -287,7 +408,7 @@ void PayloadBresser::encodeCo2Sensor(int idx, LoraEncoder &encoder) if (weatherSensor.sensor[idx].co2.co2_init) { log_i("CO2: init"); - encoder.writeUint16(0xFFFF); + encoder.writeUint16(INV_UINT16); } else { @@ -302,16 +423,16 @@ void PayloadBresser::encodeHchoVocSensor(int idx, LoraEncoder &encoder) if (idx == -1) { log_i("-- Air Quality (HCHO/VOC) Sensor Failure"); - // fill with suspicious dummy values - encoder.writeUint16(0xFFFF); - encoder.writeUint8(0xFF); + // Invalidate + encoder.writeUint16(INV_UINT16); + encoder.writeUint8(INV_UINT8); } else { if (weatherSensor.sensor[idx].voc.hcho_init) { log_i("HCHO: init"); - encoder.writeUint16(0xFFFF); + encoder.writeUint16(INV_UINT16); } else { @@ -322,7 +443,7 @@ void PayloadBresser::encodeHchoVocSensor(int idx, LoraEncoder &encoder) if (weatherSensor.sensor[idx].voc.voc_init) { log_i("VOC: init"); - encoder.writeUint8(0xFF); + encoder.writeUint8(INV_UINT8); } else { diff --git a/src/PayloadBresser.h b/src/PayloadBresser.h index f60f9e0..1f94814 100644 --- a/src/PayloadBresser.h +++ b/src/PayloadBresser.h @@ -32,6 +32,8 @@ // History: // // 20240521 Created +// 20240523 Added encodeWeatherSensor(), +// added defines for signalling invalid data // // ToDo: // - @@ -54,6 +56,13 @@ #include +// Encoding of invalid values +#define INV_FLOAT 0xFFFFFFFF +#define INV_UINT32 0xFFFFFFFF +#define INV_UINT16 0xFFFF +#define INV_UINT8 0xFF +#define INV_TEMP 0x7FFF + class PayloadBresser { public: @@ -90,6 +99,7 @@ class PayloadBresser void encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder); private: + void encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &encoder); void encodeThermoHygroSensor(int idx, LoraEncoder &encoder); void encodePoolThermometer(int idx, LoraEncoder &encoder); void encodeSoilSensor(int idx, LoraEncoder &encoder); From 3435457eb26020f7349b3cc0fd1ff4094e2a9c9e Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 08:11:53 +0200 Subject: [PATCH 17/81] Added appPayloadCfgDef, setAppPayloadCfg() & getAppPayloadCfg(), moved code to Payload* --- src/AppLayer.cpp | 473 ++++++++--------------------------------------- src/AppLayer.h | 138 ++++++++------ 2 files changed, 156 insertions(+), 455 deletions(-) diff --git a/src/AppLayer.cpp b/src/AppLayer.cpp index f482679..8aedaff 100644 --- a/src/AppLayer.cpp +++ b/src/AppLayer.cpp @@ -51,54 +51,17 @@ // 20240507 Added configuration of max_sensors/rx_flags via LoRaWAN // 20240508 Added configuration of en_decoders via LoRaWAN // 20240515 Added getOneWireTemperature() -// +// 20240520 Moved 1-Wire sensor code to PayloadOneWire.h/.cpp +// 20240524 Added appPayloadCfgDef, setAppPayloadCfg() & getAppPayloadCfg() +// Moved code to PayloadBresser, PayloadAnalog & PayloadDigital // // ToDo: -// - +// - Move BLE code to separate class // /////////////////////////////////////////////////////////////////////////////// #include "AppLayer.h" -#ifdef ONEWIRE_EN - // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) - static OneWire oneWire(PIN_ONEWIRE_BUS); //!< OneWire bus - - // Pass our oneWire reference to Dallas Temperature. - static DallasTemperature owTempSensors(&oneWire); //!< Dallas temperature sensors connected to OneWire bus -#endif - -#ifdef ONEWIRE_EN - /*! - * \brief Get temperature from Maxim OneWire Sensor - * - * \param index sensor index - * - * \returns temperature in degrees Celsius or DEVICE_DISCONNECTED_C - */ - float - AppLayer::getOneWireTemperature(uint8_t index) - { - // Call sensors.requestTemperatures() to issue a global temperature - // request to all devices on the bus - owTempSensors.requestTemperatures(); - - // Get temperature by index - float tempC = owTempSensors.getTempCByIndex(index); - - // Check if reading was successful - if (tempC != DEVICE_DISCONNECTED_C) - { - log_d("Temperature = %.2f°C", tempC); - } - else - { - log_d("Error: Could not read temperature data"); - } - - return tempC; - }; -#endif uint8_t AppLayer::decodeDownlink(uint8_t port, uint8_t *payload, size_t size) @@ -226,6 +189,27 @@ AppLayer::decodeDownlink(uint8_t port, uint8_t *payload, size_t size) return 0; } + + if ((port == CMD_GET_APP_PAYLOAD_CFG) && (payload[0] == 0x00) && (size == 1)) + { + log_d("Get AppLayer payload configuration"); + return CMD_GET_APP_PAYLOAD_CFG; + } + + if ((port == CMD_SET_SENSORS_CFG) && (size == 16)) + { + log_d("Set AppLayer payload configuration"); + for (size_t i=0; i < 16; i++) { + log_d("Type%02d: 0x%X", i, payload[i]); + } + log_d("1-Wire: 0x%04X", payload[9] << 8 | payload[8]); + log_d("Analog: 0x%04X", (payload[11] << 8) | payload[10]); + log_d("Digital: 0x%08X", (payload[15] << 24) | (payload[14] << 16) | (payload[13] << 8) | payload[12]); + + setAppPayloadCfg(payload, APP_PAYLOAD_CFG_SIZE); + return 0; + } + #endif return 0; } @@ -243,10 +227,6 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) { (void)port; // suppress warning regarding unused parameter -#ifdef PIN_SUPPLY_IN - uint16_t supply_voltage = getVoltage(PIN_SUPPLY_IN, SUPPLY_SAMPLES, SUPPLY_DIV); -#endif - uint16_t battery_voltage = getBatteryVoltage(); bool mithermometer_valid = false; #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) float indoor_temp_c; @@ -264,208 +244,36 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) // Get sensor data - run BLE scan for bleSensors.getData(ble_scantime, ble_active); #endif -#ifdef LIGHTNINGSENSOR_EN - time_t lightn_ts; - int lightn_events; - uint8_t lightn_distance; -#endif - - weatherSensor.begin(); - weatherSensor.clearSlots(); - appPrefs.begin("BWS-LW-APP", false); - uint8_t ws_timeout = appPrefs.getUChar("ws_timeout", WEATHERSENSOR_TIMEOUT); - log_d("Preferences: weathersensor_timeout: %u s", ws_timeout); - appPrefs.end(); - log_i("Waiting for Weather Sensor Data; timeout %u s", ws_timeout); - bool decode_ok = weatherSensor.getData(ws_timeout * 1000, DATA_ALL_SLOTS); - (void)decode_ok; - log_i("Receiving Weather Sensor Data %s", decode_ok ? "o.k." : "failed"); log_v("Port: %d", port); -#ifdef RAINDATA_EN - // Check if time is valid - if (*_rtcLastClockSync > 0) - { - // Get local date and time - struct tm timeinfo; - time_t tnow = _rtc->getLocalEpoch(); - localtime_r(&tnow, &timeinfo); - - // Find weather sensor and determine rain gauge overflow limit - // Try to find SENSOR_TYPE_WEATHER0 - int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); - if (ws > -1) - { - rainGauge.set_max(1000); - } - else - { - // Try to find SENSOR_TYPE_WEATHER1 - ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - rainGauge.set_max(100000); - } - - // If weather sensor has be found and rain data is valid, update statistics - if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) - { - rainGauge.update(tnow, weatherSensor.sensor[ws].w.rain_mm, weatherSensor.sensor[ws].startup); - } - } -#endif - -#ifdef LIGHTNINGSENSOR_EN - // Check if time is valid - if (*_rtcLastClockSync > 0) - { - // Get local date and time - time_t tnow = _rtc->getLocalEpoch(); - - // Find lightning sensor - int ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); - - // If lightning sensor has be found and data is valid, run post-processing - if ((ls > -1) && weatherSensor.sensor[ls].valid) - { - lightningProc.update(tnow, weatherSensor.sensor[ls].lgt.strike_count, weatherSensor.sensor[ls].lgt.distance_km, weatherSensor.sensor[ls].startup); - } - } -#endif - // - // Find Bresser sensor data in array - // - - // Try to find SENSOR_TYPE_WEATHER0 - int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); - if (ws < 0) - { - // Try to find SENSOR_TYPE_WEATHER1 - ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - } - - int s1 = -1; -#ifdef SOILSENSOR_EN - // Try to find SENSOR_TYPE_SOIL - s1 = weatherSensor.findType(SENSOR_TYPE_SOIL, 1); -#endif - - int ls = -1; -#ifdef LIGHTNINGSENSOR_EN - // Try to find SENSOR_TYPE_LIGHTNING - ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); -#endif - log_i("--- Uplink Data ---"); - // Debug output for weather sensor data - if (ws > -1) - { - if (weatherSensor.sensor[ws].w.temp_ok) - { - log_i("Air Temperature: %3.1f °C", weatherSensor.sensor[ws].w.temp_c); - } - else - { - log_i("Air Temperature: --.- °C"); - } - if (weatherSensor.sensor[ws].w.humidity_ok) - { - log_i("Humidity: %2d %%", weatherSensor.sensor[ws].w.humidity); - } - else - { - log_i("Humidity: -- %%"); - } - if (weatherSensor.sensor[ws].w.rain_ok) - { - log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[ws].w.rain_mm); - } - else - { - log_i("Rain Gauge: ---.- mm"); - } - log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1 / 10.0); - log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1 / 10.0); - log_i("Wind Direction: %4.1f °", weatherSensor.sensor[ws].w.wind_direction_deg_fp1 / 10.0); - } - else - { - log_i("-- Weather Sensor Failure"); - } - -// Debug output for soil sensor data -#ifdef SOILSENSOR_EN - if (s1 > -1) - { - log_i("Soil Temperature 1: %3.1f °C", weatherSensor.sensor[s1].soil.temp_c); - log_i("Soil Moisture 1: %2d %%", weatherSensor.sensor[s1].soil.moisture); - } - else - { - log_i("-- Soil Sensor 1 Failure"); - } -#endif + // TODO: Handle battery status flags in PayloadBresser + // // Sensor status flags + // encoder.writeBitmap(0, + // mithermometer_valid, + // (ls > -1) ? weatherSensor.sensor[ls].valid : false, + // (ls > -1) ? weatherSensor.sensor[ls].battery_ok : false, + // (s1 > -1) ? weatherSensor.sensor[s1].valid : false, + // (s1 > -1) ? weatherSensor.sensor[s1].battery_ok : false, + // (ws > -1) ? weatherSensor.sensor[ws].valid : false, + // (ws > -1) ? weatherSensor.sensor[ws].battery_ok : false); -// Debug output for lightning sensor data -#ifdef LIGHTNINGSENSOR_EN - if (ls > -1) - { - log_i("Lightning counter: %4d", weatherSensor.sensor[ls].lgt.strike_count); - log_i("Lightning distance: %2d km", weatherSensor.sensor[ls].lgt.distance_km); - } - else - { - log_i("-- Lightning Sensor Failure"); - } - if (lightningProc.lastEvent(lightn_ts, lightn_events, lightn_distance)) - { -#if CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - struct tm timeinfo; - char tbuf[25]; - - localtime_r(&lightn_ts, &timeinfo); - strftime(tbuf, 25, "%Y-%m-%d %H:%M:%S", &timeinfo); -#endif - log_i("Last lightning event @%s: %d events, %d km", tbuf, lightn_events, lightn_distance); - } - else - { - log_i("-- No Lightning Event Data Available"); - } -#endif +encodeBresser(appPayloadCfg, encoder); #ifdef ONEWIRE_EN - float water_temp_c = getOneWireTemperature(); - - // Debug output for auxiliary sensors/voltages - if (water_temp_c != DEVICE_DISCONNECTED_C) - { - log_i("Water Temperature: % 2.1f °C", water_temp_c); - } - else - { - log_i("Water Temperature: --.- °C"); - water_temp_c = -30.0; - } -#endif -#ifdef DISTANCESENSOR_EN - if (distance_mm > 0) - { - log_i("Distance: %4d mm", distance_mm); - } - else - { - log_i("Distance: ---- mm"); - } -#endif -#ifdef PIN_SUPPLY_IN - log_i("Supply Voltage: %4d mV", supply_voltage); -#endif -#if defined(ADC_EN) && defined(PIN_ADC_IN) - log_i("Battery Voltage: %4d mV", battery_voltage); + encodeOneWire(appPayloadCfg, encoder); #endif +// Voltages / auxiliary analog sensor data +encodeAnalog(appPayloadCfg, encoder); + +// Digital Sensors (GPIO, UART, I2C, SPI, ...) +encodeDigital(appPayloadCfg, encoder); + +// BLE Temperature/Humidity Sensors #if defined(MITHERMOMETER_EN) float div = 100.0; #elif defined(THEENGSDECODER_EN) @@ -479,172 +287,19 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) indoor_humidity = bleSensors.data[0].humidity / div; log_i("Indoor Air Temp.: % 3.1f °C", bleSensors.data[0].temperature / div); log_i("Indoor Humidity: %3.1f %%", bleSensors.data[0].humidity / div); + encoder.writeTemperature(indoor_temp_c); + encoder.writeUint8(static_cast(indoor_humidity + 0.5)); } else { log_i("Indoor Air Temp.: --.- °C"); log_i("Indoor Humidity: -- %%"); - indoor_temp_c = -30; - indoor_humidity = 0; - } -#endif - log_v("-"); - -#ifdef SENSORID_EN - if (ws > -1) - { - encoder.writeUint32(weatherSensor.sensor[ws].sensor_id); - } - else - { - encoder.writeUint32(0); - } -#endif - - // Sensor status flags - encoder.writeBitmap(0, - mithermometer_valid, - (ls > -1) ? weatherSensor.sensor[ls].valid : false, - (ls > -1) ? weatherSensor.sensor[ls].battery_ok : false, - (s1 > -1) ? weatherSensor.sensor[s1].valid : false, - (s1 > -1) ? weatherSensor.sensor[s1].battery_ok : false, - (ws > -1) ? weatherSensor.sensor[ws].valid : false, - (ws > -1) ? weatherSensor.sensor[ws].battery_ok : false); - - // Weather sensor data - if (ws > -1) - { - // weather sensor data available - if (weatherSensor.sensor[ws].w.temp_ok) - { - encoder.writeTemperature(weatherSensor.sensor[ws].w.temp_c); - } - else - { - encoder.writeTemperature(-30); - } - if (weatherSensor.sensor[ws].w.humidity_ok) - { - encoder.writeUint8(weatherSensor.sensor[ws].w.humidity); - } - else - { - encoder.writeUint8(0); - } -#ifdef ENCODE_AS_FLOAT - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_gust_meter_sec); - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_avg_meter_sec); - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_direction_deg); -#else - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_direction_deg_fp1); -#endif - if (weatherSensor.sensor[ws].w.rain_ok) - { - encoder.writeRawFloat(weatherSensor.sensor[ws].w.rain_mm); - } - else - { - encoder.writeRawFloat(0); - } - } - else - { - // fill with suspicious dummy values - encoder.writeTemperature(-30); - encoder.writeUint8(0); -#ifdef ENCODE_AS_FLOAT - encoder.writeRawFloat(0); - encoder.writeRawFloat(0); - encoder.writeRawFloat(0); -#else - encoder.writeUint16(0); - encoder.writeUint16(0); - encoder.writeUint16(0); -#endif - encoder.writeRawFloat(0); + encoder.writeUint16(INV_UINT16); + encoder.writeUint8(INV_UINT8); } - -// Voltages / auxiliary sensor data -#ifdef PIN_SUPPLY_IN - encoder.writeUint16(supply_voltage); -#endif -#if defined(PIN_ADC_IN) - encoder.writeUint16(battery_voltage); -#endif -#ifdef ONEWIRE_EN - encoder.writeTemperature(water_temp_c); -#endif -#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) - encoder.writeTemperature(indoor_temp_c); - encoder.writeUint8((uint8_t)(indoor_humidity + 0.5)); - - // BLE Tempoerature/Humidity Sensor: delete results fromBLEScan buffer to release memory + // BLE Temperature/Humidity Sensors: delete results fromBLEScan buffer to release memory bleSensors.clearScanResults(); #endif - -// Soil sensor data -#ifdef SOILSENSOR_EN - if (s1 > -1) - { - // soil sensor data available - encoder.writeTemperature(weatherSensor.sensor[s1].soil.temp_c); - encoder.writeUint8(weatherSensor.sensor[s1].soil.moisture); - } - else - { - // fill with suspicious dummy values - encoder.writeTemperature(-30); - encoder.writeUint8(0); - } -#endif - -// Rain data statistics -#ifdef RAINDATA_EN - if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) - { - log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); - log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); - log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); - log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); - encoder.writeRawFloat(rainGauge.pastHour()); - encoder.writeRawFloat(rainGauge.currentDay()); - encoder.writeRawFloat(rainGauge.currentWeek()); - encoder.writeRawFloat(rainGauge.currentMonth()); - } - else - { - log_i("Current rain gauge statistics not valid."); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - } -#endif - -// Distance sensor data -#ifdef DISTANCESENSOR_EN - encoder.writeUint16(distance_mm); -#endif - -// Lightning sensor data -#ifdef LIGHTNINGSENSOR_EN - if (ls > -1) - { - // Lightning sensor data available - encoder.writeUnixtime(lightn_ts); - encoder.writeUint16(lightn_events); - encoder.writeUint8(lightn_distance); - } - else - { - // Fill with suspicious dummy values - encoder.writeUnixtime(0); - encoder.writeUint16(0); - encoder.writeUint8(0); - } -#endif } void AppLayer::getPayloadStage2(uint8_t port, LoraEncoder &encoder) @@ -718,6 +373,16 @@ void AppLayer::getConfigPayload(uint8_t cmd, uint8_t &port, LoraEncoder &encoder port = CMD_GET_BLE_ADDR; } #endif + else if (cmd == CMD_GET_APP_PAYLOAD_CFG) + { + uint8_t payload[APP_PAYLOAD_CFG_SIZE]; + getAppPayloadCfg(payload, APP_PAYLOAD_CFG_SIZE); + for (size_t i = 0; i < APP_PAYLOAD_CFG_SIZE; i++) + { + encoder.writeUint8(payload[i]); + } + port = CMD_GET_APP_PAYLOAD_CFG; + } } #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) @@ -776,3 +441,23 @@ std::vector AppLayer::getBleAddr(void) return bleAddr; } #endif + +bool AppLayer::getAppPayloadCfg(uint8_t *bytes, uint8_t size) +{ + bool res = false; + appPrefs.begin("BWS-LW-APP", false); + if (appPrefs.isKey("payloadcfg")) { + appPrefs.getBytes("payloadcfg", bytes, size); + res = true; + } + appPrefs.end(); + return res; +} + +void AppLayer::setAppPayloadCfg(uint8_t *bytes, uint8_t size) +{ + appPrefs.begin("BWS-LW-APP", false); + appPrefs.putBytes("payloadcfg", bytes, size); + appPrefs.end(); + memcpy(appPayloadCfg, bytes, size); +} \ No newline at end of file diff --git a/src/AppLayer.h b/src/AppLayer.h index e0c6bec..cfcf960 100644 --- a/src/AppLayer.h +++ b/src/AppLayer.h @@ -39,9 +39,12 @@ // 20240426 Moved bleAddrInit() out of begin() // 20240504 Added BresserWeatherSensorLWCmd.h // 20240515 Added getOneWireTemperature() +// 20240520 Moved 1-Wire sensor code to PayloadOneWire.h/.cpp +// 20240524 Added appPayloadCfgDef, setAppPayloadCfg() & getAppPayloadCfg() +// Moved code to PayloadBresser, PayloadAnalog & PayloadDigital // // ToDo: -// - +// - Move BLE code to separate class // /////////////////////////////////////////////////////////////////////////////// @@ -54,7 +57,10 @@ #include #include "../BresserWeatherSensorLWCfg.h" #include "../BresserWeatherSensorLWCmd.h" -#include "adc/adc.h" +#include "PayloadBresser.h" +#include "PayloadOneWire.h" +#include "PayloadAnalog.h" +#include "PayloadDigital.h" #include #if defined(MITHERMOMETER_EN) @@ -64,27 +70,45 @@ #if defined(THEENGSDECODER_EN) #include "BleSensors/BleSensors.h" #endif -#ifdef RAINDATA_EN -#include "RainGauge.h" -#endif -#ifdef LIGHTNINGSENSOR_EN -#include "Lightning.h" -#endif -#ifdef ONEWIRE_EN -// Dallas/Maxim OneWire Temperature Sensor -#include -#endif -#ifdef DISTANCESENSOR_EN -// A02YYUW / DFRobot SEN0311 Ultrasonic Distance Sensor -#include -#endif + +/// Default AppLayer payload configuration +const uint8_t appPayloadCfgDef[APP_PAYLOAD_CFG_SIZE] = { + APP_PAYLOAD_CFG_TYPE00, + APP_PAYLOAD_CFG_TYPE01, + APP_PAYLOAD_CFG_TYPE02, + APP_PAYLOAD_CFG_TYPE03, + APP_PAYLOAD_CFG_TYPE04, + APP_PAYLOAD_CFG_TYPE05, + APP_PAYLOAD_CFG_TYPE06, + APP_PAYLOAD_CFG_TYPE07, + APP_PAYLOAD_CFG_TYPE08, + APP_PAYLOAD_CFG_TYPE09, + APP_PAYLOAD_CFG_TYPE10, + APP_PAYLOAD_CFG_TYPE11, + APP_PAYLOAD_CFG_TYPE12, + APP_PAYLOAD_CFG_TYPE13, + APP_PAYLOAD_CFG_TYPE14, + APP_PAYLOAD_CFG_TYPE15, + APP_PAYLOAD_CFG_ONEWIRE1, // onewire[15:8] + APP_PAYLOAD_CFG_ONEWIRE0, // onewire[7:0] + APP_PAYLOAD_CFG_ANALOG1, // analog[15:0] + APP_PAYLOAD_CFG_ANALOG0, // analog[7:0] + APP_PAYLOAD_CFG_DIGITAL3, // digital[31:24] + APP_PAYLOAD_CFG_DIGITAL2, // digital[23:16] + APP_PAYLOAD_CFG_DIGITAL1, // digital[15:8] + APP_PAYLOAD_CFG_DIGITAL0 // digital[7:0] +}; /*! * \brief LoRaWAN node application layer * * Contains all device specific methods and attributes */ -class AppLayer +class AppLayer : public PayloadBresser, PayloadAnalog, PayloadDigital +#ifdef ONEWIRE_EN + , + PayloadOneWire +#endif { private: ESP32Time *_rtc; @@ -93,8 +117,8 @@ class AppLayer /// Preferences (stored in flash memory) Preferences appPrefs; - /// Bresser Weather Sensor Receiver - WeatherSensor weatherSensor; + /// AppLayer payload configuration + uint8_t appPayloadCfg[APP_PAYLOAD_CFG_SIZE]; #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) /// Default BLE MAC addresses @@ -112,43 +136,21 @@ class AppLayer BleSensors bleSensors; #endif -#ifdef RAINDATA_EN - /// Rain data statistics - RainGauge rainGauge; -#endif - -#ifdef LIGHTNINGSENSOR_EN - /// Lightning sensor post-processing - Lightning lightningProc; -#endif - -#ifdef DISTANCESENSOR_EN -#if defined(ESP32) - /// Ultrasonic distance sensor - DistanceSensor_A02YYUW distanceSensor(&Serial2); -#else - /// Ultrasonic distance sensor - DistanceSensor_A02YYUW distanceSensor(&Serial1); -#endif -#endif - public: -#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) /*! * \brief Constructor with BLE sensors * * \param rtc Real time clock object * \param clocksync Timestamp of last clock synchronization */ - AppLayer(ESP32Time *rtc, time_t *clocksync) : bleSensors() -#else - /*! - * \brief Constructor without BLE sensors - * - * \param rtc Real time clock object - * \param clocksync Timestamp of last clock synchronization - */ - AppLayer(ESP32Time *rtc, time_t *clocksync) + AppLayer(ESP32Time *rtc, time_t *clocksync) : PayloadBresser(rtc, clocksync), PayloadAnalog(), PayloadDigital() +#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) + , + bleSensors() +#endif +#ifdef ONEWIRE_EN + , + PayloadOneWire() #endif { _rtc = rtc; @@ -162,6 +164,14 @@ class AppLayer void begin(void) { bleAddrInit(); + PayloadBresser::begin(); + PayloadAnalog::begin(); + PayloadDigital::begin(); + + if (!getAppPayloadCfg(appPayloadCfg, APP_PAYLOAD_CFG_SIZE)) + { + memcpy(appPayloadCfg, appPayloadCfgDef, APP_PAYLOAD_CFG_SIZE); + } }; /*! @@ -199,18 +209,6 @@ class AppLayer #endif }; -#ifdef ONEWIRE_EN - /*! - * \brief Get temperature from Maxim OneWire Sensor - * - * \param index sensor index - * - * \returns temperature in degrees Celsius or DEVICE_DISCONNECTED_C - */ - float - getOneWireTemperature(uint8_t index = 0); -#endif - /*! * \brief Decode app layer specific downlink messages * @@ -294,5 +292,23 @@ class AppLayer */ std::vector getBleAddr(void); #endif + + /*! + * Set AppLayer payload config in Preferences + * + * \param bytes buffer + * \param size buffer size in bytes + */ + void setAppPayloadCfg(uint8_t *bytes, uint8_t size); + + /*! + * Get AppLayer payload config from Preferences + * + * \param bytes buffer + * \param size buffer size in bytes + * + * \returns true if available in Preferences, else false + */ + bool getAppPayloadCfg(uint8_t *bytes, uint8_t size); }; #endif // _APPLAYER_H From 64557991218d6fd8fc0266f5e7fa1514ddb8bb61 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 08:15:52 +0200 Subject: [PATCH 18/81] Moved rainGauge, appPrefs and time members from AppLayer into the class --- src/PayloadBresser.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/PayloadBresser.h b/src/PayloadBresser.h index 1f94814..251f63d 100644 --- a/src/PayloadBresser.h +++ b/src/PayloadBresser.h @@ -34,6 +34,8 @@ // 20240521 Created // 20240523 Added encodeWeatherSensor(), // added defines for signalling invalid data +// 20240524 Moved rainGauge, appPrefs and time members from AppLayer +// into the class // // ToDo: // - @@ -46,6 +48,8 @@ #include "../BresserWeatherSensorLWCfg.h" #include "WeatherSensorCfg.h" #include +#include +#include #ifdef RAINDATA_EN #include "RainGauge.h" @@ -69,7 +73,18 @@ class PayloadBresser /// Bresser Weather Sensor Receiver WeatherSensor weatherSensor; +#ifdef RAINDATA_EN + /// Rain data statistics + RainGauge rainGauge; +#endif + private: + ESP32Time *_rtc; + time_t *_rtcLastClockSync; + + /// Preferences (stored in flash memory) + Preferences appPrefs; + #ifdef LIGHTNINGSENSOR_EN time_t lightn_ts; int lightn_events; @@ -83,7 +98,11 @@ class PayloadBresser /*! * \brief Constructor */ - PayloadBresser(){}; + PayloadBresser(ESP32Time *rtc, time_t *clocksync) + { + _rtc = rtc; + _rtcLastClockSync = clocksync; + }; /*! * \brief Bresser sensors startup code From f3cf2ed75ffd4f39f37286a8624a4b8dfb35cbfa Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 08:19:21 +0200 Subject: [PATCH 19/81] Moved Weather Sensor and rain gauge/lightning post processing from AppLayer into this class --- src/PayloadBresser.cpp | 87 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/src/PayloadBresser.cpp b/src/PayloadBresser.cpp index 1cd8f78..01d9587 100644 --- a/src/PayloadBresser.cpp +++ b/src/PayloadBresser.cpp @@ -34,11 +34,12 @@ // 20240521 Created // 20240523 Added encodeWeatherSensor(), // added defines for signalling invalid data +// 20240524 Moved Weather Sensor and rain gauge/lightning post processing +// from AppLayer into this class // // ToDo: // - Add handling of Weather Sensor flags // - Add handling of Professional Rain Gauge -// - Add rain gauge statistics // /////////////////////////////////////////////////////////////////////////////// @@ -46,6 +47,17 @@ void PayloadBresser::begin(void) { + weatherSensor.begin(); + weatherSensor.clearSlots(); + appPrefs.begin("BWS-LW-APP", false); + uint8_t ws_timeout = appPrefs.getUChar("ws_timeout", WEATHERSENSOR_TIMEOUT); + log_d("Preferences: weathersensor_timeout: %u s", ws_timeout); + appPrefs.end(); + + log_i("Waiting for Weather Sensor Data; timeout %u s", ws_timeout); + bool decode_ok = weatherSensor.getData(ws_timeout * 1000, DATA_ALL_SLOTS); + (void)decode_ok; + log_i("Receiving Weather Sensor Data %s", decode_ok ? "o.k." : "failed"); } void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) @@ -59,11 +71,32 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) { // Try to find SENSOR_TYPE_WEATHER1 int idx = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - if (idx < 0) + if (idx > 0) { + rainGauge.set_max(100000); + } + else { // Try to find SENSOR_TYPE_WEATHER0 idx = weatherSensor.findType(SENSOR_TYPE_WEATHER0); + rainGauge.set_max(1000); + } + +#ifdef RAINDATA_EN + // Check if time is valid + if (*_rtcLastClockSync > 0) + { + // Get local date and time + struct tm timeinfo; + time_t tnow = _rtc->getLocalEpoch(); + localtime_r(&tnow, &timeinfo); + + // If weather sensor has be found and rain data is valid, update statistics + if ((idx > -1) && weatherSensor.sensor[idx].valid && weatherSensor.sensor[idx].w.rain_ok) + { + rainGauge.update(tnow, weatherSensor.sensor[idx].w.rain_mm, weatherSensor.sensor[idx].startup); + } } +#endif encodeWeatherSensor(idx, flags, encoder); } @@ -78,6 +111,25 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) if (type == SENSOR_TYPE_LIGHTNING) { int idx = weatherSensor.findType(type, 0); +#ifdef LIGHTNINGSENSOR_EN + + // Check if time is valid + if (*_rtcLastClockSync > 0) + { + // Get local date and time + time_t tnow = _rtc->getLocalEpoch(); + + // If lightning sensor has be found and data is valid, run post-processing + if ((idx > -1) && weatherSensor.sensor[idx].valid) + { + lightningProc.update( + tnow, + weatherSensor.sensor[idx].lgt.strike_count, + weatherSensor.sensor[idx].lgt.distance_km, + weatherSensor.sensor[idx].startup); + } + } +#endif encodeLightningSensor(idx, appPayloadCfg[type], encoder); continue; @@ -217,19 +269,44 @@ void PayloadBresser::encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &en log_i("UV Index: %3.1f", weatherSensor.sensor[idx].w.uv); encoder.writeUint8(static_cast(weatherSensor.sensor[idx].w.uv * 10)); } - else { + else + { log_i("UV Index: --.-"); encoder.writeUint8(INV_UINT8); } - if (weatherSensor.sensor[idx].w.light_ok) { + if (weatherSensor.sensor[idx].w.light_ok) + { log_i("Light intensity: %06f lx", weatherSensor.sensor[idx].w.light_lux); encoder.writeUint32(static_cast(weatherSensor.sensor[idx].w.light_lux)); } - else { + else + { log_i("Light intensity: ------ lx"); encoder.writeUint32(INV_UINT32); } } + // Rain data statistics +#ifdef RAINDATA_EN + if ((idx) && weatherSensor.sensor[idx].valid && weatherSensor.sensor[idx].w.rain_ok) + { + log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); + log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); + log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); + log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); + encoder.writeRawFloat(rainGauge.pastHour()); + encoder.writeRawFloat(rainGauge.currentDay()); + encoder.writeRawFloat(rainGauge.currentWeek()); + encoder.writeRawFloat(rainGauge.currentMonth()); + } + else + { + log_i("Current rain gauge statistics not valid."); + encoder.writeRawFloat(INV_FLOAT); + encoder.writeRawFloat(INV_FLOAT); + encoder.writeRawFloat(INV_FLOAT); + encoder.writeRawFloat(INV_FLOAT); + } +#endif } void PayloadBresser::encodeThermoHygroSensor(int idx, LoraEncoder &encoder) From 7d79d45b82de6aefb14a8ce0e0d5587b00ea65e0 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 08:44:41 +0200 Subject: [PATCH 20/81] Added sensor feature flags --- BresserWeatherSensorLWCfg.h | 48 +++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index c5ffb19..9d5d1a1 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -48,6 +48,7 @@ // Moved LoRaWAN command interface to BresserWeatherSensorLWCmd.h // 20240520 Added definitions for AppLayer payload configuration // 20240521 Added UBATT_CH/USUPPLY_CH +// 20240524 Added sensor feature flags // // Note: // Depending on board package file date, either @@ -303,22 +304,45 @@ const uint8_t UBATT_SAMPLES = 10; /// AppLayer payload configuration size in bytes #define APP_PAYLOAD_CFG_SIZE 24 -// Default AppLayer payload configuration +// --- Default AppLayer payload configuration --- + // For each sensor/interface type, there is a set of flags. // If a flag is set, the "channel" is enabled (according to the flags bit position). +// For sensors which use a fixed channel, the flags are used to select +// which signals (features) shall be included in the payload. + +// -- Sensor feature flags -- + +// Weather Sensor +#define PAYLOAD_WS_HUMIDITY 0b00000010 +#define PAYLOAD_WS_WIND 0b00000100 +#define PAYLOAD_WS_RAINGAUGE 0b00001000 +#define PAYLOAD_WS_LIGHT 0b00010000 +#define PAYLOAD_WS_UV 0b00100000 +#define PAYLOAD_WS_RAIN_H 0b01000000 // Rain post-processing; hourly rainfall +#define PAYLOAD_WS_RAIN_DWM 0b10000000 // Rain post-processing; daily, weekly, monthly + +// Lightning sensor +#define PAYLOAD_LIGHTNING_RAW 0b00010000 // Sensor raw data +#define PAYLOAD_LIGHTNING_PROC 0b00100000 // Post-processed lightning data // -- 868 MHz Sensor Types -- // 0 - Weather Station; 1 Ch +// Note: Included in APP_PAYLOAD_CFG_TYPE01 #define APP_PAYLOAD_CFG_TYPE00 0x00 // 1 - Weather Station; 1 Ch // - Professional Wind Gauge (with T and H); 1 Ch -// Flags: -// 0x10 -> Rain Gauge -// 0x20 -> Light Intensity -// 0x40 -> UV Index -// 0x80 -> Rain Statistics -#define APP_PAYLOAD_CFG_TYPE01 0x01 +#define APP_PAYLOAD_CFG_TYPE01 ( \ + 1 /* enable sensor */ | \ + PAYLOAD_WS_HUMIDITY | \ + PAYLOAD_WS_WIND | \ + PAYLOAD_WS_RAINGAUGE | \ + PAYLOAD_WS_LIGHT | \ + PAYLOAD_WS_UV | \ + PAYLOAD_WS_RAIN_H | \ + PAYLOAD_WS_RAIN_DWM \ +) // 2 - Thermo-/Hygro-Sensor; 7 Ch // Ch: 1 @@ -348,11 +372,11 @@ const uint8_t UBATT_SAMPLES = 10; // 9 - Lightning Sensor; 1 Ch // - Professional Rain Gauge (with T); 1 Ch // Ch: 0 -// Flags: -// 0x10 -> Ligning Sensor Raw Data -// 0x20 -> Ligning Preprocessed Data -// 0x80 -> Rain Gauge -#define APP_PAYLOAD_CFG_TYPE09 0x31 +#define APP_PAYLOAD_CFG_TYPE09 ( \ + 1 /* enable sensor */ | \ + PAYLOAD_LIGHTNING_PROC | \ + PAYLOAD_LIGHTNING_RAW \ +) // 10 - CO2 Sensor; 4 Ch #define APP_PAYLOAD_CFG_TYPE10 0x00 From 49dca011a6e8ac00edd22f3d37d8f1480ad7553d Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 09:06:49 +0200 Subject: [PATCH 21/81] Added handling of sensor feature flags --- src/PayloadBresser.cpp | 181 ++++++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 74 deletions(-) diff --git a/src/PayloadBresser.cpp b/src/PayloadBresser.cpp index 01d9587..d825be9 100644 --- a/src/PayloadBresser.cpp +++ b/src/PayloadBresser.cpp @@ -34,11 +34,11 @@ // 20240521 Created // 20240523 Added encodeWeatherSensor(), // added defines for signalling invalid data -// 20240524 Moved Weather Sensor and rain gauge/lightning post processing +// 20240524 Moved Weather Sensor and rain gauge/lightning post processing // from AppLayer into this class +// Added handling of sensor feature flags // // ToDo: -// - Add handling of Weather Sensor flags // - Add handling of Professional Rain Gauge // /////////////////////////////////////////////////////////////////////////////// @@ -71,7 +71,8 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) { // Try to find SENSOR_TYPE_WEATHER1 int idx = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - if (idx > 0) { + if (idx > 0) + { rainGauge.set_max(100000); } else @@ -201,19 +202,26 @@ void PayloadBresser::encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &en log_i("-- Weather Sensor Failure"); // Invalidate encoder.writeUint16(INV_TEMP); // Temperature - encoder.writeUint8(INV_UINT8); // Humidity + if (flags & PAYLOAD_WS_HUMIDITY) + encoder.writeUint8(INV_UINT8); // Humidity + if (flags & PAYLOAD_WS_WIND) + { #ifdef ENCODE_AS_FLOAT - encoder.writeRawFloat(INV_FLOAT); // Wind gust - encoder.writeRawFloat(INV_FLOAT); // Wind avg - encoder.writeRawFloat(INV_FLOAT); // Wind dir + encoder.writeRawFloat(INV_FLOAT); // Wind gust + encoder.writeRawFloat(INV_FLOAT); // Wind avg + encoder.writeRawFloat(INV_FLOAT); // Wind dir #else - encoder.writeUint16(INV_UINT16); // Wind gust - encoder.writeUint16(INV_UINT16); // Wind avg - encoder.writeUint16(INV_UINT16); // Wind dir + encoder.writeUint16(INV_UINT16); // Wind gust + encoder.writeUint16(INV_UINT16); // Wind avg + encoder.writeUint16(INV_UINT16); // Wind dir #endif - encoder.writeRawFloat(INV_FLOAT); // Rain - encoder.writeRawFloat(INV_UINT32); // Light - encoder.writeUint8(INV_UINT8); // UV + } + if (flags & PAYLOAD_WS_RAINGAUGE) + encoder.writeRawFloat(INV_FLOAT); // Rain + if (flags & PAYLOAD_WS_UV) + encoder.writeUint8(INV_UINT8); // UV + if (flags & PAYLOAD_WS_LIGHT) + encoder.writeRawFloat(INV_UINT32); // Light } else { @@ -227,84 +235,109 @@ void PayloadBresser::encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &en log_i("Air Temperature: --.- °C"); encoder.writeUint16(INV_UINT16); } - if (weatherSensor.sensor[idx].w.humidity_ok) - { - log_i("Humidity: %2d %%", weatherSensor.sensor[idx].w.humidity); - } - else - { - log_i("Humidity: -- %%"); - encoder.writeUint8(INV_UINT8); - } - if (weatherSensor.sensor[idx].w.rain_ok) - { - log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[idx].w.rain_mm); - encoder.writeRawFloat(weatherSensor.sensor[idx].w.rain_mm); - } - else - { - log_i("Rain Gauge: ---.- mm"); - encoder.writeRawFloat(INV_FLOAT); - } - if (weatherSensor.sensor[idx].w.wind_ok) - { - log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1 / 10.0); - log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1 / 10.0); - log_i("Wind Direction: %4.1f °", weatherSensor.sensor[idx].w.wind_direction_deg_fp1 / 10.0); - encoder.writeUint16(weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[idx].w.wind_direction_deg_fp1); - } - else + if (flags & PAYLOAD_WS_HUMIDITY) { - log_i("Wind Speed (avg.): --.- m/s"); - log_i("Wind Speed (max.): --.- m/s"); - log_i("Wind Direction: ---.- °"); - encoder.writeUint16(INV_UINT16); - encoder.writeUint16(INV_UINT16); - encoder.writeUint16(INV_UINT16); + if (weatherSensor.sensor[idx].w.humidity_ok) + { + log_i("Humidity: %2d %%", weatherSensor.sensor[idx].w.humidity); + } + else + { + log_i("Humidity: -- %%"); + encoder.writeUint8(INV_UINT8); + } } - if (weatherSensor.sensor[idx].w.uv_ok) + if (flags & PAYLOAD_WS_RAINGAUGE) { - log_i("UV Index: %3.1f", weatherSensor.sensor[idx].w.uv); - encoder.writeUint8(static_cast(weatherSensor.sensor[idx].w.uv * 10)); + if (weatherSensor.sensor[idx].w.rain_ok) + { + log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[idx].w.rain_mm); + encoder.writeRawFloat(weatherSensor.sensor[idx].w.rain_mm); + } + else + { + log_i("Rain Gauge: ---.- mm"); + encoder.writeRawFloat(INV_FLOAT); + } } - else + if (flags & PAYLOAD_WS_WIND) { - log_i("UV Index: --.-"); - encoder.writeUint8(INV_UINT8); + if (weatherSensor.sensor[idx].w.wind_ok) + { + log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1 / 10.0); + log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1 / 10.0); + log_i("Wind Direction: %4.1f °", weatherSensor.sensor[idx].w.wind_direction_deg_fp1 / 10.0); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_avg_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_gust_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[idx].w.wind_direction_deg_fp1); + } + else + { + log_i("Wind Speed (avg.): --.- m/s"); + log_i("Wind Speed (max.): --.- m/s"); + log_i("Wind Direction: ---.- °"); + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); + encoder.writeUint16(INV_UINT16); + } } - if (weatherSensor.sensor[idx].w.light_ok) + if (flags & PAYLOAD_WS_UV) { - log_i("Light intensity: %06f lx", weatherSensor.sensor[idx].w.light_lux); - encoder.writeUint32(static_cast(weatherSensor.sensor[idx].w.light_lux)); + if (weatherSensor.sensor[idx].w.uv_ok) + { + log_i("UV Index: %3.1f", weatherSensor.sensor[idx].w.uv); + encoder.writeUint8(static_cast(weatherSensor.sensor[idx].w.uv * 10)); + } + else + { + log_i("UV Index: --.-"); + encoder.writeUint8(INV_UINT8); + } } - else + if (flags & PAYLOAD_WS_LIGHT) { - log_i("Light intensity: ------ lx"); - encoder.writeUint32(INV_UINT32); + if (weatherSensor.sensor[idx].w.light_ok) + { + log_i("Light intensity: %06f lx", weatherSensor.sensor[idx].w.light_lux); + encoder.writeUint32(static_cast(weatherSensor.sensor[idx].w.light_lux)); + } + else + { + log_i("Light intensity: ------ lx"); + encoder.writeUint32(INV_UINT32); + } } } // Rain data statistics #ifdef RAINDATA_EN if ((idx) && weatherSensor.sensor[idx].valid && weatherSensor.sensor[idx].w.rain_ok) { - log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); - log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); - log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); - log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); - encoder.writeRawFloat(rainGauge.pastHour()); - encoder.writeRawFloat(rainGauge.currentDay()); - encoder.writeRawFloat(rainGauge.currentWeek()); - encoder.writeRawFloat(rainGauge.currentMonth()); + if (flags & PAYLOAD_WS_RAIN_H) + { + log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); + encoder.writeRawFloat(rainGauge.pastHour()); + } + if (flags & PAYLOAD_WS_RAIN_DWM) + { + log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); + log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); + log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); + encoder.writeRawFloat(rainGauge.currentDay()); + encoder.writeRawFloat(rainGauge.currentWeek()); + encoder.writeRawFloat(rainGauge.currentMonth()); + } } else { log_i("Current rain gauge statistics not valid."); - encoder.writeRawFloat(INV_FLOAT); - encoder.writeRawFloat(INV_FLOAT); - encoder.writeRawFloat(INV_FLOAT); - encoder.writeRawFloat(INV_FLOAT); + if (flags & PAYLOAD_WS_RAIN_H) + encoder.writeRawFloat(INV_FLOAT); + if (flags & PAYLOAD_WS_RAIN_DWM) + { + encoder.writeRawFloat(INV_FLOAT); + encoder.writeRawFloat(INV_FLOAT); + encoder.writeRawFloat(INV_FLOAT); + } } #endif } @@ -424,7 +457,7 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) #ifdef LIGHTNINGSENSOR_EN void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder &encoder) { - if (flags & 0x21) + if (flags & (PAYLOAD_LIGHTNING_RAW | 1)) { // Raw sensor values if (idx == -1) @@ -443,7 +476,7 @@ void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder & } } - if (flags & 0x20) + if (flags & (PAYLOAD_LIGHTNING_PROC | 1)) { // Post-processed sensor values if (lightningProc.lastEvent(lightn_ts, lightn_events, lightn_distance)) From a081330a8cb2bb8b0b47e9398e87d34fb3a9325f Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 11:20:35 +0200 Subject: [PATCH 22/81] Added logging.h --- src/PayloadAnalog.h | 1 + src/PayloadBresser.h | 1 + src/PayloadDigital.h | 1 + src/PayloadOneWire.h | 6 ++---- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PayloadAnalog.h b/src/PayloadAnalog.h index 9cb849e..1fb9106 100644 --- a/src/PayloadAnalog.h +++ b/src/PayloadAnalog.h @@ -44,6 +44,7 @@ #include "../BresserWeatherSensorLWCfg.h" #include "adc/adc.h" #include +#include "logging.h" class PayloadAnalog diff --git a/src/PayloadBresser.h b/src/PayloadBresser.h index 251f63d..e9def93 100644 --- a/src/PayloadBresser.h +++ b/src/PayloadBresser.h @@ -59,6 +59,7 @@ #endif #include +#include "logging.h" // Encoding of invalid values #define INV_FLOAT 0xFFFFFFFF diff --git a/src/PayloadDigital.h b/src/PayloadDigital.h index c4f7b70..cca1981 100644 --- a/src/PayloadDigital.h +++ b/src/PayloadDigital.h @@ -44,6 +44,7 @@ #include "../BresserWeatherSensorLWCfg.h" #include +#include "logging.h" #ifdef DISTANCESENSOR_EN // A02YYUW / DFRobot SEN0311 Ultrasonic Distance Sensor diff --git a/src/PayloadOneWire.h b/src/PayloadOneWire.h index 05e0d3f..93e9a30 100644 --- a/src/PayloadOneWire.h +++ b/src/PayloadOneWire.h @@ -47,11 +47,9 @@ // Dallas/Maxim OneWire Temperature Sensor #include -#include - -// Dallas/Maxim OneWire Temperature Sensor -#include +#include +#include "logging.h" class PayloadOneWire { From 54d73690823b2ca019e116be9aec698e32fddc0f Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 11:21:01 +0200 Subject: [PATCH 23/81] Removed unused variable --- src/AppLayer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AppLayer.cpp b/src/AppLayer.cpp index 8aedaff..e29e30c 100644 --- a/src/AppLayer.cpp +++ b/src/AppLayer.cpp @@ -227,7 +227,6 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) { (void)port; // suppress warning regarding unused parameter - bool mithermometer_valid = false; #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) float indoor_temp_c; float indoor_humidity; @@ -282,7 +281,6 @@ encodeDigital(appPayloadCfg, encoder); #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) if (bleSensors.data[0].valid) { - mithermometer_valid = true; indoor_temp_c = bleSensors.data[0].temperature / div; indoor_humidity = bleSensors.data[0].humidity / div; log_i("Indoor Air Temp.: % 3.1f °C", bleSensors.data[0].temperature / div); From fa0b766e532bc09e39fe0b3039f532458eb5f0a6 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 17:50:13 +0200 Subject: [PATCH 24/81] Added payload size check, changed bitmap order --- src/PayloadAnalog.cpp | 7 ++++--- src/PayloadDigital.cpp | 5 +++-- src/PayloadOneWire.cpp | 9 +++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/PayloadAnalog.cpp b/src/PayloadAnalog.cpp index 6832a9c..96d096a 100644 --- a/src/PayloadAnalog.cpp +++ b/src/PayloadAnalog.cpp @@ -32,6 +32,7 @@ // History: // // 20240521 Created +// 20240524 Added payload size check, changed bitmap order // // ToDo: // - @@ -51,19 +52,19 @@ void PayloadAnalog::encodeAnalog(uint8_t *appPayloadCfg, LoraEncoder &encoder) unsigned ch = (APP_PAYLOAD_BYTES_ANALOG * 8) - 1; for (int i = APP_PAYLOAD_BYTES_ANALOG - 1; i >= 0; i--) { - for (int bit = 7; bit >= 0; bit--) + for (uint8_t bit = 0; bit <= 7; bit++) { // Check if channel is enabled if ((appPayloadCfg[APP_PAYLOAD_OFFS_ANALOG + i] >> bit) & 0x1) { - if (ch == UBATT_CH) + if ((ch == UBATT_CH) && (encoder.getLength() <= PAYLOAD_SIZE - 2)) { uint16_t uBatt = getBatteryVoltage(); log_i("ch %02u: U_batt: %04u mv", ch); encoder.writeUint16(uBatt); } - if (ch == USUPPLY_CH) + if ((ch == USUPPLY_CH) && (encoder.getLength() <= PAYLOAD_SIZE - 2)) { uint16_t uSupply = getSupplyVoltage(); log_i("ch %02u: U_supply: %04u mv", ch); diff --git a/src/PayloadDigital.cpp b/src/PayloadDigital.cpp index 90bb129..826d984 100644 --- a/src/PayloadDigital.cpp +++ b/src/PayloadDigital.cpp @@ -32,6 +32,7 @@ // History: // // 20240520 Created +// 20240524 Added payload size check, changed bitmap order // // ToDo: // - @@ -62,13 +63,13 @@ void PayloadDigital::encodeDigital(uint8_t *appPayloadCfg, LoraEncoder &encoder) unsigned ch = (APP_PAYLOAD_BYTES_DIGITAL * 8) - 1; for (int i = APP_PAYLOAD_BYTES_DIGITAL - 1; i >= 0; i--) { - for (int bit = 7; bit >= 0; bit--) + for (uint8_t bit = 0; bit <= 7; bit++) { if ((appPayloadCfg[APP_PAYLOAD_OFFS_DIGITAL + i] >> bit) & 0x1) { #ifdef DISTANCESENSOR_EN // Check if channel is enabled - if (ch == DISTANCESENSOR_CH) + if ((ch == DISTANCESENSOR_CH) && (encoder.getLength() <= PAYLOAD_SIZE - 2)) { uint16_t distance_mm = readDistanceSensor(); if (distance_mm > 0) diff --git a/src/PayloadOneWire.cpp b/src/PayloadOneWire.cpp index f6f87c5..51411b3 100644 --- a/src/PayloadOneWire.cpp +++ b/src/PayloadOneWire.cpp @@ -32,6 +32,7 @@ // History: // // 20240520 Created +// 20240524 Added payload size check, changed bitmap order // // ToDo: // - @@ -78,10 +79,14 @@ void PayloadOneWire::encodeOneWire(uint8_t *appPayloadCfg, LoraEncoder &encoder) unsigned index = (APP_PAYLOAD_BYTES_ONEWIRE * 8) - 1; for (int i = APP_PAYLOAD_BYTES_ONEWIRE - 1; i >= 0; i--) { - for (int bit = 7; bit >= 0; bit--) + for (uint8_t ch = 0; ch <= 7; ch++) { + // Check if enough space is left in payload buffer + if (encoder.getLength() > PAYLOAD_SIZE - 2) + return; + // Check if sensor with given index is enabled - if ((appPayloadCfg[APP_PAYLOAD_OFFS_ONEWIRE + i] >> bit) & 0x1) + if ((appPayloadCfg[APP_PAYLOAD_OFFS_ONEWIRE + i] >> ch) & 0x1) { // Get temperature by index float tempC = owTempSensors.getTempCByIndex(index); From 2f3ae73678d841b14f4f1b8af2bda7b2873f0aaf Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 17:51:53 +0200 Subject: [PATCH 25/81] Added isSpaceLeft(), payloadSize[] & sensorTypes[] --- src/PayloadBresser.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/PayloadBresser.h b/src/PayloadBresser.h index e9def93..964ba02 100644 --- a/src/PayloadBresser.h +++ b/src/PayloadBresser.h @@ -36,6 +36,7 @@ // added defines for signalling invalid data // 20240524 Moved rainGauge, appPrefs and time members from AppLayer // into the class +// Added isSpaceLeft(), payloadSize[] & sensorTypes[] // // ToDo: // - @@ -74,6 +75,43 @@ class PayloadBresser /// Bresser Weather Sensor Receiver WeatherSensor weatherSensor; + // Payload size in bytes + const uint8_t payloadSize[16] = { + 0, + 23, // SENSOR_TYPE_WEATHER1 (max.) + 3, // SENSOR_TYPE_THERMO_HYGRO + 2, // SENSOR_TYPE_POOL_THERMO + 3, // SENSOR_TYPE_SOIL + 1, // SENSOR_TYPE_LEAKAGE + 0, // reserved + 0, // reserved + 6, // SENSOR_TYPE_AIR_PM + 3, // SENSOR_TYPE_LIGHTNING (min.) + 2, // SENSOR_TYPE_CO2 + 3, // SENSOR_TYPE_HCHO_VOC + 0, // reserved + 0, // reserved + 0, // reserved + 0 // reserved + }; + +#if CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + const char * sensorTypes[16] = { + "Weather", + "Weather", + "Thermo/Hygro", + "Pool Temperature", + "Soil", + "Leakage", + "", + "", + "Air Quality (PM)", + "Lightning", + "CO2", + "Air Quality (HCHO/VOC)" + }; +#endif + #ifdef RAINDATA_EN /// Rain data statistics RainGauge rainGauge; @@ -130,5 +168,10 @@ class PayloadBresser #endif void encodeCo2Sensor(int idx, LoraEncoder &encoder); void encodeHchoVocSensor(int idx, LoraEncoder &encoder); + + bool isSpaceLeft(LoraEncoder &encoder, uint8_t type) + { + return (encoder.getLength() + payloadSize[type] <= PAYLOAD_SIZE); + }; }; #endif //_PAYLOAD_BRESSER \ No newline at end of file From fc425206ff52a630fa316f905e0a350d2cdb80ea Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 17:57:49 +0200 Subject: [PATCH 26/81] Added payload size check, modified log messages, changed bitmap order --- src/PayloadBresser.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/PayloadBresser.cpp b/src/PayloadBresser.cpp index d825be9..43de4b9 100644 --- a/src/PayloadBresser.cpp +++ b/src/PayloadBresser.cpp @@ -138,13 +138,20 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) #endif // Handle sensors with channel selection - for (int bit = 7; bit >= 0; bit--) + for (uint8_t ch = 1; ch <= 7; ch++) { // Check if channel is enabled - if (!((appPayloadCfg[type] >> bit) & 0x1)) + if (!((appPayloadCfg[type] >> ch) & 0x1)) continue; - int idx = weatherSensor.findType(type, bit); + if (!isSpaceLeft(encoder, type)) + break; + + log_i("%s Sensor Ch %u", sensorTypes[type], ch); + int idx = weatherSensor.findType(type, ch); + if (idx == -1) { + log_i("-- Failure"); + } if (type == SENSOR_TYPE_THERMO_HYGRO) { @@ -185,6 +192,7 @@ void PayloadBresser::encodeBresser(uint8_t *appPayloadCfg, LoraEncoder &encoder) } } +// Payload size: 2...17 bytes (ENCODE_AS_FLOAT == false) / 2...23 bytes (ENCODE_AS_FLOAT == true) void PayloadBresser::encodeWeatherSensor(int idx, uint8_t flags, LoraEncoder &encoder) { // Weather Stations Professional 3-in-1 Professional @@ -346,7 +354,6 @@ void PayloadBresser::encodeThermoHygroSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Thermo/Hygro Sensor Failure"); // Invalidate encoder.writeUint16(INV_TEMP); encoder.writeUint8(INV_UINT8); @@ -364,7 +371,6 @@ void PayloadBresser::encodePoolThermometer(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Pool Thermometer Failure"); // Invalidate encoder.writeUint16(INV_TEMP); } @@ -379,7 +385,6 @@ void PayloadBresser::encodeSoilSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Soil Sensor Failure"); // Invalidate encoder.writeUint16(INV_TEMP); encoder.writeUint8(INV_UINT8); @@ -397,7 +402,6 @@ void PayloadBresser::encodeLeakageSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Leakage Sensor Failure"); // Invalidate encoder.writeUint8(INV_UINT8); } @@ -412,7 +416,6 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Air Quality (PM) Sensor Failure"); // Invalidate encoder.writeUint16(INV_UINT16); encoder.writeUint16(INV_UINT16); @@ -420,7 +423,6 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) } else { - // Air Quality (Particular Matter) Sensor if (weatherSensor.sensor[idx].pm.pm_1_0_init) { log_i("PM1.0: init"); @@ -455,6 +457,7 @@ void PayloadBresser::encodeAirPmSensor(int idx, LoraEncoder &encoder) } #ifdef LIGHTNINGSENSOR_EN +// Payload size: 3 bytes (raw) / 7 bytes (pre-processed) / 10 bytes (both) void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder &encoder) { if (flags & (PAYLOAD_LIGHTNING_RAW | 1)) @@ -462,7 +465,6 @@ void PayloadBresser::encodeLightningSensor(int idx, uint8_t flags, LoraEncoder & // Raw sensor values if (idx == -1) { - log_i("-- Lightning Sensor Failure"); // Invalidate encoder.writeUint8(INV_UINT8); encoder.writeUint16(INV_UINT16); @@ -508,13 +510,11 @@ void PayloadBresser::encodeCo2Sensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- CO2 Sensor Failure"); // Invalidate encoder.writeUint16(INV_UINT16); } else { - // CO2 Sensor if (weatherSensor.sensor[idx].co2.co2_init) { log_i("CO2: init"); @@ -532,7 +532,6 @@ void PayloadBresser::encodeHchoVocSensor(int idx, LoraEncoder &encoder) { if (idx == -1) { - log_i("-- Air Quality (HCHO/VOC) Sensor Failure"); // Invalidate encoder.writeUint16(INV_UINT16); encoder.writeUint8(INV_UINT8); From 780a91c2a66268453ae70455f1ccac0a3a4c6cfd Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 18:01:41 +0200 Subject: [PATCH 27/81] Modified PAYLOAD_SIZE --- BresserWeatherSensorLW.ino | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/BresserWeatherSensorLW.ino b/BresserWeatherSensorLW.ino index 44a3904..7f13a7f 100644 --- a/BresserWeatherSensorLW.ino +++ b/BresserWeatherSensorLW.ino @@ -82,6 +82,8 @@ // 20240504 PowerFeather: added BATTERY_CAPACITY_MAH to init() // Added BresserWeatherSensorLWCmd.h // 20240505 Implemented loading of LoRaWAN secrets from file on LittleFS (if available) +// 20230524 Modified PAYLOAD_SIZE: Moved define to header file, added small reserve +// to uplinkPayload[], modified actual size in sendReceive() // // ToDo: // - @@ -167,10 +169,6 @@ using namespace PowerFeather; // Time zone info const char *TZ_INFO = TZINFO_STR; -// Uplink message payload size -// The maximum allowed for all data rates is 51 bytes. -const uint8_t PAYLOAD_SIZE = 51; - // Time source & status, see below // // bits 0..3 time source @@ -746,8 +744,8 @@ void setup() gotoSleep(sleepDuration()); } - // build payload byte array - uint8_t uplinkPayload[PAYLOAD_SIZE]; + // build payload byte array (+ reserve to prevent overflow with configuration at run-time) + uint8_t uplinkPayload[PAYLOAD_SIZE + 8]; LoraEncoder encoder(uplinkPayload); @@ -899,11 +897,29 @@ void setup() // perform an uplink & optionally receive downlink if (fcntUp % 64 == 0) { - state = node.sendReceive(uplinkPayload, encoder.getLength(), port, downlinkPayload, &downlinkSize, true, &uplinkDetails, &downlinkDetails); + state = node.sendReceive( + uplinkPayload, + min(encoder.getLength(), static_cast(PAYLOAD_SIZE)), + port, + downlinkPayload, + &downlinkSize, + true, + &uplinkDetails, + &downlinkDetails + ); } else { - state = node.sendReceive(uplinkPayload, encoder.getLength(), port, downlinkPayload, &downlinkSize, false, nullptr, &downlinkDetails); + state = node.sendReceive( + uplinkPayload, + min(encoder.getLength(), static_cast(PAYLOAD_SIZE)), + port, + downlinkPayload, + &downlinkSize, + false, + nullptr, + &downlinkDetails + ); } debug((state != RADIOLIB_LORAWAN_NO_DOWNLINK) && (state != RADIOLIB_ERR_NONE), "Error in sendReceive", state, false); From eb1c19fc7f48112cb49cdb5ce5e7f4e6bb04d341 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Fri, 24 May 2024 18:02:54 +0200 Subject: [PATCH 28/81] Moved PAYLOAD_SIZE from BresserWeatherSensorLW.ino --- BresserWeatherSensorLWCfg.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index 9d5d1a1..ee8c040 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -49,6 +49,7 @@ // 20240520 Added definitions for AppLayer payload configuration // 20240521 Added UBATT_CH/USUPPLY_CH // 20240524 Added sensor feature flags +// Moved PAYLOAD_SIZE from BresserWeatherSensorLW.ino // // Note: // Depending on board package file date, either @@ -94,6 +95,10 @@ //#define ARDUINO_THINGPULSE_EPULSE_FEATHER #endif +// Uplink message payload size +// The maximum allowed for all data rates is 51 bytes. +const uint8_t PAYLOAD_SIZE = 51; + // Battery voltage thresholds for energy saving & deep-discharge prevention // If battery voltage <= BATTERY_WEAK [mV], MCU will sleep for SLEEP_INTERVAL_LONG From fb997984c98fc84de36dc8f4d88af3a22f1c7743 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Fri, 24 May 2024 18:14:25 +0200 Subject: [PATCH 29/81] Disabled target m5stack-core2 --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 573da71..241b4e7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -23,7 +23,7 @@ jobs: #- esp32:esp32:heltec_wifi_lora_32_V2 - esp32:esp32:heltec_wifi_lora_32_V3 #- esp32:esp32:featheresp32 - - esp32:esp32:m5stack-core2 + #- esp32:esp32:m5stack-core2 - esp32:esp32:esp32s3_powerfeather - esp32:esp32:adafruit_feather_esp32s2 - rp2040:rp2040:adafruit_feather:dbgport=Serial From 244ab2e28baa9f982434e3d554f3cfa48027ed0a Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sun, 26 May 2024 11:23:01 +0200 Subject: [PATCH 30/81] Created --- extras/confighelper/confighelper.html | 410 ++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 extras/confighelper/confighelper.html diff --git a/extras/confighelper/confighelper.html b/extras/confighelper/confighelper.html new file mode 100644 index 0000000..ba44084 --- /dev/null +++ b/extras/confighelper/confighelper.html @@ -0,0 +1,410 @@ + + + + + Config Helper + + + + +

Welcome to Config Helper

+ +
+ + + + + + + + + + + + + + + + +

+ + + + + + +

+ +

+
+ + + +

+ + + +

+ + + +

+ +
+

+ +
+

+ +
+

+ +
+ + + \ No newline at end of file From e6ca7bf94dcfeb092c70adfeaf411a5f5b7a4342 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sun, 26 May 2024 22:26:21 +0200 Subject: [PATCH 31/81] Added usage, improved formatting. --- extras/confighelper/confighelper.html | 83 ++++++++++++++++++--------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/extras/confighelper/confighelper.html b/extras/confighelper/confighelper.html index ba44084..1a7e7b4 100644 --- a/extras/confighelper/confighelper.html +++ b/extras/confighelper/confighelper.html @@ -40,6 +40,11 @@ Config Helper +