Ble Architecture Advice

Hi All,

I am hoping someone can give me a steer in the right direction. I am just trying to make some decisions on the best approach to design a 1 to Many BLE relationship, Boron (Central Device) to Argons (Peripheral Devices). At a high level, when our boron devices identifies a certain sensor based event it scans and connects to a designated argon. Once connected it sends a command and awaits acknowledgement. It continues sending command to argon until acknowledged. After ACK is received, it returns to normal operation.

While all this is happening, one or more argons are cycling through sleep for x seconds, they wakeup and wait for a set period for a potential targeted connection from the central Boron. If a connection is established the Boron will send a command for the argon to execute, after executing it will wait for a short period of time to see if an additional command comes from the central device. If this does not happen then the device disconnects and returns to sleep.

I have attempted to illustrate this workflow in the attached diagram, apologies that it is not laid out very well.

IMPLEMENTATION
At present I am planning on using Ricks “BleSerialPeripheralRK.h” library for my Argon Peripherals. I will attach the device numbers to advertised data and this can be used to enable the central Boron to target a specific peripheral to connect to. This seems to be working when tested with an android app.

For the central boron I am trying to use some code from the particle docs:

https://docs.particle.io/tutorials/device-os/bluetooth-le/#uart-central

Actual code below. When I try to load this to a Boron it gets stuck in a cycle where it tries to connect to cell (green flash) then tries to connect to cloud (rapid cyan flash) then just when I would expect to see breathing cyan, the device resets as if the reset button was pressed.

Questions

  • Is using BLE UART a good idea for this use case?
  • Does anyone know what I am doing incorrectly with the UART central code below?
  • How do I get the central device to scan for a specific peripheral device? Hoping to use device numbers here to connect to correct peripheral?
  • Does anyone know what a suitable cycle time is for an argon to wake from sleep, turn on ble and wait for a connection to be established by the central?

Thanks in advance


// This example does not require the cloud so you can run it in manual mode or
// normal cloud-connected mode
// SYSTEM_MODE(MANUAL);

// These UUIDs were defined by Nordic Semiconductor and are now the defacto standard for
// UART-like services over BLE. Many apps support the UUIDs now, like the Adafruit Bluefruit app.
const BleUuid serviceUuid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
const BleUuid rxUuid("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
const BleUuid txUuid("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");

const size_t UART_TX_BUF_SIZE = 20;
const size_t SCAN_RESULT_COUNT = 20;

BleScanResult scanResults[SCAN_RESULT_COUNT];

BleCharacteristic peerTxCharacteristic;
BleCharacteristic peerRxCharacteristic;
BlePeerDevice peer;


uint8_t txBuf[UART_TX_BUF_SIZE];
size_t txLen = 0;

const unsigned long SCAN_PERIOD_MS = 2000;
unsigned long lastScan = 0;

void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
    for (size_t ii = 0; ii < len; ii++) {
        Serial.write(data[ii]);
    }
}

void setup() {
    Serial.begin();
	BLE.on();
    peerTxCharacteristic.onDataReceived(onDataReceived, &peerTxCharacteristic);
}

void loop() {
    if (BLE.connected()) {
        while (Serial.available() && txLen < UART_TX_BUF_SIZE) {
            txBuf[txLen++] = Serial.read();
            Serial.write(txBuf[txLen - 1]);
        }
        if (txLen > 0) {
        	// Transmit the data to the BLE peripheral
            peerRxCharacteristic.setValue(txBuf, txLen);
            txLen = 0;
        }
    }
    else {
    	if (millis() - lastScan >= SCAN_PERIOD_MS) {
    		// Time to scan
    		lastScan = millis();

    		size_t count = BLE.scan(scanResults, SCAN_RESULT_COUNT);
			if (count > 0) {
				for (uint8_t ii = 0; ii < count; ii++) {
					// Our serial peripheral only supports one service, so we only look for one here.
					// In some cases, you may want to get all of the service UUIDs and scan the list
					// looking to see if the serviceUuid is anywhere in the list.
					BleUuid foundServiceUuid;
					size_t svcCount = scanResults[ii].advertisingData().serviceUUID(&foundServiceUuid, 1);
					if (svcCount > 0 && foundServiceUuid == serviceUuid) {
						peer = BLE.connect(scanResults[ii].address());
						if (peer.connected()) {
							peer.getCharacteristicByUUID(peerTxCharacteristic, txUuid);
							peer.getCharacteristicByUUID(peerRxCharacteristic, rxUuid);

							// Could do this instead, but since the names are not as standardized, UUIDs are better
							// peer.getCharacteristicByDescription(peerTxCharacteristic, "tx");
						}
						break;
					}
				}
			}
    	}

    }
} ```

When you do a scan, it returns a list of everything it finds, but you can then loop through that list to find the address (or name if sent) of the device you are expecting to see. You can’t say scan for a particular device.

Though I’m not sure about the efficiency of what you are doing, especially when I’m assuming the argon is battery powered. It sounds like a lot of time might be spent waiting for a known order to happen. What if a fault upsets that order?

You can have multiple connected BLE clients and rather than send a command, you can define a characteristic to monitor with a callback. What then can happen is the central device can continually scan for any new device it finds with the advertised service set, or a known address - some way of knowing it is a device you are interested in.

Once found, connect to that device and do peer.getCharacteristic, to watch that characteristic. When your argon does a setValue then the Boron firmware will automatically execute your call back routine. Once it has all the data then it can disconnect that client.

On your argon, you can power on, wait to form a connection, only do a set value once the connection has been initiated and then sleep if it disconnects again. For both states you could have a timeout and just sleep if that timeout has been hit.

That should mean your central device is not waiting on anything and should quickly notice a new device. If so then your argons are likely to send the data almost immediately they turn BLE.

It also means if your boron is just a gateway, you can have multiple ones. As long as one of them connects to the argon, the data will get out your network.

I’m currently working in similar for some sensors of my own. I was thinking a timeout of 30 seconds, but that may be way longer than what is needed.

Hi Dave, thanks for the message.

So a potential alternative is as follows?

  1. Central scans for all devices, loops through list of devices, if target device found then central connects to peripheral. After connection, it monitors a specific characteristic.

  2. Peripheral sets value of that characteristic. This triggers a callback on the central device.

Do you think using characteristics is a better solution then ble UART?

Hi,

Yes, that is what I’m doing. I had not compared it to BLE UART though, so I’m afraid I can’t answer that.

Though I should mention that I did say that once data is received for a characteristic, disconnect the client and have the client code detect that to go into a deepsleep. The forced disconnect seems to be causing my gateway to crash.

Does anyone know why this code is causing my devices to reset? Am I supposed to be defining the device that I want to connect to at:

BleUuid foundServiceUuid;

I have a feeling that the device is resetting at the line above or else at the next line:

size_t svcCount = scanResults[ii].advertisingData().serviceUUID(&foundServiceUuid, 1);

EDIT
So the system is resetting at one of these lines:

BleUuid foundServiceUuid;
size_t svcCount = scanResults[ii].advertisingData().serviceUUID(&foundServiceUuid, 1);

EDIT2
Ok so I think the device was restarting because the following for loop was iterating outside of the length of my scanResults variable.

for (uint8_t ii = 0; ii < count; ii++) {

Hi, maybe @mariano can provide insights here?
thank you

Is it still resetting? Also, what DeviceOS version are you using? If you’re using 3.0.0 or newer, you can use the new BLE.scanWithFilter() API that does the filtering for you. See the documentation here:
https://docs.particle.io/reference/device-os/firmware/#ble-scanwithfilter-

You could do something like this:

BleScanFilter filter;
filter.serviceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
Vector<BleScanResult> scanResults = BLE.scanWithFilter(filter);

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