I’ve been experimenting with Bluetooth on an ESP32 WROOM module. Using one of the example codes, I successfully got it working with the Adafruit Bluefruit app. However, I’ve encountered two issues:
In the app the Uart tab displays an endless stream of random symbols, making it difficult to understand the output.
While I can send messages from my device to the serial monitor, I’m unable to send messages the other way around (from the serial monitor to my device).
I’d greatly appreciate any guidance or suggestions on how to address these problems. Thank you for your time and support!
/*
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 = 0;
// 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) {
String rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue[i]);
}
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("Alex");
// 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_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
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;
}
}
is this app for bluetooth? for wired UART? or both?
Your libraries have "BLE" in their filename.
What is the app for ? classical bluetooth which is something different than BLE (acronym for Bluetooth Low-Energy)
This is my first time messing with bluetooth so I have no idea what the any of these terms mean. I do know that the adafruit bluefruit app is made for bluetooth Low Energy but I am using this tutorial and it looks like it works in the video and the person said they barely changed the code. I don't know enough about bluetooth to understand the problem.
Regarding the first issue, you are only sending a single uint8_t value which may not be recognized as an ascii character. In the app there is a selection for data mode which can be set to either Hex or Ascii.
Once you have read a message into the sketch and you want to have it read by the BLE app on the phone , you can use a boolean control variable set to true when there is a new message to send and your output code can look like this
if (deviceConnected) {
if (newMessageToSend) //boolean control variable declared as global
{
newMessageToSend = false;
pTxCharacteristic->setValue("message from monitor");
pTxCharacteristic->notify();
delay(100); // bluetooth stack will go into congestion, if too many packets are sent
}
}
I tried replacing &txvalue, 1 for message to send. Now I get an endless stream of "message to send" which helped me narow down the problem. Every time the loop restarts it sends the the message over and over again. Any Idea how to fix that?
/*
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 = 0;
// 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) {
String rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue[i]);
}
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("Alex");
// 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_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
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) {
//I tried replacing &txvalue, 1 for message to send. Now I get an endless stream of "message to send" which helped me narow down the problem. Every time the loop restarts it sends the the message over and over again.
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;
}
}
Yes, as I mentioned in my first reply, you need a boolean control variable to determine if the sending should happen or not.
if (deviceConnected) {
if (newMessageToSend == true) //boolean control variable declared as global
{
pTxCharacteristic->setValue("message from monitor");
pTxCharacteristic->notify();
newMessageToSend = false; //block further message sending until set back to true
delay(100); // bluetooth stack will go into congestion, if too many packets are sent
}
}
For your UART application, I think you want to set the control variable to true when a new message is read from Serial.
Since you are using an ESP32 it is acceptable to use Serial.readStringUntil('\n') where the new line is set as the eol character in the Bluefruit App.
Your loop() can start like this
void loop() {
if (Serial.available())
{
newMessageReceived = Serial.readStringUntil('\n');
newMessageToSend = true;
}
if (deviceConnected) {
if (newMessageToSend)
{
newMessageToSend = false;
//pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->setValue(newMessageReceived);
pTxCharacteristic->notify();
//txValue++;
delay(100); // bluetooth stack will go into congestion, if too many packets are sent
}
}
It seems like you're encountering two issues with your ESP32 Bluetooth setup. Let's address both of them step by step.
Issue 1: Random Symbols in the UART tab of the Adafruit Bluefruit App
The random symbols in the UART tab typically occur when there is a mismatch between the data encoding or improper handling of the received data. This can happen when the data format sent over Bluetooth is not correctly interpreted by the app.
Possible causes:
Data Encoding/Format: The way the data is sent might not be matching the expected format on the receiving side. For instance, if you're sending binary data or non-ASCII characters, the app may display these as random symbols.
Flow Control: The serial data might be getting mixed due to flow control issues, such as the buffer overflowing or sending data too quickly.
Suggestions:
Make sure that the data being sent over Bluetooth is in a readable format (e.g., ASCII) for the UART tab to display it properly.
Adjust the txValue incrementation logic. You may want to send a simple string or a clearly formatted message to see if the issue persists.
Modify the sending section of your loop function to send readable ASCII data:
cpp
Copy code
void loop() {
if (deviceConnected) {
String message = "Message: " + String(txValue); // Sending a simple message
pTxCharacteristic->setValue(message.c_str());
pTxCharacteristic->notify();
txValue++;
delay(500); // Slow down sending data to avoid congestion
}
}
This ensures that you’re sending a clear text message (e.g., "Message: 1", "Message: 2", etc.), which should be easy for the app to display.
Issue 2: Unable to Send Messages from Serial Monitor to the ESP32 Device
Your setup has the BLE characteristic for receiving data (CHARACTERISTIC_UUID_RX), but the onWrite callback might not be properly receiving or interpreting the messages sent from the Serial Monitor.
Possible causes:
Data Type Mismatch: The Serial Monitor sends text-based messages (strings) by default, but the BLE onWrite function may be expecting the data to come in a different format.
Serial Monitor to Bluetooth Communication: When you send data from the Serial Monitor, it needs to be correctly received and forwarded to the onWrite function on the ESP32.
Suggestions:
Double-check that you are writing to the correct characteristic in the Serial Monitor and that the format is correct.
Ensure that the onWrite callback is triggering when data is written to the CHARACTERISTIC_UUID_RX characteristic.
Here’s a debugging step:
Add a print statement inside the onWrite function to confirm that data is being received by the ESP32.
cpp
Copy code
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
Serial.println(rxValue); // Print entire received string
Serial.println("*********");
}
}
};
Additionally, ensure:
You are correctly writing data from the Serial Monitor to the BLE characteristic in the Adafruit Bluefruit app or another BLE terminal app that supports sending messages to a Bluetooth peripheral.
To send data to the ESP32, you would typically:
Open the UART tab in the Adafruit app.
Type a message and press "Send."
Check if the message appears correctly in the Serial Monitor.
If everything is set up correctly, this should allow messages to be sent from the Serial Monitor to your ESP32 through the BLE connection.
Summary of Recommendations:
For the random symbols issue: Modify the data being sent to a readable format like a simple ASCII string (e.g., "Message: " followed by a number) and reduce the frequency of notifications (delay(500)).
For the sending issue: Confirm that the BLE onWrite callback is receiving the correct data by adding debugging print statements. Ensure that your BLE client app (Adafruit Bluefruit or another app) is sending the data correctly to the correct characteristic.
I found this code that should let me use the control pad but I get an error on line 65 (std::string rxValue = pCharacteristic->getValue();). Compilation error: conversion from 'String' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string'} requested
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
float txValue = 0;
const int button = 0; // button on PIN G0
const int readPin = 32; // analog pin G32
const int LEDpin = 2; // LED on pin G2
bool convert = false;
String rxString = "";
std::string rxValue; // rxValue gathers input data
// UART service UUID data
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#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) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
convert = true; // flag to invoke convertControlpad routine
Serial.println(" ");
Serial.print("Received data: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue[i]);
rxString = rxString + rxValue[i]; // build string from received data
}
}
}
};
// ***************************** SETUP *******************************
void setup() {
Serial.begin(115200);
pinMode(LEDpin, OUTPUT);
pinMode(button, INPUT);
BLEDevice::init("Wim's ESP32 UART"); // give the BLE device a name
BLEServer *pServer = BLEDevice::createServer(); // create BLE server
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY);
pCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start(); // start the service
pServer->getAdvertising()->start(); // start advertising
Serial.println("Waiting a client connection to notify...");
Serial.println(" ");
}
// *************************** MAIN PROGRAM *********************************
void loop() {
if (deviceConnected) {
txValue = analogRead(readPin); // pick up value on readPin
char txString[8]; // convert the value to a char array
dtostrf(txValue, 1, 2, txString); // float_val, min_width, decimal_digits, char_buffer
// pCharacteristic->setValue(&txValue, 1); // to send the integer value
// pCharacteristic->setValue("Hello!"); // to send a test message
pCharacteristic->setValue(txString); // prepare to send array
if (digitalRead(button) == LOW) { // send when button is pressed
pCharacteristic->notify(); // send the value to the app!
pCharacteristic->setValue(" "); // send a space
pCharacteristic->notify();
digitalWrite(LEDpin, HIGH); // switch on the LED
Serial.println(" ");
Serial.print("*** Peripheral sends: ");
Serial.print(txString); // report value on serial line
Serial.println(" ***");
}
else digitalWrite(LEDpin, LOW);
if (convert) convertControlpad();
}
delay(50);
}
// ************************* CONVERT CONTROLPAD CODE ************************
void convertControlpad() {
convert = false;
Serial.print(" ");
if (rxString == "!B11:") Serial.println("********** Start Action 1");
else if (rxString == "!B219") Serial.println("********** Start Action 2");
else if (rxString == "!B318") Serial.println("********** Start Action 3");
else if (rxString == "!B417") Serial.println("********** Start Action 4");
else if (rxString == "!B516") Serial.println("********** Start Action UP");
else if (rxString == "!B615") Serial.println("********** Start Action DOWN");
else if (rxString == "!B714") Serial.println("********** Start Action LEFT");
else if (rxString == "!B813") Serial.println("********** Start Action RIGHT");
else if (rxString == "!B10;") Serial.println("********** Stop Action 1");
else if (rxString == "!B20:") Serial.println("********** Stop Action 2");
else if (rxString == "!B309") Serial.println("********** Stop Action 3");
else if (rxString == "!B408") Serial.println("********** Stop Action 4");
else if (rxString == "!B507") Serial.println("********** Stop Action UP");
else if (rxString == "!B606") Serial.println("********** Stop Action DOWN");
else if (rxString == "!B705") Serial.println("********** Stop Action LEFT");
else if (rxString == "!B804") Serial.println("********** Stop Action RIGHT");
rxString = "";
}