Skip to content

Commit

Permalink
Feat scan 868mhz sensors (#79)
Browse files Browse the repository at this point in the history
* Fixed handling of rxFlags by getData() in PayloadBesser::begin()
* Added scanning for Bresser sensors with CMD_SCAN_SENSORS
  • Loading branch information
matthias-bs authored Jul 17, 2024
1 parent 655c641 commit a15a32f
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
#declare -a required_libs=("https://github.com/matthias-bs/BresserWeatherSensorReceiver.git"
declare -a required_libs=(
"RadioLib@6.6.0"
"BresserWeatherSensorReceiver@0.28.8"
"BresserWeatherSensorReceiver@0.28.9"
"LoRa Serialization@3.2.1"
"ESP32Time@2.0.6"
"OneWireNg@0.13.3"
Expand Down
15 changes: 8 additions & 7 deletions BresserWeatherSensorLW.ino
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
// RadioLib 6.6.0
// LoRa_Serialization 3.2.1
// ESP32Time 2.0.6
// BresserWeatherSensorReceiver 0.28.2
// BresserWeatherSensorReceiver 0.28.9
// OneWireNg 0.13.1 (optional)
// DallasTemperature 3.9.0 (optional)
// NimBLE-Arduino 1.4.1 (optional)
Expand Down Expand Up @@ -91,6 +91,7 @@
// 20240606 Changed appStatusUplinkInterval from const to variable
// 20240608 Added LoRaWAN device status uplink
// 20240630 Switched to lwActivate() from radiolib-persistence/examples/LoRaWAN_ESP32
// 20240716 Modified port to allow modifications by appLayer.getPayloadStage<1|2>()
//
// ToDo:
// -
Expand Down Expand Up @@ -861,7 +862,8 @@ void setup()

LoraEncoder encoder(uplinkPayload);

appLayer.getPayloadStage1(1, encoder);
uint8_t port = 1;
appLayer.getPayloadStage1(port, encoder);

int16_t state = 0; // return value for calls to RadioLib

Expand Down Expand Up @@ -926,13 +928,9 @@ void setup()
appStatusUplinkPending = true;
}

// ----- and now for the main event -----
log_i("Sending uplink");

// get payload immediately before uplink - not used here
appLayer.getPayloadStage2(1, encoder);
appLayer.getPayloadStage2(port, encoder);

uint8_t port = 1;
uint8_t downlinkPayload[MAX_DOWNLINK_SIZE]; // Make sure this fits your plans!
size_t downlinkSize; // To hold the actual payload size rec'd
LoRaWANEvent_t uplinkDetails;
Expand All @@ -945,6 +943,9 @@ void setup()
payloadSize = PAYLOAD_SIZE;
}

// ----- and now for the main event -----
log_i("Sending uplink; port %u, size %u", port, payloadSize);

// perform an uplink & optionally receive downlink
if (fCntUp % 64 == 0)
{
Expand Down
23 changes: 22 additions & 1 deletion BresserWeatherSensorLWCmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@
// 20240608 Added CMD_GET_LW_STATUS
// 20240609 Refactored command encoding
// 20240614 Renamed CMD_RESET_RAINGAUGE to CMD_RESET_WS_POSTPROC
// 20240716 Added CMD_SCAN_SENSORS
//
// ToDo:
// -
//
///////////////////////////////////////////////////////////////////////////////

#if !defined(_LWCMD_H)
#define LWCMD_H
#define _LWCMD_H

// ===========================
// LoRaWAN command interface
Expand Down Expand Up @@ -271,6 +272,26 @@

// Uplink: n.a.

// CMD_SCAN_SENSORS
// -----------------
// Note: Scan for 868 MHz sensors
// Port: CMD_SCAN_SENSORS
#define CMD_SCAN_SENSORS 0xC4

// Downlink (command):
// byte0: ws_scantime[ 7: 0]

// Uplink (response):
// byte0: id0[31:24]
// byte1: id0[23:16]
// byte2: id0[15: 8]
// byte3: id0[ 7: 0]
// byte4: decoder0[3:0] << 4 | type0[3:0]
// byte5: ch0[7:0]
// byte6: data_flags0[7:0]
// byte7: rssi0[7:0]
// ...

// CMD_GET_SENSORS_INC
// --------------------
// Note: Get sensors include list (0...12 IDs)
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,13 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather
| <long_sleep> | 0: regular sleep interval / 1: long sleep interval (depending on U_batt) |
| \<epoch\> | Unix epoch time, see https://www.epochconverter.com/ ( \<integer\> / "0x....") |
| <reset_flags> | Raingauge reset flags; 0...15 (1: hourly / 2: daily / 4: weekly / 8: monthly) / "0x0"..."0xF" |
| <ws_scantime> | Bresser sensor scan time in seconds; 0...255 (only for CMD_SCAN_SENSORS) |
| \<idX\> | Sensor ID |
| \<decoderX\> | Matching payload decoder |
| \<typeX\> | Sensor type |
| \<chX\> | Sensor channel |
| <data_flagsX> | Sensor data flags |
| \<rssi\> | Sensor radio signal RSSI in dBm (sign inverted) |
| <rtc_source> | Real time clock source; 0x00: GPS / 0x01: RTC / 0x02: LORA / 0x03: unsynched / 0x04: set (source unknown) |
| <sensors_incX> | Bresser sensor IDs include list; e.g. "0xDEADBEEF"; "0x00000000" => empty list => default values |
| <sensors_excX> | Bresser sensor IDs include list; e.g. "0xDEADBEEF"; "0x00000000" => empty list => default values |
Expand Down Expand Up @@ -438,16 +445,17 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather
| CMD_GET_WS_TIMEOUT | 0xC0 (192) | 0x00 | ws_timeout[7:0] |
| CMD_SET_WS_TIMEOUT | 0xC1 (193) | ws_timeout[7:0] | n.a. |
| CMD_RESET_RAINGAUGE | 0xC3 (195) | flags[7:0] | n.a. |
| CMD_SCAN_SENSORS | 0xC4 (196) | ws_scantime[7:0] | id0[31:24]<br>id0[23:16]<br>id0[15:8]<br>id0[7:0]<br>decoder0[3:0]<br>type0[3:0]<br>ch0[7:0]<br>data_flags0[7:0]<br>rssi0[7:0]<br>... |
| CMD_GET_SENSORS_INC | 0xC6 (198) | 0x00 | sensors_inc0[31:24]<br>sensors_inc0[23:15]<br>sensors_inc0[16:8]<br>sensors_inc0[7:0]<br>... |
| CMD_SET_SENSORS_INC | 0xC7 (199) | sensors_inc0[31:24]<br>sensors_inc0[23:15]<br>sensors_inc0[16:8]<br>sensors_inc0[7:0]<br>... | n.a. |
| CMD_SET_SENSORS_INC | 0xC7 (199) | sensors_inc0[31:24]<br>sensors_inc0[23:16]<br>sensors_inc0[15:8]<br>sensors_inc0[7:0]<br>... | n.a. |
| CMD_GET_SENSORS_EXC | 0xC8 (200) | 0x00 | sensors_exc0[31:24]<br>sensors_exc0[23:15]<br>sensors_exc0[16:8]<br>sensors_exc0[7:0]<br>... |
| CMD_SET_SENSORS_EXC | 0xC9 (201) | sensors_exc0[31:24]<br>sensors_exc0[23:15]<br>sensors_exc0[16:8]<br>sensors_exc0[7:0]<br>... | n.a. |
| CMD_SET_SENSORS_EXC | 0xC9 (201) | sensors_exc0[31:24]<br>sensors_exc0[23:16]<br>sensors_exc0[15:8]<br>sensors_exc0[7:0]<br>... | n.a. |
| CMD_GET_SENSORS_CFG | 0xCA (202) | 0x00 | max_sensors[7:0]<br>rx_flags[7:0]<br>en_decoders<7:0> |
| CMD_SET_SENSORS_CFG | 0xCB (203) | max_sensors[7:0]<br>rx_flags[7:0]<br>en_decoders<7:0> | n.a. |
| CMD_GET_BLE_CONFIG | 0xD0 (208) | 0x00 | ble_active[7:0]<br>ble_scantime[7:0] |
| CMD_SET_BLE_CONFIG | 0xD1 (209) | ble_active[7:0]<br>ble_scantime[7:0] | n.a. |
| CMD_GET_BLE_ADDR | 0xD2 (210) | 0x00 | ble_addr0[47:40]<br>ble_addr0[39:32]<br>ble_addr0[31:24]<br>ble_addr0[23:15]<br>ble_addr0[16:8]<br>ble_addr0[7:0]<br>... |
| CMD_SET_BLE_ADDR | 0xD3 (211) | ble_addr0[47:40]<br>ble_addr0[39:32]<br>ble_addr0[31:24]<br>ble_addr0[23:15]<br>ble_addr0[16:8]<br>ble_addr0[7:0]<br>... | n.a. |
| CMD_GET_BLE_ADDR | 0xD2 (210) | 0x00 | ble_addr0[47:40]<br>ble_addr0[39:32]<br>ble_addr0[31:24]<br>ble_addr0[23:16]<br>ble_addr0[15:8]<br>ble_addr0[7:0]<br>... |
| CMD_SET_BLE_ADDR | 0xD3 (211) | ble_addr0[47:40]<br>ble_addr0[39:32]<br>ble_addr0[31:24]<br>ble_addr0[23:16]<br>ble_addr0[15:8]<br>ble_addr0[7:0]<br>... | n.a. |


#### The Things Network Examples
Expand Down Expand Up @@ -486,6 +494,7 @@ Many software parameters can be defined at compile time, i.e. in [BresserWeather
| CMD_GET_WS_TIMEOUT | {"cmd": "CMD_GET_WS_TIMEOUT"} | {"ws_timeout": <ws_timeout>} |
| CMD_SET_WS_TIMEOUT | {"ws_timeout": <ws_timeout>} | n.a. |
| CMD_RESET_RAINGAUGE | {"reset_flags": <reset_flags>} | n.a. |
| CMD_SCAN_SENSORS | {"ws_scantime": <ws_scantime>} | {"found_sensors": [{"id": \<id0\>, "decoder": \<decoder0\>, "type": \<type0\>, "ch": \<ch0\>, "data_flags": <data_flags0>, "rssi": \<rssi0\>}, ...]}
| CMD_GET_SENSORS_INC | {"cmd": "CMD_GET_SENSORS_INC"} | {"sensors_inc": [<sensors_inc0>, ..., <sensors_incN>]} |
| CMD_SET_SENSORS_INC | {"sensors_inc": [<sensors_inc0>, ..., <sensors_incN>]} | n.a. |
| CMD_GET_SENSORS_EXC | {"cmd": "CMD_GET_SENSORS_EXC"} | {"sensors_exc": [<sensors_exc0>, ..., <sensors_excN>]} |
Expand Down
15 changes: 15 additions & 0 deletions scripts/downlink_formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
// 20240608 Added CMD_GET_LW_STATUS
// 20240609 Refactored command encoding
// 20240614 Renamed CMD_RESET_RAINGAUGE to CMD_RESET_WS_POSTPROC
// 20240615 Added CMD_SCAN_SENSORS
//
// ToDo:
// -
Expand All @@ -149,6 +150,7 @@ const CMD_SET_APP_PAYLOAD_CFG = 0x47;
const CMD_GET_WS_TIMEOUT = 0xC0;
const CMD_SET_WS_TIMEOUT = 0xC1;
const CMD_RESET_WS_POSTPROC = 0xC3;
const CMD_SCAN_SENSORS = 0xC4;
const CMD_GET_SENSORS_INC = 0xC6;
const CMD_SET_SENSORS_INC = 0xC7;
const CMD_GET_SENSORS_EXC = 0xC8;
Expand Down Expand Up @@ -429,6 +431,13 @@ function encodeDownlink(input) {
warnings: [],
errors: []
};
} else if (input.data.hasOwnProperty('ws_scantime')) {
return {
bytes: [input.data.ws_scantime],
fPort: CMD_SCAN_SENSORS,
warnings: [],
errors: []
};
} else if (input.data.hasOwnProperty('status_interval')) {
return {
bytes: [input.data.status_interval],
Expand Down Expand Up @@ -616,6 +625,12 @@ function decodeDownlink(input) {
reset_flags: "0x" + uint8(input.bytes).toString(16)
}
};
case CMD_SCAN_SENSORS:
return {
data: {
ws_scantime: uint8(input.bytes)
}
};
case CMD_SET_STATUS_INTERVAL:
return {
data: {
Expand Down
58 changes: 57 additions & 1 deletion scripts/uplink_formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
// 20240610 Fixed CMD_GET_SENSORS_CFG and CMD_GET_APP_PAYLOAD_CFG,
// decode function 'bits8'
// 20240704 Fixed/improved compatibility mode
// 20240716 Added CMD_SCAN_SENSORS
//
// ToDo:
// -
Expand All @@ -161,6 +162,7 @@ function decoder(bytes, port) {
const CMD_GET_SENSORS_STAT = 0x42;
const CMD_GET_APP_PAYLOAD_CFG = 0x46;
const CMD_GET_WS_TIMEOUT = 0xC0;
const CMD_SCAN_SENSORS = 0xC4;
const CMD_GET_SENSORS_INC = 0xC6;
const CMD_GET_SENSORS_EXC = 0xC8;
const CMD_GET_SENSORS_CFG = 0xCA;
Expand All @@ -175,6 +177,33 @@ function decoder(bytes, port) {
0x04: "set (source unknown)"
};

const sensor_types = {
0: "Weather Sensor",
1: "Weather Sensor",
2: "Thermo-/Hygro-Sensor",
3: "Pool / Spa Thermometer",
4: "Soil Temperature and Moisture Sensor",
5: "Water Leakage Sensor",
6: "undefined",
7: "undefined",
8: "Air Quality Sensor (Particulate Matter)",
9: "Lightning Sensor",
10: "CO2 Sensor",
11: "Air Quality Sensor (HCHO and VOC)",
12: "undefined",
13: "undefined",
14: "undefined",
15: "undefined"
};

const sensor_decoders = {
0: "5-in-1",
1: "6-in-1",
2: "7-in-1",
3: "Lightning",
4: "Leakage"
}

var rtc_source = function (bytes) {
if (bytes.length !== rtc_source.BYTES) {
throw new Error('rtc_source must have exactly 1 byte');
Expand Down Expand Up @@ -470,6 +499,29 @@ function decoder(bytes, port) {
};
sensor_status.BYTES = 26;

function found_sensors(bytes) {
let res = [];
for (let i = 0; i < bytes.length; i += 8) {
const decoded_id = hex32(bytes.slice(i, i + 4));
const tmp = uint8(bytes.slice(i + 4, i + 5));
const decoded_type = sensor_types[tmp & 0x0F];
const decoded_decoder = sensor_decoders[tmp >> 4];
const decoded_channel = uint8(bytes.slice(i + 5, i + 6));
const decoded_flags = "0x" + byte2hex(bytes[i + 6]);
const decoded_rssi = -uint8(bytes.slice(i + 7, i + 8));
res.push({
'id': decoded_id,
'type': decoded_type,
'decoder': decoded_decoder,
'ch': decoded_channel,
'flags': decoded_flags,
'rssi': decoded_rssi
});
}
return res;
}


/**
* Decodes the given bytes using the provided mask and names.
*
Expand Down Expand Up @@ -562,6 +614,7 @@ function decoder(bytes, port) {
uint8fp1: uint8fp1,
uint16fp1: uint16fp1,
rtc_source: rtc_source,
found_sensors: found_sensors,
decode: decode
};
}
Expand Down Expand Up @@ -753,7 +806,10 @@ function decoder(bytes, port) {
['status_interval'
]
);
} else if (port === CMD_GET_SENSORS_STAT) {
} else if (port === CMD_SCAN_SENSORS) {
return {'found_sensors': found_sensors(bytes)};
}
else if (port === CMD_GET_SENSORS_STAT) {
return decode(
port,
bytes,
Expand Down
28 changes: 26 additions & 2 deletions src/AppLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
// 20240606 Added CMD_GET_STATUS_INTERVAL/CMD_SET_STATUS_INTERVAL
// 20240614 Added lightning statistics reset
// 20240701 Fixed CMD_GET_BLE_ADDR (get default if Preferences are empty/zero)
// 20240716 Added CMD_SCAN_SENSORS
//
// ToDo:
// -
Expand All @@ -79,10 +80,19 @@ void AppLayer::genPayload(uint8_t port, LoraEncoder &encoder)
weatherSensor.genMessage(1, 0xfff1, SENSOR_TYPE_SOIL);
}

void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder)
void AppLayer::getPayloadStage1(uint8_t &port, LoraEncoder &encoder)
{
(void)port; // eventually suppress warning regarding unused parameter

if (ws_scantime)
{
log_i("Scan sensors");
scanBresser(ws_scantime, encoder);
port = CMD_SCAN_SENSORS;
ws_scantime = 0;
return;
}

log_v("Port: %d", port);

log_i("--- Uplink Data ---");
Expand Down Expand Up @@ -121,7 +131,7 @@ void AppLayer::getPayloadStage1(uint8_t port, LoraEncoder &encoder)
}
}

void AppLayer::getPayloadStage2(uint8_t port, LoraEncoder &encoder)
void AppLayer::getPayloadStage2(uint8_t &port, LoraEncoder &encoder)
{
(void)port;
(void)encoder;
Expand Down Expand Up @@ -149,6 +159,20 @@ AppLayer::decodeDownlink(uint8_t port, uint8_t *payload, size_t size)
return 0;
}

if ((port == CMD_SCAN_SENSORS) && (size == 1))
{
log_d("Scan sensors - time: %u s", payload[0]);
// 1. Set flag in Preferences to trigger sensor scan and set scan time
// 2. If flag is set, perform sensors scan instead of normal operation in
// PayloadBresser::begin(void)
// 3. Reset flag after scan
// 4. Uplink scan results instead of normal sensor data
appPrefs.begin("BWS-LW-APP", false);
appPrefs.putUChar("ws_scan_t", payload[0]);
appPrefs.end();
return 0;
}

if ((port == CMD_GET_WS_TIMEOUT) && (payload[0] == 0x00) && (size == 1))
{
log_d("Get weathersensor_timeout");
Expand Down
12 changes: 10 additions & 2 deletions src/AppLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
// 20240531 Moved BLE specific code to PayloadBLE.h
// 20240603 Added appStatus[]
// 20240607 Added getAppStatusUplinkInterval()
// 20240716 Added CMD_SCAN_SENSORS
//
// ToDo:
// -
Expand Down Expand Up @@ -157,6 +158,13 @@ class AppLayer : public PayloadBresser, PayloadAnalog, PayloadDigital
{
// bleAddrInit();
PayloadBresser::begin();

// Sensor scan requested,
// no other payload encoders will be used
if (ws_scantime) {
return;
}

PayloadAnalog::begin();
PayloadDigital::begin();
#if defined(MITHERMOMETER_EN) || defined(THEENGSDECODER_EN)
Expand Down Expand Up @@ -200,7 +208,7 @@ class AppLayer : public PayloadBresser, PayloadAnalog, PayloadDigital
* \param port LoRaWAN port
* \param encoder uplink encoder object
*/
void getPayloadStage1(uint8_t port, LoraEncoder &encoder);
void getPayloadStage1(uint8_t &port, LoraEncoder &encoder);

/*!
* \brief Get payload before uplink
Expand All @@ -213,7 +221,7 @@ class AppLayer : public PayloadBresser, PayloadAnalog, PayloadDigital
* \param port LoRaWAN port
* \param encoder uplink encoder object
*/
void getPayloadStage2(uint8_t port, LoraEncoder &encoder);
void getPayloadStage2(uint8_t &port, LoraEncoder &encoder);

/*!
* \brief Get configuration data for uplink
Expand Down
2 changes: 1 addition & 1 deletion src/PayloadAnalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void PayloadAnalog::encodeAnalog(uint8_t *appPayloadCfg, LoraEncoder &encoder)
if ((ch == UBATT_CH) && (encoder.getLength() <= PAYLOAD_SIZE - 2))
{
uint16_t uBatt = getBatteryVoltage();
log_i("ch %02u: U_batt: %04u mv", ch, uBatt);
log_i("ch %02u: U_batt: %04u mv", ch, uBatt);
encoder.writeUint16(uBatt);
}

Expand Down
Loading

0 comments on commit a15a32f

Please sign in to comment.