BLE didnt sent the right data from one argon board to the other argon board

bluetooth
argon
Tags: #<Tag:0x00007fe220129940> #<Tag:0x00007fe2201295f8>

#1

Hi,

I want to set up a Bluetooth connection between two argon boards. I read some tutorials and wrote the code shown below based on two tutorials and the connection was set up. But the data read from the receiver is a strange number. So I would like to ask some questions about how to use BLE APIs to get the data based on my codes.

Here is my code for receiver:

const size_t SCAN_RESULT_MAX = 30;
const char * serviceUuid = "2baa45e8-1ecc-46b8-ac73-d716bd18696e";
const char * valueUuid = "8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc";

BleCharacteristic valueCharacteristic;

BleScanResult scanResults[SCAN_RESULT_MAX];

BlePeerDevice peer;

void onDataReceived(const uint8_t* data, size_t len, const 
BlePeerDevice& peer, void* context);

void setup() {
    
    Serial.begin();
    
    valueCharacteristic.onDataReceived(onDataReceived, NULL);
    
}

void loop() {
    
    if (BLE.connected())
    {
        //we are currently connected to the board
        Serial.println("connected");
    }
    
    else 
    {
        //we are not connected to the board, scan for it.
        
        int count = BLE.scan(scanResults, SCAN_RESULT_MAX);

        for (int ii = 0; ii < count; ii++) 
        {
            uint8_t buf[BLE_MAX_ADV_DATA_LEN];
            size_t len;
            
            //we are looking for devices that have a the same uuid
            len = scanResults[ii].advertisingData.get(BleAdvertisingDataType::SERVICE_UUID_128BIT_COMPLETE, buf, BLE_MAX_ADV_DATA_LEN);
            
            if (len > 0) 
            {
                for (size_t jj = 0; jj < len; jj += 1)
                {   // it should be uint128_t, but it was not able to be complied, so using uint64_t here
                    if(*(uint64_t *)buf[jj] == *serviceUuid) 
                    { 
                        // Found the right device 
                        peer = BLE.connect(scanResults[ii].address);
                        if (peer.connected()) 
                        {
                            Serial.println("successfully connected!");
                            //get the sensing data
                            valueCharacteristic = peer.getCharacteristicByUUID(BleUuid(valueUuid));
                            
                        }
                        else 
                        {
                            Serial.println("connection failed!");    
                        }
                        

                    }
                
                }
            }
        
        }
    }
}
void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
    uint16_t value;
    memcpy(&value, &data, sizeof(data));
    
    Serial.println("this is the value:");
    Serial.println(value);
}

Here is my code for transmitter:

const BleUuid serviceUuid("2baa45e8-1ecc-46b8-ac73-d716bd18696e");
const BleUuid valueUuid("8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc");

BleCharacteristic *valueCharacteristic("value", BleCharacteristicProperty::NOTIFY, valueUuid, serviceUuid);

const unsigned long UPDATE_PERIOD_MS = 100;
unsigned long lastUpdate = 0;

void setup() {
  Serial.begin();
  
  // Advertising data
  BLE.addCharacteristic(valueCharacteristic);
  
  BleAdvertisingData advData;
  advData.appendServiceUUID(serviceUuid);
  // Start advertising!
  BLE.advertise(&advData);

}

typedef union {
    struct {
        float x;
        float y;
        float z;
    } sample;
    uint8_t bytes[12];
} Sample;

void loop() {
    if (BLE.connected()){
        if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
            lastUpdate = millis();
            
            Sample sample;
            sample.sample.x = 1;
            sample.sample.y = 1;
            sample.sample.z = 1;
            
            valueCharacteristic.setValue(sample.bytes, sizeof(Sample));
        }
        Serial.println("connected");
    }
    else{
        Serial.println("connecting");
    }
}

Result:
In the transmitter code, I set the x, y z to be 1 respectively. But what the receiver printed out in onDataReceived() function is 41144.

Question:
In the transmitter code, I have valueCharacteristic, and I have this line:

valueCharacteristic.setValue(sample.bytes, sizeof(Sample));

based on this webpage, https://rickkas7.github.io/ble-imu/
But I dont quite understand this line. I am not sure whether the data (x, y, z) needs to be assigned to valueCharacteristic which makes more sense than assigned with a buffer. Not sure why not assign x, y, z to the valueCharacteristic in the tutorial.

Then in the receiver code, I called onDataReceived() based on the tutorial in this link: https://docs.particle.io/tutorials/device-os/bluetooth-le/#examples

But I am also not sure how to get the sensing data from it. I understand it is a callback function. I understand to use BLE.scan to check the match of Uuid. Then get the characteristic based on its Uuid once found the right service. Next, based on the tutorial, the data will be read from onDataReceived(). I am not sure the logic here. I thought the data should be read from the characteristic.

Secondly, I am not sure how to check the 128bit uuid, as it was not able to be compiled and I used uin64_t instead.

And I am not sure why in the tutorial, the for loop shown below is used to check uuid. Not quite sure the meaning of checking buf[jj] for the serviceUuid. scanResults[ii] is the iith scaned service and buf[] stores its serviceUuid through
advertisingData.get(BleAdvertisingDataType::SERVICE_UUID_128BIT_COMPLETE, ....)
Not sure why needs for loop.

for (size_t jj = 0; jj < len; jj += 1)
                {   // it should be uint128_t, but it was not able to be complied, so using uint64_t here
                    if(*(uint64_t *)buf[jj] == *serviceUuid) 
                    { 
                        // Found the right device 
...

Thank you for your time and help.


#2

Your uint16_t value in onDataReceived cannot hold 12 bytes of data you try to catch.

This line just hands a reference to the “block” of data you want to transmit (along with it’s size - 3*4 = 12 bytes) to the write characteristic for it to send it out.

Because the write characteristic doesn’t care what all the individual bytes are meant to be, it just needs to know which bytes to push and how many of them.
A byte buffer is the common denominator for all collections of data.

No, the BLE interface does that - the services and characteristics are just there to tell all kinds of datastreams and protocols apart.

Not quite, buf[] stores the BleAdvertisingData object which may consist of multiple parts.
And the loop would allow you to iterate over individual bytes of the requested data.
In case of the example you may get more than one SERVICE_UUID_16BIT_COMPLETE entries and in order to find the correct one you’d loop over the list in 2byte steps (hence jj+=2 in the sample).
But if you want to compare the 128bit UUID you cannot just cast that to uint64_t* and expect it to compare correctly to a const char*.
Your should rather use BleUuid. This class also provides the correct means to compare two instances correctly (and if there should be more than one you’d iterate via jj+=16 - 128bit equals 16byte).


#4

Thanks so much ScruffR!

I made some changes, but now even the connection doesnt work.

Your uint16_t value in onDataReceived cannot hold 12 bytes of data you try to catch.

I changed to const unsigned char* as shown below:

void onDataReceived(const unsigned char* data, size_t len, const 
BlePeerDevice& peer, void* context);

Does this make sense?

Not quite, buf[] stores the BleAdvertisingData object which may consist of multiple parts. And the loop would allow you to iterate over individual bytes of the requested data. In case of the example you may get more than one SERVICE_UUID_16BIT_COMPLETE entries and in order to find the correct one you’d loop over the list in 2byte steps (hence jj+=2 in the sample). But if you want to compare the 128bit UUID you cannot just cast that to uint64_t* and expect it to compare correctly to a const char* . Your should rather use BleUuid . This class also provides the correct means to compare two instances correctly (and if there should be more than one you’d iterate via jj+=16 - 128bit equals 16byte).

I tried BleUuid, but seems wrongly used it as the connection was not set up.

if (len > 0) 
            {
                for (size_t jj = 0; jj < len; jj += 16)
                {
                    BleUuid receivedUuid(buf[jj]);
                    BleUuid serviceUuid(serviceUuid);
                    // if(*(uint64_t *)buf[jj] == *serviceUuid) 
                    if (receivedUuid == serviceUuid)
                    { 
                        // Found the right device
...

Thank you for your help.


#5

Actually no, the thing you changed was originally a const uin8_t* data but I explicitly said uint16_t value.
You need to keep the callback signature unaltered but correct the implementation.

Here you should have the same struct that you already used in your transmitter in order to receive the same data.

With regards to BleUuid, I rather meant something along this line (in your receiver code)

// not this
// const char * serviceUuid = "2baa45e8-1ecc-46b8-ac73-d716bd18696e";
// const char * valueUuid = "8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc";
// but
const BleUuid serviceUuid("2baa45e8-1ecc-46b8-ac73-d716bd18696e");
const BleUuid valueUuid("8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc");
// as you have in your transmiter code
...
                    if(*(BleUuid *)&buf[jj] == serviceUuid) 

(and don’t forget to adjust the increment in your for() loop)


#7

Thanks ScruffR!

Unfortunately, after correcting the mistakes, the connection was still not built up.

Here is my updated code for receiver.

const size_t SCAN_RESULT_MAX = 30;

const BleUuid serviceUuid("2baa45e8-1ecc-46b8-ac73-d716bd18696e");
const BleUuid valueUuid("8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc");

BleCharacteristic valueCharacteristic;
BleScanResult scanResults[SCAN_RESULT_MAX];
BlePeerDevice peer;

void onDataReceived(const uint8_t* data, size_t len, const 
BlePeerDevice& peer, void* context);

void setup() {
    
    Serial.begin();
    
    valueCharacteristic.onDataReceived(onDataReceived, NULL);
    
}

void loop() {
    Serial.println("verion 2");
    if (BLE.connected())
    {
        //we are currently connected to the board
        Serial.println("connected");
    }  
    else 
    {
        //we are not connected to the board, scan for it.
        int count = BLE.scan(scanResults, SCAN_RESULT_MAX);
        
        for (int ii = 0; ii < count; ii++) 
        {
            uint8_t buf[BLE_MAX_ADV_DATA_LEN];
            size_t len;
            
            //we are looking for devices that have a the same uuid
            len = scanResults[ii].advertisingData.get(BleAdvertisingDataType::SERVICE_UUID_128BIT_COMPLETE, buf, BLE_MAX_ADV_DATA_LEN);
            
            if (len > 0) 
            {   //changed increment from 1 to 16
                for (size_t jj = 0; jj < len; jj += 16)
                {
                    if (*(BleUuid *)buf[jj] == serviceUuid) 
                    { 
                        // Found the device s1
                        peer = BLE.connect(scanResults[ii].address);
                        if (peer.connected()) 
                        {
                            Serial.println("successfully connected!");
                            //get the sensing data
                            // valueCharacteristic = peer.getCharacteristicByUUID(BleUuid(valueUuid));
                            valueCharacteristic = peer.getCharacteristicByUUID(valueUuid);
                        }
                        else 
                        {
                            Serial.println("connection failed!");    
                        }
                    }
                }
            }
        }
    }
}


void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
    //changed from uint16_t to uint8_t
    uint8_t value;
    memcpy(&value, &data, sizeof(data));
    
    Serial.println("this is the rate:");
    Serial.println(value);
}

Thank you for the help.


#8

How are you building the code and then flashing to the device? Cloud building is not working right for BLE we have found out. Building locally and flashing locally via Workbench does work.


BLE OTA uploading
#9

Thanks RWB!
I am using web IDE now. I will try the workbench. But the code I originally posted was able to build up the BLE connection. Was that just an accident?


#10

Probably not an accident but we do know that new code dealing with BLE will not properly take effect if flashed over the air. For now it’s best to build and flash locally to avoid any potential issues as others have noticed recently.


#11

Thanks RWB!


#12

Now you can only store one byte and try to cram four bytes (sizeof(data ) will be 4 as data is a pointer and not an array) into value.
You want to store len (probably 12) bytes

When you want to debug such an issue you should add some more Serial.print() statements in as many strategically important places (e.g. all else cases) to get an idea what might be the cause - that’s better than guessing it might be some issue outside of your own reach.
Only after you can be certain your code does everything right, you should assume an “outside” issue.


#13

Thanks, ScruffR! I guess here is why you mentioned 12 bytes, which are in my transmitter code.

typedef union {
    struct {
        float x;
        float y;
        float z;
    } sample;
    // 12 bytes here
    uint8_t bytes[12];
} Sample;

void loop() {
    if (BLE.connected()){
        if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
            lastUpdate = millis();
            
            Sample sample;
            sample.sample.x = 1;
            sample.sample.y = 1;
            sample.sample.z = 1;
            
            valueCharacteristic.setValue(sample.bytes, sizeof(Sample));

Since uint8_t bytes[12];, does it make sense to have uint8_t value; in onDataReceived()?

Or 12 bytes (3*4) means 4 bytes for each (x, y, z)?

By the way, thanks for the suggestion on adding serial.println(). I have that in my code but just to make the code in a post more readable, I deleted them before I posted.

I found the problem now is that the BLE connection cannot be set up which worked in my original posted code.

Thank you for your help.


#14

No it doesn’t - uint8_t value this is one single byte while uint8_t bytes[12] are twelve and forcing 12 people on 1 chair won’t work, cramming 12 bytes into a single one won’t do either.

When this is the data on the sending side why not use the same struct on the receiving side?

Yes, I gathered that, but where in code does it fail?

BTW, there was a minor mistake in my suggested equality check (corrected above)
Instead of

if(*(BleUuid *)buf[jj] == serviceUuid) 

it should have been

if(*(BleUuid *)&buf[jj] == serviceUuid) 

I normally don’t “spoon feed” solutions, but it took long enough to get here so this is working code including strategically well placed Serial.print() statements that would help understand why things don’t work out as they should.

#define IS_RECEIVER 1 // set to 0 when you want to build the transmitter code

// common to receiver and transmitter 

SYSTEM_MODE(MANUAL)
SYSTEM_THREAD(ENABLED)

const BleUuid serviceUuid("2baa45e8-1ecc-46b8-ac73-d716bd18696e");
const BleUuid valueUuid("8b9c77e1-54a4-41c5-bc70-7b5cd4c037cc");
const size_t   SCAN_RESULT_MAX  = 30;
const uint32_t UPDATE_PERIOD_MS = 500;

typedef union {
  struct
  {
    float x;
    float y;
    float z;
  } coordinates;
  uint8_t bytes[12];
} Sample;
Sample sample;

#if (IS_RECEIVER) // receiver code

BleCharacteristic valueCharacteristic;
BleScanResult scanResults[SCAN_RESULT_MAX];
BlePeerDevice peer;

void onDataReceived(const uint8_t *data, size_t len, const BlePeerDevice &peer, void *context);

void setup()
{
  Mesh.off();
  Serial.begin();
  valueCharacteristic.onDataReceived(onDataReceived, NULL);
}

void loop()
{
  static uint32_t msLastPrint = 0;
  if (millis() - msLastPrint < UPDATE_PERIOD_MS) return;
  msLastPrint = millis();

  if (BLE.connected())
    Serial.println("connected");
  else {
    int count = BLE.scan(scanResults, SCAN_RESULT_MAX);

    for (int ii = 0; ii < count; ii++) {
      uint8_t buf[BLE_MAX_ADV_DATA_LEN];
      size_t len;
      Serial.printlnf("%02x:%02x:%02x:%02x:%02x:%02x"
                     , scanResults[ii].address.addr[0]
                     , scanResults[ii].address.addr[1]
                     , scanResults[ii].address.addr[2]
                     , scanResults[ii].address.addr[3]
                     , scanResults[ii].address.addr[4]
                     , scanResults[ii].address.addr[5]
                     );

      len = scanResults[ii].advertisingData.get(BleAdvertisingDataType::SERVICE_UUID_128BIT_COMPLETE, buf, BLE_MAX_ADV_DATA_LEN);
      if (len > 0) {
        for (size_t jj = 0; jj < len; jj += 16)
        {
          if (*(BleUuid *)&buf[jj] == serviceUuid)
          {
            peer = BLE.connect(scanResults[ii].address);
            if (peer.connected()) {
              Serial.println("successfully connected!");
              valueCharacteristic = peer.getCharacteristicByUUID(BleUuid(valueUuid));
            }
            else {
              Serial.println("connection failed!");
            }
            break; // out of for(jj) loop
          }
          else {
            for (int u = 0; u < 16; u++)
              Serial.printlnf("%02x %c= %02x", ((BleUuid *)&buf[jj])->full()[u], ((BleUuid *)&buf[jj])->full()[u] == serviceUuid.full()[u] ? '=' : '!', serviceUuid.full()[u]);
            Serial.println("not the same UUID");
          }
        }
      }
      else {
        Serial.printlnf("len == %d", len);
      }
    }
  }
}

void onDataReceived(const uint8_t *data, size_t len, const BlePeerDevice &peer, void *context)
{
  memcpy(sample.bytes, data, len);
  Serial.printlnf("%.2f / %.2f / %.2f", sample.coordinates.x, sample.coordinates.y, sample.coordinates.z);
}

#else // transmitter code

BleCharacteristic valueCharacteristic("value", BleCharacteristicProperty::NOTIFY, valueUuid, serviceUuid);

void setup()
{
  Mesh.off();
  Serial.begin();
  BLE.addCharacteristic(valueCharacteristic);
  BleAdvertisingData advData;
  advData.appendServiceUUID(serviceUuid);
  BLE.advertise(&advData);
}

void loop()
{
  static uint32_t msLastUpdate = 0;
  if (BLE.connected()) {
    if (millis() - msLastUpdate >= UPDATE_PERIOD_MS) {
      msLastUpdate = millis();

      sample.coordinates.x = 1.1;
      sample.coordinates.y = 1.2;
      sample.coordinates.z = 1.3;

      valueCharacteristic.setValue(sample.bytes, sizeof(Sample));
      Serial.println("Data sent");
    }
  }
  else {
    Serial.println("Waiting for client to connect");
  }
}

#endif

However, there is some issue with your transmitter code (which I haven’t looked into at all) that sends the transmitter into SOS+10 panic when you start the receiver after the transmitter.


#15

Thanks soooo much ScruffR! :smiling_face_with_three_hearts:
That is super nice of you! I have learned a lot by chatting with you! Thanks so much for kindly sharing your knowledge!