BLE - Getting started help

Hello. I’ve been struggling to understand how to do what I think is a simple thing with BLE on the Particle units. I really want to use Particle to read this sensor. I have been going over the Particle bluetooth docs off and on for two weeks and I’m ready to give up.

I feel I understand the protocol itself and what I’m trying to do, I just don’t undersatnd how to use the Particle code to make it work. I see a lot of short fragments of examples, but I’m just not finding anything that clearly puts together what steps are needed to do a few simple things. I am struggling to understand how to actually modify/use the examples.

I am attempting to connect to a BLE device, then I want to write to a UUID and I want to then read from a UUID.

I think I am looking for an example that would look something like the following, which is what I’d expect for something like a SPI port or I2C handler.

BLE.Connect(my_target_device);
BLE.UUIDWrite(UUID, WriteValue);
myResult = BLE.UUIDRead(UUID);

I am trying to follow the example here:
https://docs.particle.io/tutorials/device-os/bluetooth-le/#heart-rate-central

But I just can’t understand what to do with it.

For example, the first significant line in the example is this, but no explanation:

BleCharacteristic heartRateMeasurementCharacteristic;

I don’t understand what that is doing, or what should be done differently to query something different. I looked up “BleCharacteristic” in the docs and it says “In a BLE central role, you typically have a receive handler to be notified when the peripheral updates each characteristic value that you care about.”. That is exactly what I want to do, but I don’t see “how” to do it. Where does the receive handler go? Is there a simple example of one that just queries a UUID?

Should I just replace “heartRateMeasurementCharacteristic” with the hex of the UUID I’m looking for? Later in setup() of the example it has this:

heartRateMeasurementCharacteristic.onDataReceived(onDataReceived, NULL);

Again, I’m not sure how that works or what exactly is happening. I think it’s telling me to do the following, but I know this is not correct:

BleCharacteristic 0xffff; //the 16-bit UUID I'm interest in
void setup(){
	0xffff.onDataReceived(onDataReceived, NULL);
}

I also don’t understand how to actually grab the returned value into a variable. I have posted a screen shot below of this example in my browser. I’m sorry but I don’t understand what part of that copies the result into a variable for use. It is very hard to follow.

If I could have a little more guidance or see some more full examples that are simple, maybe I can logically relate these parts together. Most of the examples I’m finding on the internet are in phthon, and they use libraries, so it’s real hard to dissect the internals of those examples to figure out what to do in C on the Particle.

Much thanks for any further help.

Thank you.

1 Like

BLE is not a mere hardware interface like SPI and I2C but a communication technology more on level with WiFi and LoRA, hence it's bound to be more complicated to use.

About Services and Characteristics Adafruit does have a nice write-up about the BLE GATT scheme here

If you look at the end of the heart rate sample you linked you'll find where the callback went in there :wink:

This won't work since 0xffff is a number literal just like 65535 would be. You couldn't write BleCharacteristic 65535 either.
But the question itself suggests you may be lacking some C/C++ basics.
What background in coding do you have we can build upon?

In a nutshell: This line declares a variable heartRateMeasurementCharacteristic which hould represent an object of type BleCharacteristic and also (implicitly) calls the class constructor to instantiate an object of that type.

From then on you can use that variable to access the related object and call its methods (e.g. onDataReceived() which hooks up your callback functioon with the object).

That'll be done inside the onReceived callback and is shown in the sample implementation. It's not the part that you show in your screenshot but futher down in the code :wink:

It is a full sample, you just need to read it to the end.

Finally, since we don't know which kind of peripheral you intend to communicate we won't be able to provide any more specific help for it.
Since you are mentioning a 0xffff characteristic (which is pretty much a wild card UUID) it may well be that the heart rate sample is not the most fitting one to look at. Maybe one of the other samples can help you out further (e.g. UART Central)

Also using a 3rd party tool like nRF Connect or Bluefruit on a BLE enabled mobile device can help investigate the Services and Characteristics exposed by your sensor.
Posting the info you get from that may help us helping.

Hello ScruffR. Thank you for the reply.

I have put a ton of time into learning Bluetooth over the past two weeks just to try to read this sensor. I’ve read all about services and UUIDs, watched a bunch of videos, purchased a BLE sniffer board, looking at low level flags, etc, and have been sniffing traffic between the sensor and a phone app. I think I know what I want to do, I just don’t understand how to modify the Particle examples to do it.

I normally write straight C for PIC and Atmel, mostly in MPLABX. This Boron is being used as a gateway for another PIC based board to send some data to the cloud and read a BLE sensor, so it is being attached to a finished project at the last minute. I understand the basics of the object oriented stuff, but the more complex syntax is still cryptic to me. I wish there were a few sentences describing each of the important lines in the tutorial. Something to spell out the steps in a little more detail.

I am trying to modify the Heart Rate Central example.
https://docs.particle.io/tutorials/device-os/bluetooth-le/#heart-rate-central

I will try to explain as best as I know how.

We are trying to read a soil moisture sensor. We think this is our sensor:

I am trying to capture the “Real-time data” string.

According to this git, I need to do the following steps:

  1. write 0xa01f to the mode change handle 0x33
  2. read the actual sensor value string from data handle 0x35

I am struggling to know how to change the example to do that.

Near the top of the git for our sensor, there is a table that lists handle 0x33 as write and 0x35 as read, and both show “Service UUID” of “00001204-0000-1000-8000-00805f9b34fb”, so I think I’m supposed to try to connect to that UUID instead of the heart rate sensor service 0x180D in the example.

So now looking at the tutorial example…

len = scanResults[ii].advertisingData.get(BleAdvertisingDataType::SERVICE_UUID_16BIT_COMPLETE, buf, BLE_MAX_ADV_DATA_LEN);

I’m trying to contact the “long” UUID above instead of the heart rate service. I think I need to tell this function to query SERVICE_UUID_128BIT_COMPLETE instead because I’m using the long string, so I made that change, but that is a guess.

I studied the section in the reference document “BleAdvertisingDataType”, and there is a short write up, but I don’t understand what it is saying or in what context the information applies. There is no example code for this entire section. The section that begins “Similarly, you typically use appendCustomData()”, and is followed by a long list of the options in bold, but there is no example of use next to the explanation. There is an example, but I don’t understand how to apply the code in the reference Example to the function above.

 if (*(uint16_t *)&buf[jj] == BLE_SIG_UUID_HEART_RATE_SVC) { // 0x180D

I think I need to make this function query the 128 bit string above, but I don’t know how to actually syntax that. Do I pass it a long hex number all run together or can it be written as shown? This function appears to be looking for a uint16_t data type so I don’t know how I would pass it a 128 bit string. If I were going to put that long UUID in a variable or define it in a constant at the top of my code, how exactly should that be written so the function will accept it? I would like to create my own define at top of my code to use in place of “BLE_SIG_UUID_HEART_RATE_SVC”

peer.getCharacteristicByUUID(heartRateMeasurementCharacteristic, BleUuid(0x0035));

I’m assuming that this BleUuid is the same as the “Handle” in the git reference. I have set it to the final target UUID of 0x0035.

I then still have the following remaining questions:

  1. I don’t really know how to deal with onDataReceived. The example sets flags. Are these flags required? I’m just interested in capturing that data string from UUID 0x0035 into something I can work with to parse and extract the useful data. Is there a simple example of onDataReceived that simply puts the received data in a global variable? Could it just put the returned hex string into an array or something?

  2. How do I write 0xa01f to the mode change handle 0x33? I assume I need to use setValue somehow, but again I’m struggling to understand how to actually use it. I don’t understand how to use it in a larger program based on the brief snip in the reference section.

I found this in one of the other tutorials:

temperatureMeasurementCharacteristic.setValue(buf, sizeof(buf));

but again, I don’t understand how to relate that to the above long UUID string. There is no comment in this example unfortunately.

Here is a screen shot of what I think are the important parts. This is formatted much easier to read.

Thank you again for any further help.

That string is merely a human readable representation of a 16 byte array holding the 128 bits of the "raw" UUID.

Here you can see how the Particle UUID is defined

and here you can browse the implementation of the BleUuid class that provides you with multiple methodes to manipulate and process (e.g. compare) UUIDs

Okay I am continuing to move forward very slowly. I have been trying to solve this single problem for nearly 3 weeks now and I’m about to abandon Particle.

I am simply trying to write to a UUID then read from a UUID. I’ve been struggling to my wits end trying to understand how to mod the example to do this. I don’t understand the syntax of the BLE portions of the reference doc well enough to understand it.

I have pasted the entire project code in current version below.

I think my present problem is I can’t get the program to match my intended service when iterating through the scan results.

To attempt to debug this, I am trying to simply get these values (adv data and address from scan results) into a variable so I can print them out, but no matter what syntax I write, I can’t get at these values. Nothing I try will compile.

Here are some helpful screen shots:

^^^ this is from BLE scanner on my phone. It shows the intended sensor is transmitting, along with a handful of other devices (probably 5 to 10 total show up in the scan list, which may be important because BLE.scan only seems to be finding 3 devices).

I have posted links to the GIT for the moisture sensors I am using above which show the UUIDs for everything.

I cannot make this line of code be true (line 120 in full example below):

if (svcCount > 0 && foundServiceUuid == serviceUuidData) { 

I have already set serviceUuidData earlier in my code like this:

const BleUuid serviceUuidData("00001204-0000-1000-8000-00805F9B34FB");  

To debug, I am attempting to get access to the “address” and the “AdveristingData” in the scanResults[ii] but I can’t figure out what syntax to use. No matter how I try to get these value in a variable to list them out the serial port, it won’t compile.

Here is the whole program:


#include "Particle.h"
#include "ee101.h"              //  Bebug serial interface library for EE101 Insight-Pro debugger (ee101.com)


// Debugging Setup Code
SYSTEM_MODE(SEMI_AUTOMATIC)     // for debugging. comment out normally. code will execute without attempting to connect to WiFi or cloud
#define led D7                  //blue LED pin on Boron board
int temp_counter_01 = 0;        //misc counter for debugging
//end Debugging Setup Code


// Bluetooth Setup Code

//UUID codes for the Services and Characteristics of the Xiaomi Flower Care Moisture Sensor 
//https://github.com/vrachieru/xiaomi-flower-care-api#protocol
const BleUuid serviceUuidRoot("0000FE95-0000-1000-8000-00805F9B34FB");  

const BleUuid serviceUuidGeneric("00001800-0000-1000-8000-00805F9B34FB");
const BleUuid charUuid_r0x0003("00002800-0000-1000-8000-00805F9B34FB"); //sends device name

const BleUuid serviceUuidData("00001204-0000-1000-8000-00805F9B34FB");  
const BleUuid charUuid_w0x0033("00001A00-0000-1000-8000-00805F9B34FB"); //device mode change (write 0xa01f to this UUID to change mode to send live data)
const BleUuid charUuid_r0x0035("00001A01-0000-1000-8000-00805F9B34FB"); //get real-time sensor values
const BleUuid charUuid_r0x0038("00001A02-0000-1000-8000-00805F9B34FB");
const BleUuid charUuid_r0x003c("00001A11-0000-1000-8000-00805F9B34FB"); //get historical sensor values
const BleUuid charUuid_w0x003e("00001A10-0000-1000-8000-00805F9B34FB");
const BleUuid charUuid_r0x0041("00001A12-0000-1000-8000-00805F9B34FB");


BlePeerDevice peer;                               //this will represent our connected device, once connected

BleCharacteristic peerWriteDeviceModeChangeCharacteristic;
BleCharacteristic peerReadRealTimeDataCharacteristic;
BleCharacteristic peerReadHistoricalDataCharacteristic;

const unsigned long SCAN_PERIOD_MS = 2000;        //delay between attempted scans when not connected
unsigned long lastScan = 0;
const size_t SCAN_RESULT_COUNT = 20;              //maxiumum found devices
BleScanResult   scanResults[SCAN_RESULT_COUNT];   //declares the scanResults object of BleScanResult class

void onDataReceived_RealTimeData(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
    for (size_t ii = 0; ii < len; ii++) {   //iterates through received data
        //Serial.write(data[ii]);             //write the data
        EE101printf (6, "data rtd %d",data[ii]);
    }
}

void onDataReceived_HistData(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
    for (size_t ii = 0; ii < len; ii++) {   //iterates through received data
        //Serial.write(data[ii]);             //write the data
        EE101printf (6, "data hst %d",data[ii]);
    }
}

//end Bluetooth Setup Code



// setup() runs once, when the device is first turned on.
void setup() {

  // Put initialization like pinMode and begin functions here.
  pinMode(led,OUTPUT);
  pinMode(A3,OUTPUT);                                 //pins A3 & A4 are used for EE101Debugger
  pinMode(A4,OUTPUT);                                 //pins A3 & A4 are used for EE101Debugger


  //peerReadCharacteristic.onDataReceived(onDataReceived, &peerReadCharacteristic);     //example

  peerReadRealTimeDataCharacteristic.onDataReceived(onDataReceived_RealTimeData, &peerReadRealTimeDataCharacteristic);     
  peerReadHistoricalDataCharacteristic.onDataReceived(onDataReceived_HistData, &peerReadHistoricalDataCharacteristic); 

  Serial.begin();

}


// loop() runs over and over again, as quickly as it can execute.
void loop() {


  //EE101Text(5, "good");  
  
  
  temp_counter_01++;

  digitalWrite(led,HIGH);
  delay(50);
  digitalWrite(led,LOW);
  delay(50);

  
  if (BLE.connected()) {
      EE101Text(5, "connected");
      delay(50);
  } else{
     
      //EE101Text(2, "not con");
     if (millis() - lastScan >= SCAN_PERIOD_MS) {                                     //if time expired since last scan attempt
            // Time to scan
            lastScan = millis();
            size_t count = BLE.scan(scanResults, SCAN_RESULT_COUNT);                  //PERFORMS ACTUAL SCAN
            EE101printf (6, "scanResults %d",count);                                  //debug count of scan results

            if (count > 0) {                                                          //if results were found...
                for (uint8_t ii = 0; ii < count; ii++) {
                    //BLE.scan returned results. Iterate through these reults looking four our intended SERVICE UUID
                    
                    BleUuid foundServiceUuid;
                    // get svcCount, which is count of available services on this device of the BLE.scan
                    size_t svcCount = scanResults[ii].advertisingData.serviceUUID(&foundServiceUuid, 1);



                    EE101printf (6, "adData %x addr %x",scanResults[ii].advertisingData, scanResults[ii].address); 



                    if (svcCount > 0 && foundServiceUuid == serviceUuidData) {        //if a service was available, see if it matches our TARGET SERVICE
                        //if our TARGET SERVICE UUID was found on a device....
                        
                        peer = BLE.connect(scanResults[ii].address);    // ... try to connect to this address
                        if (peer.connected()) {                         // ... if a connection as sucessful...
                            EE101Text(5, "peer.connected");
                            
                            //peer.getCharacteristicByUUID(peerWriteCharacteristic, charUuid_w0x0033);    //example uses
                            //peer.getCharacteristicByUUID(peerReadCharacteristic, charUuid_r0x003c);     //example uses

                            peer.getCharacteristicByUUID(peerWriteDeviceModeChangeCharacteristic, charUuid_w0x0033);  //get device mode change characteristic

                            peer.getCharacteristicByUUID(peerReadRealTimeDataCharacteristic, charUuid_r0x0035);       //get real-time data characteristic
                            peer.getCharacteristicByUUID(peerReadHistoricalDataCharacteristic, charUuid_r0x003c);     //get historical data characteristic

                            
                            uint16_t theValue = 0xa01f;                                       //value to write to the target characteristic
                            peerWriteDeviceModeChangeCharacteristic.setValue(theValue);       //actually write the target characteristic
                            
                            
                            // Could do this instead, but since the names are not as standardized, UUIDs are better
                            // peer.getCharacteristicByDescription(peerTxCharacteristic, "tx");
                        }
                        break;
                    }
                }
            }

      }
  }

}


Have you tried writing/reading to/from the characteristics with the nRF Connect app?
Did this work?

In your screenshot you have this
image
Which has a NOTIFY property, hence this should be hooked up with a callback - pure read characteristics (without NOTIFY) won't need a callback as they are read on demand.

Do you get any debug info back from your getCharacteristicByUUID() and .setValue() calls?
After registering the characteristics you may also access their properties/methods in order to debug what's going on or not.
You may also read back from your peerWriteDeviceModeChangeCharacteristic as it's indicated to be a READ/WRITE characteristic by the nRF Connect app (unlike the GitHub docs which only indicate it to be a WRITE characteristic).

As said in the PM discussion a few days back: Without such a sensor at hand we need to rely on your input to help you out and for that you need to gather and provide as much background data as possible.
From a remote observer's perspective I'd say your code should do most things correctly, but you/we need to find out where the logic breaks down.
For that you should add plenty of debugging statements that give you a thorough idea about the internal state of the variables and objects at any relevant time.

  • does the scanned address match the address you get with the nRF Connect app?
  • print out the found service UUIDs and device addresses and compare them visually
  • if you have a combined logical condition, what are its individual components?
  • do you get a valid connection?
  • do the characteristics get registered correctly?
  • what do the characteristics objects tell you about themselves after registration?
  • what's the mode change value before you write to it?
  • what does the .setValue() call return?
  • what's the value after you wrote to it?
  • what does a .getValue() call on the real time data characteristic give you back?
  • do your callbacks get called at all?
  • ...

What error messages would come with that?

I’m totally totally overwhelmed by the syntax of this whole thing. I’ve done some object oriented stuff before but I am totally spun in circles on this.

I have done a BLE.scan. I figured out how to iterate through it from another example. I am seeing the MAC address of my sensor being returned.

I am now trying to figure out exactly what line of code to write to read a UUID out of it. I can’t figure that out. I’ve been clicking around in the docs and internals of these libraries trying to figure it out. I just don’t know what to type. I think I know exactly what value I want.

I’m eventually going to try to match something in the scan results to my desired service. I can’t even do that because I don’t understand the exact syntax to use.

Here is a main loop() I’m working on right now…

void loop() {

    loop_counter++;
    Log.trace("loop %d", loop_counter);
    //delay(100);


    Log.trace("BLE.scan");                                            
    auto start_time = millis();                                       //mark start of the BLE scan
    int scan_result_count = BLE.scan(scanResults, SCAN_RESULT_MAX);   //SCAN FOR NEARBY BLE DEVICES, returns count of found BLE devices nearby
    auto finish_time = millis();                                      //mark end of the BLE scan
    Log.trace("Scan duration (ms): %d", finish_time - start_time);    //Serial.printlnf("Scan duration (ms): %d", finish_time - start_time);
    Log.trace("BLE.scan found %d devices", scan_result_count);        //Serial.printlnf("Found %d results.", count);
    

    for (int ii = 0; ii < scan_result_count; ii++) {
        
        BleUuid foundServiceUuid;        
        size_t svcCount = scanResults[ii].advertisingData.serviceUUID(&foundServiceUuid, 1);

        Log.trace("MAC: %02X:%02X:%02X:%02X:%02X:%02X | RSSI: %dBm Services: %d",
                scanResults[ii].address[0], scanResults[ii].address[1], scanResults[ii].address[2],
                scanResults[ii].address[3], scanResults[ii].address[4], scanResults[ii].address[5], scanResults[ii].rssi,
                svcCount);
        
        
        Log.trace(scanResults[ii].scanResponse);


        //Log.trace(foundServiceUuid.~BleUuid);
        //Log.trace(serviceUuidData.~BleUuid);
        //foundServiceUuid == serviceUuidData
        
        //String sstuff = scanResults[ii].advertisingData.

        //String name = scanResults[ii].advertisingData.deviceName();
        //if (name.length() > 0) {
        //    Log.trace("Advertising name: %s", name.c_str());
        //}
    }

I keep randomly typing keywords into the log function trying to get lucky and get something useful from any of the many keywords being used throughout the entire process.

I was trying to figure out why the “if” was never true when looking for the UUID, so I’ve been trying to just get the values of these variables out on a screen to debug. I can’t even figure out how to do that, just feeling overwhelmed. If this were in C it would take me about 5 seconds to write what I’m looking for.

That's where I'm running into problems. I don't know what lines of code to type to do most of the things you are asking. I keep trying pure guess work but all the examples I find are for ESP32 or they are for Particle but they don't have hardly any comments or explanation.

I really appreciate your help on this. Sorry to be so frustrated just don't know what else to do.

I can't figure out how to get at the UUID to print it. I have no idea what to type. I tried to use Serial.print, the Log. functions, my EE101 debugger functions, etc. I do not know the exact syntax to get a UUID into a String variable to even print it or compare it or work with it in any way.

I don't know if any of the .getValue or .setValue work at all because the program never gets that far. I can't get it to match the UUID of the service to even being the peer.connect process. I can't get that "if" statement to be true.

I would like to keep working a path with my current attempt, maybe you would be willing to just help a couple of steps? If I just know the syntax of a couple lines I think I will have this working in seconds.

Here is the present code below.

My Trace log to serial is showing the following:

BLE.scan found 3 devices
0000092966 [app] TRACE: MAC: AB:CC:6A:8D:7C:C4 | RSSI: -62Bm Services: 1
0000092967 [app] TRACE: MAC: 64:08:FA:CA:9C:7C | RSSI: -45Bm Services: 0
0000092968 [app] TRACE: MAC: 24:38:0E:34:CA:78 | RSSI: -84Bm Services: 0

^^^ That’s more progress than I’ve had in two weeks right there.

I want the C4:7C Mac with the 1 service (I think that’s what the 1 is, but not sure)

I am now trying to read the UUID of that one service, but I don’t know what line of code to write to do that.

void loop() {

    loop_counter++;
    Log.trace("loop %d", loop_counter);
    //delay(100);


    Log.trace("BLE.scan");                                            
    auto start_time = millis();                                       //mark start of the BLE scan
    int scan_result_count = BLE.scan(scanResults, SCAN_RESULT_MAX);   //SCAN FOR NEARBY BLE DEVICES, returns count of found BLE devices nearby
    auto finish_time = millis();                                      //mark end of the BLE scan
    Log.trace("Scan duration (ms): %d", finish_time - start_time);    //Serial.printlnf("Scan duration (ms): %d", finish_time - start_time);
    Log.trace("BLE.scan found %d devices", scan_result_count);        //Serial.printlnf("Found %d results.", count);
    

    for (int ii = 0; ii < scan_result_count; ii++) {
        
        BleUuid foundServiceUuid;        
        size_t svcCount = scanResults[ii].advertisingData.serviceUUID(&foundServiceUuid, 1);

        Log.trace("MAC: %02X:%02X:%02X:%02X:%02X:%02X | RSSI: %dBm Services: %d",
                scanResults[ii].address[0], scanResults[ii].address[1], scanResults[ii].address[2],
                scanResults[ii].address[3], scanResults[ii].address[4], scanResults[ii].address[5], scanResults[ii].rssi,
                svcCount);
        
        
        //Log.trace(scanResults[ii].scanResponse);


    }


    delay(3000);

    
}

You already know the UUIDs of the characteristics so you "just" need to gain access to it via the getCharacteristicByUUID() method. This should return a reference to the characteristics object and with that you should be able to investigate its state.

If you want to read from a READ characteristic you'd use one of these functions

Usually the (raw) error messages and related notes from the compiler give you some hints of how to correct a given error.

I come from a C background and have transitioned to C++ somewhat later and found you shouldn't really let yourself be distracted by those extra two plus signs - when it comes to complie errors and debugging they are not that different when comparing a complex C project with an equally complex C++ one.
With C++ you typically just have more "custom" functions (wrapped in objects) you need to consider the interfaces of. For the Particle ecosystem the open source repo is something that provides all insight you need but the (lagging behind) docs won't provide (yet).
For BLE this is my go-to source

In there you'll also find how to convert a UUID into a string via the .toString() method (line #236 in the above linked header file).
And if you want to use a String object with printf() you'd need to cast that to (const char*)someString or use the String class method someString.c_str().

So in your snippet above you should be able to add this to see the service UUID

  if (foundServiceUuid.isValid())
    Serial.printlnf("Service UUID: %s", (const char*)foundServiceUuid.toString());
1 Like

I don't know how to do that. That is what I am saying. I don't understand.

^^^ this will not compile. I don't know how to ask the question any differently. The "Reference" page is over my head. I do not understand how to use the examples. I don't understand how all the objects and methods and stuff are related.

If you can write me an example to do the following, I think it would be immensely helpful.

  1. Iterate through a scan result and print the interesting parts to log.

  2. Show how to print the serviceUUID to the log on each iteration of the loop. I think this involves the object BleUuid foundServiceUuid, but I don't know. I can't understand the code. Also the following line that populates svcCount, I don't understand exactly what it is doing.

  3. For anything that has svcCount > 0, I intend to scan it's service UUIDs and see if my target UUID is in that list. But I don't know what exact line of code to write do do this. How do you scan or get access to the service UUID? I don't know if it's separate process to scan or not.

Thank you again.

Have you tried it? VS Code IntelliSense is not always understanding the code correctly and hence may flag syntax that's otherwise perfectly fine :wink:
But in this case you cannot just call a method without its contining object. getCharacteristicByUUID belongs to a BlePeerDevice object and hence you have to call it like

Log.trace(peer.getCharacteristicByUUID(yourCharacteristicsObjectVariable, yourDesiredCharacteristicsUuidObjectVariable))

(this will only print the bool return value of the function call tho')

Without a connected peer that "owns" that charactersitic and is "willing" to grant you access to it, you won't get to it.

e.g. look at the linked header file (lines #446 - owning class - and #469 - method definition).


a quick breakdown

  BleUuid foundServiceUuid;       // this can only hold *one* service UUID
  // scan for one and only one service UUID (for devices which expose more an array and its extent want to be passed in
  size_t svcCount = scanResults[ii].advertisingData.serviceUUID(&foundServiceUuid, 1);
  // svcCount can be 0 (no services found) or 1 (since we only scanned for one)

  Log.trace("Found %d services", svcCount);
  for (int i = 0; i < svcCount; i++) 
    Log.trace("Service %d, UUID: %s", i, (const char*)foundServiceUuid.toString());     

It does not compile. I get this error:
error: ‘getCharacteristicByUUID’ was not declared in this scope

I don’t understand how peer is related here. Please just show me a complete line of code that can be directly copied and complied.

As I’m iterating through scanResults[ii], what exactly would I write to log a published service UUID to the log? That’s literally all I’m asking here.

There is no peer at this point, I’m still trying to iterate through the scan results. I want to know the UUID of any services of the unit I am presently on as I iterate through scanResults.

BLE uses a hierarchical structur (lending itself to OOP) where a

  • device
    • exposes 0…n services
      • which expose 1…n characteristics

In order to access a service you need to connect to the device first. Once connected this device is considered your peer device and hence is “willing” to grant you access to its services.

  1. in order to get access to a desired service you first scan for all devices around you
  2. you get a list of devices
  3. you traverse that list for any device that “pretends” to expose your desired service
  4. if there are any you connect to one of them (make it a peer)
  5. you ask your peer for access to the service
  6. if it really exposes that service and is willing to grant you access you can ask move on
  7. once you have found that service you can ask the peer for access to the service characteristics
  8. once you got a reference to the characteristics object you can use it

I wouldn't have come up with that in a million years. That's extremely cryptic to me.

: MAC: AB:CC:6A:8D:7C:C4 | RSSI: -64Bm Services: 1
0000061152 [app] TRACE: Found 1 services
0000061153 [app] TRACE: Service 537052816, UUID:

^^^So now I'm getting this response.

Is there a way to see the Service in standard UUID format or something similar? I'm looking at the Log.trace and it accepts a %d and a %s, but there is only one argument. The argument is far far too cryptic for me to understand.

Sorry, there was an error - I missed one variable in the second Log.trace() line.

But I’m not sure what’s cryptic in that - in C you would find similar things
e.g. consider this

struct sUUID {
  int  id;
  char toString[1][32];
};
sUUID foundServiceUuid;

Log.trace("Service# %d, UUID: %s", foundServiceUuid.id, (const char*)foundServiceUuid.toString[0]); 

not that much difference IMO

Awesome. lol. I’m not crazy for once. lol.

The Service I’m looking for is “00001204-0000-1000-8000-00805f9b34fb”, but this function is returning 537052816.

You have no idea man, we just made like 90% progress right there.:smiley:

Have you corrected the error I made?

Coming from C the missing i variable that should have gone into the %d field of the format string should have been obvious.
Due to that omission the string pointer was taken for %d and hence you got 537052816 in place of the index and the %s field didn’t get anything to display (there was probably a compiler warning about the fact tho’).

I had no idea. I am so lost right now. I have no idea where an “i” would go in any of that.

Log.trace("Service %d, UUID: %s", (const char*)foundServiceUuid.toString());

I have no idea what the argument of this does. I never would have figured that out in a million years without an example.

I don’t know what term is missing.