Sending byte array from arduino nano 33 ble Sense to mobile app

Hello there. We are trying to send distance data from Arduino nano 33 ble sense card to the mobile application we created with react native. But we saw that the distance was reset after 255 meters. The reason was that Blebytecharacteristic sent 1 byte of data. my question is; How can I send 2 bytes of distance data?

Note: React Native decodes data in base64 format.

Thanks in advance.

#include <ArduinoBLE.h>

#define deviceServiceUuid  "19b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceUuid2  "18b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceUuid3  "17b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceUuid4  "16b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceUuid5  "15b10000-e8f2-537e-4f6c-d104768a1214"

#define deviceServicegaitStyleCharacteristicUuid  "19b10001-e8f2-537e-4f6c-d104768a1214"
#define deviceServicegaitDistanceCharacteristicUuid  "18b10001-e8f2-537e-4f6c-d104768a1214"
#define deviceServicegaitSpeedCharacteristicUuid  "17b10001-e8f2-537e-4f6c-d104768a1214"
#define deviceServicegaitMaxspeedCharacteristicUuid  "16b10001-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceTimeCharacteristicUuid  "15b10001-e8f2-537e-4f6c-d104768a1214"


//char buf[20];


BLEService gaitService(deviceServiceUuid);
BLEService distanceService(deviceServiceUuid2);
BLEService speedService(deviceServiceUuid3);
BLEService maxspeedService(deviceServiceUuid4);
BLEService timeService(deviceServiceUuid5);

BLEByteCharacteristic gaitStyleChar(deviceServicegaitStyleCharacteristicUuid, BLERead | BLENotify);
BLEUnsignedShortCharacteristic gaitDistanceFloat(deviceServicegaitDistanceCharacteristicUuid, BLERead | BLENotify);
BLEByteCharacteristic gaitSpeedInt(deviceServicegaitSpeedCharacteristicUuid, BLERead | BLENotify);
BLEByteCharacteristic gaitMaxspeedInt(deviceServicegaitMaxspeedCharacteristicUuid, BLERead | BLENotify);
BLEUnsignedShortCharacteristic TimeInt(deviceServiceTimeCharacteristicUuid, BLERead | BLENotify);


unsigned long wt;
unsigned long tt;
unsigned long kt;
unsigned long gt;

double ddwalk;
byte Speed=0;
byte maxspeed=0;
byte gaitStyle=0 ;

void setup() {
  BLE.begin();
  BLE.setAdvertisedService(gaitService);
  gaitService.addCharacteristic(gaitStyleChar);
  BLE.addService(gaitService);
  
  BLE.setAdvertisedService(distanceService);
  distanceService.addCharacteristic(gaitDistanceFloat);
  BLE.addService(distanceService);
  
  BLE.setAdvertisedService(speedService);
  speedService.addCharacteristic(gaitSpeedInt);
  BLE.addService(speedService);
  
  BLE.setAdvertisedService(maxspeedService);
  maxspeedService.addCharacteristic(gaitMaxspeedInt);
  BLE.addService(maxspeedService);
  
  BLE.setAdvertisedService(timeService);
  timeService.addCharacteristic(TimeInt);
  BLE.addService(timeService);

   BLE.advertise();

}

void loop() {
  BLEDevice central = BLE.central();
  if(central){
    
  while(central.connected()){
     gaitStyleChar.writeValue(gaitStyle);
    gaitDistanceFloat.writeValue((unsigned short)ddwalk);
    gaitSpeedInt.writeValue(Speed);
    gaitMaxspeedInt.writeValue(maxspeed);
    TimeInt.writeValue((unsigned short)wt);
  }}
}

How can I send 2 bytes of distance data?

Perhaps you should be using one of the other typed characteristics.
https://www.arduino.cc/en/Reference/BLECharacteristicConstructor

When I define it in this way, it sends 1 byte of data. I only needed to write blecharacteristic for 2 bytes. But I couldn't find how to write it exactly. That's why I shared the code sample.

double ddwalk;
BLEUnsignedShortCharacteristic gaitDistanceFloat(deviceServicegaitDistanceCharacteristicUuid, BLERead | BLENotify);
gaitDistanceFloat.writeValue((unsigned short)ddwalk);

I do not understand. You cast the double to unsigned short (uint16_t) and write that value which should send two bytes.

If you are only seeing one byte being sent, perhaps the issue is with the code which is reading what is sent.

I am using base64 library to decode the code. I saw that it would be enough.

I know nothing about decoding with the base 64 library.
I don't think I can help you further. Sorry. :frowning_face:

An alternative methodology is to send/receive a structure:

https://github.com/LowPowerLab/RFM69/tree/master/Examples

Look at the Struct_send & struct_receive examples.

#include <ArduinoBLE.h>

#define deviceServiceUuid  "19b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceTimeCharacteristicUuid  "15b10001-e8f2-537e-4f6c-d104768a1214"


//char buf[20];



BLEService timeService(deviceServiceUuid);


BLECharacteristic TimeInt(deviceServiceTimeCharacteristicUuid, BLERead | BLENotify,sizeof(uint16_t));


uint16_t times = 1278;

void setup() {
  BLE.begin();

  
  BLE.setAdvertisedService(timeService);
  timeService.addCharacteristic(TimeInt);
  BLE.addService(timeService);

   BLE.advertise();

}

void loop() {
  BLEDevice central = BLE.central();
  if(central){
    
  while(central.connected()){
  uint8_t bytes [sizeof(int)]
    {
      ((uint16_t)times >> 0) & 0xFF,
      ((uint16_t)times >> 8) & 0xFF,
    };
  
    TimeInt.writeValue(bytes,sizeof(uint16_t));
  }}
}

Is this code valid? Now when I send data with this code, it still only receives the first 8 bits. I cannot exceed 254.

When I load your latest code into a Nano33 BLE and connect with nrfConnect I see the service and characteristic and receive the value fe04 in hex. I'm not clear of the endian situation with the app reported value but

04fe = 1278 decimal

Indeed, in your first posted sketch when I give a value
unsigned long wt = 0xABCD;

I can see that value in nrfConnect for the characteristic defined
BLEUnsignedShortCharacteristic TimeInt(deviceServiceTimeCharacteristicUuid, BLERead | BLENotify);

As expected, the app reported the value as 0xCD-AB

You can use the generic option where you set the size of your byte array (as default is a variable sized array you simply set a max limit or alternatively set fixedLength to true):

BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);

Otherwise, as suggested by another use one of the typed characteristics which has equivalent size of 2 bytes.

The OP has indeed tried both approaches. My advice has been to focus on the receiving app, since nrfConnect can see the correct 2 bytes with either approach.

Ah, I see that now. Yes I would agree with your advice. As you say, the BLE utility app nrfConnect can correctly see the 2 bytes with either approach.

Welcome to the forum.

Here is some general info about BLE that may help understand the situation.

  • In BLE the peripheral/server device application layer does not send data. You write to the characteristic and that is it.

  • This is true even with notifications. A client can subscribe to a characteristic and still ignore the notifications and not read the data (most generic apps do read them automatically, but they do not have to).

  • The BLE characteristic you declare describes how many bytes are available to a central/client that likes to read/write it. If you make it two bytes in size that is what a client will see and then it can decide to read that. If you write only one byte the client will still see two bytes in the characteristic.

This is more like a newspaper web page. They can update their page as often as they want if nobody reads it for an hour nothing happens. BLE is more like that then television.

About your code:

In reply #8. Writing to a characteristic in a tight loop is not a good idea. You must give the radio time to notify any subscribed clients and they may want to read the data. In some cases, this may even affect the BLE stack. Some Arduinos have a separate module for BLE and need to talk to it through a serial interface. That may not like it when you bother it all the time with updating the characteristic. I recommend you add some code to update the characteristic only when it has changed and limit the maximum amount using a millis timer.

In post #1. There is only one advertised service. Choose the one your app will be looking for during scan. Any other service will be found during discovery.

A service can have multiple characteristics. Services are just groups for characteristics. They are useful to have a single UUID for scanning. Multiple services with just one characteristic each is likely not a good choice, but their could be use cases were this is OK.

I managed to send it as a byte array, but I still see the number 1278 as 49,50,51.0 on the mobile application screen. Thanks, I'll take a look.

This seems very peculiar. If you have decided to sent the number 1278 as the ascii bytes of text, then you should see (depending on endianess) 49,50,55,56 for "1278".

If your app is showing 51.0 , where is that coming from?

If your app can now show two bytes of an attempt to send four bytes of ascii, then what was the issue with displaying the two bytes of a uint16_t?

When I write the code like this, I get the following output. I wonder if the problem is in my arduino code or in my mobile app code? I think there is a problem while decoding with react native base64.

#include <ArduinoBLE.h>

#define deviceServiceUuid  "19b10000-e8f2-537e-4f6c-d104768a1214"
#define deviceServiceTimeCharacteristicUuid  "15b10001-e8f2-537e-4f6c-d104768a1214"





BLEService timeService(deviceServiceUuid);


BLECharacteristic TimeInt(deviceServiceTimeCharacteristicUuid, BLERead | BLENotify,sizeof(uint16_t));


unsigned long previousMillis = 0;       
const long interval = 500;  

uint16_t times = 1278;

void setup() {
  BLE.begin();

  
  BLE.setAdvertisedService(timeService);
  timeService.addCharacteristic(TimeInt);
  BLE.addService(timeService);

   BLE.advertise();

}

void loop() {
  BLEDevice central = BLE.central();
  if(central){
    
  while(central.connected()){

  unsigned long currentMillis = millis();
  
  uint8_t bytes [sizeof(times)]
    {
      ((uint16_t)times >> 8) & 0xFF,
      ((uint16_t)times >> 0) & 0xFF,
      
     
    };
    
    if (currentMillis - previousMillis >= interval) {
    
    previousMillis = currentMillis;
    TimeInt.writeValue(bytes,sizeof(uint16_t));
}
  
     
  }}}

screenshot from my mobile app;

I do not know. I tried quite a way. I'm wondering if it's a problem with the app. Thanks for your support

You are certainly now reading two bytes, but the print out looks like a mixture of hex and decimal. As discussed earlier 1278 is 04fe.

Yes true. Then what is the way to display it directly 1278? That's the real problem.

You are asking us to resolve a problem on the app side, and its not clear what you are doing except that you have mentioned "react native" and I'm not familiar with that.

If you define a characteristic of 4 bytes send the bytes of ascii text "1278" can you receive the 4 bytes and display them as characters?