{"id":17648680,"url":"https://github.com/matthias-bs/bresserweathersensorlw","last_synced_at":"2026-02-03T07:18:52.311Z","repository":{"id":231745101,"uuid":"782600879","full_name":"matthias-bs/BresserWeatherSensorLW","owner":"matthias-bs","description":"Bresser 868 MHz Weather Sensor Radio Receiver based on ESP32/RP2040 and SX1262/SX1276/LR1121 - sends data to a LoRaWAN Network","archived":false,"fork":false,"pushed_at":"2026-01-28T13:18:31.000Z","size":5213,"stargazers_count":22,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-01-29T03:08:37.591Z","etag":null,"topics":["arduino","arduino-pico","ble","bluetooth","bluetooth-low-energy","chirpstack","esp32","esp32-arduino","helium-network","lorawan","lorawan-application","radiolib","rp2040","thethingsnetwork","ttn-application","weather-sensors","weather-station"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/matthias-bs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-04-05T16:21:03.000Z","updated_at":"2026-01-28T12:47:16.000Z","dependencies_parsed_at":"2026-01-16T15:14:28.857Z","dependency_job_id":null,"html_url":"https://github.com/matthias-bs/BresserWeatherSensorLW","commit_stats":null,"previous_names":["matthias-bs/bresserweathersensorlw"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/matthias-bs/BresserWeatherSensorLW","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthias-bs%2FBresserWeatherSensorLW","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthias-bs%2FBresserWeatherSensorLW/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthias-bs%2FBresserWeatherSensorLW/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthias-bs%2FBresserWeatherSensorLW/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matthias-bs","download_url":"https://codeload.github.com/matthias-bs/BresserWeatherSensorLW/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthias-bs%2FBresserWeatherSensorLW/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29037156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T06:39:36.383Z","status":"ssl_error","status_checked_at":"2026-02-03T06:39:32.787Z","response_time":96,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-pico","ble","bluetooth","bluetooth-low-energy","chirpstack","esp32","esp32-arduino","helium-network","lorawan","lorawan-application","radiolib","rp2040","thethingsnetwork","ttn-application","weather-sensors","weather-station"],"created_at":"2024-10-23T11:19:47.725Z","updated_at":"2026-02-03T07:18:52.303Z","avatar_url":"https://github.com/matthias-bs.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BresserWeatherSensorLW\n\n[![CI](https://github.com/matthias-bs/BresserWeatherSensorLW/actions/workflows/CI.yml/badge.svg)](https://github.com/matthias-bs/BresserWeatherSensorLW/actions/workflows/CI.yml)\n[![GitHub release](https://img.shields.io/github/release/matthias-bs/BresserWeatherSensorLW?maxAge=3600)](https://github.com/matthias-bs/BresserWeatherSensorLW/releases)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/main/LICENSE)\n\nBresser 868 MHz Weather Sensor Radio Receiver based on ESP32/RP2040 and SX1262/SX1276/LR1121 \u0026mdash; sends data to a [LoRaWAN Network](https://lora-alliance.org/)\n\nMoreover, this project provides a base for a generic LoRaWAN device, which transmits sensor data, digital or analog input signals.\n\nThis was originally a remake of [BresserWeatherSensorTTN](https://github.com/matthias-bs/BresserWeatherSensorTTN) based on [RadioLib](https://github.com/jgromes/RadioLib) instead of [MCCI Arduino LoRaWAN Library](https://github.com/mcci-catena/arduino-lorawan) for LoRaWAN communication.\n\n## Important Notes\n\n* This should not be the first Arduino sketch you are ever trying to flash to your board - try something simple first (e.g. `blink.ino`) to get familiar with the tools and workflow.\n* If you are new to LoRaWAN\n   * Check out [The Things Fundamentals on LoRaWAN](https://www.thethingsnetwork.org/docs/lorawan/)\n   * Read the excellent article [RadioLib LoRaWAN on TTN starter script](https://github.com/jgromes/RadioLib/blob/master/examples/LoRaWAN/LoRaWAN_Starter/notes.md)\n* You need [RadioLib v7.5.0](https://github.com/jgromes/RadioLib/releases/tag/7.5.0) or later\n* You need [espressif/arduino-esp32 v3.X.Y](https://github.com/espressif/arduino-esp32)\n* Try and configure [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver) ([examples/BresserWeatherSensorBasic](https://github.com/matthias-bs/BresserWeatherSensorReceiver/tree/main/examples/BresserWeatherSensorBasic)) stand-alone before using it with BresserWeatherSensorLW\n* If you previously used [BresserWeatherSensorTTN](https://github.com/matthias-bs/BresserWeatherSensorTTN)\n   * The default payload configuration is different\n   * LoRaWAN downlink commands (and responses) are different\n* [Don't Panic](https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Don't_Panic)\n\n  While the software has quite many configuration options, most users won't need any or just a few. See [Required Configuration](#required-configuration) for the bare minimum configuration (and maybe [Default Parameter Values](#default-parameter-values) \u0026 [Default Configuration](#default-configuration)).\n\n## Features\n\n* Single 868 MHz Radio Transceiver for both Sensor Data Reception and LoRaWAN Connection\n* Protocols supported by RadioLib\n  * LoRaWAN Specification 1.1.0\n  * LoRaWAN Specification 1.0.4\n  * RP001 Regional Parameters 1.1 revision B\n  * RP002 Regional Parameters 1.0.4\n* Tested with [The Things Network](https://www.thethingsnetwork.org/), [ChirpStack](https://www.chirpstack.io/) and [Helium IoT](https://www.helium.com/)\n* Supports multiple 868 MHz Sensors (e.g. Weather Sensor and Soil Moisture Sensor or Indoor Thermometer/Hygrometer)\n* See [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver) for supported sensors\n* Low Power Design (using ESP32 Deep Sleep Mode / RP2040 Sleep State)\n* Fast LoRaWAN Joining after Deep Sleep (using ESP32 RTC RAM / RP2040 RAM)\n* [ATC MiThermometer](https://github.com/pvvx/ATC_MiThermometer) Bluetooth Low Energy Thermometer/Hygrometer Integration (optional)\n* [Theengs Decoder](https://github.com/theengs/decoder) Bluetooth Low Energy Sensors Integration (optional)\n* OneWire Temperature Sensor Integration (optional)\n* ESP32/RP2040 Analog Digital Converter Integration (optional)\n* [A02YYUW / DFRobot SEN0311 Ultrasonic Distance Sensor](https://wiki.dfrobot.com/_A02YYUW_Waterproof_Ultrasonic_Sensor_SKU_SEN0311) (30...4500mm) (optional)\n* [Remote Configuration via LoRaWAN Downlink](#remote-configuration-commands--status-requests-via-lorawan)\n* Easy [Sensor Data Uplink Payload Configuration](#payload-configuration) \n* Implementation with Separation between LoRaWAN Network Layer and Application Layer for easy Repurposing\n* Loading of LoRaWAN Secrets from JSON File on LittleFS (optional)\n* Loading of Hardware/Deployment specific Configuration Parameters from JSON file on LittleFS (optional)\n* External RTC (with Backup Battery) Integration (optional)\n* LoRaWAN Codec API compliant [Uplink/Downlink Payload Formatters](scripts/bresserweathersensorlw-codec/README.md)\n\n## Contents\n\n* [LoRaWAN Uplink Messages](#lorawan-uplink-messages)\n  * [Sensor Data Message](#sensor-data-message)\n  * [LoRaWAN Node Status Message](#lorawan-node-status-message)\n  * [Application Layer / Sensor Status Message](#application-layer--sensor-status-message)\n* [Supported Hardware](#supported-hardware)\n  * [Predefined Board Configurations](#predefined-board-configurations)\n  * [User-Defined Pinout and Radio Chip Configurations](#user-defined-pinout-and-radio-chip-configurations)\n  * [User-Defined Battery Voltage Measurement](#user-defined-battery-voltage-measurement)\n  * [Real-Time Clock (RTC)](#real-time-clock-rtc)\n* [LoRaWAN Network Service Configuration](#lorawan-network-service-configuration)\n* [Software Build Configuration](#software-build-configuration)\n  * [Required Configuration](#required-configuration)\n  * [Optional Configuration](#optional-configuration)\n  * [Enabling Debug Output](#enabling-debug-output)\n  * [Test Run](#test-run)\n* [LoRaWAN Payload Formatters](#lorawan-payload-formatters)\n  * [Encoding of Unavailable or Invalid Data](#encoding-of-unavailable-or-invalid-data) \n  * [The Things Network Payload Formatters Setup](#the-things-network-payload-formatters-setup)\n* [MQTT Integration](#mqtt-integration)\n  * [The Things Network MQTT Integration](#the-things-network-mqtt-integration)\n  * [Home Assistant Integration](#home-assistant-integration)\n  * [ChirpStack and InfluxDB Integration](#chirpstack-and-influxdb-integration)\n* [Datacake Integration](#datacake-integration)\n  * [Datacake / The Things Network Setup](#datacake--the-things-network-setup)\n  * [Desktop Dashboard](#desktop-dashboard)\n  * [Mobile Dashboard](#mobile-dashboard)\n* [Remote Configuration Commands / Status Requests via LoRaWAN](#remote-configuration-commands--status-requests-via-lorawan)\n  * [Parameters](#parameters)\n  * [Using Raw Data](#using-raw-data)\n  * [Using the Javascript Uplink/Downlink Formatters](#using-the-javascript-uplinkdownlink-formatters)\n* [Scanning for Sensors](#scanning-for-sensors)\n* [Loading LoRaWAN Network Service Credentials from File](#loading-lorawan-network-service-credentials-from-file)\n* [Loading LoRaWAN Node Configuration from File](#loading-lorawan-node-configuration-from-file)\n* [Payload Configuration](#payload-configuration)\n  * [Default Configuration](#default-configuration)\n  * [Config Helper](#config-helper)\n* [Customizing the Application Layer](#customizing-the-application-layer)\n  * [AppLayer Programming Interface](#applayer-programming-interface)\n* [Implementation](#implementation)\n  * [Class Diagram](#class-diagram)\n* [Doxygen Generated Source Code Documentation](#doxygen-generated-source-code-documentation)\n* [References](#references)\n* [Legal](#legal)\n\n## LoRaWAN Uplink Messages\n\nWith the default configuration, the device will periodically send 3 different uplink messages. The [LoRaWAN Node Status](#lorawan-node-status) message and the [Application Layer / Sensor Status](#application-layer--sensor-status) message can be disabled by setting the corresponding interval to zero.\n\n### Sensor Data Message\n\n* Payload: see [Default Configuration](#default-configuration)\n* Port: 1\n* Interval: ~`\u003csleep_interval\u003e` / `\u003csleep_interval_long\u003e` (depending on battery voltage); see [Default Parameter Values](#default-parameter-values)\n\n### LoRaWAN Node Status Message\n\n* Payload:\n\n  | Signal                    | description                           | Unit    | Type        | Bytes |\n  | ------------------------- | ------------------------------------- | ------- | ----------- | ----- |\n  | ubatt_mv                  | Battery Voltage                       | mv      | uint16      |     2 |\n  | long_sleep                | Flag: Long sleep time (energy saving) | \u0026mdash; | uint8       |     1 |\n  | **PowerFeather specific**                                                                         |\n  | usupply_mv                | Supply Voltage (VDC or USB input)     | mv      | uint16      |     2 |\n  | isupply_ma                | Supply Current (VDC or USB input)     | mA      | int16       |     2 |\n  | isupply_ma                | Battery Current                       | mA      | int16       |     2 |\n  | soc                       | Battery State of Charge               | %       | uint8       |     1 |\n  | soh                       | Battery State of Health               | %       | uint8       |     1 |\n  | battery_cycles            | Estimated Battery Cycles              | \u0026mdash; | uint16      |     2 |\n  | batt_time_min             | Estimated time to charge/discharge    | min     | int32       |     4 |\n  | batt_temp_c               | Battery Temperature                   | °C      | temperature |     2 |\n\n\nThe data types are implemented in [lora-serialization](https://github.com/thesolarnomad/lora-serialization) and the [Payload Formatters]\n(#lorawan-payload-formatters). `int16` and `int32` are extensions in the payload formatter for signed integers (implemented as `uint\u003c16|32\u003e` + offset).\n\n* Port: `CMD_GET_LW_STATUS`\n* Interval: `\u003clw_status_interval\u003e` (uplink frames); see [Default Parameter Values](#default-parameter-values)\n\n### Application Layer / Sensor Status Massage\n\n* Payload: Bresser/BLE Sensor Battery Status (Bitmap)\n* Port: `CMD_GET_SENSORS_STAT`\n* Interval: `\u003capp_status_interval\u003e` (uplink frames); see [Default Parameter Values](#default-parameter-values)\n\nSee [Parameters](#parameters) for more details.\n\n\n## Supported Hardware\n\n  |  Status       | Setup                                                                                                               | Board (/ Revision)   | Define (Prefix: ARDUINO_) | Radio Module | Notes    |\n  | ---------     | ------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------------------- | ------------ | -------- |\n  |  :hourglass:  | [LILYGO®TTGO-LORA32 V1](https://github.com/Xinyuan-LilyGo/TTGO-LoRa-Series) | TTGO LoRa32-OLED /\u003cbr\u003eTTGO LoRa32 V1 (No TFCard) | TTGO_LORA32_V1 | SX1276 (HPD13A) | -   |\n  |  :hourglass:  | [LILYGO®TTGO-LORA32 V2](https://github.com/LilyGO/TTGO-LORA32) | TTGO LoRa32-OLED /\u003cbr\u003eTTGO LoRa32 V2             | TTGO_LoRa32_V2 | SX1276 (HPD13A) | For LMIC only: Wire DIO1 to GPIO33 |\n  |  :white_check_mark:  |  [LILYGO®TTGO-LORA32 V2.1](https://www.lilygo.cc/products/lora3?variant=42272562282677)    | TTGO LoRa32-OLED /\u003cbr\u003eTTGO LoRa32 V2.1 (1.6.1) | TTGO_LoRa32_v21new |  SX1276 (HPD13A) | - |\n  |  :white_check_mark:  |  [LILYGO®T3 S3 SX1262](https://lilygo.cc/products/t3s3-v1-0?variant=42586879721653)        | \"LilyGo T3-S3\" / \"Radio-SX1262\" | LILYGO_T3S3_SX1262 |  SX1262  | - |\n  |  :white_check_mark:  |  [LILYGO®T3 S3 LR1121](https://lilygo.cc/products/t3-s3-lr1121)                            | \"LilyGo T3-S3\" / \"Radio-LR1121\" | LILYGO_T3S3_LR1121 |  LR1121  | - |\n  |  :hourglass:  | [Heltec Wireless Stick](https://heltec.org/project/wireless-stick/)   | Heltec Wireless Stick     | HELTEC_WIRELESS_STICK  |  SX1276  | - |\n  |  :hourglass:  | [Heltec WiFi LoRa 32 V2](https://heltec.org/project/wifi-lora-32/)    | Heltec WiFi LoRa 32(V2)   | HELTEC_WIFI_LORA_32_V2 |  SX1276  | - |\n  |  :white_check_mark:  | [Heltec WiFi LoRa 32 V3](https://heltec.org/project/wifi-lora-32-v3/) | Heltec WiFi LoRa 32(V3)   | HELTEC_WIFI_LORA_32_V3 |  SX1262  | - |\n  |  :white_check_mark:  | [LoRaWAN_Node](https://github.com/matthias-bs/LoRaWAN_Node)      | FireBeetle-ESP32 | DFROBOT_FIREBEETLE_ESP32 \u0026 LORAWAN_NODE     | SX1276 (RFM95W) | -      |\n  |  :white_check_mark:  | [DFRobot FireBeetle ESP32 IoT Microcontroller](https://www.dfrobot.com/product-1590.html) with [FireBeetle Cover LoRa Radio 868MHz](https://www.dfrobot.com/product-1831.html) | FireBeetle-ESP32 |  DFROBOT_FIREBEETLE_ESP32 \u0026 DFROBOT_COVER_LORA | SX1276 (LoRa1276) | Wiring on the cover: \u003cbr\u003eD2 to RESET\u003cbr\u003eD3 to DIO0\u003cbr\u003eD4 to CS\u003cbr\u003eD5 to DIO1 |\n  |  :hourglass:  | [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 | FEATHER_ESP32S2   | SX1276 (RFM95W) | **No Bluetooth available!**\u003cbr\u003eWiring on the Featherwing:\u003cbr\u003eE to IRQ\u003cbr\u003eD to CS\u003cbr\u003eC to RST\u003cbr\u003eA to DI01 |\n  |  :white_check_mark:  | [Thingpulse ePulse Feather](https://thingpulse.com/product/epulse-feather-low-power-esp32-development-board/) with [Adafruit LoRa Radio FeatherWing](https://www.adafruit.com/product/3231)     | ThingPulse ePulse Feather | THINGPULSE_EPULSE_FEATHER   | SX1276 (RFM95W) | Wiring on the Featherwing:\u003cbr\u003eE to IRQ\u003cbr\u003eD to CS\u003cbr\u003eC to RST\u003cbr\u003eA to DI01 |\n  |  :white_check_mark:  | [M5Stack Core2](https://docs.m5stack.com/en/core/core2) with [M5Stack Module LoRa868](https://docs.m5stack.com/en/module/lora868)   | M5Core2 | M5STACK_CORE2   | SX1276\u003cbr\u003e(RA-01H) | Wiring on the LoRa868 Module: \u003cbr\u003eDIO1 to GPIO35\u003cbr\u003e\u003cbr\u003e\"M5Unified\" must be installed \u003cbr\u003e`M5.begin()`is called to control power management\u003cbr\u003e\u003cbr\u003eSee [#178](https://github.com/matthias-bs/BresserWeatherSensorLW/issues/178) |\n  |  :white_check_mark:  | [ESP32-S3 PowerFeather](https://powerfeather.dev/) with [Adafruit LoRa Radio FeatherWing](https://www.adafruit.com/product/3231)     | ESP32-S3 PowerFeather | ESP32S3_POWERFEATHER | SX1276 (RFM95W) | Wiring on the Featherwing:\u003cbr\u003eE to IRQ\u003cbr\u003eD to CS\u003cbr\u003eC to RST\u003cbr\u003eA to DI01\u003cbr\u003e\u003cbr\u003e\"PowerFeather-SDK\" must be installed\u003cbr\u003e`Board.init(BATTERY_CAPACITY_MAH);` is called to control power management\u003cbr\u003e\u003cbr\u003esee [Wiki](wiki/PowerFeather) for additional hints |\n  |  :white_check_mark:  | [Seeed Studio XIAO ESP32S3 \u0026 Wio-SX1262 Kit](https://wiki.seeedstudio.com/wio_sx1262_with_xiao_esp32s3_kit_class) | XIAO_ESP32S3 |  XIAO_ESP32S3 | SX1262 | B2B Connector |\n  |  :white_check_mark:  | [Adafruit Feather RP2040](https://www.adafruit.com/product/4884) with [Adafruit LoRa Radio FeatherWing](https://www.adafruit.com/product/3231)     | Adafruit Feather RP2040 | ADAFRUIT_FEATHER_RP2040   | SX1276 (RFM95W) | **No Bluetooth available!**\u003cbr\u003e**Configuration: Choose an entry with \"FS\" in section __Flash Size__!**\u003cbr\u003eWiring on the Featherwing:\u003cbr\u003eE to IRQ\u003cbr\u003eD to CS\u003cbr\u003eC to RST\u003cbr\u003eA to DI01 |\n\n:hourglass: \u0026mdash; confirmation pending\n\n:white_check_mark: \u0026mdash; confirmed\n\n### Predefined Board Configurations\n\n\u003e [!NOTE]\n\u003e By using one of the boards listed in [Supported Hardware](#supported-hardware) and selecting this **Board** / **Board Revision** in the Arduino IDE, you get a working hardware configuration.\n \nBy selecting a Board and a Board Revision in the Arduino IDE, a define is passed to the preprocessor/compiler. A default configuration is assumed based on this define. If this is not what you need, you have to switch to Manual Configuration.\n\nIf you are not using the Arduino IDE, you can use the defines in [Supported Hardware](#supported-hardware) with your specific tool chain to get the same result.\n\nIf enabled in the Arduino IDE Preferences (\"Verbose Output\"), the preprosessor will provide some output regarding the selected configuration, e.g.\n\n```\nARDUINO_ADAFRUIT_FEATHER_ESP32S2 defined; assuming RFM95W FeatherWing will be used\n[...]\nRadio chip: SX1276\nPin config: RST-\u003e0 , IRQ-\u003e5 , NSS-\u003e6 , GPIO-\u003e11\n```\n### User-Defined Pinout and Radio Chip Configurations\n\n#### Required Information\n* Check the board manufacturer's datasheet, pinout specifications and schematic.\n* Check the board's pin definitions file (`pins_arduino.h`) in the [arduino-esp32 project](https://github.com/espressif/arduino-esp32/tree/master/variants)\n\n* Which LoRaWAN radio chip is used? SX1262 or SX1276?\n* Which pins are used for SPI (SCK, MISO and MOSI)?\n* On-board LoRaWAN radio chip:\n  * Which GPIO pins are connected to NSS (CSN), RST, IRQ and GPIO?\n* Separate LoRaWAN module:\n  * Which GPIO pins are available (i.e. otherwise unconnected) for NSS (CSN), RST, IRQ and GPIO?\n  * Connect the ESP32 board with the LoRaWAN module according to the selected GPIO pins.\n  * Connect the SPI and power supply pins as required.\n\n\u003e [!NOTE]\n\u003e Alternative pin names:\n\u003e SX1262: IRQ =\u003e DIO0, GPIO =\u003e BUSY\n\u003e SX1276: IRQ =\u003e DIO0, GPIO =\u003e DIO1 \n\n\u003e [!IMPORTANT]\n\u003e With the information above, the source code in both [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver) and [BresserWeatherSensorLW](https://github.com/matthias-bs/BresserWeatherSensorLW) has to be modified!\n\n#### Board Identification\n\nTo find out which `#define` is set for identifying your board:\n\nIn the Arduino IDE \u0026mdash;\n\n* In the `File` menu, open `Preferences` and enable the checkbox \"Show verbose output during **compile**\"\n* In the `Tools` menu, select your board (and board variant, if available)\n* Compile any sketch (e.g. `Blink`from the Arduino examples)\n* Search for a string starting with `-DARDUINO_` in the output window (e.g. `-DARDUINO_FEATHER_ESP32`)\n\nThe string which resembles your board name \u0026mdash; without the preceding `-D` \u0026mdash; is the wanted define (e.g. `ARDUINO_FEATHER_ESP32`).\n\nThis can be used by the C++ preprocessor to select board specific code, e.g.\n\n```\n#if defined(ARDUINO_FEATHER_ESP32)\n  // Put Adafruit Feather ESP32 specific code here\n#endif\n```\n\n#### BresserWeatherSensorReceiver Configuration\n\nIn `WeatherSensorCfg.h`:\n\n* Select or create a code section which will actually be used by the C++ preprocessor (`#if defined(\u003cYOUR_BOARD_DEFINE\u003e) ...).\n* Set the radio chip according to your hardware by (un-)commenting `USE_SX1262` or `USE_SX1276`.\n* Set the pin definitions `PIN_RECEIVER_CS`, `PIN_RECEIVER_IRQ`, `PIN_RECEIVER_GPIO` and `PIN_RECEIVER_RST` according to your hardware.\n* Cross check in the compiler log messages if the desired settings are actually used.\n\n#### BresserWeatherSensorLW\n\nIn `config.h`:\n\n* Select or create a code section which will actually be used by the C++ preprocessor (`#if defined(\u003cYOUR_BOARD_DEFINE\u003e) ...).\n* Set the radio chip according to your hardware defining `LORA_CHIP`.\n* Set the pin definitions `#define PIN_LORA_NSS`, `PIN_LORA_RST`, `PIN_LORA_IRQ` and `PIN_LORA_GPIO` according to your hardware.\n* Cross check in the compiler log messages if the desired settings are actually used.\n\n#### Provide Feedback\n\nIf your setup is working \u0026mdash; congratulations! Be nice and provide your insights to the project to help others!\n\n### User-Defined Battery Voltage Measurement\n\n\u003e [!WARNING]\n\u003e Exceeding the allowed supply voltage or analog digital converter (ADC) input voltage range or reversing the polarity will destroy your board! \n\n#### Overview\n\nWhile the battery voltage measurement is not crucial for operation, it is still important if the device is powered from a battery.\n\nThe battery voltage is used for:\n* Providing battery status to the LoRaWAN network server on request\n* Battery deep-discharge protection and energy saving (eco) mode\n* Monitoring battery status via uplink (e.g. for optimization of transmission interval)\n\n\u003e [!CAUTION]\n\u003e **The following section is meant as a general introduction. Actual implementations may vary. Consult you board's documentation for details!**\u003cbr\u003e\n\u003e The boards used in this project can be supplied by 5V via USB or by another supply voltage via a second power supply connector. Many have an integrated lithium-ion battery charger. A lithium-ion battery has a voltage range of ~2.4...4.2V. The usable voltage range for the board depends on the actual circuit. If a voltage regulator is used (and no voltage converter), the usable battery voltage range is ~3.3...4.2V.\n\nThe MCUs used in this project have an integrated ADC with an input voltage range of 0...3.3V. Therefore, the battery voltage has to be reduced by a voltage divider to provide a voltage range suitable for the ADC.\n\nThe ADC input circuitry may come in a few different flavors:\n1. A voltage divider is directly connected to the battery and to the ADC input\n2. Resistors for a voltage divider are present, but solder bridges are required to actually connect them\n3. A voltage divider is implemented, but an electronic switch has to be enabled for using it\n4. A voltage divider has to be implemented as external circuit\n\nLast, but not least, some boards provide a separate battery monitoring chip.\n\nOnly the cases 1 and 2 will be covered here.\n\n(Also see [Wiki: Battery Voltage Measurement](https://github.com/matthias-bs/BresserWeatherSensorLW/wiki/Battery-Voltage-Measurement)).\n\n#### ADC Input Pin and Voltage Divider Ratio\n\nFind the voltage divider and the ADC input pin used for battery voltage measurement (if available) in your board's circuit diagram.\n\nIn `BresserWeatherSensorLWCfg.h`:\n* Add a code section with your board definition (see [Board Identification](#board-identification)).\n* Define `PIN_ADC_IN` with your board specific pin.\n* Define `UBATT_DIV` with your voltage divider ratio if it differs from the default value of `0.5`.\n\nThe function `getBatteryVoltage()` in [adc.cpp](https://github.com/matthias-bs/BresserWeatherSensorLW/src/adc.cpp) provides the battery voltage. Any board specific implementation should be placed there. `getBatteryVoltage()` returns `0` for any unknown board or a known board with out a default ADC input circuit to indicate that the battery voltage cannot be measured.\n\n#### Real-Time Clock (RTC)\n\nThe MCU's built-in RTC provides time and date for scheduling wake-up from sleep mode and for algorithms like rain gauge or lightning counter post-processing.\n\nThe internal RTC retains operation while the MCU is in sleep mode. It can be set from different sources:\n\n1. LoRaWAN Network Time\n\n   The `Device_Time_Req` MAC command allows to request the time from the LoRaWAN network service. This is not supported by all LNS (e.g. not available with Helium Network).\n\n2. LoRaWAN Downlink Command\n\n   The command `CMD_SET_DATETIME` (see [Remote Configuration Commands / Status Requests via LoRaWAN](#remote-configuration-commands--status-requests-via-lorawan)) allows to set the RTC manually.\n\n   The time between queuing `CMD_SET_DATETIME` and the RTC actually being set is rather unpredictable due to the LNS's downlink scheduling. Furthermore, a loss of power (e.g. in case of a battery/solar powered node) will reset the MCU's integrated RTC.\n\n3. External RTC\n\n   An external RTC chip with backup battery retains operation independently of the node's power supply. It is initially set when the LoRaWAN node is built (see [RTCSet.ino](extras/RTCSet/RTCSet.ino)).\n\n   A module with an RTC chip supported by the [Adafruit RTClib](https://github.com/adafruit/RTClib) is connected to the 3.3V power supply and to the MCU's I²C bus pins.\n\n   \u003e [!IMPORTANT]\n   \u003e Check if the I²C interface requires additional pull-up resistors.\n\n   If enabled by setting `EXT_RTC` in [BresserWeatherSensorLWCfg.h](BresserWeatherSensorLWCfg.h), the external RTC takes precedence over the LoRaWAN Network Time.\n\n4. M5Stack: Integrated RTC\n  \n   The integrated RTC chip with backup battery retains operation independently of the node's power supply. It must be set initially via WiFi and SNTP or manually when the LoRaWAN node is built (see [M5Unified/examples/Basic/Rtc/Rtc.ino](https://github.com/m5stack/M5Unified/blob/master/examples/Basic/Rtc/Rtc.ino)).\n\n   The integrated RTC takes precedence over the LoRaWAN Network Time.\n\n### LoRaWAN Network Service Configuration\n\nCreate an account and set up a device configuration in your LoRaWAN network provider's web console, e.g. [The Things Network](https://www.thethingsnetwork.org/).\n\n* LoRaWAN v1.1\n* Regional Parameters 1.1 Revision A\n* Device class A\n* Over the air activation (OTAA)\n\n\u003e [!IMPORTANT]\n\u003e Check the maximum permitted payload size and uplink/downlink rate according to your regional parameters and change the configuration if required!\n\u003e See [Airtime calculator for LoRaWAN](https://avbentem.github.io/airtime-calculator/ttn/eu868).\n\n## Software Build Configuration\n\n### Required Configuration\n\n* Install the Arduino ESP32 board package in the Arduino IDE\n* Select your ESP32 board\n* Install all libraries as listed in [package.json](package.json) \u0026mdash; section 'dependencies' \u0026mdash; via the Arduino IDE Library Manager \n* Clone (or download and unpack) the latest ([BresserWeatherSensorLW Release](https://github.com/matthias-bs/BresserWeatherSensorLW/releases))\n* Set your LoRaWAN Network Service credentials \u0026mdash; `RADIOLIB_LORAWAN_DEV_EUI`, `RADIOLIB_LORAWAN_NWK_KEY` and `RADIOLIB_LORAWAN_APP_KEY` \u0026mdash; in [secrets.h](secrets.h):\n\n```\n// The Device EUI \u0026 two keys can be generated on the TTN console\n\n// Replace with your Device EUI\n#define RADIOLIB_LORAWAN_DEV_EUI   0x---------------\n\n// Replace with your App Key\n#define RADIOLIB_LORAWAN_APP_KEY   0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--\n\n// Put your Nwk Key here\n#define RADIOLIB_LORAWAN_NWK_KEY   0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--\n```\n\n* Load the sketch [BresserWeatherSensorLW.ino](BresserWeatherSensorLW.ino)\n* Compile and Upload\n\n### Optional Configuration\n\n**Header:** [BresserWeatherSensorLWCfg.h](BresserWeatherSensorLWCfg.h).\n\n**Downlink:** see [Remote Configuration Commands](#remote-configuration-commands--status-requests-via-lorawan)\n\n**File**: see [Loading LoRaWAN Node Configuration from File](#loading-lorawan-node-configuration-from-file)\n\n| Parameter              | Description                                                | Header | Downlink | File |\n| ---------------------- | ---------------------------------------------------------- |:------:|:--------:|:----:|\n| `TZ_INFO` / `timezone` | your time zone                                             |    X   |          |   X  |\n| `KNOWN_BLE_ADDRESSES`  | BLE Sensor MAC Addresses                                   |    X   |     X    |      |\n| `SLEEP_INTERVAL`\u003cbr\u003e`SLEEP_INTERVAL_LONG`\u003cbr\u003e`LW_STATUS_INTERVAL`\u003cbr\u003e`APP_STATUS_INTERVAL`\u003cbr\u003e`WEATHERSENSOR_TIMEOUT` | Timing parameters                                                                            |    X   |     X    |      |\n| `en_decoders`          | Enabled sensor decoders\u003cbr\u003e(disabling unused decoders saves CPU cycles / energy)        |        |     X    |      |\n| `VOLTAGE_CRITICAL`\u003cbr\u003e`VOLTAGE_ECO_EXIT`\u003cbr\u003e`VOLTAGE_ECO_ENTER`\u003cbr\u003e`BATTERY_DISCHARGE_LIM`\u003cbr\u003e`BATTERY_CHARGE_LIM` | Battery voltage levels in mV                                                                                    |    X   |          |   X  |\n| see header file        | ADC's input pins, dividers and oversampling                |    X   |          |      |\n| **PowerFeather specific Configuration**                                                                        |\n| `BATTERY_CAPACITY_MAH` /\u003cbr\u003e`powerfeather/battery_capacity` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)                                                                                  |    X   |          |   X  |\n| `PF_TEMPERATURE_MEASUREMENT` / \u003cbr\u003e`powerfeather/temperature_measurement` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)                                                                                  |    X   |          |   X  |\n| `PF_BATTERY_FUEL_GAUGE` / \u003cbr\u003e`powerfeather/battery_fuel_gauge` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)                                                                                  |    X   |          |   X  |\n| `PF_SUPPLY_MAINTAIN_VOLTAGE` / \u003cbr\u003e`powerfeather/supply_maintain_voltage` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)                                                                                  |    X   |          |   X  |\n| `PF_MAX_CHARGE_CURRENT_MAH` / \u003cbr\u003e`powerfeather/max_charge_current` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)                                                                              |  X   |          |   X  |\n| `SOC_CRITICAL`\u003cbr\u003e`SOC_ECO_EXIT`\u003cbr\u003e`SOC_ECO_ENTER` | Battery state of charge thresholds in % | X |   | X | \n| **M5Stack specific Configuration**                                                                                          |\n| `SOC_CRITICAL`\u003cbr\u003e`SOC_ECO_EXIT`\u003cbr\u003e`SOC_ECO_ENTER` | Battery state of charge thresholds in % | X |   | X | \n\n### Enabling Debug Output\n\n[Debug Output Configuration in Arduino IDE](Debug_Output.md)\n\n### Test Run\n\nWatch your board's debug output in the serial console and the LoRaWAN communication in your network provider's web console.\n\n## LoRaWAN Payload Formatters\n\nUpload [Uplink Formatter](scripts/uplink_formatter.js) and [Downlink Formatter](scripts/downlink_formatter.js) scripts in your LoRaWAN network service provider's web console to allow decoding / encoding of raw data to / from JSON format.\n\nThe npm package [bresserweathersensorlw-codec](https://www.npmjs.com/package/bresserweathersensorlw-codec) is generated from [scripts/bresserweathersensorlw-codec](https://github.com/matthias-bs/BresserWeatherSensorLW/tree/main/scripts/bresserweathersensorlw-codec).\n\nSee [The Things Network MQTT Integration and Payload Formatters](https://github.com/matthias-bs/BresserWeatherSensorTTN/blob/main/README.md#the-things-network-mqtt-integration-payload-formatters) and [TS013-1.0.0 Payload Codec API](https://resources.lora-alliance.org/technical-specifications/ts013-1-0-0-payload-codec-api) for more details.\n\n### Encoding of Unavailable or Invalid Data\n\nFor various reasons, data can be (temporarily) unavailable or invalid, e.g. due to a sensor radio message reception failure, sensor initialization, or a low sensor battery. The sensor data uplink message has a fixed format (see  [Payload Configuration](#payload-configuration)), therefore it is not possible to simply omit any data. The allowed payload size of a LoRaWAN frame is very small, therefore space should not be wasted by dedicated 'data valid' flags.\n\nAs a solution, unavailable/invalid data are encoded as special values \u0026mdash; out of the normal range \u0026mdash; in the data fields. E.g. a humidity value, which is encoded as 8-bit unsigned value with a range of 0 to 100 percent, is encoded as 255 (0xFF) to indicate invalid data. Those special values defined in [BresserWeatherSensorLWCfg.h](BresserWeatherSensorLWCfg.h).\n\nThe [Uplink Payload Formatter](scripts/uplink_formatter.js) detects and skips this data, i.e. the JSON output string contains only valid data. This can be changed by setting `SKIP_INVALID_SIGNALS = false`.\n\n### The Things Network Payload Formatters Setup\n\n#### Uplink Formatter\n\nDecode uplink payload (a sequence of bytes) into JSON format, i.e. data structures which are readable/suitable for further processing.\n\nIn The Things Network Console:\n1. Go to \"Payload formatters\" -\u003e \"Uplink\"\n2. Select \"Formatter type\": \"Custom Javascript formatter\"\n3. \"Formatter code\": Paste [scripts/uplink_formatter.js](scripts/uplink_formatter.js)\n4. Apply \"Save changes\"\n\n![TTN Uplink Formatter](https://github.com/matthias-bs/BresserWeatherSensorTTN/assets/83612361/38b66478-688a-4028-974a-c517cddae662)\n\n\u003e [!NOTE]\n\u003e The actual payload depends on the options selected in the Arduino sketch (see [BresserWeatherSensorsLW.cfg](BresserWeatherSensorsLW.cfg)) \u0026mdash; the decoder must be edited accordingly (add or remove data types and JSON identifiers). The configuration dependent part of the decoder can be created with a C++ preprocessor and the Python script [generate_decoder.py](scripts/generate_decoder.py).\n\n#### Downlink Formatter\n\nEncode downlink payload from JSON to a sequence of bytes.\n\nIn The Things Network Console:\n1. Go to \"Payload formatters\" -\u003e \"Downlink\"\n2. Select \"Formatter type\": \"Custom Javascript formatter\"\n3. \"Formatter code\": Paste [scripts/downlink_formatter.js](scripts/downlink_formatter.js)\n4. Apply \"Save changes\"\n\n## MQTT Integration\n\n### The Things Network MQTT Integration\n\nTTN provides an MQTT broker.\nHow to receive and decode the payload with an MQTT client -\nsee https://www.thethingsnetwork.org/forum/t/some-clarity-on-mqtt-topics/44226/2\n\nV3 topic:\n\n`v3/\u003cttn app id\u003e\u003cat symbol\u003ettn/devices/\u003cttn device id\u003e/up`\n\n  \nv3 message key field jsonpaths:\n  \n```\n\u003cttn device id\u003e = .end_device_ids.device_id\n\u003cttn app id\u003e = .end_device_ids.application_ids.application_id  // (not including the \u003cat symbol\u003ettn in the topic)\n\u003cpayload\u003e = .uplink_message.frm_payload\n```  \n\n\nJSON-Path with Uplink-Decoder (see [scripts/uplink_formatter.js](scripts/uplink_formatter.js))\n\n`.uplink_message.decoded_payload.bytes.\u003cvariable\u003e`\n\n### Home Assistant Integration\n\nThis solution builds on top of the The Things Network MQTT Integration, but should work is a similar way for other LoRaWAN Network Services.\n\n#### Create a Bridge between TTN MQTT Broker and your Mosquitto MQTT Broker\n\n\u003e [!NOTE]\n\u003e This might not be necessary if it is possible to integrate the TTN MQTT Broker in Home Assistant.\n\nCustomize and add the following configurations to your `/etc/mosquitto/conf.d/local.conf`:\n\n```\nconnection bridge-01\naddress eu1.cloud.thethings.network:8883\nremote_username YOUR_TTN_USERNAME\nremote_password YOUR_TTN_PASSWORD\ntry_private false\nbridge_cafile /etc/ssl/certs/ISRG_Root_X1.pem\ntopic # in 1\n```\n\nChange `address` as required. The bridge works in both directions, i.e. you can publish and subscribe messages to/from your LoRaWAN node using your local MQTT broker.\n\n#### Home Assistant Configuration\n\nCustomize [scripts/home_assistant_configuration.yaml](scripts/home_assistant_configuration.yaml) and add it to your `/homeassistant/configuration.yaml`:\n\n`ws_topic`: TTN LoRaWAN uplink data topic\\\n`ws_expiry`: The sensor value is set to 'unavailable' `\u003cws_expiry\u003e` seconds after the last update\\\n`ws_device`: Common device configuration for all sensors\n\n![Home_Assistant-1](https://github.com/user-attachments/assets/e34b04f2-a11b-470a-9b53-6ab52b083b67)\n\n### ChirpStack and InfluxDB Integration\n\n[ChirpStack and InfluxDB Integration](docs/influxdb_integration/influxdb_integration.md) kindly provided by [Davide D'Asaro](https://github.com/evon800c).\n\n## Datacake Integration\n\n### Datacake / The Things Network Setup\n\nYouTube Video: [Get started for free with LoRaWaN on The Things Network and Datacake IoT Platform](https://youtu.be/WGVFgYp3k3s)\n\n### Desktop Dashboard\n\n![Datacake_Dashboard_Desktop](https://github.com/matthias-bs/BresserWeatherSensorTTN/assets/83612361/2a876ba1-06b9-4ea3-876c-2fad3d559b01)\n\n### Mobile Dashboard\n![Datacake_Dashboard_Mobile](https://github.com/matthias-bs/BresserWeatherSensorTTN/assets/83612361/fbc0948c-bfd8-4d7d-9780-c113d576d3cf)\n\n## Remote Configuration Commands / Status Requests via LoRaWAN\n\nMany software parameters can be defined at compile time, i.e. in [BresserWeatherSensorLWCfg.h](BresserWeatherSensorLWCfg.h). A few [parameters](#parameters) can also be changed and queried at run time via LoRaWAN, either [using raw data](#using-raw-data) or [using Javascript Uplink/Downlink Formatters](#using-the-javascript-uplinkdownlink-formatters).\n\n### Parameters\n\n| Parameter             | Description                                                                 |\n| --------------------- | --------------------------------------------------------------------------- |\n| \u003cws_timeout\u003e          | Weather sensor receive timeout in seconds; 0...255                          |\n| \u003csleep_interval\u003e      | Sleep interval (regular) in seconds; 0...65535                              |\n| \u003csleep_interval_long\u003e | Sleep interval (energy saving mode) in seconds; 0...65535                   |\n| \u003clw_status_interval\u003e  | LoRaWAN node status message uplink interval in no. of uplink frames; 0...255; 0: disabled |\n| \u003cubatt_mv\u003e            | Battery voltage in mV                                                       |\n| \u003clong_sleep\u003e          | 0: regular sleep interval / 1: long sleep interval (depending on U_batt)    |\n| \\\u003cepoch\\\u003e             | Unix epoch time, see https://www.epochconverter.com/ ( \\\u003cinteger\\\u003e / \"0x....\") |\n| \u003creset_flags\u003e         | Raingauge reset flags; 0...15 (1: hourly / 2: daily / 4: weekly / 8: monthly) / \"0x0\"...\"0xF\" |\n| \u003cws_scantime\u003e         | Bresser sensor scan time in seconds; 0...255 (only for CMD_SCAN_SENSORS)    |\n| \\\u003cidX\\\u003e               | Sensor ID                                                                   |\n| \\\u003cdecoderX\\\u003e          | Matching payload decoder                                                    |\n| \\\u003ctypeX\\\u003e             | Sensor type                                                                 |\n| \\\u003cchX\\\u003e               | Sensor channel                                                              |\n| \u003cdata_flagsX\u003e         | Sensor data flags                                                           |\n| \\\u003crssi\\\u003e              | Sensor radio signal RSSI in dBm (sign inverted)                             |\n| \u003crtc_source\u003e          | Real time clock source; 0x00: GPS / 0x01: RTC / 0x02: LORA / 0x03: unsynched / 0x04: set (source unknown) |\n| \u003csensors_incX\u003e        | Bresser sensor IDs include list; e.g. \"0xDEADBEEF\"; \"0x00000000\" =\u003e empty list =\u003e default values          |\n| \u003csensors_excX\u003e        | Bresser sensor IDs include list; e.g. \"0xDEADBEEF\"; \"0x00000000\" =\u003e empty list =\u003e default values          |\n| \u003cmax_sensors\u003e         | Max. number of Bresser sensors per receive cycle; 1...8                     |\n| \u003crx_flags\u003e            | Flags for getData(); see [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/627d0c68ee9304acb9ecfb5f226e4d7be99467cb/src/WeatherSensor.h#L130) |\n| \u003cen_decoders\u003e         | Enabled sensor data decoders; see [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/627d0c68ee9304acb9ecfb5f226e4d7be99467cb/src/WeatherSensor.h#L135)\n| \u003cble_active\u003e          | BLE active scan; 1 (active scan) / 0 (passive scan)                         |\n| \u003cble_scantime\u003e        | BLE scan time in seconds; 0...255                                           |\n| \u003cble_addrX\u003e           | BLE sensor MAC addresses; e.g. \"DE:AD:BE:EF:12:23\"                          |\n| \\\u003ctypeN\\\u003e             | Bitmap for enabling Bresser sensors of type \\\u003cN\\\u003e; each bit position corresponds to a channel,\u003cbr\u003ee.g. bit 0 controls channel 0; unused bits can be used to select features |\n| \\\u003conewire\\\u003e           | Bitmap for enabling 1-Wire sensors; each bit position corresponds to an index |\n| \\\u003canalog\\\u003e            | Bitmap for enabling analog input channels; each bit position corresponds to a channel |\n| \\\u003cdigital\\\u003e           | Bitmap for enabling digital input channels in a broader sense \u0026mdash; GPIO, SPI, I2C, UART, ... |\n| \u003ctypeN_st\u003e            | Bitmap for Bresser sensor type \\\u003cN\\\u003e battery status; each bit position corresponds to a channel |\n| \u003capp_status_interval\u003e | App Layer (sensor status) message uplink interval in no. of uplink frames; 0...255; 0: disabled |\n| \u003conewire_st\u003e          | Bitmap for 1-Wire sensor status; each bit position corresponds to an index |\n| \u003canalog_st\u003e           | Bitmap for analog input status; each bit position corresponds to a channel |\n| \u003cdigital_st\u003e          | Bitmap for digital input channel status |\n| \u003cble_st\u003e              | Bitmap for BLE sensor battery status |\n\n\u003e [!NOTE]\n\u003e See [Payload Configuration](#payload-configuration) for more details!\n\n\u003e [!WARNING]\n\u003e Confirmed downlinks should not be used! (see [here](https://www.thethingsnetwork.org/forum/t/how-to-purge-a-scheduled-confirmed-downlink/56849/7) for an explanation.)\n\n\u003e [!IMPORTANT]\n\u003e To set sensors_inc / sensors_exc to the compile time default configuration, set the first ID in CMD_SET_SENSORS_INC / CMD_SET_SENSORS_EXC to \"0x00000000\".\n\u003e To set the BLE sensor addresses to the compile time default configuration, set the first address in CMD_SET_BLE_ADDR to \"00:00:00:00:00:00\".\n\n\n#### Default Parameter Values\n\n* Sleep interval (long): `SLEEP_INTERVAL`, `SLEEP_INTERVAL_LONG`; see [BresserWeatherSensorLWCfg.h](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/3760ae7ea98417d702f97aa62d3a03b0e704f715/BresserWeatherSensorLWCfg.h#L120C9-L120C23)\n* `LW_STATUS_INTERVAL`: see [BresserWeatherSensorLWCfg.h](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/3760ae7ea98417d702f97aa62d3a03b0e704f715/BresserWeatherSensorLWCfg.h#L129)\n* `APP_STATUS_INTERVAL`: see [BresserWeatherSensorLWCfg.h](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/3760ae7ea98417d702f97aa62d3a03b0e704f715/BresserWeatherSensorLWCfg.h#L132)\n* BLE addresses and scan parameters: `BLE_SCAN_TIME`, `BLE_SCAN_MODE`, `KNOWN_BLE_ADDRESSES`; see [BresserWeatherSensorLWCfg.h](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/3760ae7ea98417d702f97aa62d3a03b0e704f715/BresserWeatherSensorLWCfg.h#L307)\n* Weather sensor receive timeout: `WEATHERSENSOR_TIMEOUT`; see [BresserWeatherSensorLWCfg.h](https://github.com/matthias-bs/BresserWeatherSensorLW/blob/3760ae7ea98417d702f97aa62d3a03b0e704f715/BresserWeatherSensorLWCfg.h#L135)\n* Sensor IDs include/exclude list: `SENSOR_IDS_EXC`/`SENSOR_IDS_INC`; see [WeatherSensorCfg.h](https://github.com/matthias-bs/BresserWeatherSensorReceiver/blob/ff450b68f669fe312af9a3e00ae9736804df12b6/src/WeatherSensorCfg.h#L83)\n* Sensor data uplink payload configuration: see [Payload Configuration](#payload-configuration)\n\n### Using Raw Data\n\n| Command                       | Port       | Downlink                                                                  | Uplink         |\n| ----------------------------- | ---------- | ------------------------------------------------------------------------- | -------------- |\n| CMD_GET_DATETIME              | 0x20  (32) | 0x00                                                                      | epoch[31:24]\u003cbr\u003eepoch[23:16]\u003cbr\u003eepoch[15:8]\u003cbr\u003eepoch[7:0]\u003cbr\u003ertc_source[7:0] |\n| CMD_SET_DATETIME              | 0x21  (33) | epoch[31:24]\u003cbr\u003eepoch[23:16]\u003cbr\u003eepoch[15:8]\u003cbr\u003eepoch[7:0]                 | n.a.           |\n| CMD_SET_SLEEP_INTERVAL        | 0x31  (49) | sleep_interval[15:8]\u003cbr\u003esleep_interval[7:0]                               | n.a.           |\n| CMD_SET_SLEEP_INTERVAL_LONG   | 0x33  (51) | sleep_interval_long[15:8]\u003cbr\u003esleep_interval_long[7:0]                     | n.a.           |\n| CMD_SET_LW_STATUS_INTERVAL    | 0x35  (53) | lw_status_interval[7:0]                                                   | n.a.           |\n| CMD_GET_LW_CONFIG             | 0x36  (54) | 0x00                                                                      | sleep_interval[15:8]\u003cbr\u003esleep_interval[7:0]\u003cbr\u003esleep_interval_long[15:8]\u003cbr\u003esleep_interval_long[7:0]\u003cbr\u003elw_status_interval[7:0] |\n| CMD_GET_LW_STATUS             | 0x38 (56) | 0x00                                                                       | ubatt_mv[15:8]\u003cbr\u003eubatt_mv[7:0]\u003cbr\u003elong_sleep[7:0] |\n| CMD_GET_APP_STATUS_INTERVAL   | 0x40  (64) | 0x00                                                                      | app_status_interval[7:0] |\n| CMD_SET_APP_STATUS_INTERVAL   | 0x41  (65) | app_status_interval[7:0]                                                  | n.a.            |\n| CMD_GET_SENSORS_STAT          | 0x42  (66) | 0x00                                                                      | type00_st[7:0]\u003cbr\u003etype01_st[7:0]\u003cbr\u003e...\u003cbr\u003etype15_st[7:0]\u003cbr\u003eonewire_st[15:8]\u003cbr\u003eonewire_st[7:0]\u003cbr\u003eanalog_st[15:8]\u003cbr\u003eanalog_st[7:0]\u003cbr\u003edigital_st[31:24]\u003cbr\u003edigital_st[23:16]\u003cbr\u003edigital_st[15:8]\u003cbr\u003edigital_st[7:0]\u003cbr\u003eble_st[15:8]\u003cbr\u003eble_st[7:0] |\n| CMD_GET_APP_PAYLOAD_CFG       | 0x46  (70) | 0x00                                                                      | type00[7:0]\u003cbr\u003etype01[7:0]\u003cbr\u003e...\u003cbr\u003etype15[7:0]\u003cbr\u003eonewire[15:8]\u003cbr\u003eonewire[7:0]\u003cbr\u003eanalog[15:8]\u003cbr\u003eanalog[7:0]\u003cbr\u003edigital[31:24]\u003cbr\u003edigital[23:16]\u003cbr\u003edigital[15:8]\u003cbr\u003edigital[7:0] |\n| CMD_SET_APP_PAYLOAD_CFG       | 0x47  (71) | type00[7:0]\u003cbr\u003etype01[7:0]\u003cbr\u003e...\u003cbr\u003etype15[7:0]\u003cbr\u003eonewire[15:8]\u003cbr\u003eonewire[7:0]\u003cbr\u003eanalog[15:8]\u003cbr\u003eanalog[7:0]\u003cbr\u003edigital[31:24]\u003cbr\u003edigital[23:16]\u003cbr\u003edigital[15:8]\u003cbr\u003edigital[7:0] | n.a. |\n| CMD_GET_WS_TIMEOUT            | 0xC0 (192) | 0x00                                                                      | ws_timeout[7:0] |\n| CMD_SET_WS_TIMEOUT            | 0xC1 (193) | ws_timeout[7:0]                                                           | n.a.            |\n| CMD_RESET_RAINGAUGE           | 0xC3 (195) | flags[7:0]                                                                | n.a.            |\n| CMD_SCAN_SENSORS              | 0xC4 (196) | ws_scantime[7:0]                                                          | id0[31:24]\u003cbr\u003eid0[23:16]\u003cbr\u003eid0[15:8]\u003cbr\u003eid0[7:0]\u003cbr\u003edecoder0[3:0]\u003cbr\u003etype0[3:0]\u003cbr\u003ech0[7:0]\u003cbr\u003edata_flags0[7:0]\u003cbr\u003erssi0[7:0]\u003cbr\u003e... | \n| CMD_GET_SENSORS_INC           | 0xC6 (198) | 0x00                                                                      | sensors_inc0[31:24]\u003cbr\u003esensors_inc0[23:15]\u003cbr\u003esensors_inc0[16:8]\u003cbr\u003esensors_inc0[7:0]\u003cbr\u003e... |\n| CMD_SET_SENSORS_INC           | 0xC7 (199) | sensors_inc0[31:24]\u003cbr\u003esensors_inc0[23:16]\u003cbr\u003esensors_inc0[15:8]\u003cbr\u003esensors_inc0[7:0]\u003cbr\u003e... | n.a. |\n| CMD_GET_SENSORS_EXC           | 0xC8 (200) | 0x00                                                                      | sensors_exc0[31:24]\u003cbr\u003esensors_exc0[23:15]\u003cbr\u003esensors_exc0[16:8]\u003cbr\u003esensors_exc0[7:0]\u003cbr\u003e... |\n| CMD_SET_SENSORS_EXC           | 0xC9 (201) | sensors_exc0[31:24]\u003cbr\u003esensors_exc0[23:16]\u003cbr\u003esensors_exc0[15:8]\u003cbr\u003esensors_exc0[7:0]\u003cbr\u003e... | n.a. |\n| CMD_GET_SENSORS_CFG           | 0xCA (202) | 0x00                                                                      | max_sensors[7:0]\u003cbr\u003erx_flags[7:0]\u003cbr\u003een_decoders\u003c7:0\u003e |\n| CMD_SET_SENSORS_CFG           | 0xCB (203) | max_sensors[7:0]\u003cbr\u003erx_flags[7:0]\u003cbr\u003een_decoders\u003c7:0\u003e                     | n.a.             |\n| CMD_GET_BLE_CONFIG            | 0xD0 (208) | 0x00                                                                      | ble_active[7:0]\u003cbr\u003eble_scantime[7:0] |\n| CMD_SET_BLE_CONFIG            | 0xD1 (209) | ble_active[7:0]\u003cbr\u003eble_scantime[7:0]                                      | n.a.            |\n| CMD_GET_BLE_ADDR              | 0xD2 (210) | 0x00                                                                      | ble_addr0[47:40]\u003cbr\u003eble_addr0[39:32]\u003cbr\u003eble_addr0[31:24]\u003cbr\u003eble_addr0[23:16]\u003cbr\u003eble_addr0[15:8]\u003cbr\u003eble_addr0[7:0]\u003cbr\u003e... |\n| CMD_SET_BLE_ADDR              | 0xD3 (211) | ble_addr0[47:40]\u003cbr\u003eble_addr0[39:32]\u003cbr\u003eble_addr0[31:24]\u003cbr\u003eble_addr0[23:16]\u003cbr\u003eble_addr0[15:8]\u003cbr\u003eble_addr0[7:0]\u003cbr\u003e... | n.a. |\n\n\n#### The Things Network Examples\n\n##### Example 1: Set SLEEP_INTERVAL to 360 seconds\n1. Set port for CMD_SET_SLEEP_INTERVAL to 49\n2. Convert interval to hex: 300 = 0x012C\n3. Set payload to 0x01 0x2C\n4. Send downlink via The Things Network Console\n\n![TTN Downlink as Hex](https://github.com/matthias-bs/BresserWeatherSensorLW/assets/83612361/ad66a77d-1aae-4083-8d62-d09c89c3f180)\n\n\n##### Example 2: Set Date/Time\n\n1. Set port for CMD_SET_DATETIME to 33\n2. Get epoch (e.g. from https://www.epochconverter.com/hex) (Example: 0x63B2BC32); add an offset (estimated) for time until received (Example: + 64 / 0x40 seconds =\u003e 0x63B2BC**7**2) \n3. Set payload to 0x63 0xB2 0xBC 0x72\n4. Send downlink via The Things Network Console\n\n### Using the Javascript Uplink/Downlink Formatters\n\n\u003e [!NOTE]\n\u003e The command (`\"cmd\": ...`) may be omitted if it can be derived from the given parameters.\n\n| Command                       | Downlink                                                                  | Uplink                       |\n| ----------------------------- | ------------------------------------------------------------------------- | ---------------------------- |\n| CMD_GET_DATETIME              | {\"cmd\": \"CMD_GET_DATETIME\"}                                               | {\"epoch\": \\\u003cepoch\\\u003e}         |\n| CMD_SET_DATETIME              | {\"epoch\": \\\u003cepoch\\\u003e}                                                      | n.a.                         |\n| CMD_SET_SLEEP_INTERVAL        | {\"sleep_interval\": \u003csleep_interval\u003e}                                      | n.a.                         |\n| CMD_SET_SLEEP_INTERVAL_LONG   | {\"sleep_interval_long\": \u003csleep_interval_long\u003e}                            | n.a.                         |\n| CMD_SET_LW_STATUS_INTERVAL    | {\"lw_status_interval\": \u003clw_status_interval\u003e}                              | n.a.                         |\n| CMD_GET_LW_CONFIG             | {\"cmd\": \"CMD_GET_LW_CONFIG\"}                                              | {\"sleep_interval\": \u003csleep_interval\u003e, \"sleep_interval_long\": \u003csleep_interval_long\u003e, \"lw_status_interval\": \u003clw_status_interval\u003e} |\n| CMD_GET_LW_STATUS             | {\"cmd\": \"CMD_GET_LW_STATUS\"}                                              | {\"ubatt_mv\": \u003cubatt_mv\u003e, \"long_sleep\": \u003clong_sleep\u003e} |\n| CMD_GET_APP_STATUS_INTERVAL   | {\"cmd\": \"CMD_GET_APP_STATUS_INTERVAL\"}                                    | {\"app_status_interval\": \u003capp_status_interval\u003e} |\n| CMD_SET_APP_STATUS_INTERVAL   | {\"app_status_interval\": \u003capp_status_interval\u003e}                            | n.a.                         |\n| CMD_GET_SENSORS_STAT          | {\"cmd\": \"CMD_GET_SENSORS_STAT\"}                                           | \"sensor_status\": {\"ble\": \u003cble_stat\u003e, \"bresser\": [\u003cbresser0_st\u003e, ..., \u003cbresser15_st\u003e]} |\n| CMD_GET_APP_PAYLOAD_CFG       | {\"cmd\": \"CMD_GET_APP_PAYLOAD_CFG\"}                                        | {\"bresser\": [\\\u003ctype0\\\u003e, \\\u003ctype1\\\u003e, ..., \\\u003ctype15\\\u003e], \"onewire\": \\\u003conewire\\\u003e, \"analog\": \\\u003canalog\\\u003e, \"digital\": \\\u003cdigital\\\u003e} |\n| CMD_SET_APP_PAYLOAD_CFG       | {\"bresser\": [\\\u003ctype0\\\u003e, \\\u003ctype1\\\u003e, ..., \\\u003ctype15\\\u003e], \"onewire\": \\\u003conewire\\\u003e, \"analog\": \\\u003canalog\\\u003e, \"digital\": \\\u003cdigital\\\u003e} | n.a. |\n| CMD_GET_WS_TIMEOUT            | {\"cmd\": \"CMD_GET_WS_TIMEOUT\"}                                             | {\"ws_timeout\": \u003cws_timeout\u003e} |\n| CMD_SET_WS_TIMEOUT            | {\"ws_timeout\": \u003cws_timeout\u003e}                                              | n.a.                         |\n| CMD_RESET_RAINGAUGE           | {\"reset_flags\": \u003creset_flags\u003e}                                            | n.a.                         |\n| CMD_SCAN_SENSORS              | {\"ws_scantime\": \u003cws_scantime\u003e}                                            | {\"found_sensors\": [{\"id\": \\\u003cid0\\\u003e, \"decoder\": \\\u003cdecoder0\\\u003e, \"type\": \\\u003ctype0\\\u003e, \"ch\": \\\u003cch0\\\u003e, \"flags\": \u003cdata_flags0\u003e, \"rssi\": \\\u003crssi0\\\u003e}, ...]}\n| CMD_GET_SENSORS_INC           | {\"cmd\": \"CMD_GET_SENSORS_INC\"}                                            | {\"sensors_inc\": [\u003csensors_inc0\u003e, ..., \u003csensors_incN\u003e]} |\n| CMD_SET_SENSORS_INC           | {\"sensors_inc\": [\u003csensors_inc0\u003e, ..., \u003csensors_incN\u003e]}                    | n.a.                         |\n| CMD_GET_SENSORS_EXC           | {\"cmd\": \"CMD_GET_SENSORS_EXC\"}                                            | {\"sensors_exc\": [\u003csensors_exc0\u003e, ..., \u003csensors_excN\u003e]} |\n| CMD_SET_SENSORS_EXC           | {\"sensors_exc\": [\u003csensors_exc0\u003e, ..., \u003csensors_excN\u003e]}                    | n.a.                         |\n| CMD_GET_SENSORS_CFG           | {\"cmd\": \"CMD_GET_SENSORS_CFG\"}                                            | {\"max_sensors\": \u003cmax_sensors\u003e, \"rx_flags\": \u003crx_flags\u003e, \"en_decoders\": \u003cen_decoders\u003e} |\n| CMD_SET_SENSORS_CFG           | {\"max_sensors\": \u003cmax_sensors\u003e, \"rx_flags\": \u003crx_flags\u003e, \"en_decoders\": \u003cen_decoders\u003e} | n.a.                         |\n| CMD_GET_BLE_CONFIG            | {\"cmd\": \"CMD_GET_BLE_CONFIG\"}                                             | {\"ble_active\": \u003cble_active\u003e, \"ble_scantime\": \u003cble_scantime\u003e} |\n| CMD_SET_BLE_CONFIG            | {\"ble_active\": \u003cble_active\u003e, \"ble_scantime\": \u003cble_scantime\u003e}              | n.a.                         |\n| CMD_GET_BLE_ADDR              | {\"cmd\": \"CMD_GET_BLE_ADDR\"}                                               | {\"ble_addr\": [\u003cble_addr0\u003e, ..., \u003cble_addrN\u003e]} |\n| CMD_SET_BLE_ADDR              | {\"ble_addr\": [\u003cble_addr0\u003e, ..., \u003cble_addrN\u003e]}                             | n.a.                         |\n\n#### The Things Network Examples \n\n##### Example 1: Set SLEEP_INTERVAL to 360 seconds\n1. Build payload as JSON string: `{\"sleep_interval\": 360}` \u0026mdash;\n   the correct port is selected automatically\n2. Send downlink via The Things Network Console\n\n![TTN Downlink as JSON](https://github.com/matthias-bs/BresserWeatherSensorLW/assets/83612361/2133676f-5d08-4d71-b580-e628c15b1229)\n\n\n##### Example 2: Set Date/Time\n1. Get epoch (e.g. from https://www.epochconverter.com) (Example: 1692729833); add an offset (estimated) for time until received (Example: + 64 seconds =\u003e 16927298**97**) \n2. Build payload as JSON string: {\"epoch\": 1692729897} \n3. Send downlink via The Things Network Console\n\n## Scanning for Sensors\n\n\u003e [!NOTE]\n\u003e The command `CMD_SCAN_SENSORS` allows to gather information about all sensors within range. \n\nThe differences between regular sensor reception and `CMD_SCAN_SENSORS` are:\n* Scanning will run for `\u003cws_scantime\u003e` seconds (as opposed to `ws_timeout`)\n* `rx_flags` is set to `DATA_ALL_SLOTS | DATA_COMPLETE` implicitly, i.e. \n   * As many sensors as possible will be received and\n   * For weather sensors using the 6-in-1 protocol, both message types have to be received\n\n   before scanning is finished. (Scanning is also stopped when `\u003cws_scantime\u003e` has expired.)\n* The sensor ID filters (include/exclude list) are disabled\n* Different information is provided in the uplink message\n\nThe number of sensors which can be reported is limited by the LoRaWAN uplink payload size, e.g. with a limit of 51 bytes, a maximum of 6 sensors can be reported. Scanning can be repeated to get another sample (due to a more or less random relation between sensor's start of transmission and start of scan process).\n\nExample uplink (response to `{\"ws_scantime\": 180}`):\n```json\n\"found_sensors\": [\n            {\n              \"ch\": 0,\n              \"decoder\": \"6-in-1\",\n              \"flags\": \"0x0c\",\n              \"id\": \"0x792882a2\",\n              \"rssi\": -100,\n              \"type\": \"Weather Sensor\"\n            },\n            {\n              \"ch\": 1,\n              \"decoder\": \"6-in-1\",\n              \"flags\": \"0x00\",\n              \"id\": \"0x22400873\",\n              \"rssi\": -52,\n              \"type\": \"Pool / Spa Thermometer\"\n            },\n            {\n              \"ch\": 0,\n              \"decoder\": \"6-in-1\",\n              \"flags\": \"0x1f\",\n              \"id\": \"0x39582376\",\n              \"rssi\": -78,\n              \"type\": \"Weather Sensor\"\n            },\n            {\n              \"ch\": 0,\n              \"decoder\": \"Lightning\",\n              \"flags\": \"0x00\",\n              \"id\": \"0x0000eefb\",\n              \"rssi\": -107,\n              \"type\": \"Lightning Sensor\"\n            },\n            {\n              \"ch\": 1,\n              \"decoder\": \"6-in-1\",\n              \"flags\": \"0x00\",\n              \"id\": \"0x67566300\",\n              \"rssi\": -96,\n              \"type\": \"Soil Temperature and Moisture Sensor\"\n            },\n            {\n              \"ch\": 3,\n              \"decoder\": \"Leakage\",\n              \"flags\": \"0x00\",\n              \"id\": \"0x28966796\",\n              \"rssi\": -106,\n              \"type\": \"Water Leakage Sensor\"\n            }\n          ]\n```\n\nThis allows the following actions:\n* `rssi`: Improvement of reception\n* `type` \u0026 `ch`: Identification of sensors\n* `type`, `ch`, `flags`: Payload configuration (`CMD_SET_APP_PAYLOAD_CFG`)\n* `id`: Configuration of reception filters (include or exclude list; `CMD_SET_SENSORS_INC`/`CMD_SET_SENSORS_EXC`)\n* `decoder`: Optimization of execution time by disabling unused decoders (`CMD_SET_SENSORS_CFG`)\n* No. of available sensors: Optimization of execution time by adjusting `max_sensors` (`CMD_SET_SENSORS_CFG`)\n\n## Loading LoRaWAN Network Service Credentials from File\n\n\u003e [!NOTE]\n\u003e To simplify deployment of a larger number of devices, LoRaWAN credentials can be read from a JSON file. This allows to use the same source code and binary file for a fleet of devices.\n\n\nIf a valid file `secrets.json` exists on LittleFS, the settings defined at compile time (in `secrets.h`) are overridden.\n\nModify the example [data/secrets.json](data/secrets.json) as required and install it to the board's Flash memory using [earlephilhower/arduino-littlefs-upload](https://github.com/earlephilhower/arduino-littlefs-upload).\n\n\u003e [!WARNING]\n\u003e Only very basic validation of the file `secrets.json` is implemented \u0026mdash; check the debug output.\n\n## Loading LoRaWAN Node Configuration from File\n\n\u003e [!NOTE]\n\u003e To simplify deployment of a larger number of devices, LoRaWAN node configuration parameters can be read from a JSON file. These parameters are used for hardware or deployment environment specific settings. This allows to use the same source code and binary file for a fleet of devices.\n\nIf a valid file `node_config.json` exists on LittleFS, the default settings defined at compile time (in `BresserWeatherSensorCfg.h`) are overridden.\nIf a parameter cannot be read from the file, its default value will be used.\n\nThe following parameters are available:\n\n| Parameter             | Description | Default Value |\n| --------------------- | ----------------------------------------------------------------------- | -------------:|\n| timezone              | Time Zone\u003cbr\u003esee [Time Zone Abbreviations](https://remotemonitoringsystems.ca/time-zone-abbreviations.php) | `\"CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00\"` |\n| voltage_eco_exit      | Voltage threshold in mV for leaving eco mode\u003cbr\u003e(long sleep interval)  | `3580` |\n| voltage_eco_enter     | Voltage threshold in mV for entering eco mode\u003cbr\u003e(long sleep interval)  | `3500` |\n| voltage_critical      | Voltage threshold in mV for deep-discharge protection\u003cbr\u003e(power off)    | `3200` |\n| battery_discharge_lim | Discharging voltage limit in mV\u003cbr\u003efor battery level estimation         | `3200` |\n| battery_charge_lim    | Charging voltage limit in mV\u003cbr\u003efor battery level estimation            | `4200` |\n| powerfeather/         | PowerFeather specific (see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)) |   |\n| \u0026nbsp;\u0026nbsp;battery_capacity      | Battery capacity in mAh\u003cbr\u003e(`0`: no battery connected)\u003cbr\u003esee [PowerFeather Docs: `init()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-inituint16_t-capacity--0-batterytype-type--batterytypegeneric_3v7) | `0`    |\n| \u0026nbsp;\u0026nbsp;supply_maintain_voltage | see [PowerFeather Docs: `setSupplyMaintainVoltage()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-setsupplymaintainvoltageuint16_t-voltage)\u003cbr\u003e`0`: not set | `0` |\n| \u0026nbsp;\u0026nbsp;max_charge_current | see [PowerFeather Docs: `setBatteryChargingMaxCurrent()`](https://docs.powerfeather.dev/sdk/api/mainboard##result-setbatterychargingmaxcurrentuint16_t-current) | `50` |\n| \u0026nbsp;\u0026nbsp;soc_eco_exit  | SOC threshold in % for leaving eco mode\u003cbr\u003e(long sleep interval)  | `20` |\n| \u0026nbsp;\u0026nbsp;soc_eco_enter | SOC threshold in % for entering eco mode\u003cbr\u003e(long sleep interval) | `25` |\n| \u0026nbsp;\u0026nbsp;soc_critical  | SOC threshold in % for deep-discharge protection\u003cbr\u003e(power off) | `5` |\n| \u0026nbsp;\u0026nbsp;temperature_measurement | see [PowerFeather Docs: `enableBatteryTempSense()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-enablebatterytempsensebool-enable) | `true` |\n| \u0026nbsp;\u0026nbsp;battery_fuel_gauge      | see [PowerFeather Docs: `enableBatteryFuelGauge()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-enablebatteryfuelgaugebool-enable) | `true` |\n| m5stack/         | M5Stack specific (see [https://docs.m5stack.com/en/arduino/m5unified/power_class](https://docs.m5stack.com/en/arduino/m5unified/power_class)) |   |\n| \u0026nbsp;\u0026nbsp;soc_eco_exit  | SOC threshold in % for leaving eco mode\u003cbr\u003e(long sleep interval)  | `20` |\n| \u0026nbsp;\u0026nbsp;soc_eco_enter | SOC threshold in % for entering eco mode\u003cbr\u003e(long sleep interval) | `25` |\n| \u0026nbsp;\u0026nbsp;soc_critical  | SOC threshold in % for deep-discharge protection\u003cbr\u003e(power off) | `5` |\n\nModify the example [data/node_config.json](data/node_config.json) as required and install it to the board's Flash memory using [earlephilhower/arduino-littlefs-upload](https://github.com/earlephilhower/arduino-littlefs-upload).\n\n\u003e [!WARNING]\n\u003e No validation of the file `node_config.json` is implemented \u0026mdash; check the debug output.\n\n## Payload Configuration\n\n### Default Configuration\n\nThe default payload configuration is as follows:\n\n| Sensor                    | Signal                          | Unit  | Type        | Bytes |\n| ------------------------- | ------------------------------- | ----- | ----------- | ----- |\n| **Bresser Sensors**                                                                       |\n| Weather                   | Temperature                     | °C    | temperature |     2 |\n| Weather                   | Humidity                        | %     | uint8       |     1 |\n| Weather                   | Rain Gauge                      | mm    | rawfloat    |     4 |\n| Weather                   | Wind Speed (Gusts)              | m/s   | uint16fp1   |     2 |\n| Weather                   | Wind Speed (Avg)                | m/s   | uint16fp1   |     2 |\n| Weather                   | Wind Direction                  | °     | uint16fp1   |     2 |\n| Weather                   | UV Index                        | -     | uint8fp1    |     1 |\n| Weather                   | Post-processed: Hourly Rain     | mm    | rawfloat    |     4 |\n| Weather                   | Post-processed: Daily Rain      | mm    | rawfloat    |     4 |\n| Weather                   | Post-processed: Weekly Rain     | mm    | rawfloat    |     4 |\n| Weather                   | Post-processed: Monthly Rain    | mm    | rawfloat    |     4 |\n| Temperature/Humidity      | Temperature                     | °C    | temperature |     2 |\n| Temperature/Humidity      | Humidity                        | %     | uint8       |     1 |\n| Soil Moisture/Temperature | Temperature                     | °C    | temperature |     2 |\n| Soil Moisture/Temperature | Moisture                        | %     | uint8       |     1 |\n| Lightning                 | Post-processed: Event timestamp | epoch | unixtime    |     4 |\n| Lightning                 | Post-processed: No. of events   | -     | uint16      |     2 |\n| Lightning                 | Post-processed: Storm distance  | km    | uint8       |     1 |\n| **1-Wire Sensors**                                                                        |\n| Temperature               | Temperature                     | °C    | temperature |     2 |\n| **Analog Interface**                                                                      |\n| Ch 00                     | Battery voltage                 | mV    | uint16      |     2 |\n| **Digital Interface**                                                                     |\n| \u0026mdash; none \u0026mdash;                                                                      |\n| **BLE Sensors**                                                                           |\n| Temperature/Humidity      | Temperature                     | °C    | temperature |     2 |\n| Temperature/Humidity      | Humidity                        | %     | uint8       |     1 |\n\nThe data types are implemented in [lora-serialization](https://github.com/thesolarnomad/lora-serialization) and the [Payload Formatters](#lorawan-payload-formatters). `uint16fp1` and `uint8fp1` are extensions in the payload formatter for fixed-point numbers with 1 decimal.\n\nThe default sensor data uplink configuration is defined in https://github.com/matthias-bs/BresserWeatherSensorLW/blob/cb918c6f17e2ef4f6e3f01d1cbb4b6a2c4e21089/BresserWeatherSensorLWCfg.h#L313 as a set of byte values, which are used in https://github.com/matthias-bs/BresserWeatherSensorLW/blob/cb918c6f17e2ef4f6e3f01d1cbb4b6a2c4e21089/src/AppLayer.h#L71 to define the array `appPayloadCfgDef[APP_PAYLOAD_CFG_SIZE]`. This array is used as a large bitmap, where each byte represents a specific sensor or interface and each bit corresponds to a channel or feature.\n\n### Config Helper\n\nChanging the configuration by setting bitmaps is not really comfortable. Therefore the Config Helper has been created.\n\n![config_helper_20240725](https://github.com/user-attachments/assets/e69ed1e3-4de6-42ea-85e1-f4edc0d58dc7)\n\n[Config Helper](https://matthias-bs.github.io/BresserWeatherSensorLW/confighelper.html)\n\nIn the Config Helper, you select the desired sensors/interfaces and the used channels/features and generate\n1. A bitmap to change the default payload configuration in the C++ source code\n2. A JSON string to configure the node via LoRaWAN downlink with the command [CMD_SET_APP_PAYLOAD_CFG](#using-the-javascript-uplinkdownlink-formatters)\n3. A JSON string to configure the [Uplink Payload Formatter](#lorawan-payload-formatters)\n\n\u003e [!NOTE]\n\u003e You do not have to modify the source code if you apply the configuration via LoRaWAN downlink!\n\n## Customizing the Application Layer\n\nBy replacing the Application Layer with your own code, you can use this project as a starting point for your own purpose.\n\nUse [extras/customization/AppLayerMinimal.h](extras/customization/AppLayerMinimal.h) and [extras/customization/AppLayerMinimal.cpp](extras/customization/AppLayerMinimal.cpp) as a template.\n\n### AppLayer Programming Interface\n\n#### Constructor\n\nIn [BresserWeatherSensorLW.ino](blob/main/BresserWeatherSensorLW.ino), the `appLayer` object is created:\n```\n/// Application layer\nAppLayer appLayer(\u0026sysCtx);\n```\n\nThe following constructor must be implemented by the AppLayer class:\n```\n/*!\n * \\brief Constructor\n *\n * \\param sysCtx System Context object\n */\nAppLayer(SystemContext* sysCtx);\n```\n#### begin()\n\n`appLayer.begin()` is called in [BresserWeatherSensorLW.ino: setup()](blob/main/BresserWeatherSensorLW.ino) shortly after getting the RTC time. It can be used for any initialization which cannot be done in the constructor.\nA typical use case would be initialization of sensors which need a certain time to 'warm up' or acquire data. Other sensors/circuits should be started at the latest possible stage to save energy.\n\n```\n/*!\n * \\brief AppLayer initialization\n */\nvoid begin(void);\n```\n\n#### getPayloadStage1() and getPayloadStage2()\n\nBoth functions provide the sensor data as uplink message payload to the LoRaWAN network layer. The parameter `port` can be used to distinguish between different kinds of messages.\n\nUsing the `LoraEncoder` object from [lora-serialization](https://github.com/thesolarnomad/lora-serialization) allows to encode common C++ data types as a sequence of bytes for transmission via LoRaWAN. Since the maximum permitted message payload size is very limited, the encoding must use as few bytes as possible.\n\ngetPayload\n\n```\n/*!\n * \\brief Prepare / get payload at startup\n *\n * Use this if\n * - A sensor needs some time for warm-up or data acquisition\n * - The data acquisition has to be done directly after startup\n * - The radio transceiver is used for sensor communication\n *   before starting LoRaWAN activities\n *\n * \\param port LoRaWAN port\n * \\param encoder uplink encoder object\n */\nvoid getPayloadStage1(uint8_t \u0026port, LoraEncoder \u0026encoder);\n\n/*!\n * \\brief Get payload before uplink\n *\n * Use this if\n * - The radio transceiver is NOT used for sensor communication\n * - The sensor preparation has been started in stage1\n * - The data aquistion has to be done immediately before uplink\n *\n * \\param port LoRaWAN port\n * \\param encoder uplink encoder object\n */\nvoid getPayloadStage2(uint8_t \u0026port, LoraEncoder \u0026encoder);\n```\n\n#### decodeDownlink()\n\nIf `node.sendReceive()` provided a downlink message, the LoRaWAN network layer tries to decode it. If this fails \u0026mdash; because according to `port`, it is not directed at the network layer \u0026mdash; the message is passed to the ApplicationLayer via `appLayer.decodeDownlink()`.\n\n```\n/*!\n * \\brief Decode app layer specific downlink messages\n *\n * \\param port downlink message port\n * \\param payload downlink message payload\n * \\param size payload size in bytes\n *\n * \\returns config uplink request or 0\n */\nuint8_t decodeDownlink(uint8_t port, uint8_t *payload, size_t size);\n```\n\n#### getConfigPayload()\n\nA non-zero return value of `decodeDownlink()` triggers execution of `getConfigPayload()`. `getConfigPayload()` passes the uplink message payload and the required uplink port to the LoRaWAN network layer.\n```\n/*!\n * \\brief Get configuration data for uplink\n *\n * Get the configuration data requested in a downlink command and\n * prepare it as payload in an uplink response.\n *\n * \\param cmd command\n * \\param port uplink port\n * \\param encoder uplink data encoder object\n */\nvoid getConfigPayload(uint8_t cmd, uint8_t \u0026port, LoraEncoder \u0026encoder);\n```\n\n#### getAppStatusUplinkInterval()\n\nIf implemented, status messages originating from the AppLayer can be sent as uplink periodically. The return value of `getAppStatusUplinkInterval()` is used by the LoRaWAN network layer to decide when such a message is due.\n\n```\n/*!\n * \\brief Get sensor status message uplink interval\n *\n * \\returns status uplink interval in frame counts (0: disabled)\n */\nuint8_t getAppStatusUplinkInterval(void);\n```\n    \n## Implementation\n\n### Class Diagram\n\n```mermaid\nclassDiagram\n    class BresserWeatherSensorLW {\n        /* BresserWeatherSensorLW.ino */\n        +LORA_CHIP radio\n        -Preferences store // LoRaWAN nonces\n        -AppLayer appLayer\n        -SystemContext sysCtx\n        -appStatusUplinkPending\n        -lwStatusUplinkPending\n        -LWsession[]\n        -setup()\n        -loop()\n        -decodeDownlink()\n        -sendCfgUplink()\n    }\n    BresserWeatherSensorLW \u003c-- AppLayer\n    BresserWeatherSensorLW \u003c-- SystemContext\n    AppLayer --\u003e SystemContext\n\n    class SystemContext {\n        +begin()\n        +isFirstBoot()\n        +resetFailedJoinCount()\n        +sleepAfterFailedJoin()\n        +setTime()\n        +printDateTime()\n        +savePreferences()\n        +getVoltages()\n        +sleepIfSupplyLow()\n        +getBattlevel()\n        +sleepInterval()\n        +longSleepActive()\n        +sleepDuration()\n        +uplinkDelay()\n        +gotoSleep()\n        +getRtcTimeSource()\n        +isRtcSynched()\n        +rtcNeedsSync()\n        +sleep_interval\n        +sleep_interval_long\n        +lw_stat_interval\n        -voltage_eco_exit\n        -voltage_eco_enter\n        -voltage_critical\n        -battery_discharge_lim\n        -battery_charge_lim\n        -batteryVoltage\n        -supplyVoltage\n        -mcuVoltage\n    }\n\n    class AppLayer {\n        -Preferences appPrefs\n        -appPayloadCfg[]\n        -appStatus[]\n        +begin()\n        +decodeDownlink()\n        +genPayload()\n        +getPayloadStage1()\n        +getPayloadStage2()\n        +getConfigPayload()\n        +getAppStatusUplinkInterval()\n        +setAppPayloadCfg()\n        +getAppPayloadCfg()\n    }\n    AppLayer \u003c|-- PayloadBresser\n    AppLayer \u003c|-- PayloadOneWire\n    AppLayer \u003c|-- PayloadAnalog\n    AppLayer \u003c|-- PayloadDigital\n    AppLayer \u003c|-- PayloadBLE\n\n    class PayloadBresser{\n        +WeatherSensor weatherSensor\n        +RainGauge rainGauge\n        -Lightning lightningProc\n        -Preferences appPrefs\n        +begin()\n        +encodeBresser()\n        -encodeWeatherSensor()\n        -encodeThermoHygroSensor()\n        -encodePoolThermometer()\n        -encodeSoilSensor()\n        -encodeLeakageSensor()\n        -encodeAirPmSensor()\n        -encodeLightningSensor()\n        -encodeCo2Sensor()\n        -encodeHchoVocSensor()\n        -isSpaceLeft()\n    }\n    class PayloadOneWire{\n        +getOneWireTemperature()\n        +encodeOneWire()\n    }\n    class PayloadAnalog{\n        +begin()\n        +encodeAnalog()\n    }\n    class PayloadDigital {\n        +begin()\n        +encodeDigital()\n    }\n    class PayloadBLE {\n        -Preferences appPrefs\n        -BleSensors bleSensors\n        +begin()\n        +setBleAddr()\n        +getBLEAddr()\n        +bleAddrInit()\n        +encodeBLE()\n    }\n```\n\u003c!-- ![Class Diagram](https://www.mermaidchart.com/raw/c78b97b9-ecd9-4fc6-a6fc-bba82e0facd7?theme=light\u0026version=v0.1\u0026format=svg) --\u003e\n\n\n## Doxygen Generated Source Code Documentation\n\n[https://matthias-bs.github.io/BresserWeatherSensorWL/index.html](https://matthias-bs.github.io/BresserWeatherSensorLW/)\n\n## References\n\nBased on\n* [BresserWeatherSensorReceiver](https://github.com/matthias-bs/BresserWeatherSensorReceiver) by Matthias Prinke\n* [RadioLib](https://github.com/jgromes/RadioLib) by Jan Gromeš\n* [Lora-Serialization](https://github.com/thesolarnomad/lora-serialization) by Joscha Feth\n* [OneWireNg](https://github.com/pstolarz/OneWireNg) by Piotr Stolarz\n* [DallasTemperature / Arduino-Temperature-Control-Library](https://github.com/milesburton/Arduino-Temperature-Control-Library) by Miles Burton\n* [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) by h2zero\n* [Theengs Decoder](https://github.com/theengs/decoder) by [Theengs Project](https://github.com/theengs)\n* [DistanceSensor_A02YYUW](https://github.com/pportelaf/DistanceSensor_A02YYUW) by Pablo Portela\n* [Preferences](https://github.com/vshymanskyy/Preferences) by Volodymyr Shymanskyy\n* [RTClib](https://github.com/adafruit/RTClib) by Adafruit \n\n## Legal\n\n\u003e This project is in no way affiliated with, authorized, maintained, sponsored or endorsed by Bresser GmbH or any of its affiliates or subsidiaries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthias-bs%2Fbresserweathersensorlw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthias-bs%2Fbresserweathersensorlw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthias-bs%2Fbresserweathersensorlw/lists"}