diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 012d4f5..d9654cc 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -76,7 +76,7 @@ jobs: #declare -a required_libs=("https://github.com/matthias-bs/BresserWeatherSensorReceiver.git" declare -a required_libs=( "RadioLib@6.5.0" - "BresserWeatherSensorReceiver@0.25.0" + "BresserWeatherSensorReceiver@0.27.0" "LoRa Serialization@3.2.1" "ESP32Time@2.0.6" "OneWireNg@0.13.3" diff --git a/BresserWeatherSensorLW.ino b/BresserWeatherSensorLW.ino index da364c9..a7549dc 100644 --- a/BresserWeatherSensorLW.ino +++ b/BresserWeatherSensorLW.ino @@ -28,7 +28,7 @@ // RadioLib 6.5.0 // LoRa_Serialization 3.2.1 // ESP32Time 2.0.6 -// BresserWeatherSensorReceiver 0.25.0 +// BresserWeatherSensorReceiver 0.26.0 // OneWireNg 0.13.1 (optional) // DallasTemperature 3.9.0 (optional) // NimBLE-Arduino 1.4.1 (optional) @@ -465,13 +465,15 @@ void setup() #endif #if defined(ARDUINO_ESP32S3_POWERFEATHER) - Board.init(); - Board.enable3V3(true); + delay(2000); + Board.init(); // Note: Battery capacity / type has to be set for voltage measurement + Board.enable3V3(true); // Power supply for FeatherWing + Board.enableVSQT(true); // Power supply for battery management chip (voltage measurement) #endif Serial.begin(115200); delay(2000); // give time to switch to the serial monitor - log_i("\nSetup"); + log_i("Setup"); #if defined(ARDUINO_ARCH_RP2040) // see pico-sdk/src/rp2_common/hardware_rtc/rtc.c diff --git a/BresserWeatherSensorLWCfg.h b/BresserWeatherSensorLWCfg.h index 3729c56..80b9af8 100644 --- a/BresserWeatherSensorLWCfg.h +++ b/BresserWeatherSensorLWCfg.h @@ -54,8 +54,8 @@ #if !defined(_LWCFG_H) #define _LWCFG_H -#include -#include +//#include +//#include // Downlink messages // ------------------ @@ -207,7 +207,8 @@ #if !defined(ARDUINO_TTGO_LoRa32_V1) && !defined(ARDUINO_TTGO_LoRa32_V2) && \ !defined(ARDUINO_TTGO_LoRa32_v21new) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) && \ !defined(ARDUINO_FEATHER_ESP32) && !defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) && \ - !defined(ARDUINO_M5STACK_Core2) && !defined(ARDUINO_M5STACK_CORE2) + !defined(ARDUINO_M5STACK_Core2) && !defined(ARDUINO_M5STACK_CORE2) && \ + !defined(ARDUINO_ESP32S3_POWERFEATHER) // Use pinning for LoRaWAN Node #define LORAWAN_NODE diff --git a/README.md b/README.md index 550f39d..ce52e5a 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,10 @@ This project is in early stage of development - stay tuned. | :white_check_mark: | [Heltec WiFi LoRa 32 V3](https://heltec.org/project/wifi-lora-32-v3/) | Heltec WiFi LoRa 32(V3) | heltec_wifi_32_lora_V3 | SX1262 | - | | :white_check_mark: | [LoRaWAN_Node](https://github.com/matthias-bs/LoRaWAN_Node) | FireBeetle-ESP32 | ESP32_DEV -> LORAWAN_NODE | SX1276 (RFM95W) | - | | :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 | ESP32_DEV & FIREBEETLE_ESP32_COVER_LORA | SX1276 (LoRa1276) | Wiring on the cover:
D2 to RESET
D3 to DIO0
D4 to CS
D5 to DIO1 | - | :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 | ADAFRUIT_FEATHER_ESP32S2 | SX1276 (RFM95W) | **No Bluetooth available!**
Wiring on the Featherwing:
E to IRQ
D to CS
C to RST
A to DI01 | + | :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!**
Wiring on the Featherwing:
E to IRQ
D to CS
C to RST
A to DI01 | | :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) | Adafruit ESP32 Feather | FEATHER_ESP32 | SX1276 (RFM95W) | Wiring on the Featherwing:
E to IRQ
D to CS
C to RST
A to DI01

**see** [**#55**](https://github.com/matthias-bs/BresserWeatherSensorTTN/issues/55) | | :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
(RA-01H) | Wiring on the LoRa868 Module:
DIO1 to GPIO35

"M5Unified" must be installed
`M5.begin()`is called to control power management | + | :hourglass: | [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:
E to IRQ
D to CS
C to RST
A to DI01

"PowerFeather-SDK" must be installed
`Board.init();` is called to control power management | | :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!**
**Configuration: Choose an entry with "FS" in section __Flash Size__!**
Wiring on the Featherwing:
E to IRQ
D to CS
C to RST
A to DI01 | :hourglass: — confirmation pending diff --git a/config.h b/config.h index bddc7b5..7df2f47 100644 --- a/config.h +++ b/config.h @@ -117,7 +117,6 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 #define PIN_LORA_IRQ 16 #define PIN_LORA_GPIO 18 #define PIN_LORA_DIO2 RADIOLIB_NC - #pragma message("NOT TESTED!!!") #pragma message("ARDUINO_ESP32S3_POWERFEATHER defined; assuming RFM95W FeatherWing will be used") #pragma message("Required wiring: A to RST, B to DIO1, D to DIO0, E to CS") #define LORA_CHIP SX1276 diff --git a/package.json b/package.json index 129b3b9..7af110c 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/matthias-bs/BresserWeatherSensorLW#README", "dependencies": { - "BresserWeatherSensorReceiver": "matthias-bs/BresserWeatherSensorReceiver#semver:^0.26.0", + "BresserWeatherSensorReceiver": "matthias-bs/BresserWeatherSensorReceiver#semver:^0.27.0", "RadioLib": "jgromes/RadioLib#semver:^6.5.0", "lora-serialization": "thesolarnomad/lora-serialization#semver:^3.2.1", "ESP32Time": "fbiego/ESP32Time#semver:^2.0.6", diff --git a/src/AppLayer.cpp b/src/AppLayer.cpp index c7741c8..0ebccdc 100644 --- a/src/AppLayer.cpp +++ b/src/AppLayer.cpp @@ -36,6 +36,8 @@ // 20240402 Created // 20240413 Refactored ADC handling // 20240414 Added separation between LoRaWAN and application layer +// 20240417 Added sensor configuration functions +// // // ToDo: // - @@ -44,7 +46,6 @@ #include "AppLayer.h" - uint8_t AppLayer::decodeDownlink(uint8_t *payload, size_t size) { @@ -73,6 +74,64 @@ AppLayer::decodeDownlink(uint8_t *payload, size_t size) appPrefs.putUChar("ws_timeout", payload[1]); appPrefs.end(); } + else if ((payload[0] == CMD_GET_SENSORS_INC) && (size == 1)) + { + log_d("Get sensors include list"); + return CMD_GET_SENSORS_INC; + } + else if ((payload[0] == CMD_SET_SENSORS_INC) && ((size - 1) % 4 == 0)) + { + log_d("Set sensors include list"); + for (size_t i = 0; i < size - 1; i += 4) + { + log_d("%08X:", + (payload[i] << 24) | + (payload[i + 1] << 16) | + (payload[i + 2] << 8) | + payload[i + 3]); + } + weatherSensor.setSensorsInc(&payload[1], size - 1); + } + else if ((payload[0] == CMD_GET_SENSORS_EXC) && (size == 1)) + { + log_d("Get sensors exclude list"); + return CMD_GET_SENSORS_EXC; + } + else if ((payload[0] == CMD_SET_SENSORS_EXC) && ((size - 1) % 4 == 0)) + { + log_d("Set sensors exclude list"); + for (size_t i = 0; i < size - 1; i += 4) + { + log_d("%08X:", + (payload[i] << 24) | + (payload[i + 1] << 16) | + (payload[i + 2] << 8) | + payload[i + 3]); + } + weatherSensor.setSensorsExc(&payload[1], size - 1); + } + else if ((payload[0] == CMD_GET_SENSORS_EXC) && (size == 1)) + { + log_d("Get BLE sensors MAC addresses"); + return CMD_GET_BLE_ADDR; + } + #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) + else if ((payload[0] == CMD_SET_BLE_ADDR) && ((size - 1) % 6 == 0)) + { + log_d("Set BLE sensors MAC addresses"); + for (size_t i = 0; i < size - 1; i += 6) + { + log_d("%02X:%02X:%02X:%02X:%02X:%02X", + payload[i], + payload[i + 1], + payload[i + 2], + payload[i + 3], + payload[i + 4], + payload[i + 5]); + } + setBleAddr(&payload[1], size - 1); + } + #endif return 0; } @@ -80,6 +139,7 @@ void AppLayer::genPayload(uint8_t port, LoraEncoder &encoder) { // unused (void)port; + (void)encoder; weatherSensor.genMessage(0, 0xfff0, SENSOR_TYPE_WEATHER1); weatherSensor.genMessage(1, 0xfff1, SENSOR_TYPE_SOIL); } @@ -116,380 +176,376 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder) log_i("Waiting for Weather Sensor Data; timeout %u s", ws_timeout); bool decode_ok = weatherSensor.getData(ws_timeout * 1000, DATA_ALL_SLOTS); + (void)decode_ok; + log_i("Receiving Weather Sensor Data %s", decode_ok ? "o.k." : "failed"); - if (decode_ok) - { - log_v("Port: %d", port); - log_i("Receiving Weather Sensor Data o.k."); + log_v("Port: %d", port); #ifdef RAINDATA_EN - // Check if time is valid - if (*rtcLastClockSync > 0) + // Check if time is valid + if (*_rtcLastClockSync > 0) + { + // Get local date and time + struct tm timeinfo; + time_t tnow = _rtc->getLocalEpoch(); + localtime_r(&tnow, &timeinfo); + + // Find weather sensor and determine rain gauge overflow limit + // Try to find SENSOR_TYPE_WEATHER0 + int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); + if (ws > -1) { - // Get local date and time - struct tm timeinfo; - time_t tnow = rtc->getLocalEpoch(); - localtime_r(&tnow, &timeinfo); - - // Find weather sensor and determine rain gauge overflow limit - // Try to find SENSOR_TYPE_WEATHER0 - int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); - if (ws > -1) - { - rainGauge.set_max(1000); - } - else - { - // Try to find SENSOR_TYPE_WEATHER1 - ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - rainGauge.set_max(100000); - } - - // If weather sensor has be found and rain data is valid, update statistics - if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) - { - rainGauge.update(tnow, weatherSensor.sensor[ws].w.rain_mm, weatherSensor.sensor[ws].startup); - } + rainGauge.set_max(1000); } + else + { + // Try to find SENSOR_TYPE_WEATHER1 + ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); + rainGauge.set_max(100000); + } + + // If weather sensor has be found and rain data is valid, update statistics + if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) + { + rainGauge.update(tnow, weatherSensor.sensor[ws].w.rain_mm, weatherSensor.sensor[ws].startup); + } + } #endif #ifdef LIGHTNINGSENSOR_EN - // Check if time is valid - if (*rtcLastClockSync > 0) - { - // Get local date and time - time_t tnow = rtc->getLocalEpoch(); + // Check if time is valid + if (*_rtcLastClockSync > 0) + { + // Get local date and time + time_t tnow = _rtc->getLocalEpoch(); - // Find lightning sensor - int ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); + // Find lightning sensor + int ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); - // If lightning sensor has be found and data is valid, run post-processing - if ((ls > -1) && weatherSensor.sensor[ls].valid) - { - lightningProc.update(tnow, weatherSensor.sensor[ls].lgt.strike_count, weatherSensor.sensor[ls].lgt.distance_km, weatherSensor.sensor[ls].startup); - } + // If lightning sensor has be found and data is valid, run post-processing + if ((ls > -1) && weatherSensor.sensor[ls].valid) + { + lightningProc.update(tnow, weatherSensor.sensor[ls].lgt.strike_count, weatherSensor.sensor[ls].lgt.distance_km, weatherSensor.sensor[ls].startup); } + } #endif - // - // Find Bresser sensor data in array - // + // + // Find Bresser sensor data in array + // - // Try to find SENSOR_TYPE_WEATHER0 - int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); - if (ws < 0) - { - // Try to find SENSOR_TYPE_WEATHER1 - ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); - } + // Try to find SENSOR_TYPE_WEATHER0 + int ws = weatherSensor.findType(SENSOR_TYPE_WEATHER0); + if (ws < 0) + { + // Try to find SENSOR_TYPE_WEATHER1 + ws = weatherSensor.findType(SENSOR_TYPE_WEATHER1); + } - int s1 = -1; + int s1 = -1; #ifdef SOILSENSOR_EN - // Try to find SENSOR_TYPE_SOIL - s1 = weatherSensor.findType(SENSOR_TYPE_SOIL, 1); + // Try to find SENSOR_TYPE_SOIL + s1 = weatherSensor.findType(SENSOR_TYPE_SOIL, 1); #endif - int ls = -1; + int ls = -1; #ifdef LIGHTNINGSENSOR_EN - // Try to find SENSOR_TYPE_LIGHTNING - ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); + // Try to find SENSOR_TYPE_LIGHTNING + ls = weatherSensor.findType(SENSOR_TYPE_LIGHTNING); #endif - log_i("--- Uplink Data ---"); + log_i("--- Uplink Data ---"); - // Debug output for weather sensor data - if (ws > -1) + // Debug output for weather sensor data + if (ws > -1) + { + if (weatherSensor.sensor[ws].w.temp_ok) { - if (weatherSensor.sensor[ws].w.temp_ok) - { - log_i("Air Temperature: %3.1f °C", weatherSensor.sensor[ws].w.temp_c); - } - else - { - log_i("Air Temperature: --.- °C"); - } - if (weatherSensor.sensor[ws].w.humidity_ok) - { - log_i("Humidity: %2d %%", weatherSensor.sensor[ws].w.humidity); - } - else - { - log_i("Humidity: -- %%"); - } - if (weatherSensor.sensor[ws].w.rain_ok) - { - log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[ws].w.rain_mm); - } - else - { - log_i("Rain Gauge: ---.- mm"); - } - log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1 / 10.0); - log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1 / 10.0); - log_i("Wind Direction: %4.1f °", weatherSensor.sensor[ws].w.wind_direction_deg_fp1 / 10.0); + log_i("Air Temperature: %3.1f °C", weatherSensor.sensor[ws].w.temp_c); } else { - log_i("-- Weather Sensor Failure"); + log_i("Air Temperature: --.- °C"); } - -// Debug output for soil sensor data -#ifdef SOILSENSOR_EN - if (s1 > -1) + if (weatherSensor.sensor[ws].w.humidity_ok) { - log_i("Soil Temperature 1: %3.1f °C", weatherSensor.sensor[s1].soil.temp_c); - log_i("Soil Moisture 1: %2d %%", weatherSensor.sensor[s1].soil.moisture); + log_i("Humidity: %2d %%", weatherSensor.sensor[ws].w.humidity); } else { - log_i("-- Soil Sensor 1 Failure"); + log_i("Humidity: -- %%"); } -#endif - -// Debug output for lightning sensor data -#ifdef LIGHTNINGSENSOR_EN - if (ls > -1) + if (weatherSensor.sensor[ws].w.rain_ok) { - log_i("Lightning counter: %4d", weatherSensor.sensor[ls].lgt.strike_count); - log_i("Lightning distance: %2d km", weatherSensor.sensor[ls].lgt.distance_km); + log_i("Rain Gauge: %7.1f mm", weatherSensor.sensor[ws].w.rain_mm); } else { - log_i("-- Lightning Sensor Failure"); + log_i("Rain Gauge: ---.- mm"); } - if (lightningProc.lastEvent(lightn_ts, lightn_events, lightn_distance)) - { + log_i("Wind Speed (avg.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1 / 10.0); + log_i("Wind Speed (max.): %3.1f m/s", weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1 / 10.0); + log_i("Wind Direction: %4.1f °", weatherSensor.sensor[ws].w.wind_direction_deg_fp1 / 10.0); + } + else + { + log_i("-- Weather Sensor Failure"); + } + +// Debug output for soil sensor data +#ifdef SOILSENSOR_EN + if (s1 > -1) + { + log_i("Soil Temperature 1: %3.1f °C", weatherSensor.sensor[s1].soil.temp_c); + log_i("Soil Moisture 1: %2d %%", weatherSensor.sensor[s1].soil.moisture); + } + else + { + log_i("-- Soil Sensor 1 Failure"); + } +#endif + +// Debug output for lightning sensor data +#ifdef LIGHTNINGSENSOR_EN + if (ls > -1) + { + log_i("Lightning counter: %4d", weatherSensor.sensor[ls].lgt.strike_count); + log_i("Lightning distance: %2d km", weatherSensor.sensor[ls].lgt.distance_km); + } + else + { + log_i("-- Lightning Sensor Failure"); + } + if (lightningProc.lastEvent(lightn_ts, lightn_events, lightn_distance)) + { #if CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - struct tm timeinfo; - char tbuf[25]; + struct tm timeinfo; + char tbuf[25]; - localtime_r(&lightn_ts, &timeinfo); - strftime(tbuf, 25, "%Y-%m-%d %H:%M:%S", &timeinfo); + localtime_r(&lightn_ts, &timeinfo); + strftime(tbuf, 25, "%Y-%m-%d %H:%M:%S", &timeinfo); #endif - log_i("Last lightning event @%s: %d events, %d km", tbuf, lightn_events, lightn_distance); - } - else - { - log_i("-- No Lightning Event Data Available"); - } + log_i("Last lightning event @%s: %d events, %d km", tbuf, lightn_events, lightn_distance); + } + else + { + log_i("-- No Lightning Event Data Available"); + } #endif #ifdef ONEWIRE_EN - // Debug output for auxiliary sensors/voltages - if (water_temp_c != DEVICE_DISCONNECTED_C) - { - log_i("Water Temperature: % 2.1f °C", water_temp_c); - } - else - { - log_i("Water Temperature: --.- °C"); - water_temp_c = -30.0; - } + // Debug output for auxiliary sensors/voltages + if (water_temp_c != DEVICE_DISCONNECTED_C) + { + log_i("Water Temperature: % 2.1f °C", water_temp_c); + } + else + { + log_i("Water Temperature: --.- °C"); + water_temp_c = -30.0; + } #endif #ifdef DISTANCESENSOR_EN - if (distance_mm > 0) - { - log_i("Distance: %4d mm", distance_mm); - } - else - { - log_i("Distance: ---- mm"); - } + if (distance_mm > 0) + { + log_i("Distance: %4d mm", distance_mm); + } + else + { + log_i("Distance: ---- mm"); + } #endif #ifdef PIN_SUPPLY_IN - log_i("Supply Voltage: %4d mV", supply_voltage); + log_i("Supply Voltage: %4d mV", supply_voltage); #endif #if defined(ADC_EN) && defined(PIN_ADC_IN) - log_i("Battery Voltage: %4d mV", battery_voltage); + log_i("Battery Voltage: %4d mV", battery_voltage); #endif #if defined(MITHERMOMETER_EN) - float div = 100.0; + float div = 100.0; #elif defined(THEENGSDECODER_EN) - float div = 1.0; + float div = 1.0; #endif #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) - if (bleSensors.data[0].valid) + if (bleSensors.data[0].valid) + { + mithermometer_valid = true; + indoor_temp_c = bleSensors.data[0].temperature / div; + indoor_humidity = bleSensors.data[0].humidity / div; + log_i("Indoor Air Temp.: % 3.1f °C", bleSensors.data[0].temperature / div); + log_i("Indoor Humidity: %3.1f %%", bleSensors.data[0].humidity / div); + } + else + { + log_i("Indoor Air Temp.: --.- °C"); + log_i("Indoor Humidity: -- %%"); + indoor_temp_c = -30; + indoor_humidity = 0; + } +#endif + log_v("-"); + +#ifdef SENSORID_EN + if (ws > -1) + { + encoder.writeUint32(weatherSensor.sensor[ws].sensor_id); + } + else + { + encoder.writeUint32(0); + } +#endif + + // Sensor status flags + encoder.writeBitmap(0, + mithermometer_valid, + (ls > -1) ? weatherSensor.sensor[ls].valid : false, + (ls > -1) ? weatherSensor.sensor[ls].battery_ok : false, + (s1 > -1) ? weatherSensor.sensor[s1].valid : false, + (s1 > -1) ? weatherSensor.sensor[s1].battery_ok : false, + (ws > -1) ? weatherSensor.sensor[ws].valid : false, + (ws > -1) ? weatherSensor.sensor[ws].battery_ok : false); + + // Weather sensor data + if (ws > -1) + { + // weather sensor data available + if (weatherSensor.sensor[ws].w.temp_ok) { - mithermometer_valid = true; - indoor_temp_c = bleSensors.data[0].temperature / div; - indoor_humidity = bleSensors.data[0].humidity / div; - log_i("Indoor Air Temp.: % 3.1f °C", bleSensors.data[0].temperature / div); - log_i("Indoor Humidity: %3.1f %%", bleSensors.data[0].humidity / div); + encoder.writeTemperature(weatherSensor.sensor[ws].w.temp_c); } else { - log_i("Indoor Air Temp.: --.- °C"); - log_i("Indoor Humidity: -- %%"); - indoor_temp_c = -30; - indoor_humidity = 0; + encoder.writeTemperature(-30); } -#endif - log_v("-"); - -#ifdef SENSORID_EN - if (ws > -1) + if (weatherSensor.sensor[ws].w.humidity_ok) { - encoder.writeUint32(weatherSensor.sensor[ws].sensor_id); + encoder.writeUint8(weatherSensor.sensor[ws].w.humidity); } else { - encoder.writeUint32(0); + encoder.writeUint8(0); } -#endif - - // Sensor status flags - encoder.writeBitmap(0, - mithermometer_valid, - (ls > -1) ? weatherSensor.sensor[ls].valid : false, - (ls > -1) ? weatherSensor.sensor[ls].battery_ok : false, - (s1 > -1) ? weatherSensor.sensor[s1].valid : false, - (s1 > -1) ? weatherSensor.sensor[s1].battery_ok : false, - (ws > -1) ? weatherSensor.sensor[ws].valid : false, - (ws > -1) ? weatherSensor.sensor[ws].battery_ok : false); - - // Weather sensor data - if (ws > -1) - { - // weather sensor data available - if (weatherSensor.sensor[ws].w.temp_ok) - { - encoder.writeTemperature(weatherSensor.sensor[ws].w.temp_c); - } - else - { - encoder.writeTemperature(-30); - } - if (weatherSensor.sensor[ws].w.humidity_ok) - { - encoder.writeUint8(weatherSensor.sensor[ws].w.humidity); - } - else - { - encoder.writeUint8(0); - } #ifdef ENCODE_AS_FLOAT - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_gust_meter_sec); - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_avg_meter_sec); - encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_direction_deg); + encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_gust_meter_sec); + encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_avg_meter_sec); + encoder.writeRawFloat(weatherSensor.sensor[ws].w.wind_direction_deg); #else - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1); - encoder.writeUint16(weatherSensor.sensor[ws].w.wind_direction_deg_fp1); + encoder.writeUint16(weatherSensor.sensor[ws].w.wind_gust_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[ws].w.wind_avg_meter_sec_fp1); + encoder.writeUint16(weatherSensor.sensor[ws].w.wind_direction_deg_fp1); #endif - if (weatherSensor.sensor[ws].w.rain_ok) - { - encoder.writeRawFloat(weatherSensor.sensor[ws].w.rain_mm); - } - else - { - encoder.writeRawFloat(0); - } + if (weatherSensor.sensor[ws].w.rain_ok) + { + encoder.writeRawFloat(weatherSensor.sensor[ws].w.rain_mm); } else { - // fill with suspicious dummy values - encoder.writeTemperature(-30); - encoder.writeUint8(0); -#ifdef ENCODE_AS_FLOAT - encoder.writeRawFloat(0); - encoder.writeRawFloat(0); encoder.writeRawFloat(0); + } + } + else + { + // fill with suspicious dummy values + encoder.writeTemperature(-30); + encoder.writeUint8(0); +#ifdef ENCODE_AS_FLOAT + encoder.writeRawFloat(0); + encoder.writeRawFloat(0); + encoder.writeRawFloat(0); #else - encoder.writeUint16(0); - encoder.writeUint16(0); - encoder.writeUint16(0); + encoder.writeUint16(0); + encoder.writeUint16(0); + encoder.writeUint16(0); #endif - encoder.writeRawFloat(0); - } + encoder.writeRawFloat(0); + } // Voltages / auxiliary sensor data #ifdef PIN_SUPPLY_IN - encoder.writeUint16(supply_voltage); + encoder.writeUint16(supply_voltage); #endif #if defined(PIN_ADC_IN) - encoder.writeUint16(battery_voltage); + encoder.writeUint16(battery_voltage); #endif #ifdef ONEWIRE_EN - encoder.writeTemperature(water_temp_c); + encoder.writeTemperature(water_temp_c); #endif #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) - encoder.writeTemperature(indoor_temp_c); - encoder.writeUint8((uint8_t)(indoor_humidity + 0.5)); + encoder.writeTemperature(indoor_temp_c); + encoder.writeUint8((uint8_t)(indoor_humidity + 0.5)); - // BLE Tempoerature/Humidity Sensor: delete results fromBLEScan buffer to release memory - bleSensors.clearScanResults(); + // BLE Tempoerature/Humidity Sensor: delete results fromBLEScan buffer to release memory + bleSensors.clearScanResults(); #endif // Soil sensor data #ifdef SOILSENSOR_EN - if (s1 > -1) - { - // soil sensor data available - encoder.writeTemperature(weatherSensor.sensor[s1].soil.temp_c); - encoder.writeUint8(weatherSensor.sensor[s1].soil.moisture); - } - else - { - // fill with suspicious dummy values - encoder.writeTemperature(-30); - encoder.writeUint8(0); - } + if (s1 > -1) + { + // soil sensor data available + encoder.writeTemperature(weatherSensor.sensor[s1].soil.temp_c); + encoder.writeUint8(weatherSensor.sensor[s1].soil.moisture); + } + else + { + // fill with suspicious dummy values + encoder.writeTemperature(-30); + encoder.writeUint8(0); + } #endif // Rain data statistics #ifdef RAINDATA_EN - if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) - { - log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); - log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); - log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); - log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); - encoder.writeRawFloat(rainGauge.pastHour()); - encoder.writeRawFloat(rainGauge.currentDay()); - encoder.writeRawFloat(rainGauge.currentWeek()); - encoder.writeRawFloat(rainGauge.currentMonth()); - } - else - { - log_i("Current rain gauge statistics not valid."); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - encoder.writeRawFloat(-1); - } + if ((ws > -1) && weatherSensor.sensor[ws].valid && weatherSensor.sensor[ws].w.rain_ok) + { + log_i("Rain past 60min: %7.1f mm", rainGauge.pastHour()); + log_i("Rain curr. day: %7.1f mm", rainGauge.currentDay()); + log_i("Rain curr. week: %7.1f mm", rainGauge.currentWeek()); + log_i("Rain curr. month: %7.1f mm", rainGauge.currentMonth()); + encoder.writeRawFloat(rainGauge.pastHour()); + encoder.writeRawFloat(rainGauge.currentDay()); + encoder.writeRawFloat(rainGauge.currentWeek()); + encoder.writeRawFloat(rainGauge.currentMonth()); + } + else + { + log_i("Current rain gauge statistics not valid."); + encoder.writeRawFloat(-1); + encoder.writeRawFloat(-1); + encoder.writeRawFloat(-1); + encoder.writeRawFloat(-1); + } #endif // Distance sensor data #ifdef DISTANCESENSOR_EN - encoder.writeUint16(distance_mm); + encoder.writeUint16(distance_mm); #endif // Lightning sensor data #ifdef LIGHTNINGSENSOR_EN - if (ls > -1) - { - // Lightning sensor data available - encoder.writeUnixtime(lightn_ts); - encoder.writeUint16(lightn_events); - encoder.writeUint8(lightn_distance); - } - else - { - // Fill with suspicious dummy values - encoder.writeUnixtime(0); - encoder.writeUint16(0); - encoder.writeUint8(0); - } -#endif + if (ls > -1) + { + // Lightning sensor data available + encoder.writeUnixtime(lightn_ts); + encoder.writeUint16(lightn_events); + encoder.writeUint8(lightn_distance); } else { - log_i("Receiving Weather Sensor Data failed."); + // Fill with suspicious dummy values + encoder.writeUnixtime(0); + encoder.writeUint16(0); + encoder.writeUint8(0); } +#endif } void AppLayer::getPayloadStage2(uint8_t port, LoraEncoder &encoder) { + (void)port; + (void)encoder; } void AppLayer::getConfigPayload(uint8_t cmd, uint8_t &port, LoraEncoder &encoder) @@ -502,4 +558,76 @@ void AppLayer::getConfigPayload(uint8_t cmd, uint8_t &port, LoraEncoder &encoder encoder.writeUint8(ws_timeout); port = CMD_GET_WS_TIMEOUT; } -} \ No newline at end of file + else if (cmd == CMD_GET_SENSORS_INC) + { + uint8_t payload[48]; + uint8_t size = weatherSensor.getSensorsInc(payload); + for (size_t i = 0; i < min(size, static_cast(48)); i++) + { + encoder.writeUint8(payload[i]); + } + } + else if (cmd == CMD_GET_SENSORS_EXC) + { + uint8_t payload[48]; + uint8_t size = weatherSensor.getSensorsExc(payload); + for (size_t i = 0; i < min(size, static_cast(48)); i++) + { + encoder.writeUint8(payload[i]); + } + } + #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) + else if (cmd == CMD_GET_BLE_ADDR) + { + uint8_t payload[48]; + uint8_t size = getBleAddr(payload); + for (size_t i = 0; i < min(size, static_cast(48)); i++) + { + encoder.writeUint8(payload[i]); + } + } + #endif +} + +#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) +void AppLayer::setBleAddr(uint8_t *bytes, uint8_t size) +{ + appPrefs.begin("BWS-LW-APP", false); + appPrefs.putBytes("ble", bytes, size); + appPrefs.end(); + + std::vector bleAddr; + for (size_t i = 0; i < size; i += 6) + { + bleAddr.push_back(BLEAddress(&bytes[i]).toString()); + } + bleSensors.setAddresses(bleAddr); +} + +uint8_t AppLayer::getBleAddr(uint8_t *payload) +{ + appPrefs.begin("BWS-LW-APP", false); + uint8_t size = appPrefs.getBytesLength("ble"); + appPrefs.getBytes("ble", payload, size); + appPrefs.end(); + + return size; +} + +std::vector AppLayer::getBleAddr(void) +{ + std::vector bleAddr; + + appPrefs.begin("BWS-LW-APP", false); + uint8_t size = appPrefs.getBytesLength("ble"); + char addrBytes[48]; + appPrefs.getBytes("ble", addrBytes, size); + for (size_t i = 0; i < size; i += 6) + { + bleAddr.push_back(BLEAddress(&addrBytes[i]).toString()); + } + appPrefs.end(); + + return bleAddr; +} +#endif \ No newline at end of file diff --git a/src/AppLayer.h b/src/AppLayer.h index b458517..326c617 100644 --- a/src/AppLayer.h +++ b/src/AppLayer.h @@ -33,6 +33,7 @@ // // 20240402 Created // 20240414 Added separation between LoRaWAN and application layer +// 20240417 Added sensor configuration functions // // ToDo: // - @@ -75,8 +76,8 @@ class AppLayer { private: - ESP32Time *rtc; - time_t *rtcLastClockSync; + ESP32Time *_rtc; + time_t *_rtcLastClockSync; /// Preferences (stored in flash memory) Preferences appPrefs; @@ -85,6 +86,7 @@ class AppLayer WeatherSensor weatherSensor; #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) + std::vector knownBLEAddressesDef; std::vector knownBLEAddresses; #endif @@ -124,16 +126,29 @@ class AppLayer public: #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) - AppLayer(ESP32Time *rtc, time_t *clocksync) : bleSensors(KNOWN_BLE_ADDRESSES) + //AppLayer(ESP32Time *rtc, time_t *clocksync) : bleSensors(KNOWN_BLE_ADDRESSES) + AppLayer(ESP32Time *rtc, time_t *clocksync) : bleSensors() #else AppLayer(ESP32Time *rtc, time_t *clocksync) #endif { - rtc = rtc; - rtcLastClockSync = clocksync; + _rtc = rtc; + _rtcLastClockSync = clocksync; #if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) - knownBLEAddresses = KNOWN_BLE_ADDRESSES; + knownBLEAddressesDef = KNOWN_BLE_ADDRESSES; + knownBLEAddresses = getBleAddr(); + if (knownBLEAddresses.size() == 0) { + // No addresses stored in Preferences, use default + knownBLEAddresses = knownBLEAddressesDef; + } + bleSensors = BleSensors(knownBLEAddresses); + log_v("BLE Addresses:"); + for(const std::string& s : knownBLEAddresses) + { + (void)s; + log_v("%s", s.c_str()); + } #endif }; @@ -182,7 +197,42 @@ class AppLayer */ void getPayloadStage2(uint8_t port, LoraEncoder &encoder); + /*! + * Get configuration data for uplink + * + * Get the configuration data requested in a downlink command and + * prepare it as payload in a uplink response. + * + * \param cmd command + * \param port uplink port + * \param encoder uplink data encoder object + */ void getConfigPayload(uint8_t cmd, uint8_t &port, LoraEncoder &encoder); -}; +#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN) + /*! + * Set BLE addresses in Preferences and bleSensors object + * + * \param bytes MAC addresses (6 bytes per address) + * \param size size in bytes + */ + void setBleAddr(uint8_t *bytes, uint8_t size); + + /*! + * Get BLE addresses from Preferences + * + * \param bytes buffer for addresses + * + * \returns number of bytes copied into buffer + */ + uint8_t getBleAddr(uint8_t *bytes); + + /*! + * Get BLE addresses from Preferences + * + * \returns BLE addresses + */ + std::vector getBleAddr(void); +#endif +}; #endif // _APPLAYER_H \ No newline at end of file diff --git a/src/BleSensors/BleSensors.h b/src/BleSensors/BleSensors.h index 06ab34e..848d117 100644 --- a/src/BleSensors/BleSensors.h +++ b/src/BleSensors/BleSensors.h @@ -35,6 +35,7 @@ // History: // // 20230211 Created +// 20240417 Added additional constructor and method setAddresses() // // ToDo: // - @@ -76,7 +77,19 @@ class BleSensors { _known_sensors = known_sensors; data.resize(known_sensors.size()); }; + + BleSensors(void) { + }; + /* + * \brief Set BLE MAC addresses of known sensors + * + * \param known_sensors vector of BLE MAC addresses (see constructor) + */ + void setAddresses(std::vector known_sensors) { + _known_sensors = known_sensors; + data.resize(known_sensors.size()); + }; /*! \brief Initialization. */