Ustatnia modyfikacja 12 marca 2025 przez Olek

Aktualizacja 2025-03-12

Jest wersja wykonana po “bożemu”

https://github.com/olekdata/esphome-bresser

Jest to opis działającego rozwiązania pod ESPHome, ale wykonanego trochę na około i dla bardziej zaawansowanych.

Ten wpis ma też zadanie przedstawienie problemu i pomóc w dalszym jego rozwiązaniu

Sprzęt analogiczny do komunikacji z licznikiem wodomierz Apataor, czyli mikrokontrolera ESP8266 na płytce D1 Mini i modułu radiowego CC1101.

Rozwiązanie w pierwszej części bazuję na projekcie udostępnionym GitHubie 1. Opiera się na komunikacji MQTT z Home Assistentem. Dla wygody i prostoty próbuje przenieś go do ESPHome2 i tu pojawiają się problemy które przedstawie.

Obrałem dwie drugi do przeniesienia projektu do ESPHome.

  • z wykorzystaniem oryginalnej biblioteki do C1101 wykorzystanej w działającym juz projekcie czyli RadioLib3, która niestety jeszcze nie była wykorzystywana w projektach ESPHome.
  • przejście na inną i prostszą bibliotekę do C1101 n.p. SmartRC-CC1101-Driver-Lib4 która jest już wykorzystywana w projektach ESPHome.

Testowanie kodu z ESPHome

Tutaj opisze wygodny sposób testowania-kompilowania projektu bez użycia HA z ESPHome. Bardzo to ułatwia pracę nad kodę bez obciążania maszyny z Home Assistantem.
Zaprojektowany wstępnie projekt w ESPHome i kompilujemy (instalujemy w terminologii ESPHome). Oczywiście składnia konfiguracji w pliku YAML musi być poprawna aby przejść do właściwej kompilacji/instalacji.
Po tej czynności w odpowiednim miejscu na dysku na maszynie z HA zostaną stworzone źródła kodu z całą inną konfiguracją pakietu PlatformIO5 z którego to korzysta ESPHome do tworzenia kodu wynikowego.
W moim przypadku źródła znajdują się w ścieżce

\hassio\addons\data\5c53de3b_esphome\build\stacja-pogody\

gdzie stacja-pogody jest nazwą projektu.

Wystarczy teraz całą gałąź przekopiować na swój komputer. Korzystając z darmowego środowiska programistycznego Visual Studio Code6 i PlatformyIO5. Możemy wygodnie pracować nad kodę analizując go i kompilując. Gotowy i skompilowany program możemy wgrywać do urządzenia za pomocą mechanizmów OTA z strony web danego urządzenia. Jeżeli takowa została uruchomiona/wskazana w projekcie następującym wpisem.

web_server:
  port: 80

Pozostaje jeszcze zwykłe wgrywanie programu poprzez port szeregowy.

Poniżej cała moja konfiguracja projektu w ESPHome. Komentowane są różne kombinacje projektu.

esphome:
  name: stacja-pogody
  friendly_name: "Stacja pogody"

  includes:
#    - bresser-smartrc.h
#    - bresser-radiolib.h
    - bresser-tem.h

  libraries:
    - Wire
    - SPI
#    - lsatan/SmartRC-CC1101-Driver-Lib@^2.5.7
#    - jgromes/RadioLib@^5.3.0


esp8266:
  board: d1_mini_lite


# Enable logging
logger:
  level: DEBUG
  logs:
    component: ERROR
    
# Enable Home Assistant API
api:
  encryption:
    key: "xxx"

ota:
  safe_mode: false
  password: "xxx"

wifi:
  networks:
  - ssid: secret wifi_ssid
    password: secret wifi_password
  
  manual_ip:
    static_ip: 192.168.x.x
    gateway: 192.168.x.x
    subnet: 255.255.255.0  
    dns1: 192.168.x.x  


  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Stacja-Pogody Fallback Hotspot"
    password: "xx"


web_server:
  port: 80

captive_portal:

sensor:
- platform: custom
  lambda: |-  
    auto my_Bresser = new Bresser();
    App.register_component(my_Bresser);
    return {
      my_Bresser->temperature_sensor, 
      my_Bresser->humidity_sensor,
      my_Bresser->wind_avg_sensor,
      my_Bresser->wind_gust_sensor,
      my_Bresser->wind_dir_sensor,
      my_Bresser->rain_sensor,
      my_Bresser->battery_sensor
      };
    
  sensors:
  - name: "Temperatura"
    unit_of_measurement: '°C'
    icon: mdi:thermometer
    accuracy_decimals: 1

  - name: "Wilgotność"
    icon: mdi:water-percent
    unit_of_measurement: '%'
    accuracy_decimals: 0

  - name: "Prędkość wiatru"
    unit_of_measurement: "m/s"
    accuracy_decimals: 1
    icon: mdi:wind-turbine

  - name: "Porywy wiatru"
    unit_of_measurement: "m/s"
    accuracy_decimals: 1
    icon: mdi:weather-windy

  - name: "Kierunek wiatru"
    unit_of_measurement: "°"
    accuracy_decimals: 1
    icon: mdi:compass-rose
  
  - name: "Deszcz"
    unit_of_measurement: "mm"
    accuracy_decimals: 1
    icon: mdi:weather-pouring

  - name: "Bateria"
    accuracy_decimals: 0
    icon: mdi:battery


#  ***********************  Podłączenie fizyczne modułów ****************************************
#
#                                    +-----------------+| 
#                                    |    |  |  |  |    |
#                              [RST] |o                o| [TX ] [GPIO1]
#                     [ ADC1 ] [ A0] |o                o| [RX ] [GPIO3]
#                     [GPIO16] [ D0] |o   W E M O S    o| [D1 ] [GPIO5]   6 zielony
#  4 fiolet    [ SCK] [GPIO14] [ D5] |o                o| [D2 ] [GPIO4]   7 żółty
#  5 niebie    [MISO] [GPIO12] [ D6] |o   D1 MINI      o| [D3 ] [GPIO0]
#  3 biały     [MOSI] [GPIO13] [ D7] |o                o| [D4 ] [GPIO2]   8 pomar
#              [ SS ] [GPIO15] [ D8] |o                o| [GND]           2 szary
#  1 czarny                    [3v3] |o                o| [5V ]
#                                      |                |
#                                      |----|usb|-------|
#
#
#                                    +---------------------+
#  1 czarny                  [ VCC]  |o                    |                 
#  2 szary                   [ GND]  |o                    |                 
#  3 biały                   [MOSI]  |o                   o| [GND]
#  4 fiolet                  [SCLK]  |o     CC1101        o| [ATN] /\/\/\/\/               
#  5 niebie                  [MISO]  |o                   o| [GND]                
#  6 zielony                 [GDO2]  |o                    |                 
#  7 żółty                   [GDO0]  |o                    |                 
#  8 pomar                   [ CSN]  |o                    |                 
#                                    +---------------------+
#
#  https://esphome.io/custom/custom_component.html

#  mosi_pin: GPIO13
#  miso_pin: GPIO12
#  clk_pin:  GPIO14
#  cs_pin:   GPIO2
#  gdo0_pin: GPIO4
#  gdo2_pin: GPIO5

Wykorzystanie biblioteki RadioLib

Korzystamy z starszej wersji biblioteki 5.3. Nowsza z jakiś nieznanych przyczyn nie odbiera danych z stacji pogodowej. Podstawowym problemem jest nie kompatybilność tej biblioteki z ESPHome. Objawia się z problemami z jej kompilacją z pojawiającymi się błędami

Pierwszy problem

In file included from .pio\libdeps\stacja-pogody\RadioLib\src\Module.cpp:1:
.pio\libdeps\stacja-pogody\RadioLib\src\Module.h:8:12: fatal error: SPI.h: No such file or directory

*************************************************************
* Looking for SPI.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:SPI.h"
* Web  > https://registry.platformio.org/search?q=header:SPI.h
*
*************************************************************

    8 |   #include <SPI.h>
      |            ^~~~~~~
compilation terminated.
In file included from .pio\libdeps\stacja-pogody\RadioLib\src\ArduinoHal.cpp:1:
.pio\libdeps\stacja-pogody\RadioLib\src\ArduinoHal.h:16:10: fatal error: SPI.h: No such file or directory

*************************************************************
* Looking for SPI.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:SPI.h"
* Web  > https://registry.platformio.org/search?q=header:SPI.h
*
*************************************************************

   16 | #include <SPI.h>

To po wielu próbach i doświadczeniach udało mi się ustalić że przyczyną jest dodatkowa opcja kompilacji samej zaimportowanej biblioteki RadioLib. Zapis ten jest w pliku library.json na jego końcu w sekcji “bulid”. Można usunąć tez zapis z pliku lub jak w moim przypadki da dałem tylko # do nazwy pola unieważniając go. Prawdę mówiąc nie zagłębiałem mocno się co ona oznacza. Ważna że przeszło dalej.

{
    "name": "RadioLib",
    "version": "6.3.0",
    "description": "Universal wireless communication library. User-friendly library for sub-GHz radio modules (SX1278, RF69, CC1101, SX1268, and many others), as well as ham radio digital modes (RTTY, SSTV, AX.25 etc.) and other protocols (Pagers, LoRaWAN).",
    "keywords": "radio, communication, morse, cc1101, aprs, sx1276, sx1278, sx1272, rtty, ax25, afsk, nrf24, rfm96, sx1231, rfm96, rfm98, sstv, sx1278, sx1272, sx1276, sx1280, sx1281, sx1282, sx1261, sx1262, sx1268, si4432, rfm22, llcc68, pager, pocsag, lorawan",
    "homepage": "https://github.com/jgromes/RadioLib",
    "repository":
    {
        "type": "git",
        "url": "https://github.com/jgromes/RadioLib.git"
    },
    "authors":
    {
        "name": "Jan Gromeš",
        "email": "gromes.jan@gmail.com",
        "maintainer": true
    },
    "license": "MIT",
    "frameworks": "*",
    "platforms": "*",
    "headers": "RadioLib.h",
    "#build":
   {
        "libLDFMode": "chain+"
    }
}

Drugi problem

Następnym błędem kompilacji był problemy z przedefiniowaniem przestrzenią nazw “esphome” w pliku main.cpp.

src\main.cpp:27:18: error: invalid use of '::'
   27 | #define delay(x) esphome::delay(x)
      |                  ^~~~~~~
.pio\libdeps\stacja-pogody\RadioLib\src/Hal.h:163:18: note: in expansion of macro 'delay'
  163 |     virtual void delay(unsigned long ms) = 0;
      |                  ^~~~~
src\main.cpp:28:30: error: invalid use of '::'

Zakomentowanie tych definicji (lub ich usuniecie) rozwiązuje proble.

const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
//#define yield() esphome::yield()
//#define millis() esphome::millis()
//#define micros() esphome::micros()
//#define delay(x) esphome::delay(x)
//#define delayMicroseconds(x) esphome::delayMicroseconds(x)
#include "bresser-radiolib.h"

Tak trochę siłową metodą, udało mi się całość skompilować i po dalszym dopieszczeniu kodu uzyskałem pełną funkcjonalność modułu.

Jednakże to nie jest metoda docelowa, bo nie jest stworzony standardowymi mechanizmami ESPHome. Dlatego czekam na jakieś sugestie, które pomogą rozwiązać powyższe utrudnienia.

Wykorzystanie biblioteki SmartRC-CC1101-Driver-Lib

Daje się prosto skompilować. Po pierwszych próbach nie działa :). Musze się mocniej wdrożyć w ustawienie CC1101 i dekodowanie ramki.

Jest wersja kompilowana tylko w ESPHome !!!

Dostałem, podpowiedz od Szczepana, jak ominąć mój problem. Tak otrzymałem wersje komponentu, którą można prosto wdrożyć i uruchomić ESPHome bez żadnych kombinacji z kompilowaniem zewnętrznym.

Poniżej fragment kodu w YAML, którym trzeba zaktualizować poprzednią konfigurację.

  platformio_options:
    lib_ldf_mode: chain

  libraries:
    - jgromes/RadioLib


  includes:
    - bresser-radiolib.h

Tutaj cały kod C++, który powinien być w pliku bresser-radiolib.h i umieszczony w katalogu konfiguracyjnym ESPHome.

/*
* na podstaw https://github.com/matthias-bs/Bresser_Weather_Sensor_CC1101_MQTT
* dodatek (inccule) do ESPHome
* odbioru danych z staji pogodowej Bresser
* 
*/

// Comment out BRESSER_6_IN_1 to use decodeBresser5In1Payload()
// Uncomment   BRESSER_6_IN_1 to use decodeBresser6In1Payload()
#define BRESSER_6_IN_1

// Print misc debug output
#define _DEBUG_MODE_

#undef delay(x)
#undef yield(x)
#undef  millis(x) 
#undef micros(x) 
#undef delayMicroseconds(x) 

// Enable RadioLib internal debug messages
//#define RADIOLIB_DEBUG

#include "esphome.h"
//#include "Arduino.h"

#include "RadioLib.h"

//#define RADIOLIB_BUILD_ARDUINO
//#define xstr(s) str(s)
//#define str(s) #s


// https://docs.openmqttgateway.com/setitup/rf.html#pinout
// Board   Receiver Pin(GDO2)  Emitter Pin(GDO0)   SCK   VCC   MOSI  MISO  CSN   GND
// ESP8266   D2/D3/D1/D8            RX/D2          D5    3V3   D7    D6    D8    GND
// ESP32         D27                 D12           D18   3V3   D23   D19   D5    GND

// Note: GDO2 does not seem to be used.

#if defined(ESP32)
    #define PIN_CC1101_CS   5
    #define PIN_CC1101_GDO0 27
    #define PIN_CC1101_GDO2 4
#elif defined(ESP8266)
    #define PIN_CC1101_CS   2
    #define PIN_CC1101_GDO0 4
    #define PIN_CC1101_GDO2 5
#endif


// Generate CC1101 radio module instance
CC1101 radio = new Module(PIN_CC1101_CS, PIN_CC1101_GDO0, RADIOLIB_NC, PIN_CC1101_GDO2);

static const char *TAG = "Bresser";

class Bresser : public PollingComponent  {
    public:

    Sensor *temperature_sensor = new Sensor();
    Sensor *humidity_sensor = new Sensor();
    Sensor *wind_avg_sensor = new Sensor();
    Sensor *wind_gust_sensor = new Sensor();
    Sensor *wind_dir_sensor = new Sensor();
    Sensor *rain_sensor = new Sensor();
    Sensor *battery_sensor = new Sensor();
    Bresser() : PollingComponent(5000) { }

//    uint32_t t1s = 0;
//    uint32_t sec = 0;
    bool decode_ok = false;

    
    void setup() override {
        ESP_LOGD(TAG, "setup");

        int state = radio.begin(868.35, 8.22, 57.136417, 270.0, 10, 32);
        if (state == RADIOLIB_ERR_NONE) {
            ESP_LOGD(TAG,"success!");
            state = radio.setCrcFiltering(false);
            if (state != RADIOLIB_ERR_NONE) {
                ESP_LOGD(TAG, "Error disabling crc filtering: [%d]", state);
    //            while (true) ;
            }
            state = radio.fixedPacketLengthMode(27);
            if (state != RADIOLIB_ERR_NONE) {
                ESP_LOGD(TAG, "Error setting fixed packet length: [%d]", 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.
            state = radio.setSyncWord(0xAA, 0x2D, 0, false);
            if (state != RADIOLIB_ERR_NONE) {
                ESP_LOGD(TAG, "Error setting sync words: [%d]", state);
    //            while (true)  ;
            }
        } else {
            ESP_LOGD(TAG, "Error initialising: [%d]", state);
            //while (true) ;
        }
        ESP_LOGD(TAG, "Setup complete - awaiting incoming messages...");
    }

    void loop() override {
//            uint32_t mil = esphome::millis();
//            if (mil - t1s > 1000){
//                t1s = mil; sec++;  ESP_LOGD(TAG,"loop %d", sec);
//            }            
        decode_ok = decode_ok or getWeatherdata();
    }

    void update() override {

        if (decode_ok) {
            if (weatherData.temp_ok){
                temperature_sensor->publish_state(weatherData.temp_c);
                humidity_sensor->publish_state(weatherData.humidity);
            }
            if (weatherData.wind_ok){
                wind_avg_sensor->publish_state(weatherData.wind_avg_meter_sec);
                wind_gust_sensor->publish_state(weatherData.wind_gust_meter_sec);
                wind_dir_sensor->publish_state(weatherData.wind_direction_deg);
            }
            if (weatherData.rain_ok)
                rain_sensor->publish_state(weatherData.rain_mm);
            battery_sensor->publish_state(weatherData.battery_ok);
            decode_ok = false;
        }
    }    

    typedef enum DecodeStatus {
        DECODE_OK, DECODE_PAR_ERR, DECODE_CHK_ERR, DECODE_DIG_ERR
    } DecodeStatus;

    struct WeatherData_S {
        uint8_t  s_type;               // only 6-in1
        uint32_t sensor_id;            // 5-in-1: 1 byte / 6-in-1: 4 bytes
        uint8_t  chan;                 // only 6-in-1
        bool     temp_ok;              // only 6-in-1
        float    temp_c;
        int      humidity;
        bool     uv_ok;                // only 6-in-1
        float    uv;                   // only 6-in-1
        bool     wind_ok;              // only 6-in-1
        float    wind_direction_deg;
        float    wind_gust_meter_sec;
        float    wind_avg_meter_sec;
        bool     rain_ok;              // only 6-in-1
        float    rain_mm;
        bool     battery_ok;
        bool     moisture_ok;          // only 6-in-1
        int      moisture;             // only 6-in-1
    };

    typedef struct WeatherData_S WeatherData_t;

    // Weather data object
    WeatherData_t weatherData;



    //////////////////////////////////////////////////////

    #if (defined(CHECK_PUB_KEY) and defined(CHECK_CA_ROOT)) or (defined(CHECK_PUB_KEY) and defined(CHECK_FINGERPRINT)) or (defined(CHECK_FINGERPRINT) and defined(CHECK_CA_ROOT)) or (defined(CHECK_PUB_KEY) and defined(CHECK_CA_ROOT) and defined(CHECK_FINGERPRINT))
    #error "cant have both CHECK_CA_ROOT and CHECK_PUB_KEY enabled"
    #endif


    //
    // Generate sample data for testing MQTT publishing -
    // use with #define _DEBUG_MQTT_
    //
    void genData(WeatherData_t *pOut)
    {
        pOut->sensor_id = 0xff;
        pOut->temp_c = 22.2f;
        pOut->humidity = 55;
        pOut->wind_direction_deg = 333;
        pOut->wind_gust_meter_sec = 44.4f;
        pOut->wind_avg_meter_sec = 11.1f;
        pOut->rain_mm = 9.9f;
        pOut->battery_ok = true;
    }


    //
    // From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/util.c
    //
    uint16_t lfsr_digest16(uint8_t const message[], unsigned bytes, uint16_t gen, uint16_t key)
    {
        uint16_t sum = 0;
        for (unsigned k = 0; k < bytes; ++k) {
            uint8_t data = message[k];
            for (int i = 7; 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 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;
    }

    /*
    // Cribbed from rtl_433 project - but added extra checksum to verify uu
    //
    // 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  R 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, RRxR = 1203 => 31.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
    //
    */
    DecodeStatus decodeBresser5In1Payload(uint8_t *msg, uint8_t msgSize, WeatherData_t *pOut) { 
        // 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) {
                ESP_LOGD(TAG,"%s: Parity wrong at %u\n", __func__, 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) {
        ESP_LOGD(TAG,"%s: Checksum wrong actual [%02X] != expected [%02X]\n", __func__, bitsSet, expectedBitsSet);
        return DECODE_CHK_ERR;
        }

        pOut->sensor_id = msg[14];

        int temp_raw = (msg[20] & 0x0f) + ((msg[20] & 0xf0) >> 4) * 10 + (msg[21] &0x0f) * 100;
        if (msg[25] & 0x0f) {
            temp_raw = -temp_raw;
        }
        pOut->temp_c = temp_raw * 0.1f;

        pOut->humidity = (msg[22] & 0x0f) + ((msg[22] & 0xf0) >> 4) * 10;

        pOut->wind_direction_deg = ((msg[17] & 0xf0) >> 4) * 22.5f;

        int gust_raw = ((msg[17] & 0x0f) << 8) + msg[16];
        pOut->wind_gust_meter_sec = gust_raw * 0.1f;

        int wind_raw = (msg[18] & 0x0f) + ((msg[18] & 0xf0) >> 4) * 10 + (msg[19] & 0x0f) * 100;
        pOut->wind_avg_meter_sec = wind_raw * 0.1f;

        int rain_raw = (msg[23] & 0x0f) + ((msg[23] & 0xf0) >> 4) * 10 + (msg[24] & 0x0f) * 100;
        pOut->rain_mm = rain_raw * 0.1f;

        pOut->battery_ok = (msg[25] & 0x80) ? false : true;

        return DECODE_OK;
    }


    //
    // From from rtl_433 project - https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_6in1.c
    //
    /**
    Decoder for Bresser Weather Center 6-in-1.
    - 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 FLAGS:4h BATT:1b CH:3d 8h 8h8h 8h8h TEMP:12h 4h MOIST:8h TRAILER:8h8h8h8h4h
    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.
    {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]
                                            TEMP  HUM
    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 FLAGS:4h BATT:1b CH:3d WSPEED:~8h~4h ~4h~8h WDIR:12h ?4h TEMP:8h.4h ?4h HUM:8h UV?~12h ?4h CHKSUM:8h
        DIGEST:8h8h ID:8h8h8h8h FLAGS:4h BATT: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

    */
    DecodeStatus decodeBresser6In1Payload(uint8_t *msg, uint8_t msgSize, WeatherData_t *pOut) {
        int const moisture_map[] = {0, 7, 13, 20, 27, 33, 40, 47, 53, 60, 67, 73, 80, 87, 93, 99}; // scale is 20/3
        
        // 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) {
            //decoder_logf(decoder, 2, __func__, "Digest check failed %04x vs %04x", chkdgst, digest);
            ESP_LOGD(TAG,"Digest check failed - %02X vs %02X",chkdgst,digest);
    //        ESP_LOGD(TAG, chkdgst, HEX);
    //        ESP_LOGD(TAG,F(" vs "));
    //        ESP_LOGD(TAG,digest, HEX);
            return DECODE_DIG_ERR;
        }
        // Checksum, add with carry
        int chksum = msg[17];
        int sum    = add_bytes(&msg[2], 16); // msg[2] to msg[17]
        if ((sum & 0xff) != 0xff) {
            //decoder_logf(decoder, 2, __func__, "Checksum failed %04x vs %04x", chksum, sum);
            ESP_LOGD(TAG,"Checksum failed -  %02X vs %02X",chksum,HEX);
    //        ESP_LOGD(TAG,chksum, HEX);
    //        ESP_LOGD(TAG,F(" vs "));
    //        ESP_LOGD(TAG,sum, HEX);
            return DECODE_CHK_ERR;
        }

        pOut->sensor_id  = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]);
        pOut->s_type     = (msg[6] >> 4); // 1: weather station, 2: indoor?, 4: soil probe
        pOut->battery_ok = (msg[6] >> 3) & 1;
        pOut->chan       = (msg[6] & 0x7);

        // temperature, humidity, shared with rain counter, only if valid BCD digits
        pOut->temp_ok  = msg[12] <= 0x99 && (msg[13] & 0xf0) <= 0x90 && msg[14] <= 0xA0 && (msg[14] & 0xf0) <= 0xA0;
        int temp_raw   = (msg[12] >> 4) * 100 + (msg[12] & 0x0f) * 10 + (msg[13] >> 4);
        float temp_c   = temp_raw * 0.1f;
        if (temp_raw > 600)
            temp_c = (temp_raw - 1000) * 0.1f;
        pOut->temp_c   = temp_c;
        pOut->humidity = (msg[14] >> 4) * 10 + (msg[14] & 0x0f);

        // apparently ff0(1) if not available
        pOut->uv_ok  = msg[15] <= 0x99 && (msg[16] & 0xf0) <= 0x90;
        int uv_raw = ((msg[15] & 0xf0) >> 4) * 100 + (msg[15] & 0x0f) * 10 + ((msg[16] & 0xf0) >> 4);
        pOut->uv   = uv_raw * 0.1f;
        int flags  = (msg[16] & 0x0f); // looks like some flags, not sure

        //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;
        pOut->wind_ok = (msg[7] <= 0x99) && (msg[8] <= 0x99) && (msg[9] <= 0x99);

        int gust_raw              = (msg[7] >> 4) * 100 + (msg[7] & 0x0f) * 10 + (msg[8] >> 4);
        pOut->wind_gust_meter_sec = gust_raw * 0.1f;
        int wavg_raw              = (msg[9] >> 4) * 100 + (msg[9] & 0x0f) * 10 + (msg[8] & 0x0f);
        pOut->wind_avg_meter_sec  = wavg_raw * 0.1f;
        pOut->wind_direction_deg  = (((msg[10] & 0xf0) >> 4) * 100 + (msg[10] & 0x0f) * 10 + ((msg[11] & 0xf0) >> 4)) * 1.0f;

        // rain counter, inverted 3 bytes BCD, shared with temp/hum, only if valid digits
        msg[12] ^= 0xff;
        msg[13] ^= 0xff;
        msg[14] ^= 0xff;
        pOut->rain_ok   = msg[12] <= 0x99 && msg[13] <= 0x99 && msg[14] <= 0x99;
        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);
        pOut->rain_mm   = rain_raw * 0.1f;

        pOut->moisture_ok = false;
        if (pOut->s_type == 4 && pOut->temp_ok && pOut->humidity >= 1 && pOut->humidity <= 16) {
            pOut->moisture_ok = true;
            pOut->moisture = moisture_map[pOut->humidity - 1];
        }
        return DECODE_OK;
    }

    #ifdef BFT_EN
    //
    // Convert wind speed from meters per second to Beaufort
    // [https://en.wikipedia.org/wiki/Beaufort_scale]
    //
    uint8_t windspeed_ms_to_bft(float ms)
    {
    if (ms < 5.5) {
        // 0..3 Bft
        if (ms < 0.9) {
        return 0;
        } else if (ms < 1.6) {
        return 1;
        } else if (ms < 3.4) {
        return 2;
        } else {
        return 3;
        }
    } else if (ms < 17.2) { 
        // 4..7 Bft
        if (ms < 8) {
        return 4;
        } else if (ms < 10.8) {
        return 5;
        } else if (ms < 13.9) {
        return 6;
        } else {
        return 7;
        }
    } else {
        // 8..12 Bft
        if (ms < 20.8) {
        return 8;
        } else if (ms < 24.5) {
        return 9;
        } else if (ms < 28.5) {
        return 10;
        } else if (ms < 32.7) {
        return 11;
        } else {
        return 12;
        }
    }
    }
    #endif


    //
    // Get weather data from CC1101 receiver and decode it
    //
    bool getWeatherdata(void)
    {
        uint8_t recvData[27];
        bool decode_ok = false;

        // Receive data
        // 1. flush RX buffer
        // 2. switch to RX mode
        // 3. wait for expected RX packet or timeout [~500us in this configuration]
        // 4. flush RX buffer
        // 5. switch to standby 
        int state = radio.receive(recvData, 27);
        

        if (state == RADIOLIB_ERR_NONE) {
            // Verify last syncword is 1st byte of payload (see above)
            if (recvData[0] == 0xD4) {
                #ifdef _DEBUG_MODE_
                    // print the data of the packet
                    ESP_LOGD(TAG," Data:");
                    printRawdata(recvData, sizeof(recvData));
    //                for(int i = 0 ; i < sizeof(recvData) ; i++) {
    //                    ESP_LOGD(TAG," %02X", recvData[i]);
    //                }
    //                ESP_LOGD(TAG,);

                    ESP_LOGD(TAG," R [0x%02X] RSSI: %f LQI: %d\n", recvData[0], radio.getRSSI(), radio.getLQI());
                #endif

                #ifdef _DEBUG_MODE_
                    //printRawdata(&recvData[1], sizeof(recvData));
                #endif
                
                // Decode the information - skip the last sync byte we used to check the data is OK
                #ifdef BRESSER_6_IN_1
                    decode_ok = (decodeBresser6In1Payload(&recvData[1], sizeof(recvData) - 1, &weatherData) == DECODE_OK);
                    ESP_LOGD(TAG,"decode_ok: %d, temp_ok: %d, wind_ok: %d, rain_ok: %d\n",
                        decode_ok, weatherData.temp_ok, weatherData.wind_ok, weatherData.rain_ok);
                #else
                    decode_ok = (decodeBresser5In1Payload(&recvData[1], sizeof(recvData) - 1, &weatherData) == DECODE_OK);
            
                    // Fixed set of data for 5-in-1 sensor
                    weatherData.temp_ok     = true;
                    weatherData.uv_ok       = false;
                    weatherData.wind_ok     = true;
                    weatherData.rain_ok     = true;
                    weatherData.moisture_ok = false;
                #endif
                
                if (decode_ok) {
                    const float METERS_SEC_TO_MPH = 2.237;
                    ESP_LOGD(TAG,"Id: [%8X] Battery: [%s] ",
                        weatherData.sensor_id,
                        weatherData.battery_ok ? "OK " : "Low");
                    #ifdef BRESSER_6_IN_1
                        ESP_LOGD(TAG,"Ch: [%d] ", weatherData.chan);
                    #endif
                    if (weatherData.temp_ok) {
                        ESP_LOGD(TAG,"Temp: [%3.1fC] Hum: [%3d%%] ",
                            weatherData.temp_c,
                            weatherData.humidity);
                    } else {
                        ESP_LOGD(TAG,"Temp: [---.-C] Hum: [---%] ");
                    }
                    if (weatherData.wind_ok) {
                        ESP_LOGD(TAG,"Wind max: [%3.1fm/s] Wind avg: [%3.1fm/s] Wind dir: [%4.1fdeg] ",
                            weatherData.wind_gust_meter_sec,
                            weatherData.wind_avg_meter_sec,
                            weatherData.wind_direction_deg);
                    } else {
                        ESP_LOGD(TAG,"Wind max: [--.-m/s] Wind avg: [--.-m/s] ");
                    }
                    if (weatherData.rain_ok) {
                        ESP_LOGD(TAG,"Rain: [%6.1fmm] ",  
                            weatherData.rain_mm);
                    } else {
                        ESP_LOGD(TAG,"Rain: [-----.-mm] "); 
                    }
                    if (weatherData.moisture_ok) {
                        ESP_LOGD(TAG,"Moisture: [%2d%%]",
                            weatherData.moisture);
                    }
                    ESP_LOGD(TAG,"\n");
                    //printf("{\"sensor_type\": \"bresser-5-in-1\", \"sensor_id\": %d, \"battery\": \"%s\", \"temp_c\": %.1f, \"hum_pc\": %d, \"wind_gust_ms\": %.1f, \"wind_speed_ms\": %.1f, \"wind_dir\": %.1f, \"rain_mm\": %.1f}\n",
                    //       sensor_id, !battery_low ? "OK" : "Low",
                    //       temperature, humidity, wind_gust, wind_avg, wind_direction_deg, rain);
                } // if (decode_ok)
                else {
                    #ifdef _DEBUG_MODE_
                        ESP_LOGD(TAG,"[CC1101] R [0x%02X] RSSI: %f LQI: %d\n", recvData[0], radio.getRSSI(), radio.getLQI());
                    #endif
                }
            } // if (recvData[0] == 0xD4)
            else if (state == RADIOLIB_ERR_RX_TIMEOUT) {
                #ifdef _DEBUG_MODE_
                    ESP_LOGD(TAG,"T");
                #endif
            } // if (state == RADIOLIB_ERR_RX_TIMEOUT)
            else {
                // some other error occurred
                ESP_LOGD(TAG,"[CC1101] Receive failed - failed, code %d\n", state);
            }
        } // if (state == RADIOLIB_ERR_NONE)

        return decode_ok;
    }



    //
    // Print raw payload
    //


    #ifdef _DEBUG_MODE_
    void printRawdata(uint8_t *msg, uint8_t msgSize) {
        char s[120];
        uint8_t ii = 0;
        s[0] = 0;
        ESP_LOGD(TAG,"Raw Data:");
        for (uint8_t p = 0 ; p < msgSize ; p++) {
            sprintf(s, "%s %02X",s,msg[p]);
            ii++;
            if (ii>40) {
                ESP_LOGD(TAG, "%s", s);
                ii = 0;
                s[0] = 0;
            };        
        }
        ESP_LOGD(TAG,"%s", s);
    }
    #endif

};
Przypisy
  1. https://github.com/matthias-bs/Bresser_Weather_Sensor_CC1101_MQTT ↩︎
  2. https://esphome.io/ ↩︎
  3. https://github.com/jgromes/RadioLib ↩︎
  4. https://github.com/LSatan/SmartRC-CC1101-Driver-Lib ↩︎
  5. https://platformio.org/ ↩︎
  6. https://code.visualstudio.com/ ↩︎
  7. https://platformio.org/ ↩︎

Dodaj komentarz