BLEArduino: 2 "Peripherals" on 1 "Centrals"

Hello

I am absolutely new to this whole world here and am trying to prototype a project.
I need 2 sensor values each from 2 Arduinos to a 3rd Arduino, which should then display this.

For this, because it also has to be small, I got myself Seeed XIAO NRF52840.

Now, with the help of a stranger (don't ask...) I tried to get the whole thing working, but to be honest I don't even know if it's the right approach.

Here is the code of the central unit:

`#include <ArduinoBLE.h>

#define PERIOD 1000

const char* ServiceUuid          = "19b10000-e8f2-537e-4f6c-d104768a1214";
const char* ServiceBattFloatUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";
const char* ServiceTempFloatUuid = "19b10002-e8f2-537e-4f6c-d104768a1214";
const char* ServiceHumdFloatUuid = "19b10003-e8f2-537e-4f6c-d104768a1214";
const char* ServiceIdStringtUuid = "19b10004-e8f2-537e-4f6c-d104768a1214";

BLEDevice *currDev, sensor[2], discovered;
String connected = "";
long lastMillis;
enum {NONE, DISCOVERED, CONNECTED, RECEIVE_DATA, DISCONNECTED};
int state = NONE;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  currDev = NULL;

  if (!BLE.begin()) {
    Serial.println("* Starting Bluetooth® Low Energy module failed!");
    while (1);
  }

  BLE.setDeviceName("Master");
  BLE.setLocalName("Master");
  BLE.advertise();

  Serial.println("Seeed XIAO  (Master Node)");
  Serial.println(" ");

  BLE.setEventHandler(BLEDisconnected, BLEPeripheralDisconnectHandler);
  BLE.setEventHandler(BLEConnected, BLEPeripheralConnectHandler);
  BLE.setEventHandler(BLEDiscovered, BLEPeripheralDiscoverHandler);
  BLE.scanForUuid(ServiceUuid);
  lastMillis = millis();
}

void loop() {
  switch (state) {
    case NONE:  BLE.poll(); break;

    case DISCOVERED:
      state = NONE;
      BLE.stopScan();
      if ((currDev != NULL) && (currDev->connected()) && (*currDev != discovered))
        currDev->disconnect();
      discovered.connect();
      break;

    case CONNECTED: {
        if ((sensor[0] == discovered) || (sensor[1] == discovered)) {
          lastMillis = millis();
          state = RECEIVE_DATA;
          break;
        }

        Serial.println("* Connected to peripheral device!");
        Serial.println(" ");
        if (!discovered.discoverAttributes()) {
          BLE.scanForUuid(ServiceUuid);
          state = NONE;
          Serial.println("Attribute discovery failed!");
          discovered.disconnect();
          break;
        }

        BLECharacteristic batteryChar = discovered.characteristic(ServiceBattFloatUuid);
        BLECharacteristic temperatureChar = discovered.characteristic(ServiceTempFloatUuid);
        BLECharacteristic humidityChar = discovered.characteristic(ServiceHumdFloatUuid);

        if ((!batteryChar) || (!temperatureChar) || (!humidityChar)) {
          BLE.scanForUuid(ServiceUuid);
          state = NONE;
          Serial.println("* Peripheral device does not have required characteristic!");
          discovered.disconnect();
          break;
        }
        
        if (!sensor[0]) {
          sensor[0] = discovered;
          currDev = &sensor[0];
          connected = "left";
        }
        else if (!sensor[1]) {
          sensor[1] = discovered;
          currDev = &sensor[1];
          connected = "right";
          BLE.stopScan();
        }
        
        batteryChar.setEventHandler(BLEUpdated, batteryLevel);
        temperatureChar.setEventHandler(BLEUpdated, temperature);
        humidityChar.setEventHandler(BLEUpdated, humidity);

        batteryChar.subscribe();
        temperatureChar.subscribe();
        humidityChar.subscribe();
        
        state = RECEIVE_DATA;
        lastMillis = millis();
        break;
      }

    case RECEIVE_DATA:
      BLE.poll();
      if ((millis() - lastMillis) > PERIOD) {
        if (currDev->connected())
          currDev->disconnect();
        else {
          BLE.stopScan();
          currDev->connect();
        }
      }
      break;

    case DISCONNECTED:
      if (!sensor[1]) {
        BLE.scanForUuid(ServiceUuid);
        state = RECEIVE_DATA;
        lastMillis = millis();
        break;
      }
      if (currDev != NULL)
        currDev->connect();
      break;
  }
}

void batteryLevel(BLEDevice device, BLECharacteristic value) {
  String name;
  float val = 0.0;
  value.readValue((byte *)&val, sizeof(float));
  Serial.println("battery_level_" +  connected  + ": " + val);
}

void temperature(BLEDevice device, BLECharacteristic value) {
  String name;
  float val = 0.0;
  value.readValue((byte *)&val, sizeof(float));
  Serial.println("temperature_" + connected + ": " + val);
}

void humidity(BLEDevice device, BLECharacteristic value) {
  String name;
  float val = 0.0;
  value.readValue((byte *)&val, sizeof(float));
  Serial.println("humidity_" + connected + ": " + val);
}

void BLEPeripheralDiscoverHandler(BLEDevice peripheral) {
  if (sensor[0] && sensor[1])
    return;
  Serial.print("Discover event, Peripheral : ");
  Serial.println(peripheral.deviceName());
  discovered = peripheral;
  state = DISCOVERED;
}

void BLEPeripheralConnectHandler(BLEDevice peripheral) {
  discovered = peripheral;
  state = CONNECTED;
}

void BLEPeripheralDisconnectHandler(BLEDevice peripheral) {
  // central disconnected event handler
  Serial.println("Disconnected event, Peripheral");
  //BLE.scanForUuid(ServiceUuid);
  state = NONE;
  if ((currDev != NULL) && (*currDev == peripheral)) {
    if ((currDev == &sensor[0]) && (sensor[1])) {
      currDev = &sensor[1];
      connected = "right";
    }
    else {
      currDev = &sensor[0];
      connected = "left";
    }
    state = DISCONNECTED;
  }
}`

and here the code of the peripherals:

#include <ArduinoBLE.h>

const char* ServiceUuid          = "19b10000-e8f2-537e-4f6c-d104768a1214";
const char* ServiceBattFloatUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";
const char* ServiceTempFloatUuid = "19b10002-e8f2-537e-4f6c-d104768a1214";
const char* ServiceHumdFloatUuid = "19b10003-e8f2-537e-4f6c-d104768a1214";
const char* ServiceIdStringtUuid = "19b10004-e8f2-537e-4f6c-d104768a1214";

BLEService service(ServiceUuid);
//BLEStringCharacteristic idCharacteristic(ServiceIdStringtUuid, BLERead, 64);
BLEFloatCharacteristic batteryCharacteristic(ServiceBattFloatUuid, BLERead | BLEWrite | BLENotify);
BLEFloatCharacteristic temperatureCharacteristic(ServiceTempFloatUuid, BLERead | BLEWrite | BLENotify);
BLEFloatCharacteristic humidityCharacteristic(ServiceHumdFloatUuid, BLERead | BLEWrite | BLENotify);

String name = "right";

void setup() {
  Serial.begin(115200);
  while (!Serial);

  if (!BLE.begin()) {
    Serial.println("- Starting Bluetooth® Low Energy module failed!");
    while (1);
  }

  BLE.setLocalName(name.c_str());
  BLE.setDeviceName(name.c_str());

  BLE.setAdvertisedService(service);
  //service.addCharacteristic(idCharacteristic);
  service.addCharacteristic(batteryCharacteristic);
  service.addCharacteristic(temperatureCharacteristic);
  service.addCharacteristic(humidityCharacteristic);
  BLE.addService(service);
      
  BLE.advertise();

      batteryCharacteristic.writeValue(0);
      temperatureCharacteristic.writeValue(0);
      humidityCharacteristic.writeValue(0);

  //idCharacteristic.writeValue(name);

  Serial.println("Seeed XIAO (Sensor Node) - " + name);
  Serial.println("");
}

void loop() {
  BLEDevice central = BLE.central();
  if (central) {
    while (central.connected()) {
      float batt  = ((float)random(1000000)) / 1000000UL;
      float temp  = ((float)random(1000000)) / 2000000UL;
      float humid = ((float)random(1000000)) / 1000000UL;

      batt = (batt * 100);
      temp = (temp * 200) - 100;
      humid = (humid * 100);

      batteryCharacteristic.writeValue(batt);
      temperatureCharacteristic.writeValue(temp);
      humidityCharacteristic.writeValue(humid);
      delay(500);
    }
  }
}

It works ok, but it's unstable as hell (disconnects after a few seconds).

21:21:56.734 -> Discover event, Peripheral :
21:21:56.842 -> * Connected to peripheral device!

If anyone can help me get on the right track (and even if I have to throw everything over) - I would be extremely grateful.

This state has taken me 2 days and I have no idea if everything is for the trash :slight_smile:

Thank you & best regards, Raphael

The first thing I would suggest is to get a copy of the Arduino Cookbook and read that. Then posting an annotated schematic as you have wired it will help us help you. A frizzy thing is useless and a waste of time. Also post links to Technical Information on the hardware devices as most of us cannot see what you have. Links to azone and other marketplace sellers is useless as they generally only have sales information.

Welcome to the forum.

Here are a few things that may help you fix your issue.

  • In your peripheral sketch replace the delay with a millis() timer (see your central code). The delay will prevent the BLE stack from working correctly. When I connect to the peripheral with BLE Scanner I can see a delay until the service is visible in the app. Communication stacks do not like it when you put the device to sleep (when you call delay).

  • I general I recommend avoiding float for interface data types. Your service and characteristics are public interfaces. There are several float specifications, the depend on the architecture, application and tools used and you do not want to convert one into the other. A good way around that issue is to use fixed point data types with a known exponent. The Bluetooth SiG has used this scheme for several characteristics specifications.

Check this example I wrote a while ago.

https://forum.arduino.cc/t/bluetooth-communications/902610/2

  • I recommend to use ALL_CAPTIAL_WITH_UNDERSCORE for constant values. That makes you code easier to read.

Regarding your central code. I do not have your hardware to test this. Does your XIAO use mbedOS?

Here are a few tips to make the code easier to read and understand.

  • PERIOD is not very specific. Spend a few more characters to describe what the period is?
  • Your naming for the BLEDevices is all over the place. Spend some characters and unify the naming.
  • I do not know why your central advertises. Centrals/clients do not advertise, peripherals/servers do.
  • I would recommend:
    -- to use unsigned long for any millis() operations.
    -- not shorten Characteristic to Char. Char is already used in most programmers minds for character data type.
    -- rename you enum to have a common start to show they belong together.
    -- if you have to use global variables give them good long names not just "state".
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.