Nano BLE swarm

I have an application in mind for which I'd like 3 or more Arduino Nano BLE-based devices to be "mutually self-aware" in that each device should turn on its LED if another device is close enough that the RSSI is greater than -80 (for example). I think this requires each Arduino to [u]both[/u] be a peripheral device which advertises a given service S with characteristic C and also be a central device which scans for and connects to peripherals advertising (S,C).

I am aware of the closed thread https://forum.arduino.cc/index.php?topic=680404.0 but haven't been able to get the idea to work.

Can anyone help? If acting as central and peripheral at the same time (i.e. both scanning for / connecting to peripherals and advertising) is not feasible then would it be possible to have my void loop() make the Arduino alternate acting as central and peripheral?

Is there any sample working code or snippets that would be helpful to me?

Thanks in advance for any help.

Welcome to the forum

adannenberg: Can anyone help? If acting as central and peripheral at the same time (i.e. both scanning for / connecting to peripherals and advertising) is not feasible then would it be possible to have my void loop() make the Arduino alternate acting as central and peripheral?

I have just done an experiment a few days ago. See reply #2 in the following post.

https://forum.arduino.cc/index.php?topic=727748.0

Hi Klaus. Thanks for the welcome, and thanks for your reply.

I have some questions (shocking, I know :D):

1) If I understood you post correctly, you have a single Nano 33 BLE board acting as both central and peripheral and that board communicates with other board(s) that are "single role" boards, i.e. boards that act solely as central or peripheral. If that's correct, do you think things would continue to work if you had a grouping of Nano 33 BLE boards, all acting as both central and peripheral device?

2) Very self-servingly, can you post your code so I can follow an example?

Thanks again.

Hi. I have just noticed that my ArduinoBLE library can be updated to version 1.2, which was released ~3 weeks ago. The previous version was a 1.1.x minor release some 8 months ago. The text in my Sketch IDE reads "This library supports creating a BLE peripheral and a BLE central mode." Is this the generic text that pre-dated version 1.2 or does version 1.2 now support the ability to run a single Nano 33 BLE board in both peripheral and central mode within the same sketch code? I looked at the Github repository and didn't see anything enlightening - certainly the examples were last updated in 2019 ,so no joy there. The obvious answer to my question is "just try it out and see" - and I will. But perhaps someone already knows, so I thought I'd ask the good folks in bulletin board land...

Sorry, I could not be more helpful. I have not had time to put a proper example together. As I wrote I did just an experiment with this. I used some code that I would not be happy to share in that state. This will need some extended testing to make sure it works reliably.

If you like to get started and need some help, please post your code and I will be happy to provide some advice if I have some.

Hi Klaus. I will try to put something together once I've determined that I can revert to the old version of the library that I'm using currently. I note that your post was from Feb 15 and I think the new library was released on Feb 20 (at least that's what this unofficial(?) site indicates https://platformio.org/lib/show/5878/ArduinoBLE/examples). If you weren't using version 1.2.0 then it may be worth trying your examples again with the current library. But I'll report on what I find... Thanks, as ever, for your clear and contentful posts.

I have two Arduino Nano 33 BLE boards running the code below, each connected to a different computer via USB. I have upgraded to the AndroidBLE 1.2.0 library.

I see only “nope” printed to the both serial monitors which seems to indicate to me that

  1. if board 1 is operating as a peripheral then it never connects to board 2 when board 2 is operating as a central (and vice versa),
  2. neither board finds the other as a peripheral when it is acting as central.

Please have a look at my code below to tell me if I’m doing something dumb, or if it’s still the case that one Nano 33 BLE board cannot act as both central and peripheral while running a single sketch file.

#include <ArduinoBLE.h>

#define UUID_SERVICE "9A48ECBA-2E92-082F-C079-9E75AAE428B1"
#define UUID_CHARACTERISTIC "1A3AC130-31EE-758A-BC50-54A61958EF81"

BLEService myService(UUID_SERVICE);
BLEStringCharacteristic myCharacteristic(UUID_CHARACTERISTIC, BLERead | BLENotify, 8 );

void setup() {

  Serial.begin(9600);
  while (!Serial);

  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);

    Serial.println("BLE Central scan");
    BLE.scanForUuid(UUID_SERVICE);
  }

  BLE.setDeviceName( "DeviceName 1" );
  BLE.setLocalName( "LocalName 1" );
  BLE.setAdvertisedService(myService);
  myService.addCharacteristic(myCharacteristic);
  BLE.addService(myService);
  myCharacteristic.writeValue("00000000");
  if (BLE.advertise()) {
    Serial.println("advertising...");
  }
}

void loop() {

  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    Serial.print(F("Disconnected from central: "));
    Serial.println(central.address());
  }

  BLE.scanForUuid(UUID_SERVICE);
  BLEDevice peripheral = BLE.available();

  if (peripheral) {
    BLE.stopScan();
    Serial.print("Found ");
    Serial.println(peripheral.localName());
    Serial.println(peripheral.address());
  } else {
    Serial.println("nope");
  }
}

Hi adannenberg, please use code tags. Your code should look like this.

Your example code

Try to modify the post. When you click on modify or Preview the tools bar will appear. </> will give you code tags.

Now about your code (not in order of importance)

  • Do you really need a string characteristic? Make use of the data types. If you want to combine values, you can use unions and structs.

  • I would move all BLE code into a separate function and call it a task e.g. bleTask

  • remember variables declared inside a function e.g. loop() will be destroyed when you leave loop(). e.g., in your case central and peripheral. These should probably be global or local static variables(my favorite). The official Arduino examples get away with it because they stay inside loop which is equally bad.

  • when you call scanForUuid() it starts a process that continuous and is not finished by the time you call BLE.available. It actually will never finish until you call stopScan(). Because you call it over and over again you cannot be sure whether it does delete the results it has so far, preventing you from finding your peripheral.

  • radio communication is an asynchronous event; you must structure your software in a way to accommodate that. Have a look at the following post reply #6. There are two examples that work together. In the central I used a state machine to handle the BLE communication. This looks intimidating at the beginning but will work much better in the long run.

  • Do not use the F() macro while you are developing. This device has enough RAM. Once everything works you can try if this helps with memory optimization. I believe I had some issues with it in the past.

  • make your variables, defines and names more specific. It helps with understanding what is going on. In microcontrollers we have temperature, pressure, … real things in a real world, myVariable is for PCs.

Hi Klaus. I’m sorry that I just saw your reply - already a month past. I’ve re-formatted the previous post and now understand the use of code tags(!). Very basic, but obviously important.

Schematically, my code contains a section like this:

void loop() {
  .
  .
  .
  BLE.scanForUuid(UUID_SERVICE);
  BLEDevice peripheral = BLE.available();

  if (peripheral) {
    //do stuff here
  }
}

I have, indeed, been having a problem having my board interact with all the boards advertising the service I’m interested in. Does the ArduinoBLE library allow one to get a list of devices that are advertising the particular UUID? If not, how can I be sure that I ever connect to each of them? It seems that BLE.available (or perhaps it’s BLE.scanForUuid) selects a single peripheral board among the set of peripherals advertising the service of interest, and that selection is not controllable by the user and seems somewhat random…

Can you elaborate on your comment about scanForUuid()? I feel that there’s something I’m missing…

Have a look at the following example:

File → Examples → ArduinoBLE → Central → Scan

The example calls BLE.scan(); once in setup. The sketch will continuously print peripherals devices it can find. Notice the pattern. In my case the sketch prints a couple of peripherals and then slows down. From time to time a repeat peripheral is shown.

Now add a BLE.scan() inside if(peripheral){} just before the closing bracket. Run the sketch again. Notice the change. In my case I see it printing continuously and repeats are much more often, and others are missing.

So, the BLE.scan function continuous to scan and it seems to filter devices it already found. Stopping scanning and restarting it, is not a good strategy to find more devices. Let the scan continue and you should get all devices nearby.

Because the devices are all your own Arduinos you can use the address to separate them. Scan for the UUID and then read the address. Create a little array and store the address when the address is not in the array yet. Otherwise, you know it is a repeat. You have enough RAM and processing power to store quite a lot of BLE devices.

This would not work with all BLE devices. Smartphones for instance randomize their address to prevent tracking.

Hi Klaus. I’ve done the experiment you suggest. Interesting results. I tried three scenarios:

  1. BLE.scan() in setup() only.
  2. BLE.scan() at the end of loop() only.
  3. A conditional inside loop() such that BLE.scan() is called once every 10 seconds.

All scenarios print out to the serial monitor the addresses of the 5 peripheral devices that happen to be nearby. Scenarios 1 and 3 print out many fewer rows.

What actually happens when you call BLE.scan() repeatedly without calling BLE.stopScan() in between? Do you end up having many concurrently running scans or does the process started by the first call automatically stop when the second call starts a new process? In other words, is BLE.stopScan invoked automatically?

The output suggests the scan is just restarted and the results saved so far are discarded. Therefore, you see duplicate entries more often and might miss some devices.

I do not think so. Most embedded code does not allocate memory and objects dynamically because the resources are limited.

I followed the source code a bit down through the stack. Not at the high level, but it might just restart the scan function at the low level of the BLE stack.

My recommendation would be start scan once. You can stop at some point and restart later. But avoid calling scan continuously.