I set up my ESP32-C3 module for BLE-UART service with periodic transmission of messages using the Tx characteristic.
My phone supports Bluetooth 5.0, LE. Using LightBlue android app on my phone, I am able to connect and subscribe to the service and can see the periodic incoming messages, but only the first 20 chars.
As per https://www.novelbits.io/bluetooth-5-sp ... hroughput/, BLE 5.0 allows ATT payload of up to 244 bytes. How do I set up BLE-Uart service ATT payload for larger sizes than 20 bytes ?
esp32-c3 ble-uart : how to transmit more than 20 bytes in payload ?
Re: esp32-c3 ble-uart : how to transmit more than 20 bytes in payload ?
Never mind, i found the answer :
1. call NimBLEDevice::set_MTU(size) after ::init
2. in receiving BLE app, set same MTU size
1. call NimBLEDevice::set_MTU(size) after ::init
2. in receiving BLE app, set same MTU size
Re: esp32-c3 ble-uart : how to transmit more than 20 bytes in payload ?
Hello, I am looking for example code to try running UART over BLEpataga wrote: ↑Sun Dec 05, 2021 6:20 amI set up my ESP32-C3 module for BLE-UART service with periodic transmission of messages using the Tx characteristic.
My phone supports Bluetooth 5.0, LE. Using LightBlue android app on my phone, I am able to connect and subscribe to the service and can see the periodic incoming messages, but only the first 20 chars.
As per https://www.novelbits.io/bluetooth-5-sp ... hroughput/, BLE 5.0 allows ATT payload of up to 244 bytes. How do I set up BLE-Uart service ATT payload for larger sizes than 20 bytes ?
(as a simple test, I just want to be able to send a small text string from my smartphone to the ESP32C3 via BLE, with the ESP32C3 just reporting that text string over the terminal);
I know I want to use the UART service, and I was hoping I could get a pointer from you on reference code for the ESP32C3.
Did you adapt code from the included ESP-IDF examples code? If so, can you please point me to that? I have tried using the GATT client, the SPP emulation, etc., but haven't yet been able to figure it out
Added in edit: I've got this working for Adafruit's nordic semiconductor based feather board using bluefruit; but I am finding it challenging in figuring out how to adapt / change that to work with ESP32. Any pointers would be welcomed!
Thank you!
Re: esp32-c3 ble-uart : how to transmit more than 20 bytes in payload ?
Hi,
I only have tested transmitting from ESP32-C3 to Android app via BLE uart, not receiving.
At the moment I don't have an ESP32-C3 to test with (expecting a module end of this week).
This was my test code ...
I only have tested transmitting from ESP32-C3 to Android app via BLE uart, not receiving.
At the moment I don't have an ESP32-C3 to test with (expecting a module end of this week).
This was my test code ...
Code: Select all
#include <Arduino.h>
#include <NimBLEDevice.h>
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
const char* GATTC_TAG = "main";
#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"
NimBLEServer* pServer = NULL;
NimBLEService* pService = NULL;
NimBLECharacteristic* pTxCharacteristic = NULL;
NimBLECharacteristic* pRxCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
//"$LK8EX1,91000,99999,0,25,4.2*21";
char SzNMEA[40];
uint8_t btmsg_nmeaChecksum(const char *szNMEA);
void btmsg_genLK8EX1(char* szmsg, int32_t alt, int32_t cps, float voltage);
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
Serial.println("Server PassKeyRequest");
return 123456;
}
bool onConfirmPIN(uint32_t pass_key){
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
return true;
}
void onAuthenticationComplete(ble_gap_conn_desc desc){
Serial.println("Starting BLE work!");
}
/*******************************************************************/
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.print("Received : ");
for (int inx = 0; inx < rxValue.length(); inx++)
Serial.print(rxValue[inx]);
Serial.println();
}
}
};
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE vario");
NimBLEDevice::init("BLEVario");
NimBLEDevice::setMTU(46);
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityPasskey(123456);
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
pService = pServer->createService(SERVICE_UUID);
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY);
pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::WRITE_AUTHEN);
pRxCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
pServer->getAdvertising()->start();
}
void send_ble_uart(const char* str, int size) {
pTxCharacteristic->setValue((const uint8_t*)str, size);
pTxCharacteristic->notify();
}
void btmsg_genLK8EX1(char* szmsg, int32_t pa, int32_t cps, int32_t celsius, float voltage) {
sprintf(szmsg, "$LK8EX1,%d,99999,%d,%d,%.1f*", pa, cps, celsius, voltage);
uint8_t cksum = btmsg_nmeaChecksum(szmsg);
char szcksum[5];
sprintf(szcksum,"%02X\r\n", cksum);
strcat(szmsg, szcksum);
}
uint8_t btmsg_nmeaChecksum(const char *szNMEA){
const char* sz = &szNMEA[1]; // skip leading '$'
uint8_t cksum = 0;
while ((*sz) != 0 && (*sz != '*')) {
cksum ^= (uint8_t) *sz;
sz++;
}
return cksum;
}
void loop() {
int32_t pa = 91000 + random(75);
int32_t cps = random(50) - 25;
int32_t celsius = 25 + random(2);
float voltage = 4.2f;
btmsg_genLK8EX1(SzNMEA, pa, cps, celsius, voltage);
Serial.println(SzNMEA);
send_ble_uart(SzNMEA, strlen(SzNMEA));
delay(1000);
}
Re: esp32-c3 ble-uart : how to transmit more than 20 bytes in payload ?
This code, generated by the free pfodDesignerV3 Andriod app, uses the bleBufferedSerial class to sends and receives unlimited UART data, in 20 byte blocks. Just write/read to/from the bleBufferedSerial
(see https://www.forward.com.au/pfod/ESP32/index.html for a tutorial on using pfodDesignerV3 for ESP32 WiFi, BLE, Bluetooth connections.)
The code here includes a pfodParser for working with the paid pfodApp, but you can omit the pfodParser code from loop() and the
and just work with the bleBufferedSerial as the I/O and use the Nordic nRF Uart Andriod app to send/receive
(see https://www.forward.com.au/pfod/ESP32/index.html for a tutorial on using pfodDesignerV3 for ESP32 WiFi, BLE, Bluetooth connections.)
The code here includes a pfodParser for working with the paid pfodApp, but you can omit the pfodParser code from loop() and the
Code: Select all
// connect parser
parser.connect(bleBufferedSerial.connect(&bleSerial)); // connect the parser to the i/o stream via buffer
Code: Select all
/* ===== pfod Command for Menu_3 ====
pfodApp msg {.} --> {,<bg n><+5><w>~Test screen`0~V1|!A<b>~This is a long message}
*/
// Using ESP32 based board programmed via Arduino IDE
// follow the steps given on http://www.forward.com.au/pfod/ESP32/index.html to install ESP32 support for Arduino IDE
//Based on Neil Kolban example https://github.com/nkolban/ESP32_BLE_Arduino
/* Code generated by pfodDesignerV3 V3.0.3931
*/
/*
* (c)2014-2021 Forward Computing and Control Pty. Ltd.
* NSW Australia, www.forward.com.au
* This code is not warranted to be fit for any purpose. You may only use it at your own risk.
* This generated code may be freely used for both private and commercial use
* provided this copyright is maintained.
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define DEBUG
// download the libraries from http://www.forward.com.au/pfod/pfodParserLibraries/index.html
// pfodParser.zip V3.52+ contains pfodParser, pfodSecurity, pfodDelay, pfodBLEBufferedSerial, pfodSMS and pfodRadio
#include <pfodParser.h>
#include <pfodBLEBufferedSerial.h> // used to prevent flooding bluetooth sends
int swap01(int); // method prototype for slider end swaps
// =========== pfodBLESerial definitions
const char* localName = "ESP32 BLE"; // <<<<<< change this string to customize the adverised name of your board
class pfodBLESerial : public Stream, public BLEServerCallbacks, public BLECharacteristicCallbacks {
public:
pfodBLESerial(); void begin(); void poll(); size_t write(uint8_t); size_t write(const uint8_t*, size_t); int read();
int available(); void flush(); int peek(); void close(); bool isConnected();
static void addReceiveBytes(const uint8_t* bytes, size_t len);
const static uint8_t pfodEOF[1]; const static char* pfodCloseConnection;
volatile static bool connected;
void onConnect(BLEServer* serverPtr);
void onDisconnect(BLEServer* serverPtr);
void onWrite(BLECharacteristic *pCharacteristic);
private:
static const int BLE_MAX_LENGTH = 20;
static const int BLE_RX_MAX_LENGTH = 256; static volatile size_t rxHead; static volatile size_t rxTail;
volatile static uint8_t rxBuffer[BLE_RX_MAX_LENGTH];
size_t txIdx; uint8_t txBuffer[BLE_MAX_LENGTH];
};
volatile size_t pfodBLESerial::rxHead = 0; volatile size_t pfodBLESerial::rxTail = 0;
volatile uint8_t pfodBLESerial::rxBuffer[BLE_RX_MAX_LENGTH]; const uint8_t pfodBLESerial::pfodEOF[1] = {(uint8_t) - 1};
const char* pfodBLESerial::pfodCloseConnection = "{!}"; volatile bool pfodBLESerial::connected = false;
#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"
BLEServer *serverPtr = NULL;
BLECharacteristic * characteristicTXPtr;
// =========== end pfodBLESerial definitions
pfodParser parser("V2"); // create a parser with menu version string to handle the pfod messages
// create a parser to handle the pfod messages
pfodBLESerial bleSerial; // create a BLE serial connection
pfodBLEBufferedSerial bleBufferedSerial; // create a BLE serial connection
unsigned long plot_msOffset = 0; // set by {@} response
bool clearPlot = false; // set by the {@} response code
// the setup routine runs once on reset:
void setup() {
#ifdef DEBUG
Serial.begin(115200);
Serial.println();
#endif
// Create the BLE Device
BLEDevice::init(localName);
// Create the BLE Server
serverPtr = BLEDevice::createServer();
serverPtr->setCallbacks(&bleSerial);
// Create the BLE Service
BLEService *servicePtr = serverPtr->createService(SERVICE_UUID);
// Create a BLE Characteristic
characteristicTXPtr = servicePtr->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
characteristicTXPtr->addDescriptor(new BLE2902());
BLECharacteristic * characteristicRXPtr = servicePtr->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
characteristicRXPtr->setCallbacks(&bleSerial);
serverPtr->getAdvertising()->addServiceUUID(BLEUUID(SERVICE_UUID));
// Start the service
servicePtr->start();
// Start advertising
serverPtr->getAdvertising()->start();
#ifdef DEBUG
Serial.println("BLE Server and Advertising started");
#endif
bleSerial.begin();
// connect parser
parser.connect(bleBufferedSerial.connect(&bleSerial)); // connect the parser to the i/o stream via buffer
// <<<<<<<<< Your extra setup code goes here
}
// the loop routine runs over and over again forever:
void loop() {
uint8_t cmd = parser.parse(); // parse incoming data from connection
// parser returns non-zero when a pfod command is fully parsed
if (cmd != 0) { // have parsed a complete msg { to }
uint8_t* pfodFirstArg = parser.getFirstArg(); // may point to \0 if no arguments in this msg.
pfod_MAYBE_UNUSED(pfodFirstArg); // may not be used, just suppress warning
long pfodLongRtn; // used for parsing long return arguments, if any
pfod_MAYBE_UNUSED(pfodLongRtn); // may not be used, just suppress warning
if ('.' == cmd) {
// pfodApp has connected and sent {.} , it is asking for the main menu
if (!parser.isRefresh()) {
sendMainMenu(); // send back the menu designed
} else {
sendMainMenuUpdate(); // menu is cached just send update
}
// handle {@} request
} else if('@'==cmd) { // pfodApp requested 'current' time
plot_msOffset = millis(); // capture current millis as offset rawdata timestamps
clearPlot = true; // clear plot on reconnect as have new plot_msOffset
parser.print(F("{@`0}")); // return `0 as 'current' raw data milliseconds
// now handle commands returned from button/sliders
// } else if('A'==cmd) { // this is a label. pfodApp NEVER sends this cmd -- 'This is a long message'
// // in the main Menu of Menu_3
} else if ('!' == cmd) {
// CloseConnection command
closeConnection(parser.getPfodAppStream());
} else {
// unknown command
parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect.
}
}
// <<<<<<<<<<< Your other loop() code goes here
}
void closeConnection(Stream *io) {
// nothing special here
}
void sendMainMenu() {
// !! Remember to change the parser version string
// every time you edit this method
parser.print(F("{,")); // start a Menu screen pfod message
// send menu background, format, prompt, refresh and version
parser.print(F("<bg n><+5><w>~Test screen`0"));
parser.sendVersion(); // send the menu version
// send menu items
parser.print(F("|!A<b>"));
parser.print(F("~This is a long message"));
parser.print(F("}")); // close pfod message
}
void sendMainMenuUpdate() {
parser.print(F("{;")); // start an Update Menu pfod message
// send menu items
parser.print(F("|!A"));
parser.print(F("}")); // close pfod message
// ============ end of menu ===========
}
// ========== pfodBLESerial methods
pfodBLESerial::pfodBLESerial() {}
bool pfodBLESerial::isConnected() {
return (connected);
}
void pfodBLESerial::begin() {}
void pfodBLESerial::close() {}
void pfodBLESerial::poll() {}
size_t pfodBLESerial::write(const uint8_t* bytes, size_t len) {
for (size_t i = 0; i < len; i++) { write(bytes[i]); }
return len; // just assume it is all written
}
size_t pfodBLESerial::write(uint8_t b) {
if (!isConnected()) { return 1; }
txBuffer[txIdx++] = b;
if ((txIdx == sizeof(txBuffer)) || (b == ((uint8_t)'\n')) || (b == ((uint8_t)'}')) ) {
flush(); // send this buffer if full or end of msg or rawdata newline
}
return 1;
}
int pfodBLESerial::read() {
if (rxTail == rxHead) { return -1; }
// note increment rxHead befor writing
// so need to increment rxTail befor reading
rxTail = (rxTail + 1) % sizeof(rxBuffer);
uint8_t b = rxBuffer[rxTail];
return b;
}
// called as part of parser.parse() so will poll() each loop()
int pfodBLESerial::available() {
flush(); // send any pending data now. This happens at the top of each loop()
int rtn = ((rxHead + sizeof(rxBuffer)) - rxTail ) % sizeof(rxBuffer);
return rtn;
}
void pfodBLESerial::flush() {
if (txIdx == 0) { return; }
characteristicTXPtr->setValue((uint8_t*)txBuffer, txIdx);
txIdx = 0;
characteristicTXPtr->notify();
}
int pfodBLESerial::peek() {
if (rxTail == rxHead) { return -1; }
size_t nextIdx = (rxTail + 1) % sizeof(rxBuffer);
uint8_t byte = rxBuffer[nextIdx];
return byte;
}
void pfodBLESerial::addReceiveBytes(const uint8_t* bytes, size_t len) {
// note increment rxHead befor writing
// so need to increment rxTail befor reading
for (size_t i = 0; i < len; i++) {
rxHead = (rxHead + 1) % sizeof(rxBuffer);
rxBuffer[rxHead] = bytes[i];
}
}
//=========== ESP32 BLE callback methods
void pfodBLESerial:: onConnect(BLEServer* serverPtr) {
// clear parser with -1 in case partial message left, should not be one
addReceiveBytes(bleSerial.pfodEOF, sizeof(pfodEOF));
connected = true;
}
void pfodBLESerial::onDisconnect(BLEServer* serverPtr) {
// clear parser with -1 and insert {!} incase connection just lost
addReceiveBytes(bleSerial.pfodEOF, sizeof(pfodEOF));
addReceiveBytes((const uint8_t*)pfodCloseConnection, sizeof(pfodCloseConnection));
serverPtr->getAdvertising()->start();
connected = false;
}
void pfodBLESerial::onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
uint8_t *data = (uint8_t*)rxValue.data();
size_t len = rxValue.length();
addReceiveBytes((const uint8_t*)data, len);
}
//======================= end pfodBLESerial methods
int swap01(int in) {
return (in==0)?1:0;
}
// ============= end generated code =========
Who is online
Users browsing this forum: axellin and 98 guests