A BLE characteristic is just a collection of bytes. You are storing these bytes as Strings. Strings are a collection of bytes with a formatting. Every byte is decodes using ASCII.
When you use Lightblue and send as String the number 1 you send a byte with value 0x31. When you send it as HEX aka byte value of 0x01 you send a Control Code that may be invisible or some weird symbol.
Try sending 4B as HEX and you should get the letter K printed.
You need to store the bytes you get from the characteristic as whatever type you need it to be. When our type is multiple bytes aka uint32_t (unsigned long) you need to send 4 bytes as HEX in the LightBlue app. When you use Serial.print the unsigned long will be converted into ASCII characters again to make it a human readable number.
So does not matter what data format I sent, the dat will still be received as string.
I have tried to use otai() function but with no luck:
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string rxValue = pCharacteristic->getValue();
Serial.print("value received = ");
Serial.println(rxValue.c_str());
//TRYING TO USE ATOI
int n = atoi(rxValue.c_str());
Serial.print("ATOI result = ");
Serial.println(n);
}
};
Now the serial monitor prints :
14:51:09.702 -> value received = K
14:51:09.702 -> ATOI result = 0
I mostly use the ArduinoBLE library with the original Arduinos with BLE support. That library is easier to use but will not work for other devices. So, we will have to figure this out together.
I looked into the source for the ESP32 library. file: BLECharacteristic.cpp
There is a function called uint8_t* BLECharacteristic::getData() that will give you the raw data.
You will need to store the data into a variable of the required type.
It's not clear what you are asking. All data is in computer registers as binary. You have 01001011 in some memory location. It can be displayed as decimal 75, char 'K', or hex value 0x4F.
I'm not exactly clear on what is being sent, but you can display it as HEX with this
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
Serial.print(rxValue[i],HEX);
Serial.println();
}
};
@cattledog The difference comes from the BLE app. There are two ways to write a value to a characteristic. One allows to write some String (you could call it ASCII mode) then you can simply use pCharacteristic->getValue() and the second is writing some HEX value (and that could be called binary mode) then you should use pCharacteristic->getData() and write the data to a variable of some type depending on how many HEX values you wrote in the app. You must be careful with the byte order in "binary mode". LSB is send first.
This method also works, the only unconvenient thing about this method is that I will need to implement some extra steps. Since hex letters are read one by one, I need to append full message to a buffer before I can do match comparison. Anyways, thank you for helping out
What exactly does public BLEServerCallbacks provide me?
From what I understand, I cannot use any custom name functions on callbacks. All functions are hardcoded in the library such as onConnect, on Disconnect, onWrite and etc?
My full code is here:
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
In this example rxValue is the data received (only accessible inside that function).
And txValue is the data to be sent, in this example just a byte incremented every second.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 10;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic)
{
uint8_t* received_data = pCharacteristic->getData();
Serial.println(*received_data,HEX);
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->addDescriptor( new BLE2902() );
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
/*
if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
txValue++;
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
}
*/
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}