Esp32-snippets: Client and Notify passing data to each other

Created on 2 Jan 2018  路  21Comments  路  Source: nkolban/esp32-snippets

Hi, just a question: I have (essentially) a version of your BLE_Notify code on an ESP32 talking to another ESP32 with a version of BLE_Client (master/slave type thing).

I wanted the master to send data of a certain type (ie a _struct_), and the slave to send data back in response of another type (another struct).

In the main loop of client I was going to use:

struct CLIENT_DATA {
    long t;
};
CLIENT_DATA clientdata;

uint8_t bs[sizeof(clientdata)];
memcpy(bs, &clientdata, sizeof(clientdata));

pRemoteCharacteristic->writeValue(bs, sizeof(bs));

...and I thought that in the Master (BLE_Notify) I would do something like this:

class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {

    struct CLIENT_DATA {
        long t;
    };
    CLIENT_DATA clientdata;

    uint8_t *rxValue = pCharacteristic->getValue();
    memcpy(&clientdata, rxValue, sizeof(clientdata));

    Serial.printf("Received Value (t): %ld \n", clientdata.t);
};

This kind of doesn't work because "pCharacteristic->getValue();" is returning a String... not a uint8_t. Is there a better way that I have missed?

Thanks for your help in advance, much appreciated!

NB: I realise I'm kind of doing it the wrong way around (maybe should be from BLE_Notify first) but I need to get both ends sending data, perhaps in a Request/Response type pattern.

All 21 comments

You can study BLE_uart example, its basicaly code to emulate uart serial port over ble. You can send and receive data.
But your thinking is good so far. To communicate from peripheral (server) you can use notifications. Then client will receive messages each time when server has something to send. But you have to remember that notification message will be truncated to (MTU-3) message size.
To send message from client to server you can simply use characteristic->writeValue() and on server side implement BLECharacteristicCallbacks::onWrite(). Here you can write long messages.

Ok so the way you describe sending from Client to Server is exactly how I'm doing it... and I also modelled on how BLE_Uart was receiving, but it is receiving a "std::string" which is what "pCharacteristic->getValue()" is returning. I ultimately want getValue() to return a pointer (or adddress to data?).

String is a bunch of bytes, you can always convert string to bytes array. Also you can make some research about descriptor 2904, which was recently added with some features.
https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml

With this descriptor you can use BLERemoteCharacteristic->readUInt8() whenever you receive notification.

Now i have to correct myself. I said earlier you can use BLECharacteristic->writeValue(), thats incorrect. You need to use BLERemoteCharacteristic instead.

Awesome. Cheers for that. Looks like I might have some reading to do :)

Let me getting it working then I'll close this.

Hmm... still not working quite right.
Client

struct CLIENT_DATA {
    long t;
};
CLIENT_DATA clientdata;
clientdata.t = millis();

char bs[sizeof(clientdata)];
memcpy(bs, &clientdata, sizeof(clientdata));

pRemote_Rx_Characteristic->writeValue(bs, sizeof(bs));

Serial.printf("Sent (clientdata.t): %ld \n", clientdata.t);

and Server

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {

        struct CLIENT_DATA {
            long t;
        };
        CLIENT_DATA clientdata;
        clientdata.t = 0;

        std::string rxValue = pCharacteristic->getValue();

        byte rxBytes[sizeof(clientdata)];
        for (int i; i<rxValue.length(); i++) {
            rxBytes[i] = rxValue[i];
        }
        memcpy(&clientdata, rxBytes, sizeof(clientdata));

        Serial.printf("Received Value: %ld \n", clientdata.t);
    }
};

The client is (as you can see) a struct with .t = millis(), but the server (when printing out) only ever shows the value "4".

Did have a win though working out how to get the Notify working properly with BLE2902.
BLE2904 seems to only deal with primitive data types... I want to furnish my struct with more values (floats as well as longs). Understand that I have a limit (250 bytes?).

I will check it tommorow, today i have day off of the esp32, i just need a break to clear my mind. Here you have example how to write value to remote characteristic. It should works (it was) and BLE_server will print out every second string:
https://github.com/nkolban/esp32-snippets/blob/f0f287603328e2fe2f5b5df85e30e529b8e028c5/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino#L126-L130

BLE2904 deals only with primitives at the moment just because there was no requests to add new types to it. You can request to add features to BLE2904 here https://github.com/nkolban/esp32-snippets/issues/314
About length limitation. Honestly, i did not check what is the limit, but in one issue devs told us (if i understand it right) that bluedroid stack will manage to fragment any message between 22 and 600 bytes. Looks like i have another test project to do.

@skelstar I have one question. In this code integer value is changed into char. This mean every 2 digits from millis is treat as ASCII char in range 00-99 and send as char. Is it intentional:

char bs[sizeof(clientdata)];
memcpy(bs, &clientdata, sizeof(clientdata));

@skelstar This is my test and looks like your code did not pass it. bs is not keeping data you think it keeps (client side code):

...
pRemote_Rx_Characteristic->writeValue(bs, sizeof(bs));

  Serial.print("Sent (clientdata.t): ");
    Serial.println(bs);
    Serial.println(clientdata.t);

Nice find. Rookie mistake on my part!

I changed to:
byte bs[sizeof(clientdata)];'
...(client side) and I'm still getting "4" on the server. Have you left the server code unchanged from the example I posted?

Really appreciate you spending time on this (tests take time, right!)... if you want a break then I understand.

To tests im using BLE_server and BLE_client with changes you posted here

There is chance we have different BLE libraries though, because im using latest cpp_utils master library which is not available thru arduino-ide.

@skelstar Still it does not change anything unless you change millis() to string before. long type is 4 bytes thats why this array will be always 4 elements array:
byte bs[sizeof(clientdata)];

Then this command will change each element from array into uint8_t which is equivalent char:
pRemote_Rx_Characteristic->writeValue(bs, sizeof(bs));
and you will send max 4 bytes.

What you can do is changing millis to string or char[], which basically its the same.

Im going to create independent test codes for server and client.

Good point re: lastest cpp_utils master library. I might just copy them into the [appropriate] arduino hardware folder (backed up first).

Interestingly I could get the struct thing working from server -> client but when I sent:

struct SERVER_DATA {
    long t;
    float f;
};

... only the "long t" would "deserialise" properly.

This is from my test case. Data which is send is a structure like this one:

    struct CLIENT_DATA {
      short a;
      int b;
      uint8_t c;
      long t;
    };

Those are logs (on left client, on right server):
test

As we can see send and received data match. Which means we don have issues with sending or receiving data. Also Data: length: 16 is consistent with structure we are sending. The issue is with decoding data on server side.
Now where is the problem. With my example we are having 2 issues:

Its a lot material and a lot work to make it works, but its clearly issue with user code and not with library.

Far out. I think I might just use strings :). I kind of follow but this level of c/c++ is beyond me (I'm a c# web dev).

Thanks for much for your help. Appreciate the time.

Sorry but working with bluetooth is not easy. I lost week or two because windows 10 is stupid. After few months intensive studying i still feel like i am appreantice. We can help with bytes order with nice macro, and first issue its not big deal, you can just drop last byte which should be always 0x00.

But in arduino you can easy cast integer values to string and vice-versa.

maybe a dump question ... I have some problems to get values by the noble implementation on an raspberry. I tried I manually by gatttool to see what's incoming.
On the ESP I use BLEServer with Characteristic READ and NOTIFY and looping periodically
pCharacteristic-->setValue() and pCharacteristic-->notify().
Using the gatttool on the raspberry, the BLE Server gives back two characteristic handlers: 2a and 2b. 2a the values of the my characteristic UUID , 2b seems to be the notification flag. I set 2b on 0100 and, as expected, periodically incoming 2a values "Notification handle = 0x002a value: XXXX" . All things fine, I think.
My question is: then I set pCharacteristic-->notify(), the BLEServer send independently the notify message to the client? Then the client received that, he can or should (?) start a read action to read (in my case) the 2a handler? Or contains the notification message the updated value (against the indication procedure)?
Thx a lot!!

When server is sending notification then the value is already sent, no need to read it.
Of course you can have some logic that is sending notification and you want to read value. In some situations it can be desired, when you cant control MTU value. In such case notification can be only 20 bytes long (if default MTU is set), but in normal situations just receiving notifications is enough.

Oh yes, many thank!!! I was really confused - so I have to check the noble implementation and see why no data are incoming.

I would suggest to test with nRF connect on android or iPhone.

Oh yes, on mobile apps it works too. I try to generate BLE Sensors and communicate along noble/homebridge to send data into the HomeKit. Have some trouble to find out, there the value "hang on", got 0 every time, without any errors. But, that not the discussion here. Thx lot, again.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mkol5222 picture mkol5222  路  5Comments

mahdikan picture mahdikan  路  4Comments

d3cline picture d3cline  路  4Comments

HarrisonOfTheNorth picture HarrisonOfTheNorth  路  8Comments

wegunterjr picture wegunterjr  路  7Comments