I have stored the uuids of multiple peripherals in my central and am able to switch between devices and read information from them. It happens in a round robin manner (I have used a while loop)
it first connects to one device, reads data from it, disconnects then goes to the next device.
What I am trying to establish is. Have one central device and establish a constant connection between multiple peripherals and whenever the characteristic of any of the peripherals gets updated the central should be notified and should read that data. Now I came across BLECharacteristic.subscribe() which notifies whenever the value is updated.
Where I am running into an issue is. I am unable to find a way to do this without using a while loop. A while loop causes my logic to get stuck and so it cannot connect to the other devices. even if I remove the while loop from the central, I cannot for the life of me, make sense of how to do the same for the peripheral device.
Is there a way in which a peripheral will just keep doing its own thing, inform the central whenever its data gets updated, the central gets notified and reads the data. (I still need the security, hence I am not using broadcast)
For a better understanding lets give an example
Peripheral 1:
it has a integer characteristic that switches its value between 0 and 1 every 200 ms
Peripheral 2:
it has an integer characteristic that switches its value between 2 and 3 every 500 ms
So I want my central to display the following in the serial monitor
0
1
2
0
1
...
To achieve the desired functionality without using a while loop, you can utilize event-driven programming and the event notification mechanism provided by the BLE library
That sounds great, I have not encountered any documentation or tutorial regarding that so far. Can you point me in the right direction. Also can I establish what I am trying to establish with a while loop?
I was able to establish a constant connection between 1 peripheral and 1 central and retrieve the data whenever the peripheral gets updated.
Peripheral code ->
#include <ArduinoBLE.h>
BLEService batteryService("180F");
BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID
BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
int oldBatteryLevel = 0; // last battery level reading from analog input
long previousMillis = 0; // last time the battery level was checked, in ms
int batteryLevel = 1;
void setup() {
Serial.begin(9600); // initialize serial communication
// while (!Serial);
pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
BLE.setLocalName("BatteryMonitor");
BLE.setAdvertisedService(batteryService); // add the service UUID
batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
BLE.addService(batteryService); // Add the battery service
batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic
BLE.advertise();
Serial.println("Bluetooth® device active, waiting for connections...");
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
digitalWrite(LED_BUILTIN, HIGH);
while (central.connected()) {
long currentMillis = millis();
if (currentMillis - previousMillis >= 2000) {
previousMillis = currentMillis;
updateBatteryLevel();
}
}
digitalWrite(LED_BUILTIN, LOW);
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}
}
void updateBatteryLevel() {
if(batteryLevel == 0){
batteryLevel = 1;
}else{
batteryLevel = 0;
}
Serial.print("Battery Level % is now: "); // print it
Serial.println(batteryLevel);
batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic
}
Central code ->
#include <ArduinoBLE.h>
void setup() {
Serial.begin(9600);
while (!Serial);
// begin initialization
if (!BLE.begin()) {
Serial.println("starting Bluetooth® Low Energy module failed!");
while (1);
}
Serial.println("Bluetooth® Low Energy Central - Peripheral Explorer");
BLE.scan();
}
void loop() {
// check if a peripheral has been discovered
BLE.scan();
BLEDevice peripheral = BLE.available();
if (peripheral) {
// discovered a peripheral, print out address, local name, and advertised service
Serial.print("Found ");
Serial.print(peripheral.localName());
Serial.println();
if (peripheral.localName() == "BatteryMonitor") {
BLE.stopScan();
if (peripheral.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
Serial.println("Discovering attributes ...");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
return;
}
BLEService service = peripheral.service("180F");
BLECharacteristic characteristic = service.characteristic("2a19");
if (characteristic.canRead()) {
characteristic.subscribe();
}
while(peripheral.connected()){
characteristic.read();
if(characteristic.valueUpdated()){
if (characteristic.valueLength() >= 0) {
Serial.print("value ");
printData(characteristic.value(), characteristic.valueLength());
Serial.println();
}
}
}
Serial.println("Disconnecting ...");
peripheral.disconnect();
Serial.println("Disconnected");
}
}
}
void printData(const unsigned char data[], int length) {
for (int i = 0; i < length; i++) {
unsigned char b = data[i];
if (b < 16) {
Serial.print("0");
}
Serial.print(b, HEX);
}
}
As the central code as a while loop, I am unable to read the data for the second peripheral.
Indeed that is the idea I was going with, I did not mention but I thought for the multiples of 200 and 500, Like every time 1000 occurs I will put an if condition and print 4. But That is where I am having the issue. I am unable to connect two peripherals simultaneously.
My current Idea is subscribe to both the peripherals using the central device then
while(1){
//code to check if any of them gets updated and print the value
}
Yep, print 4 when it is 1000ms, Or will you recommend I go with something else.
Currently the code that I shared establishes a connection between 1 peripheral with 1 central. And this is a constant connection. I.e the central stay connected to the peripheral. And everytime the value of the peripheral is updated (it changes its own state) it advertises a notification stating that its value has been changed
As the central is subscribed to that peripheral. It gets informed about the changed. And using .valueUpdated() checks if the value has been updated. And if it has been updated. It is to print the new data using the printData function
Now I want to establish something similar with 2 peripherals and 1 central device. A constant connection between both the peripherals and the central device. And each time the value of any of the peripheral gets updated. I want the central device to print it.
wow, thanks a lot. I understand it. And great simulation, it will come a lot in handy. But actually I was using 200 and 500 ms as arbitrary examples and they can be switched for anything really 125, 550 etc. I was having issues establishing a constant connection between multiple Peripherals and a central. I have changed my central code a bit and now am able to connect to two different BLEs and read data from both of them
central code
#include <ArduinoBLE.h>
void setup() {
Serial.begin(9600);
while (!Serial)
;
// begin initialization
if (!BLE.begin()) {
Serial.println("starting Bluetooth® Low Energy module failed!");
while (1)
;
}
Serial.println("Bluetooth® Low Energy Central - peripheral1 Explorer");
BLE.scan();
}
void loop() {
// check if a peripheral1 has been discovered
char arr[3][30] = { "180F", "181F" };
BLE.scanForUuid(arr[0]);
BLEDevice peripheral1 = BLE.available();
if (peripheral1.localName() == "BatteryMonitor") {
// BLE.stopScan();
if (peripheral1.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
Serial.println("Discovering attributes ...");
if (peripheral1.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral1.disconnect();
return;
}
}
BLEService service1 = peripheral1.service("180F");
BLECharacteristic characteristic1 = service1.characteristic("2a19");
if (characteristic1.canRead()) {
characteristic1.subscribe();
}
BLE.scanForUuid(arr[1]);
BLEDevice peripheral2 = BLE.available();
if (peripheral2.localName() == "BatteryMonitor") {
// BLE.stopScan();
if (peripheral2.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
Serial.println("Discovering attributes ...");
if (peripheral2.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral1.disconnect();
return;
}
}
BLEService service2 = peripheral2.service("181F");
BLECharacteristic characteristic2 = service2.characteristic("2a19");
if (characteristic2.canRead()) {
characteristic2.subscribe();
}
// BLE.stopScan();
while (peripheral1.connected() && peripheral2.connected()) {
characteristic1.read();
characteristic2.read();
if (characteristic1.valueUpdated()) {
if (characteristic1.valueLength() >= 0) {
Serial.print("value ");
printData(characteristic1.value(), characteristic1.valueLength());
Serial.println();
}
}
if (characteristic2.valueUpdated()) {
if (characteristic2.valueLength() >= 0) {
Serial.print("value ");
printData(characteristic2.value(), characteristic2.valueLength());
Serial.println();
}
}
}
peripheral1.disconnect(); // if we do not add this we face random disconnection
peripheral2.disconnect();
}
void printData(const unsigned char data[], int length) {
for (int i = 0; i < length; i++) {
unsigned char b = data[i];
if (b < 16) {
Serial.print("0");
}
Serial.print(b, HEX);
}
}
But the connection time is still extremely unstable, I believe it's because I am not doing BLE.stopScan() after a BLE has been discovered and it's trying to establish a connection. But so far this has been the only way I have been able to connect to multiple BLEs at the same time.
That is true, But this is the case for only some time. After a while like 5 min or so. there is some random loss and the pattern changes for a while, and then it comes back to this.
Yeh, it does not seem to follow the simulation. I believe it has something to do with my code.