Nano 33 BLE Dual Roles

I'm trying to build a device that I can control using a mobile device (central) that allows me to connect to a nano 33 ble (peripheral) to enable / disable functions and return data. I also want to be able to relay commands sent from the mobile device to the primary nano which then connects to another nano 33 ble to enable / disable an additional function. More / less rather than a master with multiple slave devices (star topology), I'm looking to chain these devices.

I found a similar discussion in the forums but I have been unsuccessful in getting what is described to work -

Simultaneous Central & Peripheral roles on one device?

And another that describes a working example but did not offer any reference code to review -

Nano 33 BLE to receive and resend data; reply #3

I have read numerous other discussions but continue to come up short of any solution that remotely works.

I'm able to easily get the examples of each respective role working independently such as peripheral > CallbackLED and central > ScanCallback but my struggle is to combine these into a unified sketch to run on the primary nano. A modified version of the CallbackLED example on the secondary nano will work for what I need at this time.

I found a very nice tutorial from adafruit but unfortunately it was written for Bleuart / Bluefruit devices but the concept is exactly what I'm looking for. It can be found by searching "bluefruit-nrf52-feather-learning-guide/dual-roles-bleuart" (forum has limited how many links I can include).

Does anyone have any suggestions or direction they can point me in? Has anyone else had any experience with combining both roles on a single device?

Thank you,
CrankySysAdm1n

Welcome to the forum.

Can you describe your application a bit more specific and post your code? It is OK if it does not work.

I would like to see:

  • your services and characteristics
  • What data are we talking about e.g., 1 byte every now and then or continuous data collection?
  • how many BLE peripheral devices do you want to connect?
  • are the BLE peripherals all the same or different?
  • what do you mean with chain? phone -> Nano33BLE <-> Nano33BLE <-> Nano33BLE ...
  • how do you intend to manage the roles?
  • do you intend the code to do any heavy lifting other than BLE?
  • what do you mean with disable functions?

There is a reason I did not post any code. This was an experiment with ugly source that would break easily e.g., when powered in the wrong order. I do not want someone to get frustrated.

Hi Klaus,

The device I'm trying to create is a timing gate for our horse arena - when complete, the I will use a mobile app to enable / reset a stopwatch function that will be triggered by an IR sensor.

This is the basic goal:

  • Allow connectivity from a mobile device running the gate timer app
  • When mobile app is connected, a user can arm the system - this will enable the IR sensor on the primary ble unit and reset / prepare a stopwatch function.
  • When armed, the primary ble will connect to a secondary ble unit located on the other side of the arena (~90 ft) enabling an IR LED pointed at the primary ble IR sensor.
  • When the participant breaks the IR beam, the stopwatch will start on the primary ble unit
  • A delay / ignore (essentially a debounce) will be added between the first break of the IR beam to account for the participant to fully cross the line (to ensure all legs of the horse have passed) then rearm the IR sensor to respond to the next trigger event to stop the stopwatch.
  • After the stop trigger event has occurred, the time will be sent from the primary ble back to the mobile app
  • The primary ble will connect to the secondary and disable the IR LED

Currently, I have the BLEService "ledService" (IR sensor enablement) and "statusService" (returning stopwatch data) working on the primary ble with the mobile app and working sketches for the stopwatch and IR sensor functionality to integrate later. I will eventually want to feed connectivity information back to the mobile app as visual reference to the user such as green / red status icon for both the mobile <-> primary ble and the primary ble <-> secondary ble but my priority is just getting the primary ble unit serving both roles at this time.

This will be limited to only the 2 Nano33BLE devices, what I've described as a primary and secondary with the primary responsible for the stopwatch using the IR sensor and the secondary only needing to simply turn on / off an IR LED (enable / disable).

#include <ArduinoBLE.h>

#define BLE_UUID_LED_SERVICE "0000FFE1-0000-1000-8000-00805F9B34FB"
#define BLE_UUID_STATUS_SERVICE "0000FFE2-0000-1000-8000-00805F9B34FB"
#define BLE_UUID_REMOTE_LED_SERVICE "0000FFE3-0000-1000-8000-00805F9B34FB"

BLEService ledService(BLE_UUID_LED_SERVICE); // BLE LED Service
BLEService statusService(BLE_UUID_STATUS_SERVICE); // BLE Status Service
BLEService remoteLedService(BLE_UUID_REMOTE_LED_SERVICE); // BLE LED Service

// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic( BLE_UUID_LED_SERVICE, BLERead | BLEWrite);
BLEStringCharacteristic statusCharacteristic( BLE_UUID_STATUS_SERVICE, BLERead | BLEWrite, 20 );
BLEByteCharacteristic remoteSwitchCharacteristic( BLE_UUID_REMOTE_LED_SERVICE, BLERead | BLEWrite);

const int ledPin = LED_BUILTIN; // pin to use for the LED
String str = "";

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

  // set LED pin to output mode
  pinMode(ledPin, OUTPUT);

  // begin initialization
  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");

    while (1);
  }

  // set advertised local name and service UUID:
  BLE.setLocalName("LZR Gate");
  BLE.setDeviceName("LZR Gate");
  BLE.setAdvertisedService(ledService);
  BLE.setAdvertisedService(statusService);
  BLE.setAdvertisedService(remoteLedService);

  // add the characteristic to the service
  ledService.addCharacteristic(switchCharacteristic);
  statusService.addCharacteristic(statusCharacteristic);
  remoteLedService.addCharacteristic(remoteSwitchCharacteristic);

  // add service
  BLE.addService(ledService);
  BLE.addService(statusService);
  BLE.addService(remoteLedService);

  // set the initial value for the characeristic:
  switchCharacteristic.writeValue(0);

  remoteSwitchCharacteristic.writeValue((byte)0x00);

  // start advertising
  Serial.println("Start advertising");
  BLE.advertise();

  // start scanning for peripherals - disabled... locks up device after 5 loops 
  //Serial.println("Scan for secondary BLE device");
  //BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
  //Serial.println("Finished scanning for secondary BLE device");

  Serial.println("BLE LED Peripheral; Start Loop");
}

void loop() {
  Serial.println("BLE Entering Loop");
  bleCentral();
  Serial.println("BLE Central Loop Completed");
  bleRemote();
  Serial.println("BLE Remote Loop Completed");

  remoteSwitchCharacteristic.writeValue((byte)0x01);
  Serial.println("remoteSwitchCharacteristic set to 1");
  delay(1000);
} 

void bleCentral(){  
  
  Serial.println("Running bleCentral function");
  // listen for BLE peripherals to connect:
  BLEDevice central = BLE.central();

  // if a central is connected to peripheral:
  if (central) {
    Serial.print("Connected to central: ");
    // print the central's MAC address:
    Serial.println(central.address());

    // while the central is still connected to peripheral:
    while (central.connected()) {
      // if the remote device wrote to the characteristic,
      // use the value to control the LED:
      if (switchCharacteristic.written()) {
        if (switchCharacteristic.value()) {   // any value other than 0
          Serial.println(switchCharacteristic.value());
          digitalWrite(ledPin, HIGH);         // will turn the LED on
        } else {                              // a 0 value
          Serial.println(F("LED off"));
          digitalWrite(ledPin, LOW);          // will turn the LED off
        }
      } 
        // If a text from the phone was sent print it to the serial monitor
        if (str != "") {
          Serial.println(str);
        }

        // Send string from serial command line to the phone. This will alert the user.
        if (Serial.available()) {
          Serial.write("send: ");
          String str = Serial.readString();
          statusCharacteristic.writeValue(str);
          Serial.print(str);
          Serial.write('\n');
        }
    }

    // when the central disconnects, print it out:
    Serial.print(F("Disconnected from central: "));
    Serial.println(central.address());
  }
}

void bleRemote() {
  Serial.println("Running bleRemote function");
  // check if a peripheral has been discovered
  BLEDevice peripheral = BLE.available();

  if (peripheral) {
    // discovered a peripheral, print out address, local name, and advertised service
    Serial.print("Found ");
    Serial.print(peripheral.address());
    Serial.print(" '");
    Serial.print(peripheral.localName());
    Serial.print("' ");
    Serial.print(peripheral.advertisedServiceUuid());
    Serial.println();

    if (peripheral.localName() != "LEDCallback") {
      return;
    }

    // stop scanning
    BLE.stopScan();

    controlLed(peripheral);

    // peripheral disconnected, start scanning again
    BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
  }
}

void controlLed(BLEDevice peripheral) {
  // connect to the peripheral
  Serial.println("Connecting ...");

  if (peripheral.connect()) {
    Serial.println("Connected");
  } else {
    Serial.println("Failed to connect!");
    return;
  }

  // discover peripheral attributes
  Serial.println("Discovering attributes ...");
  if (peripheral.discoverAttributes()) {
    Serial.println("Attributes discovered");
  } else {
    Serial.println("Attribute discovery failed!");
    peripheral.disconnect();
    return;
  }

  // retrieve the LED characteristic
  BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10001-e8f2-537e-4f6c-d104768a1214");

  if (!ledCharacteristic) {
    Serial.println("Peripheral does not have LED characteristic!");
    peripheral.disconnect();
    return;
  } else if (!ledCharacteristic.canWrite()) {
    Serial.println("Peripheral does not have a writable LED characteristic!");
    peripheral.disconnect();
    return;
  }

  while (peripheral.connected()) {
    // while the peripheral is connected
    // if the remote device wrote to the characteristic,
    // use the value to control the LED:
    if (remoteSwitchCharacteristic.written()) {
      if (remoteSwitchCharacteristic.value()) {   // any value other than 0
        Serial.println("LED on");
        //digitalWrite(ledPin, HIGH);         // will turn the LED on
        ledCharacteristic.writeValue((byte)0x01);
      } else {                              // a 0 value
        Serial.println(F("LED off"));
        //digitalWrite(ledPin, LOW);          // will turn the LED off
        ledCharacteristic.writeValue((byte)0x00);
      }
      //Toggle led off for loop testing
      remoteSwitchCharacteristic.writeValue((byte)0x00);
    }
  }

  Serial.println("Peripheral disconnected");
}

The "remoteLedService" will really likely only be used during setup / aiming of IR beam - during typical operation, the trigger to connect to the secondary ble to enable / disable the IR LED will be within the stopwatch function.

I'm curious as to why you have chosen BLE for the communication between the two units.

Is it driven by an i-phone BLE app? Is it driven by having two Nano 33 BLE units in hand?

I would think that the communications back and forth between the two units would be easier with other wireless technologies and let the phone talk to the primary unit with classic bluetooth.

Hi Cattledog,

Initially, I started with a Uno using a DSD-Tech HM-19 BLE module but ran into issues early on with using MIT AppInventor when trying to connect to an iPhone (no support for BLE on iPhone yet). I picked up a couple new Nano 33 BLE units to be able to have more control of the the BLE services and characteristics and switched from MIT AI to Thunkable for the iPhone app and had early success with this combination with the exception of the communication between the primary and secondary ble devices.

My first version started with battery operated laser diode (secondary side) pointed to a LDR on the primary - it was somewhat functional but too much maintenance to adjust the LDR for changing lighting conditions (this is located outdoors). Focusing the laser posed its own challenges and I didn't care for any innocent children staring into the beam. This prompted me to update the design using IR rather than a laser diode / LDR for safety and to eliminate the ambient lighting conditions as well.

I'm not locked in on any specific type of technology - I hadn't worked with BLE in the past and thought I would give it an attempt at least. My next move if I cannot get this setup working is to look at alternate wireless / RF modules to trigger the on / off of the remote IR LED. I have plenty of IR LEDs, sensors, and plano lenses so worst case scenario, I can add an IR LED to the primary ble device pointed to a sensor on the secondary and add the logic as a relay to turn on the trigger beam.

Initially, I started with a Uno using a DSD-Tech HM-19 BLE module but ran into issues early on with using MIT AppInventor when trying to connect to an iPhone (no support for BLE on iPhone yet). I picked up a couple new Nano 33 BLE units to be able to have more control of the the BLE services and characteristics and switched from MIT AI to Thunkable for the iPhone app and had early success with this combination with the exception of the communication between the primary and secondary ble devices.

I hadn't worked with BLE in the past and thought I would give it an attempt at least.

You've been more successful than may of the posters on this forum. Well done. :grinning:

My next move if I cannot get this setup working is to look at alternate wireless / RF modules to trigger the on / off of the remote IR LED. I have plenty of IR LEDs, sensors, and plano lenses so worst case scenario, I can add an IR LED to the primary ble device pointed to a sensor on the secondary and add the logic as a relay to turn on the trigger beam.

It's certainly possible that one of the BLE experts on the forum (certainly not me) might come up with the solution, but
both these alternative approaches sound more simple to me than trying to implement BLE for the on / off of the remote IR LED on the secondary Nano, while at the same time having the primary unit involved with a complex control app master from an i-phone.

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