Browse Source

feat: send battery voltage and unread count in BLE advert

pull/2387/head
Dmitry Polshakov 2 months ago
parent
commit
50d460cc72
  1. 22
      examples/companion_radio/MyMesh.cpp
  2. 2
      examples/companion_radio/MyMesh.h
  3. 3
      src/helpers/BaseSerialInterface.h
  4. 86
      src/helpers/esp32/SerialBLEInterface.cpp
  5. 29
      src/helpers/esp32/SerialBLEInterface.h

22
examples/companion_radio/MyMesh.cpp

@ -144,6 +144,8 @@
#define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM) #define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM)
#define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR) #define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR)
#define BLE_ADV_UPDATE_INTERVAL_MS 30000 // Update BLE advertising every 30 seconds
void MyMesh::writeOKFrame() { void MyMesh::writeOKFrame() {
uint8_t buf[1]; uint8_t buf[1];
buf[0] = RESP_CODE_OK; buf[0] = RESP_CODE_OK;
@ -237,6 +239,8 @@ void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) {
memcpy(offline_queue[offline_queue_len].buf, frame, len); memcpy(offline_queue[offline_queue_len].buf, frame, len);
offline_queue_len++; offline_queue_len++;
} }
updateBLEUnreadCount();
} }
int MyMesh::getFromOfflineQueue(uint8_t frame[]) { int MyMesh::getFromOfflineQueue(uint8_t frame[]) {
@ -248,11 +252,19 @@ int MyMesh::getFromOfflineQueue(uint8_t frame[]) {
for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue
offline_queue[i] = offline_queue[i + 1]; offline_queue[i] = offline_queue[i + 1];
} }
updateBLEUnreadCount();
return len; return len;
} }
return 0; // queue is empty return 0; // queue is empty
} }
void MyMesh::updateBLEUnreadCount() {
if (!_serial) return;
_serial->setUnreadCount((uint8_t)min(offline_queue_len, 255));
}
float MyMesh::getAirtimeBudgetFactor() const { float MyMesh::getAirtimeBudgetFactor() const {
return _prefs.airtime_factor; return _prefs.airtime_factor;
} }
@ -2158,6 +2170,8 @@ void MyMesh::checkSerialInterface() {
} }
void MyMesh::loop() { void MyMesh::loop() {
static unsigned long last_adv_update = 0;
BaseChatMesh::loop(); BaseChatMesh::loop();
if (_cli_rescue) { if (_cli_rescue) {
@ -2172,6 +2186,14 @@ void MyMesh::loop() {
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
} }
// Periodically update battery level in BLE advertising
if (millis() - last_adv_update >= BLE_ADV_UPDATE_INTERVAL_MS) {
if (_serial) {
_serial->setBatteryMilliVolts(board.getBattMilliVolts());
}
last_adv_update = millis();
}
#ifdef DISPLAY_CLASS #ifdef DISPLAY_CLASS
if (_ui) _ui->setHasConnection(_serial->isConnected()); if (_ui) _ui->setHasConnection(_serial->isConnected());
#endif #endif

2
examples/companion_radio/MyMesh.h

@ -98,6 +98,8 @@ public:
void loop(); void loop();
void handleCmdFrame(size_t len); void handleCmdFrame(size_t len);
bool advert(); bool advert();
void updateBLEUnreadCount();
void enterCLIRescue(); void enterCLIRescue();
int getRecentlyHeard(AdvertPath dest[], int max_num); int getRecentlyHeard(AdvertPath dest[], int max_num);

3
src/helpers/BaseSerialInterface.h

@ -18,4 +18,7 @@ public:
virtual bool isWriteBusy() const = 0; virtual bool isWriteBusy() const = 0;
virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0;
virtual size_t checkRecvFrame(uint8_t dest[]) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0;
virtual void setUnreadCount(uint8_t count) {}
virtual void setBatteryMilliVolts(uint16_t millivolts) {}
}; };

86
src/helpers/esp32/SerialBLEInterface.cpp

@ -50,7 +50,10 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
pRxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM); pRxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM);
pRxCharacteristic->setCallbacks(this); pRxCharacteristic->setCallbacks(this);
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID); // Setup advertising with manufacturer data
pAdvertising = pServer->getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
applyAdvertisingData();
} }
// -------- BLESecurityCallbacks methods // -------- BLESecurityCallbacks methods
@ -140,10 +143,10 @@ void SerialBLEInterface::enable() {
// Start advertising // Start advertising
//pServer->getAdvertising()->setMinInterval(500); //pAdvertising->setMinInterval(500);
//pServer->getAdvertising()->setMaxInterval(1000); //pAdvertising->setMaxInterval(1000);
pServer->getAdvertising()->start(); pAdvertising->start();
adv_restart_time = 0; adv_restart_time = 0;
} }
@ -152,7 +155,7 @@ void SerialBLEInterface::disable() {
BLE_DEBUG_PRINTLN("SerialBLEInterface::disable"); BLE_DEBUG_PRINTLN("SerialBLEInterface::disable");
pServer->getAdvertising()->stop(); pAdvertising->stop();
pServer->disconnect(last_conn_id); pServer->disconnect(last_conn_id);
pService->stop(); pService->stop();
oldDeviceConnected = deviceConnected = false; oldDeviceConnected = deviceConnected = false;
@ -223,8 +226,8 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
BLE_DEBUG_PRINTLN("SerialBLEInterface -> disconnecting..."); BLE_DEBUG_PRINTLN("SerialBLEInterface -> disconnecting...");
//pServer->getAdvertising()->setMinInterval(500); //pAdvertising->setMinInterval(500);
//pServer->getAdvertising()->setMaxInterval(1000); //pAdvertising->setMaxInterval(1000);
adv_restart_time = millis() + ADVERT_RESTART_DELAY; adv_restart_time = millis() + ADVERT_RESTART_DELAY;
} else { } else {
@ -232,7 +235,7 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
BLE_DEBUG_PRINTLN("SerialBLEInterface -> connecting..."); BLE_DEBUG_PRINTLN("SerialBLEInterface -> connecting...");
// connecting // connecting
// do stuff here on connecting // do stuff here on connecting
pServer->getAdvertising()->stop(); pAdvertising->stop();
adv_restart_time = 0; adv_restart_time = 0;
} }
oldDeviceConnected = deviceConnected; oldDeviceConnected = deviceConnected;
@ -241,13 +244,78 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
if (adv_restart_time && millis() >= adv_restart_time) { if (adv_restart_time && millis() >= adv_restart_time) {
if (pServer->getConnectedCount() == 0) { if (pServer->getConnectedCount() == 0) {
BLE_DEBUG_PRINTLN("SerialBLEInterface -> re-starting advertising"); BLE_DEBUG_PRINTLN("SerialBLEInterface -> re-starting advertising");
pServer->getAdvertising()->start(); // re-Start advertising pAdvertising->start(); // re-Start advertising
} }
adv_restart_time = 0; adv_restart_time = 0;
} }
// Apply pending advertising data changes when not connected
if (_advDataDirty && !deviceConnected) {
applyAdvertisingData();
_advDataDirty = false;
}
return 0; return 0;
} }
bool SerialBLEInterface::isConnected() const { bool SerialBLEInterface::isConnected() const {
return deviceConnected; //pServer != NULL && pServer->getConnectedCount() > 0; return deviceConnected; //pServer != NULL && pServer->getConnectedCount() > 0;
} }
void SerialBLEInterface::setUnreadCount(uint8_t count) {
if (_advStatus.unread_count == count) return;
_advStatus.unread_count = count;
onAdvStatusChanged();
}
void SerialBLEInterface::setBatteryMilliVolts(uint16_t millivolts) {
uint8_t encoded;
if (millivolts < 2500) {
encoded = 0;
} else if (millivolts > 5000) {
encoded = 250;
} else {
encoded = (millivolts - 2500) / 10;
}
if (_advStatus.battery_voltage == encoded) return;
_advStatus.battery_voltage = encoded;
onAdvStatusChanged();
}
void SerialBLEInterface::onAdvStatusChanged() {
if (deviceConnected) {
_advDataDirty = true;
} else if (_isEnabled) {
applyAdvertisingData();
}
}
void SerialBLEInterface::applyAdvertisingData() {
if (!pAdvertising) {
return;
}
// Build manufacturer specific data:
// Bytes 0-1: Manufacturer ID (little-endian)
// Bytes 2-3: AdvertisingStatus struct
uint8_t mfr_data[4];
mfr_data[0] = MESHCORE_MANUFACTURER_ID & 0xFF; // Manufacturer ID low byte
mfr_data[1] = (MESHCORE_MANUFACTURER_ID >> 8) & 0xFF; // Manufacturer ID high byte
mfr_data[2] = _advStatus.unread_count;
mfr_data[3] = _advStatus.battery_voltage;
// Slave Connection Interval Range (AD type 0x12)
// min=40ms (0x0020), max=80ms (0x0040)
uint8_t conn_interval[] = {0x05, 0x12, 0x20, 0x00, 0x40, 0x00};
BLEAdvertisementData advData;
advData.setFlags(ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
advData.setCompleteServices(BLEUUID(SERVICE_UUID));
advData.setManufacturerData(std::string((char*)mfr_data, sizeof(mfr_data)));
advData.addData(std::string((char*)conn_interval, sizeof(conn_interval)));
pAdvertising->setAdvertisementData(advData);
BLE_DEBUG_PRINTLN("applyAdvertisingData: unread=%d, battery_voltage=%d",
_advStatus.unread_count, _advStatus.battery_voltage);
}

29
src/helpers/esp32/SerialBLEInterface.h

@ -5,11 +5,25 @@
#include <BLEServer.h> #include <BLEServer.h>
#include <BLEUtils.h> #include <BLEUtils.h>
#include <BLE2902.h> #include <BLE2902.h>
#include <BLEAdvertising.h>
// Manufacturer ID for MeshCore (using 0xFFFF for development/testing)
// In production, you should register with Bluetooth SIG for a unique ID
#define MESHCORE_MANUFACTURER_ID 0xFFFF
// Advertising data structure (packed into manufacturer specific data)
// Byte 0: unread message count (0-255)
// Byte 1: battery voltage encoded as (mV - 2500) / 10, range 0-250 (2500-5000 mV), 0xFF = unknown
struct AdvertisingStatus {
uint8_t unread_count;
uint8_t battery_voltage;
};
class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLEServerCallbacks, BLECharacteristicCallbacks { class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLEServerCallbacks, BLECharacteristicCallbacks {
BLEServer *pServer; BLEServer *pServer;
BLEService *pService; BLEService *pService;
BLECharacteristic * pTxCharacteristic; BLECharacteristic * pTxCharacteristic;
BLEAdvertising *pAdvertising;
bool deviceConnected; bool deviceConnected;
bool oldDeviceConnected; bool oldDeviceConnected;
bool _isEnabled; bool _isEnabled;
@ -18,6 +32,10 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE
unsigned long _last_write; unsigned long _last_write;
unsigned long adv_restart_time; unsigned long adv_restart_time;
// Advertising status data
AdvertisingStatus _advStatus;
bool _advDataDirty;
struct Frame { struct Frame {
uint8_t len; uint8_t len;
uint8_t buf[MAX_FRAME_SIZE]; uint8_t buf[MAX_FRAME_SIZE];
@ -52,6 +70,7 @@ public:
SerialBLEInterface() { SerialBLEInterface() {
pServer = NULL; pServer = NULL;
pService = NULL; pService = NULL;
pAdvertising = NULL;
deviceConnected = false; deviceConnected = false;
oldDeviceConnected = false; oldDeviceConnected = false;
adv_restart_time = 0; adv_restart_time = 0;
@ -59,6 +78,9 @@ public:
_last_write = 0; _last_write = 0;
last_conn_id = 0; last_conn_id = 0;
send_queue_len = recv_queue_len = 0; send_queue_len = recv_queue_len = 0;
_advStatus.unread_count = 0;
_advStatus.battery_voltage = 0xFF; // unknown
_advDataDirty = false;
} }
/** /**
@ -79,6 +101,13 @@ public:
bool isWriteBusy() const override; bool isWriteBusy() const override;
size_t writeFrame(const uint8_t src[], size_t len) override; size_t writeFrame(const uint8_t src[], size_t len) override;
size_t checkRecvFrame(uint8_t dest[]) override; size_t checkRecvFrame(uint8_t dest[]) override;
void setUnreadCount(uint8_t count) override;
void setBatteryMilliVolts(uint16_t millivolts) override;
private:
void onAdvStatusChanged();
void applyAdvertisingData();
}; };
#if BLE_DEBUG_LOGGING && ARDUINO #if BLE_DEBUG_LOGGING && ARDUINO

Loading…
Cancel
Save