From a37e000653cb25e1641999ffacc24affd2781b2e Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Thu, 8 Jun 2023 06:26:29 +0200 Subject: [PATCH 01/16] Update WeatherSensor.h --- src/WeatherSensor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WeatherSensor.h b/src/WeatherSensor.h index a8f03c04..d61de043 100644 --- a/src/WeatherSensor.h +++ b/src/WeatherSensor.h @@ -74,13 +74,14 @@ // Sensor Types // 0 - Weather Station 5-in-1; PN 7002510..12/7902510..12 // 1 - Weather Station 6-in-1; PN 7002585 +// - Professional Wind Gauge 6-in-1; PN 7002531 // 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 // 4 - Soil Moisture Sensor 6-in-1; PN 7009972 // 9 - Professional Rain Gauge (5-in-1 decoder) // ? - Air Quality Sensor // ? - Water Leakage Sensor // ? - Pool Thermometer -// ? - Lightning Sensor +// ? - Lightning Sensor PN 7009976 #define SENSOR_TYPE_WEATHER0 0 // Weather Station #define SENSOR_TYPE_WEATHER1 1 // Weather Station #define SENSOR_TYPE_THERMO_HYGRO 2 // Thermo-/Hygro-Sensor From 885801fdfa1ee722527506fb76a209319654a2d8 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sat, 24 Jun 2023 09:36:26 +0200 Subject: [PATCH 02/16] Added Bresser Lightning Sensor decoder --- .../BresserWeatherSensorBasic.ino | 132 ++++++++++-------- src/WeatherSensor.cpp | 107 +++++++++++++- src/WeatherSensor.h | 46 ++++-- src/WeatherSensorCfg.h | 4 +- 4 files changed, 216 insertions(+), 73 deletions(-) diff --git a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino index 34627b72..9d9ba3ec 100644 --- a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino +++ b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino @@ -44,6 +44,7 @@ // 20220810 Changed to modified WeatherSensor class; fixed Soil Moisture Sensor Handling // 20220815 Changed to modified WeatherSensor class; added support of multiple sensors // 20221227 Replaced DEBUG_PRINT/DEBUG_PRINTLN by Arduino logging functions +// 20230624 Added Bresser Lightning Sensor decoder // // ToDo: // - @@ -81,66 +82,81 @@ void loop() int decode_status = weatherSensor.getMessage(); if (decode_status == DECODE_OK) { - - Serial.printf("Id: [%8X] Typ: [%X] Battery: [%s] ", - weatherSensor.sensor[i].sensor_id, - weatherSensor.sensor[i].s_type, - weatherSensor.sensor[i].battery_ok ? "OK " : "Low"); - #ifdef BRESSER_6_IN_1 - Serial.printf("Ch: [%d] ", weatherSensor.sensor[i].chan); - #endif - if (weatherSensor.sensor[i].temp_ok) { - Serial.printf("Temp: [%5.1fC] ", - weatherSensor.sensor[i].temp_c); + if (weatherSensor.sensor[i].s_type != SENSOR_TYPE_LIGHTNING) { + Serial.printf("Id: [%8X] Typ: [%X] Battery: [%s] ", + weatherSensor.sensor[i].sensor_id, + weatherSensor.sensor[i].s_type, + weatherSensor.sensor[i].battery_ok ? "OK " : "Low"); + #ifdef BRESSER_6_IN_1 + Serial.printf("Ch: [%d] ", weatherSensor.sensor[i].chan); + #endif + if (weatherSensor.sensor[i].temp_ok) { + Serial.printf("Temp: [%5.1fC] ", + weatherSensor.sensor[i].temp_c); + } else { + Serial.printf("Temp: [---.-C] "); + } + if (weatherSensor.sensor[i].humidity_ok) { + Serial.printf("Hum: [%3d%%] ", + weatherSensor.sensor[i].humidity); + } + else { + Serial.printf("Hum: [---%%] "); + } + if (weatherSensor.sensor[i].wind_ok) { + Serial.printf("Wind max: [%4.1fm/s] Wind avg: [%4.1fm/s] Wind dir: [%5.1fdeg] ", + weatherSensor.sensor[i].wind_gust_meter_sec, + weatherSensor.sensor[i].wind_avg_meter_sec, + weatherSensor.sensor[i].wind_direction_deg); + } else { + Serial.printf("Wind max: [--.-m/s] Wind avg: [--.-m/s] Wind dir: [---.-deg] "); + } + if (weatherSensor.sensor[i].rain_ok) { + Serial.printf("Rain: [%7.1fmm] ", + weatherSensor.sensor[i].rain_mm); + } else { + Serial.printf("Rain: [-----.-mm] "); + } + if (weatherSensor.sensor[i].moisture_ok) { + Serial.printf("Moisture: [%2d%%] ", + weatherSensor.sensor[i].moisture); + } + else { + Serial.printf("Moisture: [--%%] "); + } + #if defined BRESSER_6_IN_1 || defined BRESSER_7_IN_1 + if (weatherSensor.sensor[i].uv_ok) { + Serial.printf("UV index: [%1.1f] ", + weatherSensor.sensor[i].uv); + } + else { + Serial.printf("UV index: [-.-%%] "); + } + #endif + #ifdef BRESSER_7_IN_1 + if (weatherSensor.sensor[i].light_ok) { + Serial.printf("Light (Klux): [%2.1fKlux] ", + weatherSensor.sensor[i].light_klx); + } + else { + Serial.printf("Light (lux): [--.-Klux] "); + } + #endif + } else { - Serial.printf("Temp: [---.-C] "); + Serial.printf("Id: [%8X] Typ: [%X] Battery: [%s] ", + weatherSensor.sensor[i].sensor_id, + weatherSensor.sensor[i].s_type, + weatherSensor.sensor[i].battery_ok ? "OK " : "Low"); + Serial.printf("Lightning Counter: [%3d] ", weatherSensor.sensor[i].lightning_count); + if (weatherSensor.sensor[i].lightning_distance_km == 0) { + Serial.printf("Distance: [%2dkm] ", weatherSensor.sensor[i].lightning_distance_km); + } else { + Serial.printf("Distance: [----] ", weatherSensor.sensor[i].lightning_distance_km); + } + Serial.printf("unknown1: [0x%03X] ", weatherSensor.sensor[i].lightning_unknown1); + Serial.printf("unknown2: [0x%04X] ", weatherSensor.sensor[i].lightning_unknown2); } - if (weatherSensor.sensor[i].humidity_ok) { - Serial.printf("Hum: [%3d%%] ", - weatherSensor.sensor[i].humidity); - } - else { - Serial.printf("Hum: [---%%] "); - } - if (weatherSensor.sensor[i].wind_ok) { - Serial.printf("Wind max: [%4.1fm/s] Wind avg: [%4.1fm/s] Wind dir: [%5.1fdeg] ", - weatherSensor.sensor[i].wind_gust_meter_sec, - weatherSensor.sensor[i].wind_avg_meter_sec, - weatherSensor.sensor[i].wind_direction_deg); - } else { - Serial.printf("Wind max: [--.-m/s] Wind avg: [--.-m/s] Wind dir: [---.-deg] "); - } - if (weatherSensor.sensor[i].rain_ok) { - Serial.printf("Rain: [%7.1fmm] ", - weatherSensor.sensor[i].rain_mm); - } else { - Serial.printf("Rain: [-----.-mm] "); - } - if (weatherSensor.sensor[i].moisture_ok) { - Serial.printf("Moisture: [%2d%%] ", - weatherSensor.sensor[i].moisture); - } - else { - Serial.printf("Moisture: [--%%] "); - } - #if defined BRESSER_6_IN_1 || defined BRESSER_7_IN_1 - if (weatherSensor.sensor[i].uv_ok) { - Serial.printf("UV index: [%1.1f] ", - weatherSensor.sensor[i].uv); - } - else { - Serial.printf("UV index: [-.-%%] "); - } - #endif - #ifdef BRESSER_7_IN_1 - if (weatherSensor.sensor[i].light_ok) { - Serial.printf("Light (Klux): [%2.1fKlux] ", - weatherSensor.sensor[i].light_klx); - } - else { - Serial.printf("Light (lux): [--.-Klux] "); - } - #endif Serial.printf("RSSI: [%5.1fdBm]\n", weatherSensor.sensor[i].rssi); } // if (decode_status == DECODE_OK) delay(100); diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index fc9b279b..f8ad4c66 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -57,6 +57,7 @@ // 20230228 Added Bresser 7 in 1 decoder by Jorge Navarro-Ortiz (jorgenavarro@ugr.es) // 20230329 Fixed issue introduced with 7 in 1 decoder // 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230624 Added Bresser Lightning Sensor decoder // // ToDo: // - @@ -245,6 +246,13 @@ DecodeStatus WeatherSensor::getMessage(void) decode_res = decodeBresser5In1Payload(&recvData[1], sizeof(recvData) - 1); } #endif + #ifdef BRESSER_LIGHTNING + if (decode_res == DECODE_INVALID || + decode_res == DECODE_PAR_ERR || + decode_res == DECODE_CHK_ERR) { + decode_res = decodeBresserLightningPayload(&recvData[1], sizeof(recvData) - 1); + } + #endif } // if (recvData[0] == 0xD4) else if (state == RADIOLIB_ERR_RX_TIMEOUT) { log_v("T"); @@ -849,7 +857,7 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi int chkdgst = (msgw[0] << 8) | msgw[1]; int digest = lfsr_digest16(&msgw[2], 23, 0x8810, 0xba95); // bresser_7in1 if ((chkdgst ^ digest) != 0x6df1) { // bresser_7in1 - log_d("Digest check failed - [%02X] Vs [%02X] (%02X)", chkdgst, digest, chkdgst ^ digest); // bresser_7in1 + log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chkdgst, digest, chkdgst ^ digest); return DECODE_DIG_ERR; } @@ -910,6 +918,8 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi sensor[slot].uv = uv_index; sensor[slot].battery_ok = !battery_low; sensor[slot].rssi = rssi; + sensor[slot].valid = true; + sensor[slot].complete = true; /* clang-format off */ /* data = data_make( @@ -936,3 +946,98 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi } #endif + +/** +Decoder for Bresser Lightning, outdoor sensor. + +https://github.com/merbanan/rtl_433/issues/2140 + +DIGEST:8h8h ID:8h8h CTR:12h ?4h8h KM:8d ?8h8h + 0 1 2 3 4 5h 5l 6 7 8 9 + +Preamble: + + aa 2d d4 + +Observed length depends on reset_limit. +The data has a whitening of 0xaa. + + +First two bytes are an LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e +*/ + +#ifdef BRESSER_LIGHTNING +DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize) +{ + // see AS3935 Datasheet, Table 17 - Distance Estimation + uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; + + // data whitening + uint8_t msgw[MSG_BUF_SIZE]; + for (unsigned i = 0; i < msgSize; ++i) { + msgw[i] = msg[i] ^ 0xaa; + } + + // LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e + int chk = (msgw[0] << 8) | msgw[1]; + int digest = lfsr_digest16(&msgw[2], 8, 0x8810, 0xabf9); + //fprintf(stderr, "DIGEST %04x vs %04x (%04x) \n", chk, digest, chk ^ digest); + //if (((chk ^ digest) != 0x899e) && ((chk ^ digest) != 0x8b9e)) { + if (((chk ^ digest) != 0x899e)) { + log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chk, digest, chk ^ digest); + return DECODE_DIG_ERR; + } + + int id_tmp = (msgw[2] << 8) | (msgw[3]); + + DecodeStatus status; + + // Find appropriate slot in sensor data array and update + int slot = findSlot(id_tmp, &status); + + if (status != DECODE_OK) + return status; + + uint8_t ctr = (msgw[4] << 4) | (msgw[5] & 0xf0) >> 4; + log_v("--> CTR RAW: %d BCD: %d", ctr, ((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4); + uint8_t battery_low = (msgw[5] & 0x08) == 0x00; + uint16_t unknown1 = ((msgw[5] & 0x0f) << 8) | msgw[6]; + uint8_t type = msgw[6] >> 4; + uint8_t distance_km = msgw[7]; + log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], ((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f, distance_map[ msgw[7] ]); + uint16_t unknown2 = (msgw[8] << 8) | msgw[9]; + + sensor[slot].sensor_id = id_tmp; + sensor[slot].s_type = type; + sensor[slot].lightning_count = ctr; + sensor[slot].lightning_distance_km = distance_km; + sensor[slot].lightning_unknown1 = unknown1; + sensor[slot].lightning_unknown2 = unknown2; + sensor[slot].battery_ok = !battery_low; + sensor[slot].lightning_ok = true; + sensor[slot].rssi = rssi; + sensor[slot].valid = true; + sensor[slot].complete = true; + + log_d("ID: 0x%04X TYPE: %d CTR: %d batt_low: %d distance_km: %d unknown1: 0x%x unknown2: 0x%04x", id_tmp, type, ctr, battery_low, distance_km, unknown1, unknown2); + + + /* clang-format off */ + /* data = data_make( + "model", "", DATA_STRING, "Bresser_lightning", + "id", "", DATA_INT, id, + "Frage1", "?", DATA_INT, frage1, + "Kilometer", "Kilometer", DATA_INT, kilometer, + "CTR", "CTR", DATA_INT, ctr, + "Frage2", "??", DATA_INT, frage2, + "mic", "Integrity", DATA_STRING, "CRC", + "battery_low", "Battery Low", DATA_INT, !battery_low, + NULL); + */ + /* clang-format on */ + + //decoder_output_data(decoder, data); + + return DECODE_OK; +} +#endif diff --git a/src/WeatherSensor.h b/src/WeatherSensor.h index d61de043..7e12ff6b 100644 --- a/src/WeatherSensor.h +++ b/src/WeatherSensor.h @@ -55,6 +55,7 @@ // 20230328 Added MSG_BUF_SIZE // 20230330 Added changes for Adafruit Feather 32u4 LoRa Radio // 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230624 Added Bresser Lightning Sensor decoder // // ToDo: // - @@ -76,15 +77,16 @@ // 1 - Weather Station 6-in-1; PN 7002585 // - Professional Wind Gauge 6-in-1; PN 7002531 // 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 +// 3 - Lightning Sensor PN 7009976 // 4 - Soil Moisture Sensor 6-in-1; PN 7009972 // 9 - Professional Rain Gauge (5-in-1 decoder) // ? - Air Quality Sensor // ? - Water Leakage Sensor // ? - Pool Thermometer -// ? - Lightning Sensor PN 7009976 #define SENSOR_TYPE_WEATHER0 0 // Weather Station #define SENSOR_TYPE_WEATHER1 1 // Weather Station #define SENSOR_TYPE_THERMO_HYGRO 2 // Thermo-/Hygro-Sensor +#define SENSOR_TYPE_LIGHTNING 3 // Lightning Sensor #define SENSOR_TYPE_SOIL 4 // Soil Temperature and Moisture (from 6-in-1 decoder) #define SENSOR_TYPE_RAIN 9 // Professional Rain Gauge (from 5-in-1 decoder) @@ -192,6 +194,7 @@ class WeatherSensor { bool rain_ok = false; //!< rain gauge level o.k. bool battery_ok = false; //!< battery o.k. bool moisture_ok = false; //!< moisture o.k. (only 6-in-1) + bool lightning_ok = false; //!< lightning o.k. (only lightning) float temp_c; //!< temperature in degC float light_klx; //!< Light KLux (only 7-in-1) float light_lux; //!< Light lux (only 7-in-1) @@ -210,9 +213,13 @@ class WeatherSensor { uint16_t wind_gust_meter_sec_fp1; //!< wind speed (gusts) in m/s (fixed point int w. 1 decimal) uint16_t wind_avg_meter_sec_fp1; //!< wind speed (avg) in m/s (fixed point int w. 1 decimal) #endif - uint8_t humidity; //!< humidity in % - uint8_t moisture; //!< moisture in % (only 6-in-1) - float rssi; //!< received signal strength indicator in dBm + uint8_t humidity; //!< humidity in % + uint8_t moisture; //!< moisture in % (only 6-in-1) + uint8_t lightning_distance_km; //!< lightning distance in km (only lightning) + uint8_t lightning_count; //!< lightning strike counter (only lightning) + uint8_t lightning_unknown1; //!< unknown part 1 + uint16_t lightning_unknown2; //!< unknown part 2 + float rssi; //!< received signal strength indicator in dBm }; typedef struct Sensor sensor_t; //!< Shortcut for struct Sensor @@ -240,15 +247,16 @@ class WeatherSensor { { for (int i=0; i< NUM_SENSORS; i++) { if ((type == 0xFF) || (sensor[i].s_type == type)) { - sensor[i].valid = false; - sensor[i].complete = false; - sensor[i].temp_ok = false; - sensor[i].humidity_ok = false; - sensor[i].light_ok = false; - sensor[i].uv_ok = false; - sensor[i].wind_ok = false; - sensor[i].rain_ok = false; - sensor[i].moisture_ok = false; + sensor[i].valid = false; + sensor[i].complete = false; + sensor[i].temp_ok = false; + sensor[i].humidity_ok = false; + sensor[i].light_ok = false; + sensor[i].uv_ok = false; + sensor[i].wind_ok = false; + sensor[i].rain_ok = false; + sensor[i].moisture_ok = false; + sensor[i].lightning_ok = false; } } }; @@ -344,6 +352,18 @@ class WeatherSensor { */ DecodeStatus decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize); #endif + #ifdef BRESSER_LIGHTNING + /*! + \brief Decode BRESSER_LIGHTNING message. (similar to 7-in-1) + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize); + #endif protected: /*! diff --git a/src/WeatherSensorCfg.h b/src/WeatherSensorCfg.h index 90401767..6456dbe4 100644 --- a/src/WeatherSensorCfg.h +++ b/src/WeatherSensorCfg.h @@ -42,6 +42,7 @@ // 20230330 Added pin definitions and changes for Adafruit Feather 32u4 (AVR) RFM95 LoRa Radio // 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 // 20230420 Added pin definitions for DFRobot FireBeetle ESP32 with FireBeetle Cover LoRa +// 20230624 Added Bresser Lightning Sensor decoder // // ToDo: // - @@ -169,7 +170,7 @@ //#define SENSOR_IDS_EXC { 0x39582376 } // List of sensor IDs to be included - if empty, handle all available sensors -#define SENSOR_IDS_INC {} +#define SENSOR_IDS_INC { 0xEEFB } //#define SENSOR_IDS_INC { 0x83750871 } // List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" @@ -292,6 +293,7 @@ #define BRESSER_5_IN_1 #define BRESSER_6_IN_1 #define BRESSER_7_IN_1 +#define BRESSER_LIGHTNING #if ( !defined(BRESSER_5_IN_1) && !defined(BRESSER_6_IN_1) && !defined(BRESSER_7_IN_1) ) #error "Either BRESSER_5_IN_1 and/or BRESSER_6_IN_1 and/or BRESSER_7_IN_1 must be defined!" From bfba011cb02476047aa7cfdff84712b2c7cb8e58 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sat, 24 Jun 2023 09:43:43 +0200 Subject: [PATCH 03/16] Fix --- .../BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino index 9d9ba3ec..ffd55817 100644 --- a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino +++ b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino @@ -152,7 +152,7 @@ void loop() if (weatherSensor.sensor[i].lightning_distance_km == 0) { Serial.printf("Distance: [%2dkm] ", weatherSensor.sensor[i].lightning_distance_km); } else { - Serial.printf("Distance: [----] ", weatherSensor.sensor[i].lightning_distance_km); + Serial.printf("Distance: [----] "); } Serial.printf("unknown1: [0x%03X] ", weatherSensor.sensor[i].lightning_unknown1); Serial.printf("unknown2: [0x%04X] ", weatherSensor.sensor[i].lightning_unknown2); From 851431f772b5a3b09ed72ae6407f0e99fd93d410 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sat, 24 Jun 2023 09:48:00 +0200 Subject: [PATCH 04/16] Added paranthesis for log_v macro --- .../WeatherSensor.cpp | 1043 +++++++++++++++++ .../BresserWeatherSensorBasic/WeatherSensor.h | 403 +++++++ .../WeatherSensorCfg.h | 458 ++++++++ src/WeatherSensor.cpp | 4 +- 4 files changed, 1906 insertions(+), 2 deletions(-) create mode 100644 examples/BresserWeatherSensorBasic/WeatherSensor.cpp create mode 100644 examples/BresserWeatherSensorBasic/WeatherSensor.h create mode 100644 examples/BresserWeatherSensorBasic/WeatherSensorCfg.h diff --git a/examples/BresserWeatherSensorBasic/WeatherSensor.cpp b/examples/BresserWeatherSensorBasic/WeatherSensor.cpp new file mode 100644 index 00000000..f8ad4c66 --- /dev/null +++ b/examples/BresserWeatherSensorBasic/WeatherSensor.cpp @@ -0,0 +1,1043 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// WeatherSensor.cpp +// +// Bresser 5-in-1/6-in-1/7-in1 868 MHz Weather Sensor Radio Receiver +// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 +// +// https://github.com/matthias-bs/BresserWeatherSensorReceiver +// +// Based on: +// --------- +// Bresser5in1-CC1101 by Sean Siford (https://github.com/seaniefs/Bresser5in1-CC1101) +// RadioLib by Jan Gromeš (https://github.com/jgromes/RadioLib) +// rtl433 by Benjamin Larsson (https://github.com/merbanan/rtl_433) +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c +// +// created: 05/2022 +// +// +// MIT License +// +// Copyright (c) 2022 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: +// +// 20220523 Created from https://github.com/matthias-bs/Bresser5in1-CC1101 +// 20220524 Moved code to class WeatherSensor +// 20220526 Implemented getData(), changed debug output to macros +// 20220731 Updated decodeBresser5In1Payload()/decodeBresser6In1Payload() from rtl_433 project +// 20220815 Added support of multiple sensors +// Moved windspeed_ms_to_bft() to WeatherUtils.h/.cpp +// 20220905 Improved code quality and added Doxygen comments +// 20221003 Fixed humidity decoding in decodeBresser5In1Payload() +// 20221024 Modified WeatherSensorCfg.h/WeatherSensor.h handling +// 20221227 Replaced DEBUG_PRINT/DEBUG_PRINTLN by Arduino logging functions +// 20230111 Added additional digit for rain gauge in 5in1-decoder (maximum is now 999.9mm) +// 20230114 Modified decodeBresser6In1Payload() to distinguish msg type based on 'flags' (msg[16]) +// 20230228 Added Bresser 7 in 1 decoder by Jorge Navarro-Ortiz (jorgenavarro@ugr.es) +// 20230329 Fixed issue introduced with 7 in 1 decoder +// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230624 Added Bresser Lightning Sensor decoder +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "WeatherSensorCfg.h" +#include "WeatherSensor.h" + +#if defined(USE_CC1101) + static CC1101 radio = new Module(PIN_RECEIVER_CS, PIN_RECEIVER_IRQ, RADIOLIB_NC, PIN_RECEIVER_GPIO); +#endif +#if defined(USE_SX1276) + static SX1276 radio = new Module(PIN_RECEIVER_CS, PIN_RECEIVER_IRQ, PIN_RECEIVER_RST, PIN_RECEIVER_GPIO); +#endif + + +// List of sensor IDs to be excluded - can be empty +uint32_t const sensor_ids_exc[] = SENSOR_IDS_EXC; + +// List of sensor IDs to be included - if empty, handle all available sensors +uint32_t const sensor_ids_inc[] = SENSOR_IDS_INC; + +// List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" +// P/N 7002531 - requiring special heandling in decodeBresser5In1Payload() +uint32_t const sensor_ids_decode3in1[] = SENSOR_IDS_DECODE3IN1; + +int16_t WeatherSensor::begin(void) { + // https://github.com/RFD-FHEM/RFFHEM/issues/607#issuecomment-830818445 + // Freq: 868.300 MHz, Bandwidth: 203 KHz, rAmpl: 33 dB, sens: 8 dB, DataRate: 8207.32 Baud + log_d("%s Initializing ... ", RECEIVER_CHIP); + // carrier frequency: 868.3 MHz + // bit rate: 8.22 kbps + // frequency deviation: 57.136417 kHz + // Rx bandwidth: 270.0 kHz (CC1101) / 250 kHz (SX1276) + // output power: 10 dBm + // preamble length: 40 bits + #ifdef USE_CC1101 + int state = radio.begin(868.3, 8.21, 57.136417, 270, 10, 32); + #else + int state = radio.beginFSK(868.3, 8.21, 57.136417, 250, 10, 32); + #endif + if (state == RADIOLIB_ERR_NONE) { + log_d("success!"); + state = radio.setCrcFiltering(false); + if (state != RADIOLIB_ERR_NONE) { + log_e("%s Error disabling crc filtering: [%d]", RECEIVER_CHIP, state); + while (true) + ; + } + state = radio.fixedPacketLengthMode(MSG_BUF_SIZE); + if (state != RADIOLIB_ERR_NONE) { + log_e("%s Error setting fixed packet length: [%d]", RECEIVER_CHIP, state); + while (true) + ; + } + // Preamble: AA AA AA AA AA + // Sync is: 2D D4 + // Preamble 40 bits but the CC1101 doesn't allow us to set that + // so we use a preamble of 32 bits and then use the sync as AA 2D + // which then uses the last byte of the preamble - we recieve the last sync byte + // as the 1st byte of the payload. + #ifdef USE_CC1101 + state = radio.setSyncWord(0xAA, 0x2D, 0, false); + #else + uint8_t sync_word[] = {0xAA, 0x2D}; + state = radio.setSyncWord(sync_word, 2); + #endif + if (state != RADIOLIB_ERR_NONE) { + log_e("%s Error setting sync words: [%d]", RECEIVER_CHIP, state); + while (true) + ; + } + } else { + log_e("%s Error initialising: [%d]", RECEIVER_CHIP, state); + while (true) + ; + } + log_d("%s Setup complete - awaiting incoming messages...", RECEIVER_CHIP); + rssi = radio.getRSSI(); + + return state; +} + + +bool WeatherSensor::getData(uint32_t timeout, uint8_t flags, uint8_t type, void (*func)()) +{ + const uint32_t timestamp = millis(); + + + while ((millis() - timestamp) < timeout) { + int decode_status = getMessage(); + + // Callback function (see https://www.geeksforgeeks.org/callbacks-in-c/) + if (func) { + (*func)(); + } + + if (decode_status == DECODE_OK) { + bool all_slots_valid = true; + bool all_slots_complete = true; + for (int i=0; i -1) { + // Update slot + log_v("find_slot(): Updating slot #%d", update_slot); + *status = DECODE_OK; + return update_slot; + } + else if (free_slot > -1) { + // Store to free slot + log_v("find_slot(): Storing into slot #%d", free_slot); + *status = DECODE_OK; + return free_slot; + } + else { + log_v("find_slot(): No slot left"); + // No slot left + *status = DECODE_FULL; + return -1; + } +} + + +// +// Find required sensor data by ID +// +int WeatherSensor::findId(uint32_t id) +{ + for (int i=0; i= 0; --i) { + // fprintf(stderr, "key at bit %d : %04x\n", i, key); + // if data bit is set then xor with key + if ((data >> i) & 1) + sum ^= key; + + // roll the key right (actually the lsb is dropped here) + // and apply the gen (needs to include the dropped lsb as msb) + if (key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +// +// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/util.c +// +int WeatherSensor::add_bytes(uint8_t const message[], unsigned num_bytes) +{ + int result = 0; + for (unsigned i = 0; i < num_bytes; ++i) { + result += message[i]; + } + return result; +} + + +// +// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c (20220212) +// +// Example input data: +// EA EC 7F EB 5F EE EF FA FE 76 BB FA FF 15 13 80 14 A0 11 10 05 01 89 44 05 00 +// CC CC CC CC CC CC CC CC CC CC CC CC CC uu II SS GG DG WW W TT T HH RR RR Bt +// - C = Check, inverted data of 13 byte further +// - uu = checksum (number/count of set bits within bytes 14-25) +// - I = station ID (maybe) +// - G = wind gust in 1/10 m/s, normal binary coded, GGxG = 0x76D1 => 0x0176 = 256 + 118 = 374 => 37.4 m/s. MSB is out of sequence. +// - D = wind direction 0..F = N..NNE..E..S..W..NNW +// - W = wind speed in 1/10 m/s, BCD coded, WWxW = 0x7512 => 0x0275 = 275 => 27.5 m/s. MSB is out of sequence. +// - T = temperature in 1/10 °C, BCD coded, TTxT = 1203 => 31.2 °C +// - t = temperature sign, minus if unequal 0 +// - H = humidity in percent, BCD coded, HH = 23 => 23 % +// - R = rain in mm, BCD coded, RRRR = 1203 => 031.2 mm +// - B = Battery. 0=Ok, 8=Low. +// - S = sensor type, only low nibble used, 0x9 for Bresser Professional Rain Gauge +// +// Parameters: +// +// msg - Pointer to message +// msgSize - Size of message +// pOut - Pointer to WeatherData +// +// Returns: +// +// DECODE_OK - OK - WeatherData will contain the updated information +// DECODE_PAR_ERR - Parity Error +// DECODE_CHK_ERR - Checksum Error +// +#ifdef BRESSER_5_IN_1 +DecodeStatus WeatherSensor::decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSize) { + // First 13 bytes need to match inverse of last 13 bytes + for (unsigned col = 0; col < msgSize / 2; ++col) { + if ((msg[col] ^ msg[col + 13]) != 0xff) { + log_d("Parity wrong at column %d", col); + return DECODE_PAR_ERR; + } + } + + // Verify checksum (number number bits set in bytes 14-25) + uint8_t bitsSet = 0; + uint8_t expectedBitsSet = msg[13]; + + for(uint8_t p = 14 ; p < msgSize ; p++) { + uint8_t currentByte = msg[p]; + while(currentByte) { + bitsSet += (currentByte & 1); + currentByte >>= 1; + } + } + + if (bitsSet != expectedBitsSet) { + log_d("Checksum wrong - actual [%02X] != [%02X]", bitsSet, expectedBitsSet); + return DECODE_CHK_ERR; + } + + uint8_t id_tmp = msg[14]; + uint8_t type_tmp = msg[15] & 0xF; + DecodeStatus status; + + // Find appropriate slot in sensor data array and update + int slot = findSlot(id_tmp, &status); + + if (status != DECODE_OK) + return status; + + sensor[slot].sensor_id = id_tmp; + sensor[slot].s_type = type_tmp; + + int temp_raw = (msg[20] & 0x0f) + ((msg[20] & 0xf0) >> 4) * 10 + (msg[21] &0x0f) * 100; + if (msg[25] & 0x0f) { + temp_raw = -temp_raw; + } + sensor[slot].temp_c = temp_raw * 0.1f; + + sensor[slot].humidity = (msg[22] & 0x0f) + ((msg[22] & 0xf0) >> 4) * 10; + + int wind_direction_raw = ((msg[17] & 0xf0) >> 4) * 225; + int gust_raw = ((msg[17] & 0x0f) << 8) + msg[16]; + int wind_raw = (msg[18] & 0x0f) + ((msg[18] & 0xf0) >> 4) * 10 + (msg[19] & 0x0f) * 100; + + +#ifdef WIND_DATA_FLOATINGPOINT + sensor[slot].wind_direction_deg = wind_direction_raw * 0.1f; + sensor[slot].wind_gust_meter_sec = gust_raw * 0.1f; + sensor[slot].wind_avg_meter_sec = wind_raw * 0.1f; +#endif +#ifdef WIND_DATA_FIXEDPOINT + sensor[slot].wind_direction_deg_fp1 = wind_direction_raw; + sensor[slot].wind_gust_meter_sec_fp1 = gust_raw; + sensor[slot].wind_avg_meter_sec_fp1 = wind_raw; +#endif + + int rain_raw = (msg[23] & 0x0f) + ((msg[23] & 0xf0) >> 4) * 10 + (msg[24] & 0x0f) * 100 + ((msg[24] & 0xf0) >> 4) * 1000; + sensor[slot].rain_mm = rain_raw * 0.1f; + + sensor[slot].battery_ok = (msg[25] & 0x80) ? false : true; + + /* check if the message is from a Bresser Professional Rain Gauge */ + if ((msg[15] & 0xF) == 0x9) { + // rescale the rain sensor readings + sensor[slot].rain_mm *= 2.5; + + // Rain Gauge has no humidity (according to description) and no wind sensor (obviously) + sensor[slot].humidity_ok = false; + sensor[slot].wind_ok = false; + + } + else { + sensor[slot].humidity_ok = true; + sensor[slot].wind_ok = true; + } + + sensor[slot].temp_ok = true; + sensor[slot].light_ok = false; + sensor[slot].uv_ok = false; + sensor[slot].rain_ok = true; + sensor[slot].moisture_ok = false; + sensor[slot].valid = true; + sensor[slot].complete = true; + + // Save rssi to sensor specific data set + sensor[slot].rssi = rssi; + + return DECODE_OK; +} +#endif + + +// +// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c (20220608) +// +// - also Bresser Weather Center 7-in-1 indoor sensor. +// - also Bresser new 5-in-1 sensors. +// - also Froggit WH6000 sensors. +// - also rebranded as Ventus C8488A (W835) +// - also Bresser 3-in-1 Professional Wind Gauge / Anemometer PN 7002531 +// +// There are at least two different message types: +// - 24 seconds interval for temperature, hum, uv and rain (alternating messages) +// - 12 seconds interval for wind data (every message) +// +// Also Bresser Explore Scientific SM60020 Soil moisture Sensor. +// https://www.bresser.de/en/Weather-Time/Accessories/EXPLORE-SCIENTIFIC-Soil-Moisture-and-Soil-Temperature-Sensor.html +// +// Moisture: +// +// f16e 187000e34 7 ffffff0000 252 2 16 fff 004 000 [25,2, 99%, CH 7] +// DIGEST:8h8h ID?8h8h8h8h STYPE:4h STARTUP:1b CH:3d 8h 8h8h 8h8h TEMP:12h ?2b BATT:1b ?1b MOIST:8h UV?~12h ?4h CHKSUM:8h +// +// Moisture is transmitted in the humidity field as index 1-16: 0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99. +// The Wind speed and direction fields decode to valid zero but we exclude them from the output. +// +// aaaa2dd4e3ae1870079341ffffff0000221201fff279 [Batt ok] +// aaaa2dd43d2c1870079341ffffff0000219001fff2fc [Batt low] +// +// {206}55555555545ba83e803100058631ff11fe6611ffffffff01cc00 [Hum 96% Temp 3.8 C Wind 0.7 m/s] +// {205}55555555545ba999263100058631fffffe66d006092bffe0cff8 [Hum 95% Temp 3.0 C Wind 0.0 m/s] +// {199}55555555545ba840523100058631ff77fe668000495fff0bbe [Hum 95% Temp 3.0 C Wind 0.4 m/s] +// {205}55555555545ba94d063100058631fffffe665006092bffe14ff8 +// {206}55555555545ba860703100058631fffffe6651ffffffff0135fc [Hum 95% Temp 3.0 C Wind 0.0 m/s] +// {205}55555555545ba924d23100058631ff99fe68b004e92dffe073f8 [Hum 96% Temp 2.7 C Wind 0.4 m/s] +// {202}55555555545ba813403100058631ff77fe6810050929ffe1180 [Hum 94% Temp 2.8 C Wind 0.4 m/s] +// {205}55555555545ba98be83100058631fffffe6130050929ffe17800 [Hum 95% Temp 2.8 C Wind 0.8 m/s] +// +// 2dd4 1f 40 18 80 02 c3 18 ff 88 ff 33 08 ff ff ff ff 80 e6 00 [Hum 96% Temp 3.8 C Wind 0.7 m/s] +// 2dd4 cc 93 18 80 02 c3 18 ff ff ff 33 68 03 04 95 ff f0 67 3f [Hum 95% Temp 3.0 C Wind 0.0 m/s] +// 2dd4 20 29 18 80 02 c3 18 ff bb ff 33 40 00 24 af ff 85 df [Hum 95% Temp 3.0 C Wind 0.4 m/s] +// 2dd4 a6 83 18 80 02 c3 18 ff ff ff 33 28 03 04 95 ff f0 a7 3f +// 2dd4 30 38 18 80 02 c3 18 ff ff ff 33 28 ff ff ff ff 80 9a 7f [Hum 95% Temp 3.0 C Wind 0.0 m/s] +// 2dd4 92 69 18 80 02 c3 18 ff cc ff 34 58 02 74 96 ff f0 39 3f [Hum 96% Temp 2.7 C Wind 0.4 m/s] +// 2dd4 09 a0 18 80 02 c3 18 ff bb ff 34 08 02 84 94 ff f0 8c 0 [Hum 94% Temp 2.8 C Wind 0.4 m/s] +// 2dd4 c5 f4 18 80 02 c3 18 ff ff ff 30 98 02 84 94 ff f0 bc 00 [Hum 95% Temp 2.8 C Wind 0.8 m/s] +// +// {147} 5e aa 18 80 02 c3 18 fa 8f fb 27 68 11 84 81 ff f0 72 00 [Temp 11.8 C Hum 81%] +// {149} ae d1 18 80 02 c3 18 fa 8d fb 26 78 ff ff ff fe 02 db f0 +// {150} f8 2e 18 80 02 c3 18 fc c6 fd 26 38 11 84 81 ff f0 68 00 [Temp 11.8 C Hum 81%] +// {149} c4 7d 18 80 02 c3 18 fc 78 fd 29 28 ff ff ff fe 03 97 f0 +// {149} 28 1e 18 80 02 c3 18 fb b7 fc 26 58 ff ff ff fe 02 c3 f0 +// {150} 21 e8 18 80 02 c3 18 fb 9c fc 33 08 11 84 81 ff f0 b7 f8 [Temp 11.8 C Hum 81%] +// {149} 83 ae 18 80 02 c3 18 fc 78 fc 29 28 ff ff ff fe 03 98 00 +// {150} 5c e4 18 80 02 c3 18 fb ba fc 26 98 11 84 81 ff f0 16 00 [Temp 11.8 C Hum 81%] +// {148} d0 bd 18 80 02 c3 18 f9 ad fa 26 48 ff ff ff fe 02 ff f0 +// +// Wind and Temperature/Humidity or Rain: +// +// DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h TEMP:8h.4h ?2b BATT:1b ?1b HUM:8h UV?~12h ?4h CHKSUM:8h +// DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h RAINFLAG:8h RAIN:8h8h UV:8h8h CHKSUM:8h +// +// Digest is LFSR-16 gen 0x8810 key 0x5412, excluding the add-checksum and trailer. +// Checksum is 8-bit add (with carry) to 0xff. +// +// Notes on different sensors: +// +// - 1910 084d 18 : RebeckaJohansson, VENTUS W835 +// - 2030 088d 10 : mvdgrift, Wi-Fi Colour Weather Station with 5in1 Sensor, Art.No.: 7002580, ff 01 in the UV field is (obviously) invalid. +// - 1970 0d57 18 : danrhjones, bresser 5-in-1 model 7002580, no UV +// - 18b0 0301 18 : konserninjohtaja 6-in-1 outdoor sensor +// - 18c0 0f10 18 : rege245 BRESSER-PC-Weather-station-with-6-in-1-outdoor-sensor +// - 1880 02c3 18 : f4gqk 6-in-1 +// - 18b0 0887 18 : npkap +// +// Parameters: +// +// msg - Pointer to message +// msgSize - Size of message +// pOut - Pointer to WeatherData +// +// Returns: +// +// DECODE_OK - OK - WeatherData will contain the updated information +// DECODE_DIG_ERR - Digest Check Error +// DECODE_CHK_ERR - Checksum Error +#ifdef BRESSER_6_IN_1 +DecodeStatus WeatherSensor::decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSize) { + (void)msgSize; // unused parameter - kept for consistency with other decoders; avoid warning + int const moisture_map[] = {0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99}; // scale is 20/3 + + // Per-message status flags + bool temp_ok = false; + bool humidity_ok = false; + bool uv_ok = false; + bool wind_ok = false; + bool rain_ok = false; + bool moisture_ok = false; + bool f_3in1 = false; + + // LFSR-16 digest, generator 0x8810 init 0x5412 + int chkdgst = (msg[0] << 8) | msg[1]; + int digest = lfsr_digest16(&msg[2], 15, 0x8810, 0x5412); + if (chkdgst != digest) { + log_d("Digest check failed - [%02X] != [%02X]", chkdgst, digest); + return DECODE_DIG_ERR; + } + // Checksum, add with carry + int sum = add_bytes(&msg[2], 16); // msg[2] to msg[17] + if ((sum & 0xff) != 0xff) { + log_d("Checksum failed"); + return DECODE_CHK_ERR; + } + + uint32_t id_tmp = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]); + uint8_t type_tmp = (msg[6] >> 4); // 1: weather station, 2: indoor?, 4: soil probe + uint8_t chan_tmp = (msg[6] & 0x7); + uint8_t flags = (msg[16] & 0x0f); + DecodeStatus status; + + // Find appropriate slot in sensor data array and update + int slot = findSlot(id_tmp, &status); + + if (status != DECODE_OK) + return status; + + // unused... + //int startup = (msg[6] >> 3) & 1; // s.a. #1214 + + sensor[slot].sensor_id = id_tmp; + sensor[slot].s_type = type_tmp; + sensor[slot].chan = chan_tmp; + + f_3in1 = is_decode3in1(id_tmp); + + // temperature, humidity(, uv) - shared with rain counter + temp_ok = humidity_ok = (flags == 0); + if (temp_ok) { + bool sign = (msg[13] >> 3) & 1; + int temp_raw = (msg[12] >> 4) * 100 + (msg[12] & 0x0f) * 10 + (msg[13] >> 4); + float temp; + + // Workaround for 3-in-1 Professional Wind Gauge / Anemometer + if (f_3in1) { + temp = ((sign) ? -temp_raw : temp_raw) * 0.1f; + } else { + temp = ((sign) ? (temp_raw - 1000) : temp_raw) * 0.1f; + } + + sensor[slot].temp_c = temp; + sensor[slot].battery_ok = (msg[13] >> 1) & 1; // b[13] & 0x02 is battery_good, s.a. #1993 + sensor[slot].humidity = (msg[14] >> 4) * 10 + (msg[14] & 0x0f); + + // apparently ff01 or 0000 if not available, ???0 if valid, inverted BCD + uv_ok = (~msg[15] & 0xff) <= (0x99 && (~msg[16] & 0xf0) <= 0x90) && !f_3in1; + if (uv_ok) { + int uv_raw = ((~msg[15] & 0xf0) >> 4) * 100 + (~msg[15] & 0x0f) * 10 + ((~msg[16] & 0xf0) >> 4); + sensor[slot].uv = uv_raw * 0.1f; + } + } + + //int unk_ok = (msg[16] & 0xf0) == 0xf0; + //int unk_raw = ((msg[15] & 0xf0) >> 4) * 10 + (msg[15] & 0x0f); + + // invert 3 bytes wind speeds + msg[7] ^= 0xff; + msg[8] ^= 0xff; + msg[9] ^= 0xff; + wind_ok = (msg[7] <= 0x99) && (msg[8] <= 0x99) && (msg[9] <= 0x99); + if (wind_ok) { + int gust_raw = (msg[7] >> 4) * 100 + (msg[7] & 0x0f) * 10 + (msg[8] >> 4); + int wavg_raw = (msg[9] >> 4) * 100 + (msg[9] & 0x0f) * 10 + (msg[8] & 0x0f); + int wind_dir_raw = ((msg[10] & 0xf0) >> 4) * 100 + (msg[10] & 0x0f) * 10 + ((msg[11] & 0xf0) >> 4); + +#ifdef WIND_DATA_FLOATINGPOINT + sensor[slot].wind_gust_meter_sec = gust_raw * 0.1f; + sensor[slot].wind_avg_meter_sec = wavg_raw * 0.1f; + sensor[slot].wind_direction_deg = wind_dir_raw * 1.0f; +#endif +#ifdef WIND_DATA_FIXEDPOINT + sensor[slot].wind_gust_meter_sec_fp1 = gust_raw; + sensor[slot].wind_avg_meter_sec_fp1 = wavg_raw; + sensor[slot].wind_direction_deg_fp1 = wind_dir_raw * 10; +#endif + } + + // rain counter, inverted 3 bytes BCD - shared with temp/hum + msg[12] ^= 0xff; + msg[13] ^= 0xff; + msg[14] ^= 0xff; + + rain_ok = (flags == 1) && (type_tmp == 1); + if (rain_ok) { + int rain_raw = (msg[12] >> 4) * 100000 + (msg[12] & 0x0f) * 10000 + + (msg[13] >> 4) * 1000 + (msg[13] & 0x0f) * 100 + + (msg[14] >> 4) * 10 + (msg[14] & 0x0f); + sensor[slot].rain_mm = rain_raw * 0.1f; + } + + moisture_ok = false; + + // the moisture sensor might present valid readings but does not have the hardware + if (sensor[slot].s_type == 4) { + wind_ok = 0; + uv_ok = 0; + } + + if (sensor[slot].s_type == 4 && temp_ok && sensor[slot].humidity >= 1 && sensor[slot].humidity <= 16) { + moisture_ok = true; + humidity_ok = false; + sensor[slot].moisture = moisture_map[sensor[slot].humidity - 1]; + } + + // Update per-slot status flags + sensor[slot].temp_ok |= temp_ok; + sensor[slot].humidity_ok |= humidity_ok; + sensor[slot].uv_ok |= uv_ok; + sensor[slot].wind_ok |= wind_ok; + sensor[slot].rain_ok |= rain_ok; + sensor[slot].moisture_ok |= moisture_ok; + log_d("Temp: %d Hum: %d UV: %d Wind: %d Rain: %d Moist: %d", temp_ok, humidity_ok, uv_ok, wind_ok, rain_ok, moisture_ok); + + sensor[slot].valid = true; + + // Weather station data is split into two separate messages (except for Professional Wind Gauge) + sensor[slot].complete = ((sensor[slot].s_type == SENSOR_TYPE_WEATHER1) && sensor[slot].temp_ok && sensor[slot].rain_ok) || + f_3in1 || + (sensor[slot].s_type != SENSOR_TYPE_WEATHER1); + + // Save rssi to sensor specific data set + sensor[slot].rssi = rssi; + + return DECODE_OK; +} +#endif + +// +// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c (20230215) +// +/** +Decoder for Bresser Weather Center 7-in-1, outdoor sensor. +See https://github.com/merbanan/rtl_433/issues/1492 +Preamble: + aa aa aa aa aa 2d d4 +Observed length depends on reset_limit. +The data has a whitening of 0xaa. +Data layout: + {271}631d05c09e9a18abaabaaaaaaaaa8adacbacff9cafcaaaaaaa000000000000000000 + {262}10b8b4a5a3ca10aaaaaaaaaaaaaa8bcacbaaaa2aaaaaaaaaaa0000000000000000 [0.08 klx] + {220}543bb4a5a3ca10aaaaaaaaaaaaaa8bcacbaaaa28aaaaaaaaaa00000 [0.08 klx] + {273}2492b4a5a3ca10aaaaaaaaaaaaaa8bdacbaaaa2daaaaaaaaaa0000000000000000000 [0.08klx] + {269}9a59b4a5a3da10aaaaaaaaaaaaaa8bdac8afea28a8caaaaaaa000000000000000000 [54.0 klx UV=2.6] + {230}fe15b4a5a3da10aaaaaaaaaaaaaa8bdacbba382aacdaaaaaaa00000000 [109.2klx UV=6.7] + {254}2544b4a5a32a10aaaaaaaaaaaaaa8bdac88aaaaabeaaaaaaaa00000000000000 [200.000 klx UV=14 + DIGEST:8h8h ID?8h8h WDIR:8h4h 4h 8h WGUST:8h.4h WAVG:8h.4h RAIN:8h8h4h.4h RAIN?:8h TEMP:8h.4hC FLAGS?:4h HUM:8h% LIGHT:8h4h,8h4hKL UV:8h.4h TRAILER:8h8h8h4h +Unit of light is kLux (not W/m²). +First two bytes are an LFSR-16 digest, generator 0x8810 key 0xba95 with a final xor 0x6df1, which likely means we got that wrong. +*/ +#ifdef BRESSER_7_IN_1 +DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize) { + + if (msg[21] == 0x00) { + log_e("DECODE_FAIL_SANITY !!!"); + } + + // data whitening + uint8_t msgw[MSG_BUF_SIZE]; + for (unsigned i = 0; i < msgSize; ++i) { + msgw[i] = msg[i] ^ 0xaa; + } + + // LFSR-16 digest, generator 0x8810 key 0xba95 final xor 0x6df1 + int chkdgst = (msgw[0] << 8) | msgw[1]; + int digest = lfsr_digest16(&msgw[2], 23, 0x8810, 0xba95); // bresser_7in1 + if ((chkdgst ^ digest) != 0x6df1) { // bresser_7in1 + log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chkdgst, digest, chkdgst ^ digest); + return DECODE_DIG_ERR; + } + + int id_tmp = (msgw[2] << 8) | (msgw[3]); + DecodeStatus status; + + // Find appropriate slot in sensor data array and update + int slot = findSlot(id_tmp, &status); + + if (status != DECODE_OK) + return status; + + int wdir = (msgw[4] >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] >> 4); + int wgst_raw = (msgw[7] >> 4) * 100 + (msgw[7] & 0x0f) * 10 + (msgw[8] >> 4); + int wavg_raw = (msgw[8] & 0x0f) * 100 + (msgw[9] >> 4) * 10 + (msgw[9] & 0x0f); + //int rain_raw = (msgw[10] >> 4) * 100000 + (msgw[10] & 0x0f) * 10000 + (msgw[11] >> 4) * 1000 + (msgw[11] & 0x0f) * 100 + (msgw[12] >> 4) * 10 + (msgw[12] & 0x0f) * 1; // 6 digits + int rain_raw = (msgw[10] >> 4) * 1000 + (msgw[10] & 0x0f) * 100 + (msgw[11] >> 4) * 10 + (msgw[11] & 0x0f) * 1; // 4 digits + float rain_mm = rain_raw * 0.1f; + int temp_raw = (msgw[14] >> 4) * 100 + (msgw[14] & 0x0f) * 10 + (msgw[15] >> 4); + float temp_c = temp_raw * 0.1f; + int flags = (msgw[15] & 0x0f); + int battery_low = (flags & 0x06) == 0x06; + if (temp_raw > 600) + temp_c = (temp_raw - 1000) * 0.1f; + int humidity = (msgw[16] >> 4) * 10 + (msgw[16] & 0x0f); + int lght_raw = (msgw[17] >> 4) * 100000 + (msgw[17] & 0x0f) * 10000 + (msgw[18] >> 4) * 1000 + (msgw[18] & 0x0f) * 100 + (msgw[19] >> 4) * 10 + (msgw[19] & 0x0f); + int uv_raw = (msgw[20] >> 4) * 100 + (msgw[20] & 0x0f) * 10 + (msgw[21] >> 4); + + float light_klx = lght_raw * 0.001f; // TODO: remove this + float light_lux = lght_raw; + float uv_index = uv_raw * 0.1f; + + // The RTL_433 decoder does not include any field to verify that these data + // are ok, so we are assuming that they are ok if the decode status is ok. + sensor[slot].temp_ok = true; + sensor[slot].humidity_ok = true; + sensor[slot].wind_ok = true; + sensor[slot].rain_ok = true; + sensor[slot].light_ok = true; + sensor[slot].uv_ok = true; + + sensor[slot].sensor_id = id_tmp; + sensor[slot].temp_c = temp_c; + sensor[slot].humidity = humidity; +#ifdef WIND_DATA_FLOATINGPOINT + sensor[slot].wind_gust_meter_sec = wgst_raw * 0.1f; + sensor[slot].wind_avg_meter_sec = wavg_raw * 0.1f; + sensor[slot].wind_direction_deg = wdir * 1.0f; +#endif +#ifdef WIND_DATA_FIXEDPOINT + sensor[slot].wind_gust_meter_sec_fp1 = wgst_raw; + sensor[slot].wind_avg_meter_sec_fp1 = wavg_raw; + sensor[slot].wind_direction_deg_fp1 = wdir * 10; +#endif + sensor[slot].rain_mm = rain_mm; + sensor[slot].light_klx = light_klx; + sensor[slot].light_lux = light_lux; + sensor[slot].uv = uv_index; + sensor[slot].battery_ok = !battery_low; + sensor[slot].rssi = rssi; + sensor[slot].valid = true; + sensor[slot].complete = true; + + /* clang-format off */ + /* data = data_make( + "model", "", DATA_STRING, "Bresser-7in1", + "id", "", DATA_INT, id, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, + "humidity", "Humidity", DATA_INT, humidity, + "wind_max_m_s", "Wind Gust", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wgst_raw * 0.1f, + "wind_avg_m_s", "Wind Speed", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wavg_raw * 0.1f, + "wind_dir_deg", "Direction", DATA_INT, wdir, + "rain_mm", "Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, + "light_klx", "Light", DATA_FORMAT, "%.3f klx", DATA_DOUBLE, light_klx, // TODO: remove this + "light_lux", "Light", DATA_FORMAT, "%.3f lux", DATA_DOUBLE, light_lux, + "uv", "UV Index", DATA_FORMAT, "%.1f", DATA_DOUBLE, uv_index, + "battery_ok", "Battery", DATA_INT, !battery_low, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); + */ + /* clang-format on */ + +// decoder_output_data(decoder, data); + + return DECODE_OK; + +} +#endif + +/** +Decoder for Bresser Lightning, outdoor sensor. + +https://github.com/merbanan/rtl_433/issues/2140 + +DIGEST:8h8h ID:8h8h CTR:12h ?4h8h KM:8d ?8h8h + 0 1 2 3 4 5h 5l 6 7 8 9 + +Preamble: + + aa 2d d4 + +Observed length depends on reset_limit. +The data has a whitening of 0xaa. + + +First two bytes are an LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e +*/ + +#ifdef BRESSER_LIGHTNING +DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize) +{ + // see AS3935 Datasheet, Table 17 - Distance Estimation + uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; + + // data whitening + uint8_t msgw[MSG_BUF_SIZE]; + for (unsigned i = 0; i < msgSize; ++i) { + msgw[i] = msg[i] ^ 0xaa; + } + + // LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e + int chk = (msgw[0] << 8) | msgw[1]; + int digest = lfsr_digest16(&msgw[2], 8, 0x8810, 0xabf9); + //fprintf(stderr, "DIGEST %04x vs %04x (%04x) \n", chk, digest, chk ^ digest); + //if (((chk ^ digest) != 0x899e) && ((chk ^ digest) != 0x8b9e)) { + if (((chk ^ digest) != 0x899e)) { + log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chk, digest, chk ^ digest); + return DECODE_DIG_ERR; + } + + int id_tmp = (msgw[2] << 8) | (msgw[3]); + + DecodeStatus status; + + // Find appropriate slot in sensor data array and update + int slot = findSlot(id_tmp, &status); + + if (status != DECODE_OK) + return status; + + uint8_t ctr = (msgw[4] << 4) | (msgw[5] & 0xf0) >> 4; + log_v("--> CTR RAW: %d BCD: %d", ctr, ((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4); + uint8_t battery_low = (msgw[5] & 0x08) == 0x00; + uint16_t unknown1 = ((msgw[5] & 0x0f) << 8) | msgw[6]; + uint8_t type = msgw[6] >> 4; + uint8_t distance_km = msgw[7]; + log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], ((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f, distance_map[ msgw[7] ]); + uint16_t unknown2 = (msgw[8] << 8) | msgw[9]; + + sensor[slot].sensor_id = id_tmp; + sensor[slot].s_type = type; + sensor[slot].lightning_count = ctr; + sensor[slot].lightning_distance_km = distance_km; + sensor[slot].lightning_unknown1 = unknown1; + sensor[slot].lightning_unknown2 = unknown2; + sensor[slot].battery_ok = !battery_low; + sensor[slot].lightning_ok = true; + sensor[slot].rssi = rssi; + sensor[slot].valid = true; + sensor[slot].complete = true; + + log_d("ID: 0x%04X TYPE: %d CTR: %d batt_low: %d distance_km: %d unknown1: 0x%x unknown2: 0x%04x", id_tmp, type, ctr, battery_low, distance_km, unknown1, unknown2); + + + /* clang-format off */ + /* data = data_make( + "model", "", DATA_STRING, "Bresser_lightning", + "id", "", DATA_INT, id, + "Frage1", "?", DATA_INT, frage1, + "Kilometer", "Kilometer", DATA_INT, kilometer, + "CTR", "CTR", DATA_INT, ctr, + "Frage2", "??", DATA_INT, frage2, + "mic", "Integrity", DATA_STRING, "CRC", + "battery_low", "Battery Low", DATA_INT, !battery_low, + NULL); + */ + /* clang-format on */ + + //decoder_output_data(decoder, data); + + return DECODE_OK; +} +#endif diff --git a/examples/BresserWeatherSensorBasic/WeatherSensor.h b/examples/BresserWeatherSensorBasic/WeatherSensor.h new file mode 100644 index 00000000..7e12ff6b --- /dev/null +++ b/examples/BresserWeatherSensorBasic/WeatherSensor.h @@ -0,0 +1,403 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// WeatherSensor.h +// +// Bresser 5-in-1/6-in-1/7-in-1 868 MHz Weather Sensor Radio Receiver +// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 +// +// https://github.com/matthias-bs/BresserWeatherSensorReceiver +// +// NOTE: Application/hardware specific configurations should be made in WeatherSensorCfg.h! +// +// Based on: +// --------- +// Bresser5in1-CC1101 by Sean Siford (https://github.com/seaniefs/Bresser5in1-CC1101) +// RadioLib by Jan Gromeš (https://github.com/jgromes/RadioLib) +// rtl433 by Benjamin Larsson (https://github.com/merbanan/rtl_433) +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c +// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c +// +// created: 05/2022 +// +// +// MIT License +// +// Copyright (c) 2022 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: +// +// 20220523 Created from https://github.com/matthias-bs/Bresser5in1-CC1101 +// 20220524 Moved code to class WeatherSensor +// 20220526 Implemented getData(), changed debug output to macros +// 20220815 Added support of multiple sensors +// Moved windspeed_ms_to_bft() to WeatherUtils.h/.cpp +// 20221207 Added SENSOR_TYPE_THERMO_HYGRO +// 20220110 Added WEATHER0_RAIN_OV/WEATHER1_RAIN_OV +// 20230228 Added Bresser 7 in 1 decoder by Jorge Navarro-Ortiz (jorgenavarro@ugr.es) +// 20230328 Added MSG_BUF_SIZE +// 20230330 Added changes for Adafruit Feather 32u4 LoRa Radio +// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230624 Added Bresser Lightning Sensor decoder +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef WeatherSensor_h +#define WeatherSensor_h + +#include +#if defined(ESP32) || defined(ESP8266) + #include +#endif +#include + + +// Sensor Types +// 0 - Weather Station 5-in-1; PN 7002510..12/7902510..12 +// 1 - Weather Station 6-in-1; PN 7002585 +// - Professional Wind Gauge 6-in-1; PN 7002531 +// 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 +// 3 - Lightning Sensor PN 7009976 +// 4 - Soil Moisture Sensor 6-in-1; PN 7009972 +// 9 - Professional Rain Gauge (5-in-1 decoder) +// ? - Air Quality Sensor +// ? - Water Leakage Sensor +// ? - Pool Thermometer +#define SENSOR_TYPE_WEATHER0 0 // Weather Station +#define SENSOR_TYPE_WEATHER1 1 // Weather Station +#define SENSOR_TYPE_THERMO_HYGRO 2 // Thermo-/Hygro-Sensor +#define SENSOR_TYPE_LIGHTNING 3 // Lightning Sensor +#define SENSOR_TYPE_SOIL 4 // Soil Temperature and Moisture (from 6-in-1 decoder) +#define SENSOR_TYPE_RAIN 9 // Professional Rain Gauge (from 5-in-1 decoder) + + +// Sensor specific rain gauge overflow threshold (mm) +#define WEATHER0_RAIN_OV 1000 +#define WEATHER1_RAIN_OV 100000 + + +// Flags for controlling completion of reception in getData() +#define DATA_COMPLETE 0x1 // only completed slots (as opposed to partially filled) +#define DATA_TYPE 0x2 // at least one slot with specific sensor type +#define DATA_ALL_SLOTS 0x8 // all slots completed + +// Message buffer size +#define MSG_BUF_SIZE 27 + +// Radio message decoding status +typedef enum DecodeStatus { + DECODE_INVALID, DECODE_OK, DECODE_PAR_ERR, DECODE_CHK_ERR, DECODE_DIG_ERR, DECODE_SKIP, DECODE_FULL +} DecodeStatus; + + +#if defined(ESP32) || defined(ESP8266) +/*! + * \struct SensorMap + * + * \brief Mapping of sensor IDs to names + */ +typedef struct SensorMap { + uint32_t id; //!< ID if sensor (as transmitted in radio message) + std::string name; //!< Name of sensor (e.g. for MQTT topic) +} SensorMap; +#endif + + +/*! + \class WeatherSensor + + \brief Receive, decode and store Bresser Weather Sensor Data + Uses CC1101 or SX1276 radio module for receiving FSK modulated signal at 868 MHz. +*/ +class WeatherSensor { + public: + /*! + \brief Constructor. + + */ + WeatherSensor() + { + }; + + + /*! + \brief Presence check and initialization of radio module. + + \returns RADIOLIB_ERR_NONE on success (otherwise does never return). + */ + int16_t begin(void); + + + /*! + \brief Wait for reception of data or occurrance of timeout. + With BRESSER_6_IN_1, data is distributed across two different messages. Reception of entire + data is tried if 'complete' is set. + + \param timeout timeout in ms. + + \param flags DATA_COMPLETE / DATA_TYPE / DATA_ALL_SLOTS + + \param type sensor type (combined with FLAGS==DATA_TYPE) + + \param func Callback function for each loop iteration. (default: NULL) + + \returns false: Timeout occurred. + true: Reception (according to parammeter 'complete') succesful. + */ + bool getData(uint32_t timeout, uint8_t flags = 0, uint8_t type = 0, void (*func)() = NULL); + + + /*! + \brief Tries to receive radio message (non-blocking) and to decode it. + Timeout occurs after a multitude of expected time-on-air. + + \returns DecodeStatus + */ + DecodeStatus getMessage(void); + + /** + * \struct Sensor + * + * \brief sensor data and status flags + */ + struct Sensor { + uint32_t sensor_id; //!< sensor ID (5-in-1: 1 byte / 6-in-1: 4 bytes) + uint8_t s_type; //!< sensor type (only 6-in1) + uint8_t chan; //!< channel (only 6-in-1) + bool valid; //!< data valid (but not necessarily complete) + bool complete; //!< data is split into two separate messages is complete (only 6-in-1 WS) + bool temp_ok = false; //!< temperature o.k. (only 6-in-1) + bool humidity_ok = false; //!< humidity o.k. + bool light_ok = false; //!< light o.k. (only 7-in-1) + bool uv_ok = false; //!< uv radiation o.k. (only 6-in-1) + bool wind_ok = false; //!< wind speed/direction o.k. (only 6-in-1) + bool rain_ok = false; //!< rain gauge level o.k. + bool battery_ok = false; //!< battery o.k. + bool moisture_ok = false; //!< moisture o.k. (only 6-in-1) + bool lightning_ok = false; //!< lightning o.k. (only lightning) + float temp_c; //!< temperature in degC + float light_klx; //!< Light KLux (only 7-in-1) + float light_lux; //!< Light lux (only 7-in-1) + float uv; //!< uv radiation (only 6-in-1) + float rain_mm; //!< rain gauge level in mm + #ifdef WIND_DATA_FLOATINGPOINT + float wind_direction_deg; //!< wind direction in deg + float wind_gust_meter_sec; //!< wind speed (gusts) in m/s + float wind_avg_meter_sec; //!< wind speed (avg) in m/s + #endif + #ifdef WIND_DATA_FIXEDPOINT + // For LoRa_Serialization: + // fixed point integer with 1 decimal - + // saves two bytes compared to "RawFloat" + uint16_t wind_direction_deg_fp1; //!< wind direction in deg (fixed point int w. 1 decimal) + uint16_t wind_gust_meter_sec_fp1; //!< wind speed (gusts) in m/s (fixed point int w. 1 decimal) + uint16_t wind_avg_meter_sec_fp1; //!< wind speed (avg) in m/s (fixed point int w. 1 decimal) + #endif + uint8_t humidity; //!< humidity in % + uint8_t moisture; //!< moisture in % (only 6-in-1) + uint8_t lightning_distance_km; //!< lightning distance in km (only lightning) + uint8_t lightning_count; //!< lightning strike counter (only lightning) + uint8_t lightning_unknown1; //!< unknown part 1 + uint16_t lightning_unknown2; //!< unknown part 2 + float rssi; //!< received signal strength indicator in dBm + }; + + typedef struct Sensor sensor_t; //!< Shortcut for struct Sensor + sensor_t sensor[NUM_SENSORS]; //!< sensor data array + float rssi; //!< received signal strength indicator in dBm + + + /*! + \brief Generates data otherwise received and decoded from a radio message. + + \returns Always true (for compatibility with getMessage()) + */ + bool genMessage(int i, uint32_t id = 0xff, uint8_t type = 1, uint8_t channel = 0); + + + /*! + \brief Clear sensor data + + If 'type' is not specified, all slots are cleared. If 'type' is specified, + only slots containing data of the given sensor type are cleared. + + \param type Sensor type + */ + void clearSlots(uint8_t type = 0xFF) + { + for (int i=0; i< NUM_SENSORS; i++) { + if ((type == 0xFF) || (sensor[i].s_type == type)) { + sensor[i].valid = false; + sensor[i].complete = false; + sensor[i].temp_ok = false; + sensor[i].humidity_ok = false; + sensor[i].light_ok = false; + sensor[i].uv_ok = false; + sensor[i].wind_ok = false; + sensor[i].rain_ok = false; + sensor[i].moisture_ok = false; + sensor[i].lightning_ok = false; + } + } + }; + + /*! + * Find slot of required data set by ID + * + * \param id sensor ID + * + * \returns slot (or -1 if not found) + */ + int findId(uint32_t id); + + + /*! + * Find slot of required data set by type and (optionally) channel + * + * \param type sensor type + * \param channel sensor channel (0xFF: don't care) + * + * \returns slot (or -1 if not found) + */ + int findType(uint8_t type, uint8_t channel = 0xFF); + + /*! + * Check if sensor ID is in sensor_ids_decode3in1[] + * + * \param id sensor ID + * + * \returns true if sensor is in sensor_ids_decode3in1[], + * false otherwise + */ + bool is_decode3in1(uint32_t id); + + private: + struct Sensor *pData; //!< pointer to slot in sensor data array + + /*! + * \brief Find slot in sensor data array + * + * 1. The current sensor ID is checked against the exclude-list (sensor_ids_exc). + * If there is a match, the current message is skipped. + * + * 2. If the include-list (sensor_ids_inc) is not empty, the current ID is checked + * against it. If there is NO match, the current message is skipped. + * + * 3. Either an existing slot with the same ID as the current message is updated + * or a free slot (if any) is selected. + * + * \param id Sensor ID from current message + * + * \returns Pointer to slot in sensor data array or NULL if ID is not wanted or + * no free slot is available. + */ + int findSlot(uint32_t id, DecodeStatus * status); + + + #ifdef BRESSER_5_IN_1 + /*! + \brief Decode BRESSER_5_IN_1 message. + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSize); + #endif + #ifdef BRESSER_6_IN_1 + /*! + \brief Decode BRESSER_6_IN_1 message. + With BRESSER_6_IN_1, data is distributed across two different messages. Additionally, + the message format supports different kinds of sensors. + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSize); + #endif + #ifdef BRESSER_7_IN_1 + /*! + \brief Decode BRESSER_7_IN_1 message. + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize); + #endif + #ifdef BRESSER_LIGHTNING + /*! + \brief Decode BRESSER_LIGHTNING message. (similar to 7-in-1) + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize); + #endif + + protected: + /*! + \brief Linear Feedback Shift Register - Digest16 (Data integrity check). + */ + uint16_t lfsr_digest16(uint8_t const message[], unsigned bytes, uint16_t gen, uint16_t key); + + /*! + \brief Calculate sum of all message bytes. + + \param message Message buffer. + \param num_bytes Number of bytes. + + \returns Sum of all message bytes. + */ + int add_bytes(uint8_t const message[], unsigned num_bytes); + + #ifdef _DEBUG_MODE_ + /*! + \brief Print raw message payload as hex byte values. + */ + void printRawdata(uint8_t *msg, uint8_t msgSize) { + DEBUG_PRINT(F("Raw Data: ")); + for (uint8_t p = 0 ; p < msgSize ; p++) { + if (msg[p] < 16) { + DEBUG_PRINT("0"); + } + DEBUG_PRINT(msg[p], HEX); + DEBUG_PRINT(" "); + } + DEBUG_PRINTLN(); + }; + #endif + +}; + +#endif diff --git a/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h b/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h new file mode 100644 index 00000000..6456dbe4 --- /dev/null +++ b/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h @@ -0,0 +1,458 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// WeatherSensorCfg.h +// +// Bresser 5-in-1/6-in-1/7-in-1 868 MHz Weather Sensor Radio Receiver +// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 +// +// https://github.com/matthias-bs/BresserWeatherSensorReceiver +// +// NOTE: Application/hardware specific configurations should be made in this file! +// +// created: 05/2022 +// +// +// MIT License +// +// Copyright (c) 2022 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: +// 20230124 Added some default settings based on selected boards in Arduino IDE +// 20230207 Added pin definitions for ARDUINO_TTGO_LoRa32_v21new +// 20230208 Added pin definitions for ARDUINO_TTGO_LoRa32_V2 +// 20230301 Added pin definitions for Wireless_Stick (from Heltec) +// 20230316 Added pin definitions for Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 +// 20230330 Added pin definitions and changes for Adafruit Feather 32u4 (AVR) RFM95 LoRa Radio +// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230420 Added pin definitions for DFRobot FireBeetle ESP32 with FireBeetle Cover LoRa +// 20230624 Added Bresser Lightning Sensor decoder +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if !defined(WEATHER_SENSOR_CFG_H) +#define WEATHER_SENSOR_CFG_H + +#include + +// ------------------------------------------------------------------------------------------------ +// --- Board --- +// ------------------------------------------------------------------------------------------------ +// Use pinning for LoRaWAN Node + + +// LILIGO TTGO LoRaP32 board with integrated RF tranceiver (SX1276) +// See pin definitions in +// https://github.com/espressif/arduino-esp32/tree/master/variants/ttgo-lora32-* +// and +// https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-2/11973 + +// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V1 (No TFCard)" +// in the Arduino IDE: +//#define ARDUINO_TTGO_LoRa32_V1 + +// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V2" +// in the Arduino IDE: +//#define ARDUINO_TTGO_LoRa32_V2 + +// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V2.1 (1.6.1)" +// in the Arduino IDE: +//#define ARDUINO_TTGO_LoRa32_V21new + +// This define is set by selecting "Board: Heltec Wireless Stick" +// in the Arduino IDE: +//#define ARDUINO_heltec_wireless_stick + +// Adafruit Feather ESP32S2 with RFM95W "FeatherWing" ADA3232 +// https://github.com/espressif/arduino-esp32/blob/master/variants/adafruit_feather_esp32s2/pins_arduino.h +// +// This define is set by selecting "Adafruit Feather ESP32-S2" in the Arduino IDE: +//#define ARDUINO_ADAFRUIT_FEATHER_ESP32S2 + +// Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 +// https://github.com/espressif/arduino-esp32/blob/master/variants/feather_esp32/pins_arduino.h +// +// This define is set by selecting "Adafruit ESP32 Feather" in the Arduino IDE: +//#define ARDUINO_FEATHER_ESP32 + +// DFRobot Firebeetle32 +// https://github.com/espressif/arduino-esp32/tree/master/variants/firebeetle32/pins_arduino.h +// +// This define (not very specific...) is set by selecting "FireBeetle-ESP32" in the Arduino IDE: +//#define ARDUINO_ESP32_DEV + +#if defined(ARDUINO_TTGO_LoRa32_V1) + #pragma message("ARDUINO_TTGO_LoRa32_V1 defined; using on-board transceiver") + #define USE_SX1276 + +#elif defined(ARDUINO_TTGO_LoRa32_V2) + #pragma message("ARDUINO_TTGO_LoRa32_V2 defined; using on-board transceiver") + #pragma message("LoRa DIO1 must be wired to GPIO33 manually!") + #define USE_SX1276 + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) + #pragma message("ARDUINO_TTGO_LoRa32_V21new defined; using on-board transceiver") + #define USE_SX1276 + +#elif defined(ARDUINO_heltec_wireless_stick) + #pragma message("ARDUINO_heltec_wireless_stick defined; using on-board transceiver") + #define USE_SX1276 + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + #pragma message("ARDUINO_ADAFRUIT_FEATHER_ESP32S2 defined; assuming RFM95W FeatherWing will be used") + #define USE_SX1276 + +#elif defined(ARDUINO_FEATHER_ESP32) + #pragma message("ARDUINO_FEATHER_ESP32 defined; assuming RFM95W FeatherWing will be used") + #define USE_SX1276 + #pragma message("Required wiring: A to RST, B to DIO1, D to DIO0, E to CS") + +#elif defined(ARDUINO_AVR_FEATHER32U4) + #pragma message("ARDUINO_AVR_FEATHER32U4 defined; assuming this is the Arduino Feather 32u4 RFM95 LoRa Radio") + #define USE_SX1276 + +#elif defined(ARDUINO_ESP32_DEV) + //#define LORAWAN_NODE + #define FIREBEETLE_ESP32_COVER_LORA + + #if !defined(LORAWAN_NODE) && !defined(FIREBEETLE_ESP32_COVER_LORA) + #pragma message("ARDUINO_ESP32_DEV defined; select either LORAWAN_NODE or FIREBEETLE_ESP32_COVER_LORA manually!") + + #elif defined(LORAWAN_NODE) + #pragma message("LORAWAN_NODE defined; assuming this is the LoRaWAN_Node board (DFRobot Firebeetle32 + Adafruit RFM95W LoRa Radio)") + #define USE_SX1276 + + #elif defined(FIREBEETLE_ESP32_COVER_LORA) + #define USE_SX1276 + #pragma message("FIREBEETLE_ESP32_COVER_LORA defined; assuming this is a FireBeetle ESP32 with FireBeetle Cover LoRa") + #pragma message("Required wiring: D2 to RESET, D3 to DIO0, D4 to CS, D5 to DIO1") + + #endif +#endif + + +// ------------------------------------------------------------------------------------------------ +// --- Radio Transceiver --- +// ------------------------------------------------------------------------------------------------ +// Select type of receiver module (if not yet defined based on the assumptions above) +#if ( !defined(USE_CC1101) && !defined(USE_SX1276) ) + //#define USE_CC1101 + #define USE_SX1276 +#endif + + +// ------------------------------------------------------------------------------------------------ +// --- Weather Sensors --- +// ------------------------------------------------------------------------------------------------ +#define NUM_SENSORS 1 // Number of sensors to be received + +// List of sensor IDs to be excluded - can be empty +#define SENSOR_IDS_EXC {} +//#define SENSOR_IDS_EXC { 0x39582376 } + +// List of sensor IDs to be included - if empty, handle all available sensors +#define SENSOR_IDS_INC { 0xEEFB } +//#define SENSOR_IDS_INC { 0x83750871 } + +// List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" +// P/N 7002531 - requiring special heandling in decodeBresser5In1Payload() +//#define SENSOR_IDS_DECODE3IN1 {} +#define SENSOR_IDS_DECODE3IN1 { 0x2C100512 } + +// ------------------------------------------------------------------------------------------------ +// --- Debug Logging Output --- +// ------------------------------------------------------------------------------------------------ +// - ESP32: +// CORE_DEBUG_LEVEL is set in Adruino IDE: +// Tools->Core Debug Level: "|||||" +// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h +// +// - ESP8266: +// DEBUG_ESP_PORT is set in Arduino IDE: +// Tools->Debug port: "||" +// +// Replacement for +// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h +// on ESP8266: +#if defined(ESP8266) + #define ARDUHAL_LOG_LEVEL_NONE 0 + #define ARDUHAL_LOG_LEVEL_ERROR 1 + #define ARDUHAL_LOG_LEVEL_WARN 2 + #define ARDUHAL_LOG_LEVEL_INFO 3 + #define ARDUHAL_LOG_LEVEL_DEBUG 4 + #define ARDUHAL_LOG_LEVEL_VERBOSE 5 + + // Set desired level here! + #define CORE_DEBUG_LEVEL ARDUHAL_LOG_LEVEL_VERBOSE + + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_NONE + #define log_e(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } + #else + #define log_e(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_ERROR + #define log_w(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } + #else + #define log_w(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_WARN + #define log_i(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } + #else + #define log_i(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_INFO + #define log_d(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } + #else + #define log_d(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_DEBUG + #define log_v(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } + #else + #define log_v(...) {} + #endif + +#endif + + +// Replacement for +// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h +// on Arduino AVR: +#if defined(ARDUINO_ARCH_AVR) + #define ARDUHAL_LOG_LEVEL_NONE 0 + #define ARDUHAL_LOG_LEVEL_ERROR 1 + #define ARDUHAL_LOG_LEVEL_WARN 2 + #define ARDUHAL_LOG_LEVEL_INFO 3 + #define ARDUHAL_LOG_LEVEL_DEBUG 4 + #define ARDUHAL_LOG_LEVEL_VERBOSE 5 + + // Set desired level here! + #define CORE_DEBUG_LEVEL ARDUHAL_LOG_LEVEL_INFO + + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_NONE + #define log_e(...) { printf(__VA_ARGS__); println(); } + #else + #define log_e(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_ERROR + #define log_w(...) { printf(__VA_ARGS__); println(); } + #else + #define log_w(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_WARN + #define log_i(...) { printf(__VA_ARGS__); println(); } + #else + #define log_i(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_INFO + #define log_d(...) { printf(__VA_ARGS__); println(); } + #else + #define log_d(...) {} + #endif + #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_DEBUG + #define log_v(...) { printf(__VA_ARGS__); println(); } + #else + #define log_v(...) {} + #endif +#endif + + +//#define _DEBUG_MODE_ // Enable debug output (serial console) +#define DEBUG_PORT Serial +#if defined(_DEBUG_MODE_) + #define DEBUG_PRINT(...) { DEBUG_PORT.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PORT.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +// Disable data type which will not be used to save RAM +#define WIND_DATA_FLOATINGPOINT +#define WIND_DATA_FIXEDPOINT + +// Select appropriate sensor message format(s) +#define BRESSER_5_IN_1 +#define BRESSER_6_IN_1 +#define BRESSER_7_IN_1 +#define BRESSER_LIGHTNING + +#if ( !defined(BRESSER_5_IN_1) && !defined(BRESSER_6_IN_1) && !defined(BRESSER_7_IN_1) ) + #error "Either BRESSER_5_IN_1 and/or BRESSER_6_IN_1 and/or BRESSER_7_IN_1 must be defined!" +#endif + +#if ( defined(USE_CC1101) && defined(USE_SX1276) ) + #error "Either USE_CC1101 OR USE_SX1276 must be defined!" +#endif + +#if defined(USE_CC1101) + #define RECEIVER_CHIP "[CC1101]" +#elif defined(USE_SX1276) + #define RECEIVER_CHIP "[SX1276]" +#else + #error "Either USE_CC1101 or USE_SX1276 must be defined!" +#endif + + +// Arduino default SPI pins +// +// Board SCK MOSI MISO +// ESP8266 D5 D7 D6 +// ESP32 D18 D23 D19 +#if defined(LORAWAN_NODE) + // Use pinning for LoRaWAN_Node (https://github.com/matthias-bs/LoRaWAN_Node) + #define PIN_RECEIVER_CS 14 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 4 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 16 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 12 + +#elif defined(FIREBEETLE_ESP32_COVER_LORA) + #define PIN_RECEIVER_CS 27 // D4 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 26 // D3 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 9 // D5 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 25 // D2 + +#elif defined(ARDUINO_TTGO_LoRa32_V1) || defined(ARDUINO_TTGO_LoRa32_V2) + // Use pinning for LILIGO TTGO LoRa32-OLED + #define PIN_RECEIVER_CS LORA_CS + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ LORA_IRQ + + // CC1101: GDO2 / RFM95W/SX127x: G1 + // n.c. on v1/v2?, LORA_D1 on v21 + #define PIN_RECEIVER_GPIO 33 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST LORA_RST + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) + // Use pinning for LILIGO TTGO LoRa32-OLED V2.1 (1.6.1) + // Same pinout for Heltec Wireless Stick + #define PIN_RECEIVER_CS LORA_CS + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ LORA_IRQ + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO LORA_D1 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST LORA_RST + +#elif defined(ARDUINO_heltec_wireless_stick) + // Use pinning for Heltec Wireless Stick + #define PIN_RECEIVER_CS SS + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ DIO0 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO DIO1 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST RST_LoRa + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + // Use pinning for Adafruit Feather ESP32S2 with RFM95W "FeatherWing" ADA3232 + #define PIN_RECEIVER_CS 6 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 5 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 11 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 9 + +#elif defined(ARDUINO_FEATHER_ESP32) + // Use pinning for Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 + #define PIN_RECEIVER_CS 14 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 32 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 33 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 27 + +#elif defined(ESP32) + // Generic pinning for ESP32 development boards + #define PIN_RECEIVER_CS 27 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 21 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 33 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 32 + +#elif defined(ESP8266) + // Generic pinning for ESP8266 development boards (e.g. LOLIN/WEMOS D1 mini) + #define PIN_RECEIVER_CS 15 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 4 + + // CC1101: GDO2 / RFM95W/SX127x: G1 + #define PIN_RECEIVER_GPIO 5 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 2 + +#elif defined(ARDUINO_AVR_FEATHER32U4) + // Pinning for Adafruit Feather 32u4 + #define PIN_RECEIVER_CS 8 + + // CC1101: GDO0 / RFM95W/SX127x: G0 + #define PIN_RECEIVER_IRQ 7 + + // CC1101: GDO2 / RFM95W/SX127x: G1 (not used) + #define PIN_RECEIVER_GPIO 99 + + // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC + #define PIN_RECEIVER_RST 4 + +#endif + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#pragma message("Receiver chip: " RECEIVER_CHIP) +#pragma message("Pin config: RST->" STR(PIN_RECEIVER_RST) ", CS->" STR(PIN_RECEIVER_CS) ", GD0/G0/IRQ->" STR(PIN_RECEIVER_IRQ) ", GDO2/G1/GPIO->" STR(PIN_RECEIVER_GPIO) ) + +#endif diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index f8ad4c66..df39b91f 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -999,12 +999,12 @@ DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t return status; uint8_t ctr = (msgw[4] << 4) | (msgw[5] & 0xf0) >> 4; - log_v("--> CTR RAW: %d BCD: %d", ctr, ((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4); + log_v("--> CTR RAW: %d BCD: %d", ctr, (((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4)); uint8_t battery_low = (msgw[5] & 0x08) == 0x00; uint16_t unknown1 = ((msgw[5] & 0x0f) << 8) | msgw[6]; uint8_t type = msgw[6] >> 4; uint8_t distance_km = msgw[7]; - log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], ((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f, distance_map[ msgw[7] ]); + log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], (((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f), distance_map[ msgw[7] ]); uint16_t unknown2 = (msgw[8] << 8) | msgw[9]; sensor[slot].sensor_id = id_tmp; From ac96bbb3f82a1385363098ccb6d7cdf2b6d81304 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sat, 24 Jun 2023 09:52:23 +0200 Subject: [PATCH 05/16] Fixed conditionally unused variable --- src/WeatherSensor.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index df39b91f..f38dcf70 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -969,9 +969,11 @@ First two bytes are an LFSR-16 digest, generator 0x8810 key 0xabf9 with a final #ifdef BRESSER_LIGHTNING DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize) { - // see AS3935 Datasheet, Table 17 - Distance Estimation - uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; - + #if CORE_DEBUG_LEVEL == ARDUHAL_LOG_LEVEL_VERBOSE + // see AS3935 Datasheet, Table 17 - Distance Estimation + uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; + #endif + // data whitening uint8_t msgw[MSG_BUF_SIZE]; for (unsigned i = 0; i < msgSize; ++i) { From 25b8e8d80596ddf577f1b819b1ff4d692496f185 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:08:22 +0200 Subject: [PATCH 06/16] Update WeatherSensor.cpp --- src/WeatherSensor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index f38dcf70..aab1c482 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -973,7 +973,10 @@ DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t // see AS3935 Datasheet, Table 17 - Distance Estimation uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; #endif + // FIXME Workaround for CI + (void)(distance_map); + // data whitening uint8_t msgw[MSG_BUF_SIZE]; for (unsigned i = 0; i < msgSize; ++i) { From d7af9fa8c689b058e261f5c5210beb5f4182ebe4 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:13:19 +0200 Subject: [PATCH 07/16] Delete WeatherSensorCfg.h --- .../WeatherSensorCfg.h | 458 ------------------ 1 file changed, 458 deletions(-) delete mode 100644 examples/BresserWeatherSensorBasic/WeatherSensorCfg.h diff --git a/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h b/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h deleted file mode 100644 index 6456dbe4..00000000 --- a/examples/BresserWeatherSensorBasic/WeatherSensorCfg.h +++ /dev/null @@ -1,458 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////// -// WeatherSensorCfg.h -// -// Bresser 5-in-1/6-in-1/7-in-1 868 MHz Weather Sensor Radio Receiver -// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 -// -// https://github.com/matthias-bs/BresserWeatherSensorReceiver -// -// NOTE: Application/hardware specific configurations should be made in this file! -// -// created: 05/2022 -// -// -// MIT License -// -// Copyright (c) 2022 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: -// 20230124 Added some default settings based on selected boards in Arduino IDE -// 20230207 Added pin definitions for ARDUINO_TTGO_LoRa32_v21new -// 20230208 Added pin definitions for ARDUINO_TTGO_LoRa32_V2 -// 20230301 Added pin definitions for Wireless_Stick (from Heltec) -// 20230316 Added pin definitions for Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 -// 20230330 Added pin definitions and changes for Adafruit Feather 32u4 (AVR) RFM95 LoRa Radio -// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 -// 20230420 Added pin definitions for DFRobot FireBeetle ESP32 with FireBeetle Cover LoRa -// 20230624 Added Bresser Lightning Sensor decoder -// -// ToDo: -// - -// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#if !defined(WEATHER_SENSOR_CFG_H) -#define WEATHER_SENSOR_CFG_H - -#include - -// ------------------------------------------------------------------------------------------------ -// --- Board --- -// ------------------------------------------------------------------------------------------------ -// Use pinning for LoRaWAN Node - - -// LILIGO TTGO LoRaP32 board with integrated RF tranceiver (SX1276) -// See pin definitions in -// https://github.com/espressif/arduino-esp32/tree/master/variants/ttgo-lora32-* -// and -// https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-2/11973 - -// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V1 (No TFCard)" -// in the Arduino IDE: -//#define ARDUINO_TTGO_LoRa32_V1 - -// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V2" -// in the Arduino IDE: -//#define ARDUINO_TTGO_LoRa32_V2 - -// This define is set by selecting "Board: TTGO LoRa32-OLED" / "Board Revision: TTGO LoRa32 V2.1 (1.6.1)" -// in the Arduino IDE: -//#define ARDUINO_TTGO_LoRa32_V21new - -// This define is set by selecting "Board: Heltec Wireless Stick" -// in the Arduino IDE: -//#define ARDUINO_heltec_wireless_stick - -// Adafruit Feather ESP32S2 with RFM95W "FeatherWing" ADA3232 -// https://github.com/espressif/arduino-esp32/blob/master/variants/adafruit_feather_esp32s2/pins_arduino.h -// -// This define is set by selecting "Adafruit Feather ESP32-S2" in the Arduino IDE: -//#define ARDUINO_ADAFRUIT_FEATHER_ESP32S2 - -// Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 -// https://github.com/espressif/arduino-esp32/blob/master/variants/feather_esp32/pins_arduino.h -// -// This define is set by selecting "Adafruit ESP32 Feather" in the Arduino IDE: -//#define ARDUINO_FEATHER_ESP32 - -// DFRobot Firebeetle32 -// https://github.com/espressif/arduino-esp32/tree/master/variants/firebeetle32/pins_arduino.h -// -// This define (not very specific...) is set by selecting "FireBeetle-ESP32" in the Arduino IDE: -//#define ARDUINO_ESP32_DEV - -#if defined(ARDUINO_TTGO_LoRa32_V1) - #pragma message("ARDUINO_TTGO_LoRa32_V1 defined; using on-board transceiver") - #define USE_SX1276 - -#elif defined(ARDUINO_TTGO_LoRa32_V2) - #pragma message("ARDUINO_TTGO_LoRa32_V2 defined; using on-board transceiver") - #pragma message("LoRa DIO1 must be wired to GPIO33 manually!") - #define USE_SX1276 - -#elif defined(ARDUINO_TTGO_LoRa32_v21new) - #pragma message("ARDUINO_TTGO_LoRa32_V21new defined; using on-board transceiver") - #define USE_SX1276 - -#elif defined(ARDUINO_heltec_wireless_stick) - #pragma message("ARDUINO_heltec_wireless_stick defined; using on-board transceiver") - #define USE_SX1276 - -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) - #pragma message("ARDUINO_ADAFRUIT_FEATHER_ESP32S2 defined; assuming RFM95W FeatherWing will be used") - #define USE_SX1276 - -#elif defined(ARDUINO_FEATHER_ESP32) - #pragma message("ARDUINO_FEATHER_ESP32 defined; assuming RFM95W FeatherWing will be used") - #define USE_SX1276 - #pragma message("Required wiring: A to RST, B to DIO1, D to DIO0, E to CS") - -#elif defined(ARDUINO_AVR_FEATHER32U4) - #pragma message("ARDUINO_AVR_FEATHER32U4 defined; assuming this is the Arduino Feather 32u4 RFM95 LoRa Radio") - #define USE_SX1276 - -#elif defined(ARDUINO_ESP32_DEV) - //#define LORAWAN_NODE - #define FIREBEETLE_ESP32_COVER_LORA - - #if !defined(LORAWAN_NODE) && !defined(FIREBEETLE_ESP32_COVER_LORA) - #pragma message("ARDUINO_ESP32_DEV defined; select either LORAWAN_NODE or FIREBEETLE_ESP32_COVER_LORA manually!") - - #elif defined(LORAWAN_NODE) - #pragma message("LORAWAN_NODE defined; assuming this is the LoRaWAN_Node board (DFRobot Firebeetle32 + Adafruit RFM95W LoRa Radio)") - #define USE_SX1276 - - #elif defined(FIREBEETLE_ESP32_COVER_LORA) - #define USE_SX1276 - #pragma message("FIREBEETLE_ESP32_COVER_LORA defined; assuming this is a FireBeetle ESP32 with FireBeetle Cover LoRa") - #pragma message("Required wiring: D2 to RESET, D3 to DIO0, D4 to CS, D5 to DIO1") - - #endif -#endif - - -// ------------------------------------------------------------------------------------------------ -// --- Radio Transceiver --- -// ------------------------------------------------------------------------------------------------ -// Select type of receiver module (if not yet defined based on the assumptions above) -#if ( !defined(USE_CC1101) && !defined(USE_SX1276) ) - //#define USE_CC1101 - #define USE_SX1276 -#endif - - -// ------------------------------------------------------------------------------------------------ -// --- Weather Sensors --- -// ------------------------------------------------------------------------------------------------ -#define NUM_SENSORS 1 // Number of sensors to be received - -// List of sensor IDs to be excluded - can be empty -#define SENSOR_IDS_EXC {} -//#define SENSOR_IDS_EXC { 0x39582376 } - -// List of sensor IDs to be included - if empty, handle all available sensors -#define SENSOR_IDS_INC { 0xEEFB } -//#define SENSOR_IDS_INC { 0x83750871 } - -// List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" -// P/N 7002531 - requiring special heandling in decodeBresser5In1Payload() -//#define SENSOR_IDS_DECODE3IN1 {} -#define SENSOR_IDS_DECODE3IN1 { 0x2C100512 } - -// ------------------------------------------------------------------------------------------------ -// --- Debug Logging Output --- -// ------------------------------------------------------------------------------------------------ -// - ESP32: -// CORE_DEBUG_LEVEL is set in Adruino IDE: -// Tools->Core Debug Level: "|||||" -// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h -// -// - ESP8266: -// DEBUG_ESP_PORT is set in Arduino IDE: -// Tools->Debug port: "||" -// -// Replacement for -// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h -// on ESP8266: -#if defined(ESP8266) - #define ARDUHAL_LOG_LEVEL_NONE 0 - #define ARDUHAL_LOG_LEVEL_ERROR 1 - #define ARDUHAL_LOG_LEVEL_WARN 2 - #define ARDUHAL_LOG_LEVEL_INFO 3 - #define ARDUHAL_LOG_LEVEL_DEBUG 4 - #define ARDUHAL_LOG_LEVEL_VERBOSE 5 - - // Set desired level here! - #define CORE_DEBUG_LEVEL ARDUHAL_LOG_LEVEL_VERBOSE - - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_NONE - #define log_e(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } - #else - #define log_e(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_ERROR - #define log_w(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } - #else - #define log_w(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_WARN - #define log_i(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } - #else - #define log_i(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_INFO - #define log_d(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } - #else - #define log_d(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_DEBUG - #define log_v(...) { DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.println(); } - #else - #define log_v(...) {} - #endif - -#endif - - -// Replacement for -// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-log.h -// on Arduino AVR: -#if defined(ARDUINO_ARCH_AVR) - #define ARDUHAL_LOG_LEVEL_NONE 0 - #define ARDUHAL_LOG_LEVEL_ERROR 1 - #define ARDUHAL_LOG_LEVEL_WARN 2 - #define ARDUHAL_LOG_LEVEL_INFO 3 - #define ARDUHAL_LOG_LEVEL_DEBUG 4 - #define ARDUHAL_LOG_LEVEL_VERBOSE 5 - - // Set desired level here! - #define CORE_DEBUG_LEVEL ARDUHAL_LOG_LEVEL_INFO - - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_NONE - #define log_e(...) { printf(__VA_ARGS__); println(); } - #else - #define log_e(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_ERROR - #define log_w(...) { printf(__VA_ARGS__); println(); } - #else - #define log_w(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_WARN - #define log_i(...) { printf(__VA_ARGS__); println(); } - #else - #define log_i(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_INFO - #define log_d(...) { printf(__VA_ARGS__); println(); } - #else - #define log_d(...) {} - #endif - #if defined(DEBUG_ESP_PORT) && CORE_DEBUG_LEVEL > ARDUHAL_LOG_LEVEL_DEBUG - #define log_v(...) { printf(__VA_ARGS__); println(); } - #else - #define log_v(...) {} - #endif -#endif - - -//#define _DEBUG_MODE_ // Enable debug output (serial console) -#define DEBUG_PORT Serial -#if defined(_DEBUG_MODE_) - #define DEBUG_PRINT(...) { DEBUG_PORT.print(__VA_ARGS__); } - #define DEBUG_PRINTLN(...) { DEBUG_PORT.println(__VA_ARGS__); } -#else - #define DEBUG_PRINT(...) {} - #define DEBUG_PRINTLN(...) {} -#endif - -// Disable data type which will not be used to save RAM -#define WIND_DATA_FLOATINGPOINT -#define WIND_DATA_FIXEDPOINT - -// Select appropriate sensor message format(s) -#define BRESSER_5_IN_1 -#define BRESSER_6_IN_1 -#define BRESSER_7_IN_1 -#define BRESSER_LIGHTNING - -#if ( !defined(BRESSER_5_IN_1) && !defined(BRESSER_6_IN_1) && !defined(BRESSER_7_IN_1) ) - #error "Either BRESSER_5_IN_1 and/or BRESSER_6_IN_1 and/or BRESSER_7_IN_1 must be defined!" -#endif - -#if ( defined(USE_CC1101) && defined(USE_SX1276) ) - #error "Either USE_CC1101 OR USE_SX1276 must be defined!" -#endif - -#if defined(USE_CC1101) - #define RECEIVER_CHIP "[CC1101]" -#elif defined(USE_SX1276) - #define RECEIVER_CHIP "[SX1276]" -#else - #error "Either USE_CC1101 or USE_SX1276 must be defined!" -#endif - - -// Arduino default SPI pins -// -// Board SCK MOSI MISO -// ESP8266 D5 D7 D6 -// ESP32 D18 D23 D19 -#if defined(LORAWAN_NODE) - // Use pinning for LoRaWAN_Node (https://github.com/matthias-bs/LoRaWAN_Node) - #define PIN_RECEIVER_CS 14 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 4 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 16 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 12 - -#elif defined(FIREBEETLE_ESP32_COVER_LORA) - #define PIN_RECEIVER_CS 27 // D4 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 26 // D3 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 9 // D5 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 25 // D2 - -#elif defined(ARDUINO_TTGO_LoRa32_V1) || defined(ARDUINO_TTGO_LoRa32_V2) - // Use pinning for LILIGO TTGO LoRa32-OLED - #define PIN_RECEIVER_CS LORA_CS - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ LORA_IRQ - - // CC1101: GDO2 / RFM95W/SX127x: G1 - // n.c. on v1/v2?, LORA_D1 on v21 - #define PIN_RECEIVER_GPIO 33 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST LORA_RST - -#elif defined(ARDUINO_TTGO_LoRa32_v21new) - // Use pinning for LILIGO TTGO LoRa32-OLED V2.1 (1.6.1) - // Same pinout for Heltec Wireless Stick - #define PIN_RECEIVER_CS LORA_CS - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ LORA_IRQ - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO LORA_D1 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST LORA_RST - -#elif defined(ARDUINO_heltec_wireless_stick) - // Use pinning for Heltec Wireless Stick - #define PIN_RECEIVER_CS SS - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ DIO0 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO DIO1 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST RST_LoRa - -#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) - // Use pinning for Adafruit Feather ESP32S2 with RFM95W "FeatherWing" ADA3232 - #define PIN_RECEIVER_CS 6 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 5 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 11 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 9 - -#elif defined(ARDUINO_FEATHER_ESP32) - // Use pinning for Adafruit Feather ESP32 with RFM95W "FeatherWing" ADA3232 - #define PIN_RECEIVER_CS 14 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 32 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 33 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 27 - -#elif defined(ESP32) - // Generic pinning for ESP32 development boards - #define PIN_RECEIVER_CS 27 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 21 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 33 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 32 - -#elif defined(ESP8266) - // Generic pinning for ESP8266 development boards (e.g. LOLIN/WEMOS D1 mini) - #define PIN_RECEIVER_CS 15 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 4 - - // CC1101: GDO2 / RFM95W/SX127x: G1 - #define PIN_RECEIVER_GPIO 5 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 2 - -#elif defined(ARDUINO_AVR_FEATHER32U4) - // Pinning for Adafruit Feather 32u4 - #define PIN_RECEIVER_CS 8 - - // CC1101: GDO0 / RFM95W/SX127x: G0 - #define PIN_RECEIVER_IRQ 7 - - // CC1101: GDO2 / RFM95W/SX127x: G1 (not used) - #define PIN_RECEIVER_GPIO 99 - - // RFM95W/SX127x - GPIOxx / CC1101 - RADIOLIB_NC - #define PIN_RECEIVER_RST 4 - -#endif - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) -#pragma message("Receiver chip: " RECEIVER_CHIP) -#pragma message("Pin config: RST->" STR(PIN_RECEIVER_RST) ", CS->" STR(PIN_RECEIVER_CS) ", GD0/G0/IRQ->" STR(PIN_RECEIVER_IRQ) ", GDO2/G1/GPIO->" STR(PIN_RECEIVER_GPIO) ) - -#endif From e80fd51b9a94b983e9f019fc9ec070eac33776b7 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:13:32 +0200 Subject: [PATCH 08/16] Delete WeatherSensor.cpp --- .../WeatherSensor.cpp | 1043 ----------------- 1 file changed, 1043 deletions(-) delete mode 100644 examples/BresserWeatherSensorBasic/WeatherSensor.cpp diff --git a/examples/BresserWeatherSensorBasic/WeatherSensor.cpp b/examples/BresserWeatherSensorBasic/WeatherSensor.cpp deleted file mode 100644 index f8ad4c66..00000000 --- a/examples/BresserWeatherSensorBasic/WeatherSensor.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////// -// WeatherSensor.cpp -// -// Bresser 5-in-1/6-in-1/7-in1 868 MHz Weather Sensor Radio Receiver -// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 -// -// https://github.com/matthias-bs/BresserWeatherSensorReceiver -// -// Based on: -// --------- -// Bresser5in1-CC1101 by Sean Siford (https://github.com/seaniefs/Bresser5in1-CC1101) -// RadioLib by Jan Gromeš (https://github.com/jgromes/RadioLib) -// rtl433 by Benjamin Larsson (https://github.com/merbanan/rtl_433) -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c -// -// created: 05/2022 -// -// -// MIT License -// -// Copyright (c) 2022 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: -// -// 20220523 Created from https://github.com/matthias-bs/Bresser5in1-CC1101 -// 20220524 Moved code to class WeatherSensor -// 20220526 Implemented getData(), changed debug output to macros -// 20220731 Updated decodeBresser5In1Payload()/decodeBresser6In1Payload() from rtl_433 project -// 20220815 Added support of multiple sensors -// Moved windspeed_ms_to_bft() to WeatherUtils.h/.cpp -// 20220905 Improved code quality and added Doxygen comments -// 20221003 Fixed humidity decoding in decodeBresser5In1Payload() -// 20221024 Modified WeatherSensorCfg.h/WeatherSensor.h handling -// 20221227 Replaced DEBUG_PRINT/DEBUG_PRINTLN by Arduino logging functions -// 20230111 Added additional digit for rain gauge in 5in1-decoder (maximum is now 999.9mm) -// 20230114 Modified decodeBresser6In1Payload() to distinguish msg type based on 'flags' (msg[16]) -// 20230228 Added Bresser 7 in 1 decoder by Jorge Navarro-Ortiz (jorgenavarro@ugr.es) -// 20230329 Fixed issue introduced with 7 in 1 decoder -// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 -// 20230624 Added Bresser Lightning Sensor decoder -// -// ToDo: -// - -// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "WeatherSensorCfg.h" -#include "WeatherSensor.h" - -#if defined(USE_CC1101) - static CC1101 radio = new Module(PIN_RECEIVER_CS, PIN_RECEIVER_IRQ, RADIOLIB_NC, PIN_RECEIVER_GPIO); -#endif -#if defined(USE_SX1276) - static SX1276 radio = new Module(PIN_RECEIVER_CS, PIN_RECEIVER_IRQ, PIN_RECEIVER_RST, PIN_RECEIVER_GPIO); -#endif - - -// List of sensor IDs to be excluded - can be empty -uint32_t const sensor_ids_exc[] = SENSOR_IDS_EXC; - -// List of sensor IDs to be included - if empty, handle all available sensors -uint32_t const sensor_ids_inc[] = SENSOR_IDS_INC; - -// List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" -// P/N 7002531 - requiring special heandling in decodeBresser5In1Payload() -uint32_t const sensor_ids_decode3in1[] = SENSOR_IDS_DECODE3IN1; - -int16_t WeatherSensor::begin(void) { - // https://github.com/RFD-FHEM/RFFHEM/issues/607#issuecomment-830818445 - // Freq: 868.300 MHz, Bandwidth: 203 KHz, rAmpl: 33 dB, sens: 8 dB, DataRate: 8207.32 Baud - log_d("%s Initializing ... ", RECEIVER_CHIP); - // carrier frequency: 868.3 MHz - // bit rate: 8.22 kbps - // frequency deviation: 57.136417 kHz - // Rx bandwidth: 270.0 kHz (CC1101) / 250 kHz (SX1276) - // output power: 10 dBm - // preamble length: 40 bits - #ifdef USE_CC1101 - int state = radio.begin(868.3, 8.21, 57.136417, 270, 10, 32); - #else - int state = radio.beginFSK(868.3, 8.21, 57.136417, 250, 10, 32); - #endif - if (state == RADIOLIB_ERR_NONE) { - log_d("success!"); - state = radio.setCrcFiltering(false); - if (state != RADIOLIB_ERR_NONE) { - log_e("%s Error disabling crc filtering: [%d]", RECEIVER_CHIP, state); - while (true) - ; - } - state = radio.fixedPacketLengthMode(MSG_BUF_SIZE); - if (state != RADIOLIB_ERR_NONE) { - log_e("%s Error setting fixed packet length: [%d]", RECEIVER_CHIP, state); - while (true) - ; - } - // Preamble: AA AA AA AA AA - // Sync is: 2D D4 - // Preamble 40 bits but the CC1101 doesn't allow us to set that - // so we use a preamble of 32 bits and then use the sync as AA 2D - // which then uses the last byte of the preamble - we recieve the last sync byte - // as the 1st byte of the payload. - #ifdef USE_CC1101 - state = radio.setSyncWord(0xAA, 0x2D, 0, false); - #else - uint8_t sync_word[] = {0xAA, 0x2D}; - state = radio.setSyncWord(sync_word, 2); - #endif - if (state != RADIOLIB_ERR_NONE) { - log_e("%s Error setting sync words: [%d]", RECEIVER_CHIP, state); - while (true) - ; - } - } else { - log_e("%s Error initialising: [%d]", RECEIVER_CHIP, state); - while (true) - ; - } - log_d("%s Setup complete - awaiting incoming messages...", RECEIVER_CHIP); - rssi = radio.getRSSI(); - - return state; -} - - -bool WeatherSensor::getData(uint32_t timeout, uint8_t flags, uint8_t type, void (*func)()) -{ - const uint32_t timestamp = millis(); - - - while ((millis() - timestamp) < timeout) { - int decode_status = getMessage(); - - // Callback function (see https://www.geeksforgeeks.org/callbacks-in-c/) - if (func) { - (*func)(); - } - - if (decode_status == DECODE_OK) { - bool all_slots_valid = true; - bool all_slots_complete = true; - for (int i=0; i -1) { - // Update slot - log_v("find_slot(): Updating slot #%d", update_slot); - *status = DECODE_OK; - return update_slot; - } - else if (free_slot > -1) { - // Store to free slot - log_v("find_slot(): Storing into slot #%d", free_slot); - *status = DECODE_OK; - return free_slot; - } - else { - log_v("find_slot(): No slot left"); - // No slot left - *status = DECODE_FULL; - return -1; - } -} - - -// -// Find required sensor data by ID -// -int WeatherSensor::findId(uint32_t id) -{ - for (int i=0; i= 0; --i) { - // fprintf(stderr, "key at bit %d : %04x\n", i, key); - // if data bit is set then xor with key - if ((data >> i) & 1) - sum ^= key; - - // roll the key right (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) - if (key & 1) - key = (key >> 1) ^ gen; - else - key = (key >> 1); - } - } - return sum; -} - -// -// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/util.c -// -int WeatherSensor::add_bytes(uint8_t const message[], unsigned num_bytes) -{ - int result = 0; - for (unsigned i = 0; i < num_bytes; ++i) { - result += message[i]; - } - return result; -} - - -// -// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c (20220212) -// -// Example input data: -// EA EC 7F EB 5F EE EF FA FE 76 BB FA FF 15 13 80 14 A0 11 10 05 01 89 44 05 00 -// CC CC CC CC CC CC CC CC CC CC CC CC CC uu II SS GG DG WW W TT T HH RR RR Bt -// - C = Check, inverted data of 13 byte further -// - uu = checksum (number/count of set bits within bytes 14-25) -// - I = station ID (maybe) -// - G = wind gust in 1/10 m/s, normal binary coded, GGxG = 0x76D1 => 0x0176 = 256 + 118 = 374 => 37.4 m/s. MSB is out of sequence. -// - D = wind direction 0..F = N..NNE..E..S..W..NNW -// - W = wind speed in 1/10 m/s, BCD coded, WWxW = 0x7512 => 0x0275 = 275 => 27.5 m/s. MSB is out of sequence. -// - T = temperature in 1/10 °C, BCD coded, TTxT = 1203 => 31.2 °C -// - t = temperature sign, minus if unequal 0 -// - H = humidity in percent, BCD coded, HH = 23 => 23 % -// - R = rain in mm, BCD coded, RRRR = 1203 => 031.2 mm -// - B = Battery. 0=Ok, 8=Low. -// - S = sensor type, only low nibble used, 0x9 for Bresser Professional Rain Gauge -// -// Parameters: -// -// msg - Pointer to message -// msgSize - Size of message -// pOut - Pointer to WeatherData -// -// Returns: -// -// DECODE_OK - OK - WeatherData will contain the updated information -// DECODE_PAR_ERR - Parity Error -// DECODE_CHK_ERR - Checksum Error -// -#ifdef BRESSER_5_IN_1 -DecodeStatus WeatherSensor::decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSize) { - // First 13 bytes need to match inverse of last 13 bytes - for (unsigned col = 0; col < msgSize / 2; ++col) { - if ((msg[col] ^ msg[col + 13]) != 0xff) { - log_d("Parity wrong at column %d", col); - return DECODE_PAR_ERR; - } - } - - // Verify checksum (number number bits set in bytes 14-25) - uint8_t bitsSet = 0; - uint8_t expectedBitsSet = msg[13]; - - for(uint8_t p = 14 ; p < msgSize ; p++) { - uint8_t currentByte = msg[p]; - while(currentByte) { - bitsSet += (currentByte & 1); - currentByte >>= 1; - } - } - - if (bitsSet != expectedBitsSet) { - log_d("Checksum wrong - actual [%02X] != [%02X]", bitsSet, expectedBitsSet); - return DECODE_CHK_ERR; - } - - uint8_t id_tmp = msg[14]; - uint8_t type_tmp = msg[15] & 0xF; - DecodeStatus status; - - // Find appropriate slot in sensor data array and update - int slot = findSlot(id_tmp, &status); - - if (status != DECODE_OK) - return status; - - sensor[slot].sensor_id = id_tmp; - sensor[slot].s_type = type_tmp; - - int temp_raw = (msg[20] & 0x0f) + ((msg[20] & 0xf0) >> 4) * 10 + (msg[21] &0x0f) * 100; - if (msg[25] & 0x0f) { - temp_raw = -temp_raw; - } - sensor[slot].temp_c = temp_raw * 0.1f; - - sensor[slot].humidity = (msg[22] & 0x0f) + ((msg[22] & 0xf0) >> 4) * 10; - - int wind_direction_raw = ((msg[17] & 0xf0) >> 4) * 225; - int gust_raw = ((msg[17] & 0x0f) << 8) + msg[16]; - int wind_raw = (msg[18] & 0x0f) + ((msg[18] & 0xf0) >> 4) * 10 + (msg[19] & 0x0f) * 100; - - -#ifdef WIND_DATA_FLOATINGPOINT - sensor[slot].wind_direction_deg = wind_direction_raw * 0.1f; - sensor[slot].wind_gust_meter_sec = gust_raw * 0.1f; - sensor[slot].wind_avg_meter_sec = wind_raw * 0.1f; -#endif -#ifdef WIND_DATA_FIXEDPOINT - sensor[slot].wind_direction_deg_fp1 = wind_direction_raw; - sensor[slot].wind_gust_meter_sec_fp1 = gust_raw; - sensor[slot].wind_avg_meter_sec_fp1 = wind_raw; -#endif - - int rain_raw = (msg[23] & 0x0f) + ((msg[23] & 0xf0) >> 4) * 10 + (msg[24] & 0x0f) * 100 + ((msg[24] & 0xf0) >> 4) * 1000; - sensor[slot].rain_mm = rain_raw * 0.1f; - - sensor[slot].battery_ok = (msg[25] & 0x80) ? false : true; - - /* check if the message is from a Bresser Professional Rain Gauge */ - if ((msg[15] & 0xF) == 0x9) { - // rescale the rain sensor readings - sensor[slot].rain_mm *= 2.5; - - // Rain Gauge has no humidity (according to description) and no wind sensor (obviously) - sensor[slot].humidity_ok = false; - sensor[slot].wind_ok = false; - - } - else { - sensor[slot].humidity_ok = true; - sensor[slot].wind_ok = true; - } - - sensor[slot].temp_ok = true; - sensor[slot].light_ok = false; - sensor[slot].uv_ok = false; - sensor[slot].rain_ok = true; - sensor[slot].moisture_ok = false; - sensor[slot].valid = true; - sensor[slot].complete = true; - - // Save rssi to sensor specific data set - sensor[slot].rssi = rssi; - - return DECODE_OK; -} -#endif - - -// -// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c (20220608) -// -// - also Bresser Weather Center 7-in-1 indoor sensor. -// - also Bresser new 5-in-1 sensors. -// - also Froggit WH6000 sensors. -// - also rebranded as Ventus C8488A (W835) -// - also Bresser 3-in-1 Professional Wind Gauge / Anemometer PN 7002531 -// -// There are at least two different message types: -// - 24 seconds interval for temperature, hum, uv and rain (alternating messages) -// - 12 seconds interval for wind data (every message) -// -// Also Bresser Explore Scientific SM60020 Soil moisture Sensor. -// https://www.bresser.de/en/Weather-Time/Accessories/EXPLORE-SCIENTIFIC-Soil-Moisture-and-Soil-Temperature-Sensor.html -// -// Moisture: -// -// f16e 187000e34 7 ffffff0000 252 2 16 fff 004 000 [25,2, 99%, CH 7] -// DIGEST:8h8h ID?8h8h8h8h STYPE:4h STARTUP:1b CH:3d 8h 8h8h 8h8h TEMP:12h ?2b BATT:1b ?1b MOIST:8h UV?~12h ?4h CHKSUM:8h -// -// Moisture is transmitted in the humidity field as index 1-16: 0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99. -// The Wind speed and direction fields decode to valid zero but we exclude them from the output. -// -// aaaa2dd4e3ae1870079341ffffff0000221201fff279 [Batt ok] -// aaaa2dd43d2c1870079341ffffff0000219001fff2fc [Batt low] -// -// {206}55555555545ba83e803100058631ff11fe6611ffffffff01cc00 [Hum 96% Temp 3.8 C Wind 0.7 m/s] -// {205}55555555545ba999263100058631fffffe66d006092bffe0cff8 [Hum 95% Temp 3.0 C Wind 0.0 m/s] -// {199}55555555545ba840523100058631ff77fe668000495fff0bbe [Hum 95% Temp 3.0 C Wind 0.4 m/s] -// {205}55555555545ba94d063100058631fffffe665006092bffe14ff8 -// {206}55555555545ba860703100058631fffffe6651ffffffff0135fc [Hum 95% Temp 3.0 C Wind 0.0 m/s] -// {205}55555555545ba924d23100058631ff99fe68b004e92dffe073f8 [Hum 96% Temp 2.7 C Wind 0.4 m/s] -// {202}55555555545ba813403100058631ff77fe6810050929ffe1180 [Hum 94% Temp 2.8 C Wind 0.4 m/s] -// {205}55555555545ba98be83100058631fffffe6130050929ffe17800 [Hum 95% Temp 2.8 C Wind 0.8 m/s] -// -// 2dd4 1f 40 18 80 02 c3 18 ff 88 ff 33 08 ff ff ff ff 80 e6 00 [Hum 96% Temp 3.8 C Wind 0.7 m/s] -// 2dd4 cc 93 18 80 02 c3 18 ff ff ff 33 68 03 04 95 ff f0 67 3f [Hum 95% Temp 3.0 C Wind 0.0 m/s] -// 2dd4 20 29 18 80 02 c3 18 ff bb ff 33 40 00 24 af ff 85 df [Hum 95% Temp 3.0 C Wind 0.4 m/s] -// 2dd4 a6 83 18 80 02 c3 18 ff ff ff 33 28 03 04 95 ff f0 a7 3f -// 2dd4 30 38 18 80 02 c3 18 ff ff ff 33 28 ff ff ff ff 80 9a 7f [Hum 95% Temp 3.0 C Wind 0.0 m/s] -// 2dd4 92 69 18 80 02 c3 18 ff cc ff 34 58 02 74 96 ff f0 39 3f [Hum 96% Temp 2.7 C Wind 0.4 m/s] -// 2dd4 09 a0 18 80 02 c3 18 ff bb ff 34 08 02 84 94 ff f0 8c 0 [Hum 94% Temp 2.8 C Wind 0.4 m/s] -// 2dd4 c5 f4 18 80 02 c3 18 ff ff ff 30 98 02 84 94 ff f0 bc 00 [Hum 95% Temp 2.8 C Wind 0.8 m/s] -// -// {147} 5e aa 18 80 02 c3 18 fa 8f fb 27 68 11 84 81 ff f0 72 00 [Temp 11.8 C Hum 81%] -// {149} ae d1 18 80 02 c3 18 fa 8d fb 26 78 ff ff ff fe 02 db f0 -// {150} f8 2e 18 80 02 c3 18 fc c6 fd 26 38 11 84 81 ff f0 68 00 [Temp 11.8 C Hum 81%] -// {149} c4 7d 18 80 02 c3 18 fc 78 fd 29 28 ff ff ff fe 03 97 f0 -// {149} 28 1e 18 80 02 c3 18 fb b7 fc 26 58 ff ff ff fe 02 c3 f0 -// {150} 21 e8 18 80 02 c3 18 fb 9c fc 33 08 11 84 81 ff f0 b7 f8 [Temp 11.8 C Hum 81%] -// {149} 83 ae 18 80 02 c3 18 fc 78 fc 29 28 ff ff ff fe 03 98 00 -// {150} 5c e4 18 80 02 c3 18 fb ba fc 26 98 11 84 81 ff f0 16 00 [Temp 11.8 C Hum 81%] -// {148} d0 bd 18 80 02 c3 18 f9 ad fa 26 48 ff ff ff fe 02 ff f0 -// -// Wind and Temperature/Humidity or Rain: -// -// DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h TEMP:8h.4h ?2b BATT:1b ?1b HUM:8h UV?~12h ?4h CHKSUM:8h -// DIGEST:8h8h ID:8h8h8h8h STYPE:4h STARTUP:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h RAINFLAG:8h RAIN:8h8h UV:8h8h CHKSUM:8h -// -// Digest is LFSR-16 gen 0x8810 key 0x5412, excluding the add-checksum and trailer. -// Checksum is 8-bit add (with carry) to 0xff. -// -// Notes on different sensors: -// -// - 1910 084d 18 : RebeckaJohansson, VENTUS W835 -// - 2030 088d 10 : mvdgrift, Wi-Fi Colour Weather Station with 5in1 Sensor, Art.No.: 7002580, ff 01 in the UV field is (obviously) invalid. -// - 1970 0d57 18 : danrhjones, bresser 5-in-1 model 7002580, no UV -// - 18b0 0301 18 : konserninjohtaja 6-in-1 outdoor sensor -// - 18c0 0f10 18 : rege245 BRESSER-PC-Weather-station-with-6-in-1-outdoor-sensor -// - 1880 02c3 18 : f4gqk 6-in-1 -// - 18b0 0887 18 : npkap -// -// Parameters: -// -// msg - Pointer to message -// msgSize - Size of message -// pOut - Pointer to WeatherData -// -// Returns: -// -// DECODE_OK - OK - WeatherData will contain the updated information -// DECODE_DIG_ERR - Digest Check Error -// DECODE_CHK_ERR - Checksum Error -#ifdef BRESSER_6_IN_1 -DecodeStatus WeatherSensor::decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSize) { - (void)msgSize; // unused parameter - kept for consistency with other decoders; avoid warning - int const moisture_map[] = {0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99}; // scale is 20/3 - - // Per-message status flags - bool temp_ok = false; - bool humidity_ok = false; - bool uv_ok = false; - bool wind_ok = false; - bool rain_ok = false; - bool moisture_ok = false; - bool f_3in1 = false; - - // LFSR-16 digest, generator 0x8810 init 0x5412 - int chkdgst = (msg[0] << 8) | msg[1]; - int digest = lfsr_digest16(&msg[2], 15, 0x8810, 0x5412); - if (chkdgst != digest) { - log_d("Digest check failed - [%02X] != [%02X]", chkdgst, digest); - return DECODE_DIG_ERR; - } - // Checksum, add with carry - int sum = add_bytes(&msg[2], 16); // msg[2] to msg[17] - if ((sum & 0xff) != 0xff) { - log_d("Checksum failed"); - return DECODE_CHK_ERR; - } - - uint32_t id_tmp = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]); - uint8_t type_tmp = (msg[6] >> 4); // 1: weather station, 2: indoor?, 4: soil probe - uint8_t chan_tmp = (msg[6] & 0x7); - uint8_t flags = (msg[16] & 0x0f); - DecodeStatus status; - - // Find appropriate slot in sensor data array and update - int slot = findSlot(id_tmp, &status); - - if (status != DECODE_OK) - return status; - - // unused... - //int startup = (msg[6] >> 3) & 1; // s.a. #1214 - - sensor[slot].sensor_id = id_tmp; - sensor[slot].s_type = type_tmp; - sensor[slot].chan = chan_tmp; - - f_3in1 = is_decode3in1(id_tmp); - - // temperature, humidity(, uv) - shared with rain counter - temp_ok = humidity_ok = (flags == 0); - if (temp_ok) { - bool sign = (msg[13] >> 3) & 1; - int temp_raw = (msg[12] >> 4) * 100 + (msg[12] & 0x0f) * 10 + (msg[13] >> 4); - float temp; - - // Workaround for 3-in-1 Professional Wind Gauge / Anemometer - if (f_3in1) { - temp = ((sign) ? -temp_raw : temp_raw) * 0.1f; - } else { - temp = ((sign) ? (temp_raw - 1000) : temp_raw) * 0.1f; - } - - sensor[slot].temp_c = temp; - sensor[slot].battery_ok = (msg[13] >> 1) & 1; // b[13] & 0x02 is battery_good, s.a. #1993 - sensor[slot].humidity = (msg[14] >> 4) * 10 + (msg[14] & 0x0f); - - // apparently ff01 or 0000 if not available, ???0 if valid, inverted BCD - uv_ok = (~msg[15] & 0xff) <= (0x99 && (~msg[16] & 0xf0) <= 0x90) && !f_3in1; - if (uv_ok) { - int uv_raw = ((~msg[15] & 0xf0) >> 4) * 100 + (~msg[15] & 0x0f) * 10 + ((~msg[16] & 0xf0) >> 4); - sensor[slot].uv = uv_raw * 0.1f; - } - } - - //int unk_ok = (msg[16] & 0xf0) == 0xf0; - //int unk_raw = ((msg[15] & 0xf0) >> 4) * 10 + (msg[15] & 0x0f); - - // invert 3 bytes wind speeds - msg[7] ^= 0xff; - msg[8] ^= 0xff; - msg[9] ^= 0xff; - wind_ok = (msg[7] <= 0x99) && (msg[8] <= 0x99) && (msg[9] <= 0x99); - if (wind_ok) { - int gust_raw = (msg[7] >> 4) * 100 + (msg[7] & 0x0f) * 10 + (msg[8] >> 4); - int wavg_raw = (msg[9] >> 4) * 100 + (msg[9] & 0x0f) * 10 + (msg[8] & 0x0f); - int wind_dir_raw = ((msg[10] & 0xf0) >> 4) * 100 + (msg[10] & 0x0f) * 10 + ((msg[11] & 0xf0) >> 4); - -#ifdef WIND_DATA_FLOATINGPOINT - sensor[slot].wind_gust_meter_sec = gust_raw * 0.1f; - sensor[slot].wind_avg_meter_sec = wavg_raw * 0.1f; - sensor[slot].wind_direction_deg = wind_dir_raw * 1.0f; -#endif -#ifdef WIND_DATA_FIXEDPOINT - sensor[slot].wind_gust_meter_sec_fp1 = gust_raw; - sensor[slot].wind_avg_meter_sec_fp1 = wavg_raw; - sensor[slot].wind_direction_deg_fp1 = wind_dir_raw * 10; -#endif - } - - // rain counter, inverted 3 bytes BCD - shared with temp/hum - msg[12] ^= 0xff; - msg[13] ^= 0xff; - msg[14] ^= 0xff; - - rain_ok = (flags == 1) && (type_tmp == 1); - if (rain_ok) { - int rain_raw = (msg[12] >> 4) * 100000 + (msg[12] & 0x0f) * 10000 - + (msg[13] >> 4) * 1000 + (msg[13] & 0x0f) * 100 - + (msg[14] >> 4) * 10 + (msg[14] & 0x0f); - sensor[slot].rain_mm = rain_raw * 0.1f; - } - - moisture_ok = false; - - // the moisture sensor might present valid readings but does not have the hardware - if (sensor[slot].s_type == 4) { - wind_ok = 0; - uv_ok = 0; - } - - if (sensor[slot].s_type == 4 && temp_ok && sensor[slot].humidity >= 1 && sensor[slot].humidity <= 16) { - moisture_ok = true; - humidity_ok = false; - sensor[slot].moisture = moisture_map[sensor[slot].humidity - 1]; - } - - // Update per-slot status flags - sensor[slot].temp_ok |= temp_ok; - sensor[slot].humidity_ok |= humidity_ok; - sensor[slot].uv_ok |= uv_ok; - sensor[slot].wind_ok |= wind_ok; - sensor[slot].rain_ok |= rain_ok; - sensor[slot].moisture_ok |= moisture_ok; - log_d("Temp: %d Hum: %d UV: %d Wind: %d Rain: %d Moist: %d", temp_ok, humidity_ok, uv_ok, wind_ok, rain_ok, moisture_ok); - - sensor[slot].valid = true; - - // Weather station data is split into two separate messages (except for Professional Wind Gauge) - sensor[slot].complete = ((sensor[slot].s_type == SENSOR_TYPE_WEATHER1) && sensor[slot].temp_ok && sensor[slot].rain_ok) || - f_3in1 || - (sensor[slot].s_type != SENSOR_TYPE_WEATHER1); - - // Save rssi to sensor specific data set - sensor[slot].rssi = rssi; - - return DECODE_OK; -} -#endif - -// -// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c (20230215) -// -/** -Decoder for Bresser Weather Center 7-in-1, outdoor sensor. -See https://github.com/merbanan/rtl_433/issues/1492 -Preamble: - aa aa aa aa aa 2d d4 -Observed length depends on reset_limit. -The data has a whitening of 0xaa. -Data layout: - {271}631d05c09e9a18abaabaaaaaaaaa8adacbacff9cafcaaaaaaa000000000000000000 - {262}10b8b4a5a3ca10aaaaaaaaaaaaaa8bcacbaaaa2aaaaaaaaaaa0000000000000000 [0.08 klx] - {220}543bb4a5a3ca10aaaaaaaaaaaaaa8bcacbaaaa28aaaaaaaaaa00000 [0.08 klx] - {273}2492b4a5a3ca10aaaaaaaaaaaaaa8bdacbaaaa2daaaaaaaaaa0000000000000000000 [0.08klx] - {269}9a59b4a5a3da10aaaaaaaaaaaaaa8bdac8afea28a8caaaaaaa000000000000000000 [54.0 klx UV=2.6] - {230}fe15b4a5a3da10aaaaaaaaaaaaaa8bdacbba382aacdaaaaaaa00000000 [109.2klx UV=6.7] - {254}2544b4a5a32a10aaaaaaaaaaaaaa8bdac88aaaaabeaaaaaaaa00000000000000 [200.000 klx UV=14 - DIGEST:8h8h ID?8h8h WDIR:8h4h 4h 8h WGUST:8h.4h WAVG:8h.4h RAIN:8h8h4h.4h RAIN?:8h TEMP:8h.4hC FLAGS?:4h HUM:8h% LIGHT:8h4h,8h4hKL UV:8h.4h TRAILER:8h8h8h4h -Unit of light is kLux (not W/m²). -First two bytes are an LFSR-16 digest, generator 0x8810 key 0xba95 with a final xor 0x6df1, which likely means we got that wrong. -*/ -#ifdef BRESSER_7_IN_1 -DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize) { - - if (msg[21] == 0x00) { - log_e("DECODE_FAIL_SANITY !!!"); - } - - // data whitening - uint8_t msgw[MSG_BUF_SIZE]; - for (unsigned i = 0; i < msgSize; ++i) { - msgw[i] = msg[i] ^ 0xaa; - } - - // LFSR-16 digest, generator 0x8810 key 0xba95 final xor 0x6df1 - int chkdgst = (msgw[0] << 8) | msgw[1]; - int digest = lfsr_digest16(&msgw[2], 23, 0x8810, 0xba95); // bresser_7in1 - if ((chkdgst ^ digest) != 0x6df1) { // bresser_7in1 - log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chkdgst, digest, chkdgst ^ digest); - return DECODE_DIG_ERR; - } - - int id_tmp = (msgw[2] << 8) | (msgw[3]); - DecodeStatus status; - - // Find appropriate slot in sensor data array and update - int slot = findSlot(id_tmp, &status); - - if (status != DECODE_OK) - return status; - - int wdir = (msgw[4] >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] >> 4); - int wgst_raw = (msgw[7] >> 4) * 100 + (msgw[7] & 0x0f) * 10 + (msgw[8] >> 4); - int wavg_raw = (msgw[8] & 0x0f) * 100 + (msgw[9] >> 4) * 10 + (msgw[9] & 0x0f); - //int rain_raw = (msgw[10] >> 4) * 100000 + (msgw[10] & 0x0f) * 10000 + (msgw[11] >> 4) * 1000 + (msgw[11] & 0x0f) * 100 + (msgw[12] >> 4) * 10 + (msgw[12] & 0x0f) * 1; // 6 digits - int rain_raw = (msgw[10] >> 4) * 1000 + (msgw[10] & 0x0f) * 100 + (msgw[11] >> 4) * 10 + (msgw[11] & 0x0f) * 1; // 4 digits - float rain_mm = rain_raw * 0.1f; - int temp_raw = (msgw[14] >> 4) * 100 + (msgw[14] & 0x0f) * 10 + (msgw[15] >> 4); - float temp_c = temp_raw * 0.1f; - int flags = (msgw[15] & 0x0f); - int battery_low = (flags & 0x06) == 0x06; - if (temp_raw > 600) - temp_c = (temp_raw - 1000) * 0.1f; - int humidity = (msgw[16] >> 4) * 10 + (msgw[16] & 0x0f); - int lght_raw = (msgw[17] >> 4) * 100000 + (msgw[17] & 0x0f) * 10000 + (msgw[18] >> 4) * 1000 + (msgw[18] & 0x0f) * 100 + (msgw[19] >> 4) * 10 + (msgw[19] & 0x0f); - int uv_raw = (msgw[20] >> 4) * 100 + (msgw[20] & 0x0f) * 10 + (msgw[21] >> 4); - - float light_klx = lght_raw * 0.001f; // TODO: remove this - float light_lux = lght_raw; - float uv_index = uv_raw * 0.1f; - - // The RTL_433 decoder does not include any field to verify that these data - // are ok, so we are assuming that they are ok if the decode status is ok. - sensor[slot].temp_ok = true; - sensor[slot].humidity_ok = true; - sensor[slot].wind_ok = true; - sensor[slot].rain_ok = true; - sensor[slot].light_ok = true; - sensor[slot].uv_ok = true; - - sensor[slot].sensor_id = id_tmp; - sensor[slot].temp_c = temp_c; - sensor[slot].humidity = humidity; -#ifdef WIND_DATA_FLOATINGPOINT - sensor[slot].wind_gust_meter_sec = wgst_raw * 0.1f; - sensor[slot].wind_avg_meter_sec = wavg_raw * 0.1f; - sensor[slot].wind_direction_deg = wdir * 1.0f; -#endif -#ifdef WIND_DATA_FIXEDPOINT - sensor[slot].wind_gust_meter_sec_fp1 = wgst_raw; - sensor[slot].wind_avg_meter_sec_fp1 = wavg_raw; - sensor[slot].wind_direction_deg_fp1 = wdir * 10; -#endif - sensor[slot].rain_mm = rain_mm; - sensor[slot].light_klx = light_klx; - sensor[slot].light_lux = light_lux; - sensor[slot].uv = uv_index; - sensor[slot].battery_ok = !battery_low; - sensor[slot].rssi = rssi; - sensor[slot].valid = true; - sensor[slot].complete = true; - - /* clang-format off */ - /* data = data_make( - "model", "", DATA_STRING, "Bresser-7in1", - "id", "", DATA_INT, id, - "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, - "humidity", "Humidity", DATA_INT, humidity, - "wind_max_m_s", "Wind Gust", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wgst_raw * 0.1f, - "wind_avg_m_s", "Wind Speed", DATA_FORMAT, "%.1f m/s", DATA_DOUBLE, wavg_raw * 0.1f, - "wind_dir_deg", "Direction", DATA_INT, wdir, - "rain_mm", "Rain", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, - "light_klx", "Light", DATA_FORMAT, "%.3f klx", DATA_DOUBLE, light_klx, // TODO: remove this - "light_lux", "Light", DATA_FORMAT, "%.3f lux", DATA_DOUBLE, light_lux, - "uv", "UV Index", DATA_FORMAT, "%.1f", DATA_DOUBLE, uv_index, - "battery_ok", "Battery", DATA_INT, !battery_low, - "mic", "Integrity", DATA_STRING, "CRC", - NULL); - */ - /* clang-format on */ - -// decoder_output_data(decoder, data); - - return DECODE_OK; - -} -#endif - -/** -Decoder for Bresser Lightning, outdoor sensor. - -https://github.com/merbanan/rtl_433/issues/2140 - -DIGEST:8h8h ID:8h8h CTR:12h ?4h8h KM:8d ?8h8h - 0 1 2 3 4 5h 5l 6 7 8 9 - -Preamble: - - aa 2d d4 - -Observed length depends on reset_limit. -The data has a whitening of 0xaa. - - -First two bytes are an LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e -*/ - -#ifdef BRESSER_LIGHTNING -DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize) -{ - // see AS3935 Datasheet, Table 17 - Distance Estimation - uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; - - // data whitening - uint8_t msgw[MSG_BUF_SIZE]; - for (unsigned i = 0; i < msgSize; ++i) { - msgw[i] = msg[i] ^ 0xaa; - } - - // LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e - int chk = (msgw[0] << 8) | msgw[1]; - int digest = lfsr_digest16(&msgw[2], 8, 0x8810, 0xabf9); - //fprintf(stderr, "DIGEST %04x vs %04x (%04x) \n", chk, digest, chk ^ digest); - //if (((chk ^ digest) != 0x899e) && ((chk ^ digest) != 0x8b9e)) { - if (((chk ^ digest) != 0x899e)) { - log_d("Digest check failed - [%04X] vs [%04X] (%04X)", chk, digest, chk ^ digest); - return DECODE_DIG_ERR; - } - - int id_tmp = (msgw[2] << 8) | (msgw[3]); - - DecodeStatus status; - - // Find appropriate slot in sensor data array and update - int slot = findSlot(id_tmp, &status); - - if (status != DECODE_OK) - return status; - - uint8_t ctr = (msgw[4] << 4) | (msgw[5] & 0xf0) >> 4; - log_v("--> CTR RAW: %d BCD: %d", ctr, ((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4); - uint8_t battery_low = (msgw[5] & 0x08) == 0x00; - uint16_t unknown1 = ((msgw[5] & 0x0f) << 8) | msgw[6]; - uint8_t type = msgw[6] >> 4; - uint8_t distance_km = msgw[7]; - log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], ((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f, distance_map[ msgw[7] ]); - uint16_t unknown2 = (msgw[8] << 8) | msgw[9]; - - sensor[slot].sensor_id = id_tmp; - sensor[slot].s_type = type; - sensor[slot].lightning_count = ctr; - sensor[slot].lightning_distance_km = distance_km; - sensor[slot].lightning_unknown1 = unknown1; - sensor[slot].lightning_unknown2 = unknown2; - sensor[slot].battery_ok = !battery_low; - sensor[slot].lightning_ok = true; - sensor[slot].rssi = rssi; - sensor[slot].valid = true; - sensor[slot].complete = true; - - log_d("ID: 0x%04X TYPE: %d CTR: %d batt_low: %d distance_km: %d unknown1: 0x%x unknown2: 0x%04x", id_tmp, type, ctr, battery_low, distance_km, unknown1, unknown2); - - - /* clang-format off */ - /* data = data_make( - "model", "", DATA_STRING, "Bresser_lightning", - "id", "", DATA_INT, id, - "Frage1", "?", DATA_INT, frage1, - "Kilometer", "Kilometer", DATA_INT, kilometer, - "CTR", "CTR", DATA_INT, ctr, - "Frage2", "??", DATA_INT, frage2, - "mic", "Integrity", DATA_STRING, "CRC", - "battery_low", "Battery Low", DATA_INT, !battery_low, - NULL); - */ - /* clang-format on */ - - //decoder_output_data(decoder, data); - - return DECODE_OK; -} -#endif From cd9f178b088c77c13383903622874c069d52fe82 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:13:43 +0200 Subject: [PATCH 09/16] Delete WeatherSensor.h --- .../BresserWeatherSensorBasic/WeatherSensor.h | 403 ------------------ 1 file changed, 403 deletions(-) delete mode 100644 examples/BresserWeatherSensorBasic/WeatherSensor.h diff --git a/examples/BresserWeatherSensorBasic/WeatherSensor.h b/examples/BresserWeatherSensorBasic/WeatherSensor.h deleted file mode 100644 index 7e12ff6b..00000000 --- a/examples/BresserWeatherSensorBasic/WeatherSensor.h +++ /dev/null @@ -1,403 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////// -// WeatherSensor.h -// -// Bresser 5-in-1/6-in-1/7-in-1 868 MHz Weather Sensor Radio Receiver -// based on CC1101 or SX1276/RFM95W and ESP32/ESP8266 -// -// https://github.com/matthias-bs/BresserWeatherSensorReceiver -// -// NOTE: Application/hardware specific configurations should be made in WeatherSensorCfg.h! -// -// Based on: -// --------- -// Bresser5in1-CC1101 by Sean Siford (https://github.com/seaniefs/Bresser5in1-CC1101) -// RadioLib by Jan Gromeš (https://github.com/jgromes/RadioLib) -// rtl433 by Benjamin Larsson (https://github.com/merbanan/rtl_433) -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_5in1.c -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c -// - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_7in1.c -// -// created: 05/2022 -// -// -// MIT License -// -// Copyright (c) 2022 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: -// -// 20220523 Created from https://github.com/matthias-bs/Bresser5in1-CC1101 -// 20220524 Moved code to class WeatherSensor -// 20220526 Implemented getData(), changed debug output to macros -// 20220815 Added support of multiple sensors -// Moved windspeed_ms_to_bft() to WeatherUtils.h/.cpp -// 20221207 Added SENSOR_TYPE_THERMO_HYGRO -// 20220110 Added WEATHER0_RAIN_OV/WEATHER1_RAIN_OV -// 20230228 Added Bresser 7 in 1 decoder by Jorge Navarro-Ortiz (jorgenavarro@ugr.es) -// 20230328 Added MSG_BUF_SIZE -// 20230330 Added changes for Adafruit Feather 32u4 LoRa Radio -// 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 -// 20230624 Added Bresser Lightning Sensor decoder -// -// ToDo: -// - -// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WeatherSensor_h -#define WeatherSensor_h - -#include -#if defined(ESP32) || defined(ESP8266) - #include -#endif -#include - - -// Sensor Types -// 0 - Weather Station 5-in-1; PN 7002510..12/7902510..12 -// 1 - Weather Station 6-in-1; PN 7002585 -// - Professional Wind Gauge 6-in-1; PN 7002531 -// 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 -// 3 - Lightning Sensor PN 7009976 -// 4 - Soil Moisture Sensor 6-in-1; PN 7009972 -// 9 - Professional Rain Gauge (5-in-1 decoder) -// ? - Air Quality Sensor -// ? - Water Leakage Sensor -// ? - Pool Thermometer -#define SENSOR_TYPE_WEATHER0 0 // Weather Station -#define SENSOR_TYPE_WEATHER1 1 // Weather Station -#define SENSOR_TYPE_THERMO_HYGRO 2 // Thermo-/Hygro-Sensor -#define SENSOR_TYPE_LIGHTNING 3 // Lightning Sensor -#define SENSOR_TYPE_SOIL 4 // Soil Temperature and Moisture (from 6-in-1 decoder) -#define SENSOR_TYPE_RAIN 9 // Professional Rain Gauge (from 5-in-1 decoder) - - -// Sensor specific rain gauge overflow threshold (mm) -#define WEATHER0_RAIN_OV 1000 -#define WEATHER1_RAIN_OV 100000 - - -// Flags for controlling completion of reception in getData() -#define DATA_COMPLETE 0x1 // only completed slots (as opposed to partially filled) -#define DATA_TYPE 0x2 // at least one slot with specific sensor type -#define DATA_ALL_SLOTS 0x8 // all slots completed - -// Message buffer size -#define MSG_BUF_SIZE 27 - -// Radio message decoding status -typedef enum DecodeStatus { - DECODE_INVALID, DECODE_OK, DECODE_PAR_ERR, DECODE_CHK_ERR, DECODE_DIG_ERR, DECODE_SKIP, DECODE_FULL -} DecodeStatus; - - -#if defined(ESP32) || defined(ESP8266) -/*! - * \struct SensorMap - * - * \brief Mapping of sensor IDs to names - */ -typedef struct SensorMap { - uint32_t id; //!< ID if sensor (as transmitted in radio message) - std::string name; //!< Name of sensor (e.g. for MQTT topic) -} SensorMap; -#endif - - -/*! - \class WeatherSensor - - \brief Receive, decode and store Bresser Weather Sensor Data - Uses CC1101 or SX1276 radio module for receiving FSK modulated signal at 868 MHz. -*/ -class WeatherSensor { - public: - /*! - \brief Constructor. - - */ - WeatherSensor() - { - }; - - - /*! - \brief Presence check and initialization of radio module. - - \returns RADIOLIB_ERR_NONE on success (otherwise does never return). - */ - int16_t begin(void); - - - /*! - \brief Wait for reception of data or occurrance of timeout. - With BRESSER_6_IN_1, data is distributed across two different messages. Reception of entire - data is tried if 'complete' is set. - - \param timeout timeout in ms. - - \param flags DATA_COMPLETE / DATA_TYPE / DATA_ALL_SLOTS - - \param type sensor type (combined with FLAGS==DATA_TYPE) - - \param func Callback function for each loop iteration. (default: NULL) - - \returns false: Timeout occurred. - true: Reception (according to parammeter 'complete') succesful. - */ - bool getData(uint32_t timeout, uint8_t flags = 0, uint8_t type = 0, void (*func)() = NULL); - - - /*! - \brief Tries to receive radio message (non-blocking) and to decode it. - Timeout occurs after a multitude of expected time-on-air. - - \returns DecodeStatus - */ - DecodeStatus getMessage(void); - - /** - * \struct Sensor - * - * \brief sensor data and status flags - */ - struct Sensor { - uint32_t sensor_id; //!< sensor ID (5-in-1: 1 byte / 6-in-1: 4 bytes) - uint8_t s_type; //!< sensor type (only 6-in1) - uint8_t chan; //!< channel (only 6-in-1) - bool valid; //!< data valid (but not necessarily complete) - bool complete; //!< data is split into two separate messages is complete (only 6-in-1 WS) - bool temp_ok = false; //!< temperature o.k. (only 6-in-1) - bool humidity_ok = false; //!< humidity o.k. - bool light_ok = false; //!< light o.k. (only 7-in-1) - bool uv_ok = false; //!< uv radiation o.k. (only 6-in-1) - bool wind_ok = false; //!< wind speed/direction o.k. (only 6-in-1) - bool rain_ok = false; //!< rain gauge level o.k. - bool battery_ok = false; //!< battery o.k. - bool moisture_ok = false; //!< moisture o.k. (only 6-in-1) - bool lightning_ok = false; //!< lightning o.k. (only lightning) - float temp_c; //!< temperature in degC - float light_klx; //!< Light KLux (only 7-in-1) - float light_lux; //!< Light lux (only 7-in-1) - float uv; //!< uv radiation (only 6-in-1) - float rain_mm; //!< rain gauge level in mm - #ifdef WIND_DATA_FLOATINGPOINT - float wind_direction_deg; //!< wind direction in deg - float wind_gust_meter_sec; //!< wind speed (gusts) in m/s - float wind_avg_meter_sec; //!< wind speed (avg) in m/s - #endif - #ifdef WIND_DATA_FIXEDPOINT - // For LoRa_Serialization: - // fixed point integer with 1 decimal - - // saves two bytes compared to "RawFloat" - uint16_t wind_direction_deg_fp1; //!< wind direction in deg (fixed point int w. 1 decimal) - uint16_t wind_gust_meter_sec_fp1; //!< wind speed (gusts) in m/s (fixed point int w. 1 decimal) - uint16_t wind_avg_meter_sec_fp1; //!< wind speed (avg) in m/s (fixed point int w. 1 decimal) - #endif - uint8_t humidity; //!< humidity in % - uint8_t moisture; //!< moisture in % (only 6-in-1) - uint8_t lightning_distance_km; //!< lightning distance in km (only lightning) - uint8_t lightning_count; //!< lightning strike counter (only lightning) - uint8_t lightning_unknown1; //!< unknown part 1 - uint16_t lightning_unknown2; //!< unknown part 2 - float rssi; //!< received signal strength indicator in dBm - }; - - typedef struct Sensor sensor_t; //!< Shortcut for struct Sensor - sensor_t sensor[NUM_SENSORS]; //!< sensor data array - float rssi; //!< received signal strength indicator in dBm - - - /*! - \brief Generates data otherwise received and decoded from a radio message. - - \returns Always true (for compatibility with getMessage()) - */ - bool genMessage(int i, uint32_t id = 0xff, uint8_t type = 1, uint8_t channel = 0); - - - /*! - \brief Clear sensor data - - If 'type' is not specified, all slots are cleared. If 'type' is specified, - only slots containing data of the given sensor type are cleared. - - \param type Sensor type - */ - void clearSlots(uint8_t type = 0xFF) - { - for (int i=0; i< NUM_SENSORS; i++) { - if ((type == 0xFF) || (sensor[i].s_type == type)) { - sensor[i].valid = false; - sensor[i].complete = false; - sensor[i].temp_ok = false; - sensor[i].humidity_ok = false; - sensor[i].light_ok = false; - sensor[i].uv_ok = false; - sensor[i].wind_ok = false; - sensor[i].rain_ok = false; - sensor[i].moisture_ok = false; - sensor[i].lightning_ok = false; - } - } - }; - - /*! - * Find slot of required data set by ID - * - * \param id sensor ID - * - * \returns slot (or -1 if not found) - */ - int findId(uint32_t id); - - - /*! - * Find slot of required data set by type and (optionally) channel - * - * \param type sensor type - * \param channel sensor channel (0xFF: don't care) - * - * \returns slot (or -1 if not found) - */ - int findType(uint8_t type, uint8_t channel = 0xFF); - - /*! - * Check if sensor ID is in sensor_ids_decode3in1[] - * - * \param id sensor ID - * - * \returns true if sensor is in sensor_ids_decode3in1[], - * false otherwise - */ - bool is_decode3in1(uint32_t id); - - private: - struct Sensor *pData; //!< pointer to slot in sensor data array - - /*! - * \brief Find slot in sensor data array - * - * 1. The current sensor ID is checked against the exclude-list (sensor_ids_exc). - * If there is a match, the current message is skipped. - * - * 2. If the include-list (sensor_ids_inc) is not empty, the current ID is checked - * against it. If there is NO match, the current message is skipped. - * - * 3. Either an existing slot with the same ID as the current message is updated - * or a free slot (if any) is selected. - * - * \param id Sensor ID from current message - * - * \returns Pointer to slot in sensor data array or NULL if ID is not wanted or - * no free slot is available. - */ - int findSlot(uint32_t id, DecodeStatus * status); - - - #ifdef BRESSER_5_IN_1 - /*! - \brief Decode BRESSER_5_IN_1 message. - - \param msg Message buffer. - - \param msgSize Message size in bytes. - - \returns Decode status. - */ - DecodeStatus decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSize); - #endif - #ifdef BRESSER_6_IN_1 - /*! - \brief Decode BRESSER_6_IN_1 message. - With BRESSER_6_IN_1, data is distributed across two different messages. Additionally, - the message format supports different kinds of sensors. - - \param msg Message buffer. - - \param msgSize Message size in bytes. - - \returns Decode status. - */ - DecodeStatus decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSize); - #endif - #ifdef BRESSER_7_IN_1 - /*! - \brief Decode BRESSER_7_IN_1 message. - - \param msg Message buffer. - - \param msgSize Message size in bytes. - - \returns Decode status. - */ - DecodeStatus decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize); - #endif - #ifdef BRESSER_LIGHTNING - /*! - \brief Decode BRESSER_LIGHTNING message. (similar to 7-in-1) - - \param msg Message buffer. - - \param msgSize Message size in bytes. - - \returns Decode status. - */ - DecodeStatus decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize); - #endif - - protected: - /*! - \brief Linear Feedback Shift Register - Digest16 (Data integrity check). - */ - uint16_t lfsr_digest16(uint8_t const message[], unsigned bytes, uint16_t gen, uint16_t key); - - /*! - \brief Calculate sum of all message bytes. - - \param message Message buffer. - \param num_bytes Number of bytes. - - \returns Sum of all message bytes. - */ - int add_bytes(uint8_t const message[], unsigned num_bytes); - - #ifdef _DEBUG_MODE_ - /*! - \brief Print raw message payload as hex byte values. - */ - void printRawdata(uint8_t *msg, uint8_t msgSize) { - DEBUG_PRINT(F("Raw Data: ")); - for (uint8_t p = 0 ; p < msgSize ; p++) { - if (msg[p] < 16) { - DEBUG_PRINT("0"); - } - DEBUG_PRINT(msg[p], HEX); - DEBUG_PRINT(" "); - } - DEBUG_PRINTLN(); - }; - #endif - -}; - -#endif From dce1c461492270659786afdb6e47ee7ae20dbef4 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:15:22 +0200 Subject: [PATCH 10/16] Update WeatherSensor.cpp --- src/WeatherSensor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index aab1c482..bfe0b5c5 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -973,9 +973,6 @@ DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t // see AS3935 Datasheet, Table 17 - Distance Estimation uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; #endif - // FIXME Workaround for CI - (void)(distance_map); - // data whitening uint8_t msgw[MSG_BUF_SIZE]; From 9b5bc1b60424c305e201b6a53dde98a2a96f1d14 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:31:05 +0200 Subject: [PATCH 11/16] Update WeatherSensor.cpp --- src/WeatherSensor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index bfe0b5c5..42c5e566 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -1001,12 +1001,12 @@ DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t return status; uint8_t ctr = (msgw[4] << 4) | (msgw[5] & 0xf0) >> 4; - log_v("--> CTR RAW: %d BCD: %d", ctr, (((msgw[4] & 0xf0) >> 4) * 100 + (msgw[4] & 0x0f) * 10 + (msgw[5] & 0xf0) >> 4)); + log_v("--> CTR RAW: %d BCD: %d", ctr, ((((msgw[4] & 0xf0) >> 4) * 100) + (msgw[4] & 0x0f) * 10 + ((msgw[5] & 0xf0) >> 4))); uint8_t battery_low = (msgw[5] & 0x08) == 0x00; uint16_t unknown1 = ((msgw[5] & 0x0f) << 8) | msgw[6]; uint8_t type = msgw[6] >> 4; uint8_t distance_km = msgw[7]; - log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], (((msgw[7] & 0xf0) >> 4) * 10 + msgw[7] & 0x0f), distance_map[ msgw[7] ]); + log_v("--> DST RAW: %d BCD: %d TAB: %d", msgw[7], ((((msgw[7] & 0xf0) >> 4) * 10) + (msgw[7] & 0x0f)), distance_map[ msgw[7] ]); uint16_t unknown2 = (msgw[8] << 8) | msgw[9]; sensor[slot].sensor_id = id_tmp; From f372190d526004c09c6e348f3c53506c46752546 Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Thu, 6 Jul 2023 20:50:27 +0200 Subject: [PATCH 12/16] Bugfix --- .../BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino | 2 +- src/WeatherSensor.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino index ffd55817..d16c4a16 100644 --- a/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino +++ b/examples/BresserWeatherSensorBasic/BresserWeatherSensorBasic.ino @@ -149,7 +149,7 @@ void loop() weatherSensor.sensor[i].s_type, weatherSensor.sensor[i].battery_ok ? "OK " : "Low"); Serial.printf("Lightning Counter: [%3d] ", weatherSensor.sensor[i].lightning_count); - if (weatherSensor.sensor[i].lightning_distance_km == 0) { + if (weatherSensor.sensor[i].lightning_distance_km != 0) { Serial.printf("Distance: [%2dkm] ", weatherSensor.sensor[i].lightning_distance_km); } else { Serial.printf("Distance: [----] "); diff --git a/src/WeatherSensor.cpp b/src/WeatherSensor.cpp index 42c5e566..4626d8c3 100644 --- a/src/WeatherSensor.cpp +++ b/src/WeatherSensor.cpp @@ -974,10 +974,16 @@ DecodeStatus WeatherSensor::decodeBresserLightningPayload(uint8_t *msg, uint8_t uint8_t const distance_map[] = { 1, 5, 6, 8, 10, 12, 14, 17, 20, 24, 27, 31, 34, 37, 40, 63 }; #endif + #if 0 + uint8_t test_data[] = { 0x73, 0x69, 0xB5, 0x08, 0xAA, 0xA2, 0x90, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15 }; + #endif + // data whitening uint8_t msgw[MSG_BUF_SIZE]; for (unsigned i = 0; i < msgSize; ++i) { msgw[i] = msg[i] ^ 0xaa; + //msgw[i] = test_data[i] ^ 0xaa; } // LFSR-16 digest, generator 0x8810 key 0xabf9 with a final xor 0x899e From 56c2e5565aae448381e4f2cc828095d1c47d1120 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:27:07 +0200 Subject: [PATCH 13/16] Update doxygen.yml --- .github/workflows/doxygen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index ba2b7c70..911e87e6 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -19,7 +19,7 @@ jobs: run: doxygen Doxyfile - name: Deploy to GitHub Pages - uses: JamesIves/github-pages-deploy-action@releases/v3 + uses: JamesIves/github-pages-deploy-action@releases/v4 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages From 5c54b04fe024bb7f366d06a9aad25a9cff3347ed Mon Sep 17 00:00:00 2001 From: Matthias Prinke Date: Sun, 9 Jul 2023 09:17:30 +0200 Subject: [PATCH 14/16] Added lightning sensor --- .../BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino | 6 ++++++ .../BresserWeatherSensorMQTTCustom.ino | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/examples/BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino b/examples/BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino index f4cc83a2..9b7cf3e6 100644 --- a/examples/BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino +++ b/examples/BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino @@ -483,6 +483,12 @@ void publishWeatherdata(bool complete) if (weatherSensor.sensor[i].moisture_ok || complete) { sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"moisture\":%d", weatherSensor.sensor[i].moisture); } + if (weatherSensor.sensor[i].lightning_ok || complete) { + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_count\":%d", weatherSensor.sensor[i].lightning_count); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_distance_km\":%d", weatherSensor.sensor[i].lightning_distance_km); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_unknown1\":\"0x%03X\"", weatherSensor.sensor[i].lightning_unknown1); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_unknown2\":\"0x%04X\"", weatherSensor.sensor[i].lightning_unknown2); + } sprintf(&mqtt_payload[strlen(mqtt_payload)], "}"); sprintf(&mqtt_payload2[strlen(mqtt_payload2)], "}"); diff --git a/examples/BresserWeatherSensorMQTTCustom/BresserWeatherSensorMQTTCustom.ino b/examples/BresserWeatherSensorMQTTCustom/BresserWeatherSensorMQTTCustom.ino index 7ec9549a..87437b89 100644 --- a/examples/BresserWeatherSensorMQTTCustom/BresserWeatherSensorMQTTCustom.ino +++ b/examples/BresserWeatherSensorMQTTCustom/BresserWeatherSensorMQTTCustom.ino @@ -488,6 +488,12 @@ void publishWeatherdata(bool complete) if (weatherSensor.sensor[i].moisture_ok || complete) { sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"moisture\":%d", weatherSensor.sensor[i].moisture); } + if (weatherSensor.sensor[i].lightning_ok || complete) { + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_count\":%d", weatherSensor.sensor[i].lightning_count); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_distance_km\":%d", weatherSensor.sensor[i].lightning_distance_km); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_unknown1\":\"0x%03X\"", weatherSensor.sensor[i].lightning_unknown1); + sprintf(&mqtt_payload[strlen(mqtt_payload)], ",\"lightning_unknown2\":\"0x%04X\"", weatherSensor.sensor[i].lightning_unknown2); + } sprintf(&mqtt_payload[strlen(mqtt_payload)], "}"); sprintf(&mqtt_payload2[strlen(mqtt_payload2)], "}"); From 9a34c4cada68ac53bc6da888e20de2641c586f40 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sun, 9 Jul 2023 09:29:42 +0200 Subject: [PATCH 15/16] Update WeatherSensorCfg.h --- src/WeatherSensorCfg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WeatherSensorCfg.h b/src/WeatherSensorCfg.h index 6456dbe4..7638ed44 100644 --- a/src/WeatherSensorCfg.h +++ b/src/WeatherSensorCfg.h @@ -170,7 +170,7 @@ //#define SENSOR_IDS_EXC { 0x39582376 } // List of sensor IDs to be included - if empty, handle all available sensors -#define SENSOR_IDS_INC { 0xEEFB } +#define SENSOR_IDS_INC {} //#define SENSOR_IDS_INC { 0x83750871 } // List of sensor IDs of the model "BRESSER 3-in-1 Professional Wind Gauge / Anemometer" From d034f139ab795fe4e3242416dd7aa7a231d21bb6 Mon Sep 17 00:00:00 2001 From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com> Date: Sun, 9 Jul 2023 09:50:50 +0200 Subject: [PATCH 16/16] Update WeatherSensor.h --- .../src/WeatherSensor.h | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/examples/BresserWeatherSensorMQTTCustom/src/WeatherSensor.h b/examples/BresserWeatherSensorMQTTCustom/src/WeatherSensor.h index f75dba49..e1104819 100644 --- a/examples/BresserWeatherSensorMQTTCustom/src/WeatherSensor.h +++ b/examples/BresserWeatherSensorMQTTCustom/src/WeatherSensor.h @@ -55,6 +55,7 @@ // 20230328 Added MSG_BUF_SIZE // 20230330 Added changes for Adafruit Feather 32u4 LoRa Radio // 20230412 Added workaround for Professional Wind Gauge / Anemometer, P/N 7002531 +// 20230624 Added Bresser Lightning Sensor decoder // 20230708 Added SENSOR_TYPE_WEATHER_7IN1 and startup flag // // ToDo: @@ -73,19 +74,21 @@ // Sensor Types -// 0 - Weather Station 5-in-1; PN 7002510..12/7902510..12 -// 1 - Weather Station 6-in-1; PN 7002585 -// 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 -// 4 - Soil Moisture Sensor 6-in-1; PN 7009972 -// 9 - Professional Rain Gauge (5-in-1 decoder) +// 0 - Weather Station 5-in-1; PN 7002510..12/7902510..12 +// 1 - Weather Station 6-in-1; PN 7002585 +// - Professional Wind Gauge 6-in-1; PN 7002531 +// 2 - Thermo-/Hygro-Sensor 6-in-1; PN 7009999 +// 3 - Lightning Sensor PN 7009976 +// 4 - Soil Moisture Sensor 6-in-1; PN 7009972 +// 9 - Professional Rain Gauge (5-in-1 decoder) // 11 - Weather Sensor 7-in-1 7-in-1; PN 7003300 // ? - Air Quality Sensor // ? - Water Leakage Sensor // ? - Pool Thermometer -// ? - Lightning Sensor #define SENSOR_TYPE_WEATHER0 0 // Weather Station #define SENSOR_TYPE_WEATHER1 1 // Weather Station #define SENSOR_TYPE_THERMO_HYGRO 2 // Thermo-/Hygro-Sensor +#define SENSOR_TYPE_LIGHTNING 3 // Lightning Sensor #define SENSOR_TYPE_SOIL 4 // Soil Temperature and Moisture (from 6-in-1 decoder) #define SENSOR_TYPE_RAIN 9 // Professional Rain Gauge (from 5-in-1 decoder) #define SENSOR_TYPE_WEATHER_7IN1 11 // Weather Sensor 7-in-1 @@ -195,6 +198,7 @@ class WeatherSensor { bool rain_ok = false; //!< rain gauge level o.k. bool battery_ok = false; //!< battery o.k. bool moisture_ok = false; //!< moisture o.k. (only 6-in-1) + bool lightning_ok = false; //!< lightning o.k. (only lightning) float temp_c; //!< temperature in degC float light_klx; //!< Light KLux (only 7-in-1) float light_lux; //!< Light lux (only 7-in-1) @@ -213,9 +217,13 @@ class WeatherSensor { uint16_t wind_gust_meter_sec_fp1; //!< wind speed (gusts) in m/s (fixed point int w. 1 decimal) uint16_t wind_avg_meter_sec_fp1; //!< wind speed (avg) in m/s (fixed point int w. 1 decimal) #endif - uint8_t humidity; //!< humidity in % - uint8_t moisture; //!< moisture in % (only 6-in-1) - float rssi; //!< received signal strength indicator in dBm + uint8_t humidity; //!< humidity in % + uint8_t moisture; //!< moisture in % (only 6-in-1) + uint8_t lightning_distance_km; //!< lightning distance in km (only lightning) + uint8_t lightning_count; //!< lightning strike counter (only lightning) + uint8_t lightning_unknown1; //!< unknown part 1 + uint16_t lightning_unknown2; //!< unknown part 2 + float rssi; //!< received signal strength indicator in dBm }; typedef struct Sensor sensor_t; //!< Shortcut for struct Sensor @@ -243,15 +251,16 @@ class WeatherSensor { { for (int i=0; i< NUM_SENSORS; i++) { if ((type == 0xFF) || (sensor[i].s_type == type)) { - sensor[i].valid = false; - sensor[i].complete = false; - sensor[i].temp_ok = false; - sensor[i].humidity_ok = false; - sensor[i].light_ok = false; - sensor[i].uv_ok = false; - sensor[i].wind_ok = false; - sensor[i].rain_ok = false; - sensor[i].moisture_ok = false; + sensor[i].valid = false; + sensor[i].complete = false; + sensor[i].temp_ok = false; + sensor[i].humidity_ok = false; + sensor[i].light_ok = false; + sensor[i].uv_ok = false; + sensor[i].wind_ok = false; + sensor[i].rain_ok = false; + sensor[i].moisture_ok = false; + sensor[i].lightning_ok = false; } } }; @@ -347,6 +356,18 @@ class WeatherSensor { */ DecodeStatus decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSize); #endif + #ifdef BRESSER_LIGHTNING + /*! + \brief Decode BRESSER_LIGHTNING message. (similar to 7-in-1) + + \param msg Message buffer. + + \param msgSize Message size in bytes. + + \returns Decode status. + */ + DecodeStatus decodeBresserLightningPayload(uint8_t *msg, uint8_t msgSize); + #endif protected: /*!