BLE, 2 characteristics, One is getting overwritten due to same *char address?

I have two ESP32. I am trying to get two characteristics sent over bluetooth, but the 2nd one seems to be overwriting the first one on the client side.

ble app LightBlue shows the two different values. But if I use my client, it doesn't work. I tried printing the client's data's address, and the pointers seem to be pointing to the same character array.

I got help on this thread about character array not working on my server side. Before this fix and some other modifications, client side was getting data usually, but about 10% of the time the 1st characteristic got overwritten. Now it is overwritten all the time.

client code:

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp32-ble-server-client/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
float myTime;
#include "BLEDevice.h"

//BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "Simple_ESP32"

/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID dhtServiceUUID("5106ba9c-a085-4bea-b210-0272d8cd40dd");

// BLE Characteristics
//Temperature Celsius Characteristic
static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");

// Humidity Characteristic
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");

//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;

//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;

//Characteristicd that we want to read
static BLERemoteCharacteristic* temperatureCharacteristic;
static BLERemoteCharacteristic* humidityCharacteristic;

//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};


//Variables to store temperature and humidity
char* temperatureChar;
char* humidityChar;

//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;
boolean newHumidity = false;

//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();

  // Connect to the remove BLE Server.
  pClient->connect(pAddress);
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(dhtServiceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(dhtServiceUUID.toString().c_str());
    return (false);
  }

  // Obtain a reference to the characteristics in the service of the remote BLE server.
  temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
  humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);

  if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID");
    return false;
  }
  Serial.println(" - Found our characteristics");

  //Assign callback functions for the Characteristics
  temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
  humidityCharacteristic->registerForNotify(humidityNotifyCallback);
  return true;
}

//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
        advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
        pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
        doConnect = true; //Set indicator, stating that we are ready to connect
        Serial.println("Device found. Connecting!");
      }
    }
};

//When the BLE Server sends a new temperature reading with the notify property
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                                      uint8_t* pData, size_t length, bool isNotify) {
  //store temperature value
  temperatureChar = (char*)pData;
  Serial.print("temp notified:");Serial.println(temperatureChar);
  newTemperature = true;
}

//When the BLE Server sends a new humidity reading with the notify property
static void humidityNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                                   uint8_t* pData, size_t length, bool isNotify) {
  //store humidity value
  humidityChar = (char*)pData;
  Serial.print("humidity notified:");Serial.println(humidityChar);
  newHumidity = true;
}

//function that prints the latest sensor readings in the OLED display
void printReadings() {
  myTime = float(millis());
  Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
  Serial.print("humidity_Server address:"); Serial.print((int)humidityChar); Serial.print(", ");
  Serial.print("tempC_Server address:"); Serial.print((int)temperatureChar); Serial.print(", ");
  Serial.println();
  Serial.print("humidity_Server:"); Serial.print(humidityChar); Serial.print(", ");
  Serial.print("tempC_Server:"); Serial.print(temperatureChar); Serial.print(", ");
  Serial.println();
}

void setup() {
  //Start serial communication
  Serial.begin(115200);

  Serial.println("Starting Arduino BLE Client application...");

  //Init BLE device
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(30);
}

void loop() {
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("We are now connected to the BLE Server.");
      //Activate the Notify property of each Characteristic
      temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2903))->writeValue((uint8_t*)notificationOn, 2, true);
      connected = true;
    } else {
      Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
    }
    doConnect = false;
  }
  //if new temperature readings are available, print in the OLED
  if (newTemperature && newHumidity) {
    // Reading temperature or humidity takes about 250 milliseconds!
    Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
    Serial.print("humidity_Client:"); Serial.print(88); Serial.print(", ");
    Serial.print("tempC_Client:"); Serial.print(10); Serial.print(", ");
    printReadings();
    newTemperature = false;
    newHumidity = false;
  }
  delay(1000); // Delay a second between loops.
}

server code:

/*
    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
    Ported to Arduino ESP32 by Evandro Copercini
    updates by chegewara
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define bleServerName "Simple_ESP32"
// Timer variables
long myTime;
unsigned long lastTime = 0;
unsigned long timerDelay = 6000;
bool deviceConnected = false;

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "5106ba9c-a085-4bea-b210-0272d8cd40dd"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLECharacteristic tempCharacteristic("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor tempCharacteristicDescriptor(BLEUUID((uint16_t)0x2902));
BLECharacteristic humidityCharacteristic("ca73b3ba-39f6-4ab3-91ae-186dc9577d99", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor humidityCharacteristicDescriptor(BLEUUID((uint16_t)0x2903));

//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};


void setup() {
  BLEDevice::init(bleServerName);
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks()); //new

  BLEService *pService = pServer->createService(SERVICE_UUID);
  /*
    BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

    pCharacteristic->setValue("Hello World says Neil");
  */
  pService->addCharacteristic(&tempCharacteristic);//new
  tempCharacteristicDescriptor.setValue("temperature Celsius");
  tempCharacteristic.addDescriptor(&tempCharacteristicDescriptor);
  pService->addCharacteristic(&humidityCharacteristic);
  humidityCharacteristicDescriptor.setValue("humidity");
  humidityCharacteristic.addDescriptor(&humidityCharacteristicDescriptor);
  //humidityCharacteristic.addDescriptor(new BLE2902());

  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();

}
void loop() {
  myTime = float(millis());

  if (deviceConnected) {
    if ((millis() - lastTime) > timerDelay) {
      int h = 99;
      int t = 20;
      static char temperatureCTemp[7];
      dtostrf(t, 6, 2, temperatureCTemp);
      tempCharacteristic.setValue(temperatureCTemp);
      tempCharacteristic.notify();
      delay(1000);
      static char humidity[7];
      dtostrf(h, 6, 2, humidity);
      humidityCharacteristic.setValue(humidity);
      humidityCharacteristic.notify();
      lastTime = millis();
    }
  }
}

ble app LightBlue shows the two different values

In spite of the Light Blue report of two characteristics , I could never subscribe to and receive both values in Light Blue. I could get one or the other depending on which was added first, but never both. In general I have been seeing issues with adding multiple characteristics for one service. I will keep investigating, but as of now do not understand the issue.

Since I could not get Light Blue to read both characteristics, I did not try you client.

There is are two work arounds however. One is to have two services with one characteristic each. The other is to combine the output for both both values into one characteristic.
Here is loop() for your sketch with that second approach. It works with Light Blue, and it may indeed work with your client.

void loop() {
  myTime = float(millis());

  if (deviceConnected) {
    if ((millis() - lastTime) > timerDelay) {
      int h = 99;
      int t = 20;
      static char temperatureCTemp[7];
      dtostrf(t, 6, 2, temperatureCTemp);
      //tempCharacteristic.setValue(temperatureCTemp);
      //tempCharacteristic.notify();
      delay(1000);
      static char humidity[7];
      dtostrf(h, 6, 2, humidity);
      //humidityCharacteristic.setValue(humidity);
      //humidityCharacteristic.notify();
      static char comboOut[20];
      sprintf(comboOut, "T%s:H%s", temperatureCTemp, humidity);
      tempCharacteristic.setValue(comboOut);
      tempCharacteristic.notify();
      
      lastTime = millis();
    }
  }
}

Output to Light Blue

T 20.00:H 99.00

1 Like

Thanks for the suggestions. In Light Blue, I actually can be on the humidity page and see updates, and also get notification popups showing temperature. However, the other way around, humidity notifications dont pop up.

I tried combining like you suggested:
But on the Client side I get a few extra characters. comboOut printed on the server side is normal though.
T: 19.80,H: 27.00@Ѕ@
vs
T: 19.80,H: 27.00

When I parse the files in my python script though, both give me an error
ValueError: could not convert string to float: '0x00'

But on the Client side I get a few extra characters.
T: 19.80,H: 27.00@Ѕ@

It's like the printing on the client side is not seeing the null terminator. sprintf() should be null terminated and the setValue() with the character string should have it to be seen by the client. Can you post the code which is giving your combined message.

When I parse the files in my python script though, both give me an error
ValueError: could not convert string to float: '0x00'

I don't understand what this means. Can you please explain more.

Here is your server code with only one characteristic. You can see if this is received any better.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define bleServerName "Simple_ESP32"
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 6000;
bool deviceConnected = false;

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "5106ba9c-a085-4bea-b210-0272d8cd40dd"

BLECharacteristic comboCharacteristic("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor comboCharacteristicDescriptor(BLEUUID((uint16_t)0x2902));

//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};


void setup() {
  BLEDevice::init(bleServerName);
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks()); //new

  BLEService *pService = pServer->createService(SERVICE_UUID);
  
  pService->addCharacteristic(&comboCharacteristic);//new
  comboCharacteristicDescriptor.setValue("temperature Celsius");
  comboCharacteristic.addDescriptor(&comboCharacteristicDescriptor);
  
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();

}
void loop() {
  
  if (deviceConnected) {
    if ((millis() - lastTime) > timerDelay) {
      float h = 27.00;
      float t = 19.80;
      static char temperatureCTemp[7];
      dtostrf(t, 6, 2, temperatureCTemp);
      static char humidity[7];
      dtostrf(h, 6, 2, humidity);
      static char comboOut[20];
      sprintf(comboOut, "T:%s,H:%s", temperatureCTemp, humidity);
    //  sprintf(comboOut, "T%s:H%s", temperatureCTemp, humidity);
      comboCharacteristic.setValue(comboOut);
      comboCharacteristic.notify();

      lastTime = millis();
    }
  }
}

If this does not show correctly in the client code, you can post your latest client code which reflects the combo message. I'll try set up a second esp32 as client and look at it.

Sorry, the Python error I posted above was a mistake because there were other header rows. The actual error is this from the Client's prints. And it works fine with the prints from my server code. The error is like this:

ValueError: could not convert string to float: ' 27.00@Ѕ\x0f@'

Using the server code in your last post, and this Client code, it is printing incorrectly still

time:0.003420, humidity_Client:88, tempC_Client:10, time:0.003489, T: 19.80,H: 27.00⸮ ⸮?

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp32-ble-server-client/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
float myTime;
#include "BLEDevice.h"

//BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "Simple_ESP32"

/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID dhtServiceUUID("5106ba9c-a085-4bea-b210-0272d8cd40dd");

// BLE Characteristics
//Temperature Celsius and Humidity Characteristic
static BLEUUID comboCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");

//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;

//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;

//Characteristicd that we want to read
static BLERemoteCharacteristic* comboCharacteristic;

//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};


//Variables to store temperature and humidity
char* comboChar;

//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;

//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();

  // Connect to the remove BLE Server.
  pClient->connect(pAddress);
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(dhtServiceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(dhtServiceUUID.toString().c_str());
    return (false);
  }

  // Obtain a reference to the characteristics in the service of the remote BLE server.
  comboCharacteristic = pRemoteService->getCharacteristic(comboCharacteristicUUID);

  if (comboCharacteristic == nullptr) {// || humidityCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID");
    return false;
  }
  Serial.println(" - Found our characteristics");

  //Assign callback functions for the Characteristics
  comboCharacteristic->registerForNotify(comboNotifyCallback);
  return true;
}

//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
        advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
        pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
        doConnect = true; //Set indicator, stating that we are ready to connect
        Serial.println("Device found. Connecting!");
      }
    }
};

//When the BLE Server sends a new temperature reading with the notify property
static void comboNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                                      uint8_t* pData, size_t length, bool isNotify) {
  //store temperature value
  comboChar = (char*)pData;
  newTemperature = true;
}

//function that prints the latest sensor readings in the OLED display
void printReadings() {
  myTime = float(millis());
  Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
  Serial.print(comboChar);
  Serial.println();
}

void setup() {
  //Start serial communication
  Serial.begin(115200);

  Serial.println("Starting Arduino BLE Client application...");

  //Init BLE device
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(30);
}

void loop() {
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("We are now connected to the BLE Server.");
      //Activate the Notify property of each Characteristic
      comboCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      connected = true;
    } else {
      Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
    }
    doConnect = false;
  }
  //if new temperature readings are available, print in the OLED
  if (newTemperature ) {
    // Reading temperature or humidity takes about 250 milliseconds!
    Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
    Serial.print("humidity_Client:"); Serial.print(88); Serial.print(", ");
    Serial.print("tempC_Client:"); Serial.print(10); Serial.print(", ");
    printReadings();
    newTemperature = false;
  }
  delay(1000); // Delay a second between loops.
}

I'm not certain why using a pointer for comboData to access and store the received pData in the callback is picking up the extra characters.

This version which uses a char array declared for comboData and the received length of pData for assignment through clean for me.

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp32-ble-server-client/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
float myTime;
#include "BLEDevice.h"

//BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "Simple_ESP32"

/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID dhtServiceUUID("5106ba9c-a085-4bea-b210-0272d8cd40dd");

// BLE Characteristics
//Temperature Celsius and Humidity Characteristic
static BLEUUID comboCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");

//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;

//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;

//Characteristicd that we want to read
static BLERemoteCharacteristic* comboCharacteristic;

//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};


//Variables to store temperature and humidity
//char* comboChar;
char comboChar[20];


//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;

//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();

  // Connect to the remove BLE Server.
  pClient->connect(pAddress);
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(dhtServiceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(dhtServiceUUID.toString().c_str());
    return (false);
  }

  // Obtain a reference to the characteristics in the service of the remote BLE server.
  comboCharacteristic = pRemoteService->getCharacteristic(comboCharacteristicUUID);

  if (comboCharacteristic == nullptr) {// || humidityCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID");
    return false;
  }
  Serial.println(" - Found our characteristics");

  //Assign callback functions for the Characteristics
  comboCharacteristic->registerForNotify(comboNotifyCallback);
  return true;
}

//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
        advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
        pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
        doConnect = true; //Set indicator, stating that we are ready to connect
        Serial.println("Device found. Connecting!");
      }
    }
};

//When the BLE Server sends a new temperature reading with the notify property
static void comboNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                                uint8_t* pData, size_t length, bool isNotify) {
  //store temperature value
  //comboChar = (char*)pData; 
  memset(comboChar, '\0', 20); //clear last values
  for (byte i = 0; i < length; i++)
  {
    comboChar[i] = pData[i];
  }
  newTemperature = true;
}

//function that prints the latest sensor readings in the OLED display
void printReadings() {
  myTime = float(millis());
  Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
  Serial.print(comboChar);
  Serial.println();
}

void setup() {
  //Start serial communication
  Serial.begin(115200);

  Serial.println("Starting Arduino BLE Client application...");

  //Init BLE device
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(30);
}

void loop() {
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("We are now connected to the BLE Server.");
      //Activate the Notify property of each Characteristic
      comboCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      connected = true;
    } else {
      Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
    }
    doConnect = false;
  }
  //if new temperature readings are available, print in the OLED
  if (newTemperature ) {
    // Reading temperature or humidity takes about 250 milliseconds!
    Serial.print("time:"); Serial.print(myTime / 1000 / 60 / 60 / 24, 6); Serial.print(", ");
    Serial.print("humidity_Client:"); Serial.print(88); Serial.print(", ");
    Serial.print("tempC_Client:"); Serial.print(10); Serial.print(", ");
    printReadings();
    newTemperature = false;
  }
  delay(1000); // Delay a second between loops.
}

00:50:45.450 -> time:0.006377, humidity_Client:88, tempC_Client:10, time:0.006447, T: 19.80,H: 27.00

1 Like

I tried printing pData, and the extra characters after the data actually changes if I just reset the devices. Did some more research, and it seems like the data might need to be less than 20 bytes. I went down to 13 characters (26 bytes), and this seems to "work" in that the byte after pData is 0x00.
T:19.8,H:27.0

I'm trying to find more about the specification, but there are a lot of different answers online.

it seems like the data might need to be less than 20 bytes

Yes, this is true, but the data in your temp/humidty characteristic fits within the 20 byte limit.

I tried printing pData, and the extra characters after the data actually changes if I just reset the devices.

Yes, I did the same, and I don't understand why pData did not have 17 bytes of char values and 3 0x00 values at the end. After the temp/humidity data I was seeing a ? (0x3f) and two 0x00 in my testing.

There were certainly issues with the pointer which was not properly initialized and the version I gave with the allocated memory and the array assignment worked with the 17 bytes for me.

//char* comboChar;
char comboChar[20];

Oh, I see. Char is 1 byte, so my data is less than 20 bytes. I misread somewhere that a char is 2 bytes.

In this code, there is no setValue() that takes a character array. There is one that takes a std::String, and I read strings are not null terminated. Could this be the problem?

(I'm not sure exactly how this works; how does it know what the arugment type is and can choose a setValue()?)

Here is an example of an Environmental Sensing Service for the ESP32 that I posted a while ago.

https://forum.arduino.cc/t/esp32-bluetooth-help/889782/10

It avoids using strings and implements the service they way the Bluetooth SIG suggested the service.

I was indeed somewhat surprised that the RandomNerd tutorial you were following used dtostrf() and then setValue() with the resulting c-string and no length parameter. However, all was looking well with the values read by LightBlue so I assumed the conversion to std::string was being handled by the BLE library correctly.
I think that with C++11 std::string is null terminated.

I think the issues were on the client side, and inadvertently accessing values outside of the valid range of 17 characters (16+null terminator).

With the latest code for the receiver without the pointer and the pData access using length do you see any remaining issues?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.