Skip to content

Commit

Permalink
Merge branch 'main' into 10-bresser-lightning-sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
matthias-bs committed Jul 9, 2023
2 parents 9a34c4c + 014a24b commit 2d530dd
Show file tree
Hide file tree
Showing 12 changed files with 1,095 additions and 66 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
#- esp32:esp32:ttgo-lora32:Revision=TTGO_LoRa32_V2
- esp32:esp32:ttgo-lora32:Revision=TTGO_LoRa32_v21new
- esp32:esp32:heltec_wireless_stick:PSRAM=disabled
- esp32:esp32:heltec_wifi_lora_32_V2
- esp32:esp32:adafruit_feather_esp32s2
- esp32:esp32:featheresp32
- esp8266:esp8266:generic:dbg=Disabled
Expand Down Expand Up @@ -86,10 +87,12 @@ jobs:
run:
|
ps -p "$$"
arduino-cli lib install RadioLib@6.0.0
arduino-cli lib install RadioLib@6.1.0
arduino-cli lib install "LoRa Serialization"@3.1.0
arduino-cli lib install MQTT@2.5.1
arduino-cli lib install ArduinoJson@6.21.2
arduino-cli lib install WiFiManager@2.0.16-rc.2
arduino-cli lib install ESP_DoubleResetDetector@1.3.2

- name: Install platform
if: ${{ env.run-build == 'true' }}
Expand Down
50 changes: 44 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,35 @@ To allow automatic handling of all Bresser weather station variants, the decoder

| Model | Type | Decoder Function |
| ------------- | ---- | ------------------------------- |
| 7002510..12 | Weather Station | decodeBresser**5In1**Payload() |
| 7002510..12, 9602510 | Weather Station | decodeBresser**5In1**Payload() |
| 7902510..12 | Weather Station (Base) | decodeBresser**5In1**Payload() |
| *7002531* | *3-in-1 Professional Wind Gauge / Anemometer* | *decodeBresser**6In1**Payload()* **1)** |
| 7002585 | Weather Station | decodeBresser**6In1**Payload() |
| 9602510 | Weather Sensor | ? |
| 7009999 | Thermo-/Hygrometer Sensor | decodeBresser**6in1**Payload() |
| 7009972 | Soil Moisture/Temperature Sensor | decodeBresser**6In1**Payload() |
| 7003600000000 and WSX3001000000 | Weather Station | decodeBresser**7In1**Payload() |
| 7003600000000 and WSX3001000000 | Weather Station | decodeBresser**7In1**Payload() **2)** |
| 7803200 | Weather Sensor | decodeBresser**7In1**Payload() |
| 7003300 | Weather Station | decodeBresser**7In1**Payload() |
| 7803300 | Weather Sensor | decodeBresser**7In1**Payload() |

**1)** negative temperatures are currently not decoded correctly, UV flag is set erroneously; see https://github.com/matthias-bs/BresserWeatherSensorReceiver/issues/42
Some guesswork:

| Numbering Scheme | Type |
| ---------------- | ---- |
| 700[25\|32\|33]* | Weather Station, Base + Sensor |
| 780[25\|32\|33]* | Weather Station Sensor (Replacement) |
| 790* | Weather Station Base (Replacement) |
| 700[99]* | Accessory Sensor |

**1)** Manual configuration required, UV flag is set erroneously; see https://github.com/matthias-bs/BresserWeatherSensorReceiver/issues/42

**2)** The part number is specific to the actual variant.

## Configuration

### Configuration by selecting a supported Board in the Arduino IDE

By selecting a Board and a Board Revision in the Arduino IDE, a define is passed to the preprocessor/compiler. For the boards in the table below, the default configuration is assumed based on this define. I.e. you could could use an Adafruit Feather ESP32-S2 with a CC1101 connected to the pins of you choice of course, but the code assumes you are using it with a LoRa Radio Featherwing with the wiring given below.
By selecting a Board and a Board Revision in the Arduino IDE, a define is passed to the preprocessor/compiler. For the boards in the table below, the default configuration is assumed based on this define. I.e. you could could use an Adafruit Feather ESP32-S2 with a CC1101 connected to the pins of your choice of course, but the code assumes you are using it with a LoRa Radio Featherwing with the wiring given below.

If you are not using the Arduino IDE, you can use the defines in the table below with your specific tool chain to get the same result.

Expand All @@ -42,7 +55,8 @@ If this is not what you need, you have to switch to **Manual Configuration**
| [LILYGO®TTGO-LORA32 V1](https://github.com/Xinyuan-LilyGo/TTGO-LoRa-Series) | "TTGO LoRa32-OLED" | "TTGO LoRa32 V1 (No TFCard)" | ARDUINO_TTGO_LORA32_V1 | SX1276 (HPD13A) | - |
| [LILYGO®TTGO-LORA32 V2](https://github.com/LilyGO/TTGO-LORA32) | "TTGO LoRa32-OLED" | "TTGO LoRa32 V2" | ARDUINO_TTGO_LoRa32_V2 | SX1276 (HPD13A) | Wire DIO1 to GPIO33 |
| [LILYGO®TTGO-LORA32 V2.1](http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1271&FId=t3:50060:3) | "TTGO LoRa32-OLED" | "TTGO LoRa32 V2.1 (1.6.1)" | ARDUINO_TTGO_LoRa32_v21new | SX1276 (HPD13A) | - |
| [Heltec Wireless Stick](https://heltec.org/project/wireless-stick/) | "Heltec Wireless Stick" | n.a. | ARDUINO_heltec_wireless_stick | SX1276 | - |
| [Heltec Wireless Stick](https://heltec.org/project/wireless-stick/) | "Heltec Wireless Stick" | n.a. | ARDUINO_heltec_wireless_stick | SX1276 | - |
| [Heltec WiFi LoRa 32 V2](https://heltec.org/project/wifi-lora-32/) | "Heltec WiFi LoRa 32(V2)" | n.a. | ARDUINO_heltec_wifi_lora_32_V2 | SX1276 | - |
| [Adafruit Feather ESP32S2 with Adafruit LoRa Radio FeatherWing](https://github.com/matthias-bs/BresserWeatherSensorReceiver#adafruit-feather-esp32s2-with-adafruit-lora-radio-featherwing) | "Adafruit Feather ESP32-S2" | n.a. | ARDUINO_ADAFRUIT_FEATHER_ESP32S2 | SX1276 (RFM95W) | Wiring on the Featherwing:<br>E to IRQ<br>D to CS<br>C to RST<br>A to DI01 |
| [Adafruit Feather ESP32 or ThingPulse ePulse Feather with Adafruit LoRa Radio FeatherWing](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/main/README.md#adafruit-feather-esp32-or-thingpulse-epulse-feather-with-adafruit-lora-radio-featherwing) | "Adafruit ESP32 Feather" | n.a. | ARDUINO_FEATHER_ESP32 | SX1276 (RFM95W) | Wiring on the Featherwing:<br>A to RST<br>B to DIO1<br>D to IRQ<br>E to CS |
| [DFRobot FireBeetle with FireBeetle Cover LoRa Radio 868MHz](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/main/README.md#dfrobot-firebeetle-esp32-with-firebeetle-cover-lora-radio-868mhz) | "FireBeetle-ESP32" | n.a. | ARDUINO_ESP32_DEV & **FIREBEETLE_ESP32_COVER_LORA**<sup>1</sup> | SX1276 (LoRa1276) | Wiring on the cover: <br>D2 to RESET<br>D3 to DIO0<br>D4 to CS<br>D5 to DIO1 |
Expand Down Expand Up @@ -143,6 +157,7 @@ $ via LWT
```
{"sensor_id":12345678,"ch":0,"battery_ok":true,"humidity":44,"wind_gust":1.2,"wind_avg":1.2,"wind_dir":150,"rain":146}
```

**Dashboard with [IoT MQTT Panel](https://snrlab.in/iot/iot-mqtt-panel-user-guide) (Example)**

<img src="https://user-images.githubusercontent.com/83612361/158457786-516467f9-2eec-4726-a9bd-36e9dc9eec5c.png" alt="IoTMQTTPanel_Bresser_5-in-1" width="400">
Expand All @@ -156,6 +171,29 @@ The file [BresserWeatherSensorReceiver/examples/BresserWeatherSensorMQTTCustom/s

See [examples/BresserWeatherSensorMQTTCustom/Readme.md](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/main/examples/BresserWeatherSensorMQTTCustom/Readme.md) for details.

### [BresserWeatherSensorMQTTWiFiMgr](https://github.com/matthias-bs/BresserWeatherSensorReceiver/examples/BresserWeatherSensorMQTTWiFiMgr)

Same core functionality as [BresserWeatherSensorMQTT](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/main/examples/BresserWeatherSensorMQTT/BresserWeatherSensorMQTT.ino), but instead of using static WiFi- and MQTT-connection data, [WiFiManager](https://github.com/tzapu/WiFiManager) is used instead.

**Note:**

When using the sketch on a device for the first time, you must format the flash file system (SPIFFS) first, otherwise the configuration cannot be saved.

**Configuration:**

* Access Point SSID: ESPWeather-<chip_id>
* Access Point Password: password
* Configuration URL: http://192.168.4.1/ (The browser must be connected to the access point above!)

Please refer to the [WiFiManager](https://github.com/tzapu/WiFiManager) documentation for details!

After a successful setup, you can perform two consecutive resets (within 10 seconds) to enable WiFiManager for changing the configuration. This is achieved by using [ESP_DoubleResetDetector](https://github.com/khoih-prog/ESP_DoubleResetDetector).

<img src="https://github.com/matthias-bs/BresserWeatherSensorReceiver/assets/83612361/86a3f629-276d-48ac-8eff-acda051e7a2b" alt="WiFiManager Start Screen" width="300">
<br>
<img src="https://github.com/matthias-bs/BresserWeatherSensorReceiver/assets/83612361/a1055ec5-dcc0-44ac-89fc-6a18497cce6e" alt="WiFiManager Configuration Screen" width="300">


### [BresserWeatherSensorDomoticz](https://github.com/matthias-bs/BresserWeatherSensorReceiver/examples/BresserWeatherSensorDomoticz)

Based on [BresserWeatherSensorMQTT](https://github.com/matthias-bs/BresserWeatherSensorReceiver/examples/BresserWeatherSensorMQTT). Provides sensor data as MQTT messages via WiFi to Domoticz (https://domoticz.com/) (MQTT plugin for Domoticz required). The MQTT topics are designed for using with Domoticz virtual sensors (see https://www.domoticz.com/wiki/Managing_Devices#Temperature and https://www.domoticz.com/wiki/Managing_Devices#Weather).
Expand Down
63 changes: 50 additions & 13 deletions examples/BresserWeatherSensorMQTTCustom/src/WeatherSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
// 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
// 20230412 Fixed 7 in 1 decoder (valid/complete flags were not set)
// 20230613 Fixed rain value in 7 in 1 decoder
// 20230708 Added startup flag in 6-in-1 and 7-in-1 decoder; added sensor type in 7-in-1 decoder
//
// ToDo:
// -
Expand All @@ -79,6 +83,10 @@ 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
Expand Down Expand Up @@ -385,6 +393,22 @@ int WeatherSensor::findType(uint8_t type, uint8_t ch)
return -1;
}

//
// Check if sensor is in sensor_ids_decode3in1[]
//
bool WeatherSensor::is_decode3in1(uint32_t id)
{
uint8_t n_3in1 = sizeof(sensor_ids_decode3in1)/4;
if (n_3in1 != 0) {
for (int i=0; i<n_3in1; i++) {
if (id == sensor_ids_decode3in1[i]) {
log_v("ID %08X is a Professional Wind Gauge", id);
return true;
}
}
}
return false;
}

//
// From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/util.c
Expand Down Expand Up @@ -494,6 +518,7 @@ DecodeStatus WeatherSensor::decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSi

sensor[slot].sensor_id = id_tmp;
sensor[slot].s_type = type_tmp;
sensor[slot].startup = false; // To Do

int temp_raw = (msg[20] & 0x0f) + ((msg[20] & 0xf0) >> 4) * 10 + (msg[21] &0x0f) * 100;
if (msg[25] & 0x0f) {
Expand Down Expand Up @@ -574,7 +599,7 @@ DecodeStatus WeatherSensor::decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSi
// 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
// DIGEST:8h8h ID?8h8h8h8h :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.
Expand Down Expand Up @@ -612,8 +637,8 @@ DecodeStatus WeatherSensor::decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSi
//
// 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:8h8h ID:8h8h8h8h :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 :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.
Expand Down Expand Up @@ -651,6 +676,7 @@ DecodeStatus WeatherSensor::decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSi
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];
Expand Down Expand Up @@ -678,27 +704,33 @@ DecodeStatus WeatherSensor::decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSi
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;
sensor[slot].startup = (msg[6] >> 3) & 1; // s.a. #1214

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 = ((sign) ? (temp_raw - 1000) : temp_raw) * 0.1f;

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;
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;
Expand Down Expand Up @@ -768,8 +800,10 @@ DecodeStatus WeatherSensor::decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSi

sensor[slot].valid = true;

// Weather station data is split into two separate messages
sensor[slot].complete = ((sensor[slot].s_type == SENSOR_TYPE_WEATHER1) && sensor[slot].temp_ok && sensor[slot].rain_ok) || (sensor[slot].s_type != SENSOR_TYPE_WEATHER1);
// 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;
Expand Down Expand Up @@ -833,8 +867,7 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi
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
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
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;
Expand All @@ -860,6 +893,8 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi
sensor[slot].uv_ok = true;

sensor[slot].sensor_id = id_tmp;
sensor[slot].s_type = msgw[6] >> 4;
sensor[slot].startup = (msgw[6] & 0x08) == 0x08;
sensor[slot].temp_c = temp_c;
sensor[slot].humidity = humidity;
#ifdef WIND_DATA_FLOATINGPOINT
Expand All @@ -877,6 +912,8 @@ DecodeStatus WeatherSensor::decodeBresser7In1Payload(uint8_t *msg, uint8_t msgSi
sensor[slot].light_lux = light_lux;
sensor[slot].uv = uv_index;
sensor[slot].battery_ok = !battery_low;
sensor[slot].valid = true;
sensor[slot].complete = true;
sensor[slot].rssi = rssi;

/* clang-format off */
Expand Down
Loading

0 comments on commit 2d530dd

Please sign in to comment.