Skip to content

Commit 6b09726

Browse files
authored
Merge pull request #43 from matthias-bs/20251013-fix-ble-scan
Updated from BresserWeatherSensorLW, added abort of scanning by touch sensor
2 parents d3a5f11 + 70a3445 commit 6b09726

2 files changed

Lines changed: 156 additions & 50 deletions

File tree

src/BleSensors/BleSensors.cpp

Lines changed: 121 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
///////////////////////////////////////////////////////////////////////////////
2-
// BleSensors.h
2+
// BleSensors.cpp
33
//
44
// Wrapper class for Theeengs Decoder (https://github.com/theengs/decoder)
55
//
@@ -11,7 +11,7 @@
1111
//
1212
// MIT License
1313
//
14-
// Copyright (c) 2023 Matthias Prinke
14+
// Copyright (c) 2025 Matthias Prinke
1515
//
1616
// Permission is hereby granted, free of charge, to any person obtaining a copy
1717
// of this software and associated documentation files (the "Software"), to deal
@@ -35,8 +35,14 @@
3535
// History:
3636
//
3737
// 20230211 Created
38-
// 20240427 Added paramter activeScan to getData()
38+
// 20240427 Added parameter activeScan to getData()
3939
// 20250121 Updated for NimBLE-Arduino v2.x
40+
// 20250725 Fixed potential buffer overflow
41+
// 20250728 Fixed naming collision with ATC_MiThermometer
42+
// 20250808 Added specific logging macros in scan callback to avoid WDT reset
43+
// 20250926 Moved decoding from callback to getData() to prevent WDT reset
44+
// Modified getData() to return number of known sensors found
45+
// 20251013 Added abort of scanning by touch sensor
4046
//
4147
// ToDo:
4248
// -
@@ -47,43 +53,52 @@
4753

4854
#include "BleSensors.h"
4955

56+
extern bool TouchTriggered(void);
57+
5058
namespace BleSensorsCallbacks
5159
{
60+
constexpr size_t JSON_SERIALIZATION_BUFFER_SIZE = 256; //!< Buffer size for JSON serialization
5261

62+
/*!
63+
* \brief BLE scan callback class
64+
*/
5365
class ScanCallbacks : public NimBLEScanCallbacks
5466
{
5567
public:
56-
std::vector<std::string> m_knownBLEAddresses; /// MAC addresses of known sensors
57-
std::vector<ble_sensors_t> *m_sensorData; /// Sensor data
58-
NimBLEScan *m_pBLEScan;
68+
std::vector<std::string> m_knownBLEAddresses; //!< MAC addresses of known sensors
69+
std::vector<ble_sensors_t> *m_sensorData; //!< Sensor data
70+
NimBLEScan *m_pBLEScan; //!< Pointer to the BLE scan object
5971

60-
private:
61-
int m_devices_found = 0; /// Number of known devices found
72+
// Raw JSON payloads collected during scan for later decoding.
73+
std::vector<std::string> m_rawBLEJsons;
74+
std::vector<int> m_rawIndexes; //!< corresponding known sensor index
6275

76+
int m_devices_found = 0; //!< Number of known devices found
77+
78+
private:
6379
void onDiscovered(const NimBLEAdvertisedDevice *advertisedDevice) override
6480
{
65-
log_v("Discovered Advertised Device: %s", advertisedDevice->toString().c_str());
81+
cb_log_v("Discovered Advertised Device: %s", advertisedDevice->toString().c_str());
6682
}
6783

6884
void onResult(const NimBLEAdvertisedDevice *advertisedDevice) override
6985
{
70-
TheengsDecoder decoder;
71-
unsigned idx;
72-
bool device_found = false;
73-
JsonDocument doc;
86+
cb_log_v("Advertised Device Result: %s", advertisedDevice->toString().c_str());
7487

75-
log_v("Advertised Device Result: %s", advertisedDevice->toString().c_str());
88+
// Build JSON representation of advertised data
89+
JsonDocument doc;
7690
JsonObject BLEdata = doc.to<JsonObject>();
7791
String mac_adress = advertisedDevice->getAddress().toString().c_str();
7892

7993
BLEdata["id"] = (char *)mac_adress.c_str();
80-
for (idx = 0; idx < m_knownBLEAddresses.size(); idx++)
94+
95+
int found_index = -1;
96+
for (unsigned idx = 0; idx < m_knownBLEAddresses.size(); idx++)
8197
{
8298
if (mac_adress == m_knownBLEAddresses[idx].c_str())
8399
{
84-
log_v("BLE device found at index %d", idx);
85-
device_found = true;
86-
m_devices_found++;
100+
cb_log_v("BLE device known at index %u", idx);
101+
found_index = (int)idx;
87102
break;
88103
}
89104
}
@@ -111,42 +126,37 @@ namespace BleSensorsCallbacks
111126
BLEdata["servicedatauuid"] = "0x181a";
112127
}
113128

114-
if (decoder.decodeBLEJson(BLEdata) && device_found)
129+
// If this is a known device, store the serialized JSON for decoding later
130+
if (found_index >= 0)
115131
{
116-
if (CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG)
117-
{
118-
char buf[512];
119-
serializeJson(BLEdata, buf);
120-
log_d("TheengsDecoder found device: %s", buf);
121-
}
132+
// Serialize into std::string
133+
std::string serialized;
134+
serialized.reserve(JSON_SERIALIZATION_BUFFER_SIZE);
135+
serializeJson(BLEdata, serialized);
136+
m_rawBLEJsons.push_back(std::move(serialized));
137+
m_rawIndexes.push_back(found_index);
138+
139+
m_devices_found++;
140+
cb_log_v("Known BLE device queued for decoding at index %d", found_index);
141+
}
122142

123-
// see https://stackoverflow.com/questions/5348089/passing-a-vector-between-functions-via-pointers
124-
(*m_sensorData)[idx].temperature = (float)BLEdata["tempc"];
125-
(*m_sensorData)[idx].humidity = (float)BLEdata["hum"];
126-
(*m_sensorData)[idx].batt_level = (uint8_t)BLEdata["batt"];
127-
(*m_sensorData)[idx].rssi = (int)BLEdata["rssi"];
128-
(*m_sensorData)[idx].valid = ((*m_sensorData)[idx].batt_level > 0);
129-
log_i("Temperature: %.1f°C", (*m_sensorData)[idx].temperature);
130-
log_i("Humidity: %.1f%%", (*m_sensorData)[idx].humidity);
131-
log_i("Battery level: %d%%", (*m_sensorData)[idx].batt_level);
132-
log_i("RSSI: %ddBm", (*m_sensorData)[idx].rssi = (int)BLEdata["rssi"]);
133-
log_d("BLE devices found: %d", m_devices_found);
134-
135-
BLEdata.remove("manufacturerdata");
136-
BLEdata.remove("servicedata");
143+
// Abort scanning by touch sensor
144+
if (TouchTriggered()) {
145+
log_i("Touch interrupt!");
146+
m_pBLEScan->stop();
137147
}
138148

139149
// Abort scanning because all known devices have been found
140-
if (m_devices_found == m_knownBLEAddresses.size())
150+
if (m_devices_found == (int)m_knownBLEAddresses.size())
141151
{
142-
log_i("All devices found.");
152+
cb_log_i("All devices found.");
143153
m_pBLEScan->stop();
144154
}
145155
}
146156

147157
void onScanEnd(const NimBLEScanResults &results, int reason) override
148158
{
149-
log_v("Scan Ended; reason = %d", reason);
159+
cb_log_v("Scan Ended; reason = %d", reason);
150160
}
151161
} scanCallbacks;
152162

@@ -170,6 +180,11 @@ using namespace BleSensorsCallbacks;
170180

171181
/**
172182
* \brief Get BLE sensor data
183+
*
184+
* Note: Decoding using TheengsDecoder is performed here after scanning,
185+
* instead of during the NimBLE callback, to avoid heavy processing
186+
* in the callback context, which can lead to the watchdog being
187+
* triggered.
173188
*/
174189
unsigned BleSensors::getData(uint32_t scanTime, bool activeScan)
175190
{
@@ -185,11 +200,74 @@ unsigned BleSensors::getData(uint32_t scanTime, bool activeScan)
185200
scanCallbacks.m_sensorData = &data;
186201
scanCallbacks.m_pBLEScan = _pBLEScan;
187202

203+
// Ensure previous scan data cleared
204+
scanCallbacks.m_rawBLEJsons.clear();
205+
scanCallbacks.m_rawIndexes.clear();
206+
scanCallbacks.m_devices_found = 0;
207+
188208
// Start scanning
189209
// Blocks until all known devices are found or scanTime is expired
190210
_pBLEScan->getResults(scanTime * 1000, false);
191211

192-
return 0;
212+
// Now perform decoding of the collected JSON payloads (outside callback)
213+
TheengsDecoder decoder;
214+
for (size_t i = 0; i < scanCallbacks.m_rawBLEJsons.size(); ++i)
215+
{
216+
const std::string &serialized = scanCallbacks.m_rawBLEJsons[i];
217+
int idx = scanCallbacks.m_rawIndexes[i];
218+
if (idx < 0 || idx >= (int)scanCallbacks.m_sensorData->size())
219+
continue;
220+
221+
// Parse JSON
222+
JsonDocument doc;
223+
DeserializationError err = deserializeJson(doc, serialized);
224+
if (err)
225+
{
226+
cb_log_w("Failed to parse stored advert JSON: %s", err.c_str());
227+
continue;
228+
}
229+
230+
JsonObject BLEdata = doc.as<JsonObject>();
231+
232+
if (decoder.decodeBLEJson(BLEdata))
233+
{
234+
if (CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG) {
235+
constexpr size_t DEBUG_BUFFER_SIZE = 512;
236+
char buf[DEBUG_BUFFER_SIZE];
237+
serializeJson(BLEdata, buf, sizeof(buf));
238+
cb_log_d("TheengsDecoder decoded device: %s", buf);
239+
}
240+
241+
// Update sensor data vector
242+
(*scanCallbacks.m_sensorData)[idx].temperature = (float)BLEdata["tempc"];
243+
(*scanCallbacks.m_sensorData)[idx].humidity = (float)BLEdata["hum"];
244+
(*scanCallbacks.m_sensorData)[idx].batt_level = (uint8_t)BLEdata["batt"];
245+
(*scanCallbacks.m_sensorData)[idx].rssi = (int)BLEdata["rssi"];
246+
(*scanCallbacks.m_sensorData)[idx].valid = ((*scanCallbacks.m_sensorData)[idx].batt_level > 0);
247+
248+
cb_log_i("Temperature: %.1f°C", (*scanCallbacks.m_sensorData)[idx].temperature);
249+
cb_log_i("Humidity: %.1f%%", (*scanCallbacks.m_sensorData)[idx].humidity);
250+
cb_log_i("Battery level: %d%%", (*scanCallbacks.m_sensorData)[idx].batt_level);
251+
cb_log_i("RSSI: %ddBm", (*scanCallbacks.m_sensorData)[idx].rssi);
252+
cb_log_d("BLE devices found: %d", scanCallbacks.m_devices_found);
253+
254+
// remove bulky fields from doc to keep log/output clean if needed
255+
BLEdata.remove("manufacturerdata");
256+
BLEdata.remove("servicedata");
257+
}
258+
else
259+
{
260+
cb_log_v("TheengsDecoder could not decode stored advert for index %d", idx);
261+
}
262+
}
263+
264+
// Clear stored raw adverts to free memory
265+
scanCallbacks.m_rawBLEJsons.clear();
266+
scanCallbacks.m_rawIndexes.clear();
267+
unsigned devices_found = scanCallbacks.m_devices_found;
268+
scanCallbacks.m_devices_found = 0;
269+
270+
return devices_found;
193271
}
194272

195273
#endif // !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) && !defined(ARDUINO_ARCH_RP2040)

src/BleSensors/BleSensors.h

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//
1212
// MIT License
1313
//
14-
// Copyright (c) 2023 Matthias Prinke
14+
// Copyright (c) 2025 Matthias Prinke
1515
//
1616
// Permission is hereby granted, free of charge, to any person obtaining a copy
1717
// of this software and associated documentation files (the "Software"), to deal
@@ -36,14 +36,20 @@
3636
//
3737
// 20230211 Created
3838
// 20240417 Added additional constructor and method setAddresses()
39-
// 20240427 Added paramter activeScan to getData()
39+
// 20240427 Added parameter activeScan to getData()
4040
// 20250121 Updated for NimBLE-Arduino v2.x
41+
// 20250808 Added specific logging macros in scan callback to avoid WDT reset
42+
// 20250926 Changed getData() to return number of known sensors found
4143
//
4244
// ToDo:
43-
// -
45+
// -
4446
//
4547
///////////////////////////////////////////////////////////////////////////////
4648

49+
/*! \file BleSensors.h
50+
* \brief Wrapper class for Theeengs Decoder (https://github.com/theengs/decoder)
51+
*/
52+
4753
#if !defined(BLE_SENSORS) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) \
4854
&& !defined(ARDUINO_ARCH_RP2040)
4955
#define BLE_SENSORS
@@ -52,6 +58,26 @@
5258
#include <NimBLEDevice.h> //!< https://github.com/h2zero/NimBLE-Arduino
5359
#include <decoder.h> //!< https://github.com/theengs/decoder
5460

61+
// Extensive logging in the callback may lead to a watchdog reset
62+
// see
63+
// https://github.com/h2zero/NimBLE-Arduino/issues/329
64+
// https://github.com/h2zero/NimBLE-Arduino/issues/351
65+
66+
//#define CB_LOGGING
67+
#if defined(CB_LOGGING)
68+
#define cb_log_i log_i //!< Info
69+
#define cb_log_w log_w //!< Warn
70+
#define cb_log_d log_d //!< Debug
71+
#define cb_log_e log_e //!< Error
72+
#define cb_log_v log_v //!< Verbose
73+
#else
74+
#define cb_log_i {} //!< Info
75+
#define cb_log_w {} //!< Warn
76+
#define cb_log_d {} //!< Debug
77+
#define cb_log_e {} //!< Error
78+
#define cb_log_v {} //!< Verbose
79+
#endif
80+
5581
/*!
5682
* \brief BLE sensor data
5783
*/
@@ -113,21 +139,23 @@ class BleSensors {
113139
*
114140
* \param duration Scan duration in seconds
115141
* \param activeScan 0: passive scan / 1: active scan
116-
*/
142+
*
143+
* \return Number of known sensors found (max. size of known_sensors vector)
144+
*/
117145
unsigned getData(uint32_t duration, bool activeScan = true);
118146

119147
/*!
120148
* \brief Set sensor data invalid.
121-
*/
149+
*/
122150
void resetData(void);
123151

124152
/*!
125153
* \brief Sensor data.
126154
*/
127-
std::vector<ble_sensors_t> data;
155+
std::vector<ble_sensors_t> data;
128156

129157
protected:
130158
std::vector<std::string> _known_sensors; /// MAC addresses of known sensors
131159
NimBLEScan* _pBLEScan; /// NimBLEScan object
132160
};
133-
#endif // !defined(BLE_SENSORS) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) && !defined(ARDUINO_ARCH_RP2040)
161+
#endif

0 commit comments

Comments
 (0)