Is It Possible to Enumerate Characteristics of BLE Peer

Pretty straightforward question. Once you’re connected to a Bluetooth peripheral, (using an Argon as the central device), is it possible to enumerate all of the characteristics of the device?

I know such a thing is possible using APIs in other tech stacks. I can’t seem to find anything in Particle’s documentation, however.

Specifically, I have a device with characteristics that I know ahead of time, but calling peer.getCharacteristicByUuid() is giving me results that I do not expect.

To add: I’ve been looking over the firmware code, and it appears as though my Argon is failing to discover any of the characteristics provided by the device. I see the following printed out to my serial console.

0000047723 [wiring.ble] TRACE: New peripheral connected.
0000047725 [wiring.ble] TRACE: Start discovering services.
0000047905 [wiring.ble] TRACE: Start discovering characteristics.
0000049345 [wiring.ble] TRACE: Start discovering characteristics.
0000050021 [wiring.ble] TRACE: Start discovering characteristics.
0000050289 [wiring.ble] TRACE: Start discovering characteristics.

That all looks fine - there are 4 services on my peripheral - but it doesn’t discover any of the characteristics associated with them. That’s a problem.

What would that be?
What do you expect and what do you get?

In BleServiceImpl you can find a vector that should hold all characteristics for a given service.
And that BleServiceImpl should be exposed via the impl member of the BleService object.

Thanks, @ScruffR. I will take a look at the impl later today.

When I scan the peripheral with nRF Connect on my phone (or I connect with code I wrote for a different platform) I can see multiple characteristics listed. The ones I am interested in are a combination of NOTIFY and WRITE characteristics.

When I connect via the Argon and retrieve the characteristics, they all have their properties simply set to 0. Per the log I posted in my second comment, I think the Argon is failing to find/record the characteristics properly, as I would expect to see LOG_DEBUG(TRACE, "New characteristic found."); called.

Can you share the code you are using to discover?

The code below is a bit of a kludge, pardon it’s appearance.

On each loop, it looks to see if their is a character available on Serial. This was my way of controlling the process without blocking. The publish() method simply logs to the serial console.

The output of this code is something like:

00
00
00
BleAddress deviceAddr;
BlePeerDevice peer;

void setup() {
    Serial.begin(9600);
    deviceAddr = (hal_ble_addr_t) { .addr = {0x65, 0x44, 0x2C, 0x1E, 0xE1, 0xF4}, .addr_type = BLE_SIG_ADDR_TYPE_PUBLIC};
}

void loop() {
    while (!Serial.available()){
        return;
    }
    
    Serial.read();
    publish("running");
    
    if (BLE.connected()) {
        // We're currently connected to a sensor
        if (peer.connected()) {
            publish("connected");
            BLE.disconnect(peer);
        } else {
            publish("wat?");
            BLE.disconnect();
        }
    }
    else {
        publish("scanning");
        BleAddress foundAddr;
        int count = BLE.scan(scanResultCallback, &foundAddr);
        publish("\n\n");
        publish("scanned", itoa(count));
        if (foundAddr[0] != 0) {
            publish("trying to connect to", bleAddressToChar(foundAddr));
            peer = BLE.connect(foundAddr);
            if (peer.connected()) {
                publish("successfully connected!");
                mainServiceCharacteristic = peer.getCharacteristicByUUID(mainService);
                connectWriteCharacteristic = peer.getCharacteristicByUUID(connectWrite);
                mainWriteCharacteristic = peer.getCharacteristicByUUID(mainWrite);
                sprintf(strBuf, "%02X", mainServiceCharacteristic.properties());
                publish("service properties", strBuf);
                sprintf(strBuf, "%02X", connectWriteCharacteristic.properties());
                publish("connect properties", strBuf);
                sprintf(strBuf, "%02X", mainWriteCharacteristic.properties());
                publish("write properties", strBuf);
            }
            else {
                publish("connection failed");
            }
        }
    }
}


void scanResultCallback(const BleScanResult *scanResult, void *context) {
    uint8_t buf[BLE_MAX_ADV_DATA_LEN];
    if (!(scanResult->address == deviceAddr)) {
        return;
    }
    
    BLE.stopScanning();
    *(BleAddress*)context = scanResult->address;
}

I finally tried to give you code a quick check but found it wasn’t really building as is (despite replacing all instances of publish()) due to some missing declarations, so I didn’t invest any more time in that - sorry.

However, after some more testing and a short chat with one of the the Particle BLE devs impl() is not supposed to be used (don’t see why it’s public then tho’) but some new APIs will be included in 1.3.1-rc.1 which should allow to traverse the service and characteristic vectors.