Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE UART Tx and Rx not working #813

Open
ednieuw opened this issue Dec 17, 2024 · 3 comments
Open

BLE UART Tx and Rx not working #813

ednieuw opened this issue Dec 17, 2024 · 3 comments

Comments

@ednieuw
Copy link

ednieuw commented Dec 17, 2024

I can not get this code working with on my Arduino Nano ESP32 with Arduino core 2.0.18 arduino 5 with NimBLE V2.1.1
It works fine with NimBLE V1.4.3

I tried using NimBLEDevice instead of BLEDevice but later discovered this is changed automatically with a #define

The call to pAdvertising->setName(BLEbroadcastName); as suggested in #772 seems not necessary because I see the name broadcasted in my app and I can connect to it. The BLEDevice::init(Mem.BLEbroadcastName); seems enough.

The terminal window of my Serial terminal app opens after connection and commands can be sent from my phone do not arrive in the Nano ESP32. Strings sent from the Nano ESP32 are not received by my phone.

With the source below a connection with a BLE serial terminal app on a IOS or Android device can be made
Commands can be sent to the ESP32 with the BLE serial terminal app and/or the serial terminal of the Arduino IDE. The commands and results are displayed on both terminals.

An UART example in the examples would be fine in a next release.

#include <NimBLEDevice.h>     
//--------------------------------------------                                                //
// BLE   //#include <NimBLEDevice.h>
//--------------------------------------------
BLEServer *pServer      = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected    = false;
bool oldDeviceConnected = false;
std::string ReceivedMessageBLE;

#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"

//----------------------------------------
// Common
//----------------------------------------
 
#define   MAXTEXT 255
char      sptext[MAXTEXT]; 
bool UseBLELongString = false;
char SSID[30];                                                                                // 
char Password[40];                                                                            // 
char BLEbroadcastName[30];                                                                    // Name of the BLE beacon




void setup() 
{
Serial.begin(115200);                                                                         // Setup the serial port to 115200 baud //
 int32_t Tick = millis(); 
 while (!Serial)  
 {if ((millis() - Tick) >5000) break;}  Tekstprintln("Serial started");                       // Wait max 5 seconds until serial port is started   
 strcpy(SSID,"");                                                                             // Default SSID
 strcpy(Password,"");                                                                         // Default password
 strcpy(BLEbroadcastName,"Edsoft");
 StartBLEService();     Tekstprintln("BLE started");                                          // Start BLE service

}

void loop() 
{
 CheckDevices();
}
//--------------------------------------------                                                //
// COMMON Check connected input devices
//--------------------------------------------
void CheckDevices(void)
{
 CheckBLE();                                                                                  // Something with BLE to do?
 SerialCheck();                                                                               // Check serial port every second 
}

//--------------------------------------------                                                //
// COMMON check for serial input
//--------------------------------------------
void SerialCheck(void)
{
 String SerialString; 
 while (Serial.available())
    { 
     char c = Serial.read();                                                                  // Serial.write(c);
     if (c>31 && c<128) SerialString += c;                                                    // Allow input from Space - Del
     else c = 0;                                                                              // Delete a CR
    }
 if (SerialString.length()>0) 
    {
     ReworkInputString(SerialString);                                                        // Rework ReworkInputString();
     SerialString = "";
    }
}

//--------------------------------------------                                                //
// COMMON common print routines
//--------------------------------------------
void Tekstprint(char const *tekst)    { if(Serial) Serial.print(tekst);  SendMessageBLE(tekst); sptext[0]=0; } 
void Tekstprintln(char const *tekst)  { sprintf(sptext,"%s\n",tekst); Tekstprint(sptext); }
void TekstSprint(char const *tekst)   { printf(tekst); sptext[0]=0;}                          // printing for Debugging purposes in serial monitor 
void TekstSprintln(char const *tekst) { sprintf(sptext,"%s\n",tekst); TekstSprint(sptext); }

//-----------------------------
// BLE  CheckBLE
//------------------------------
void CheckBLE(void)
{
 if(!deviceConnected && oldDeviceConnected)                                                   // Disconnecting
   {
    delay(300);                                                                               // Give the bluetooth stack the chance to get things ready
    pServer->startAdvertising();                                                              // Restart advertising
    TekstSprint("Start advertising\n");
    oldDeviceConnected = deviceConnected;
   }
 if(deviceConnected && !oldDeviceConnected)                                                   // Connecting
   { 
    oldDeviceConnected = deviceConnected;
   }
 if(ReceivedMessageBLE.length()>0)
   {
    SendMessageBLE(ReceivedMessageBLE);
    String BLEtext = ReceivedMessageBLE.c_str();
    ReceivedMessageBLE = "";
    ReworkInputString(BLEtext); 
   }
}

// ********************** NimBLE code

//--------------------------------------------                                                //
// BLE 
// SendMessage by BLE Slow in packets of 20 chars
// or fast in one long string.
// Fast can be used in IOS app BLESerial Pro
//------------------------------
void SendMessageBLE(std::string Message)
{
 if(deviceConnected) 
   {
    if (UseBLELongString)                                                                     // If Fast transmission (for Apple IOS) is possible
     {
      pTxCharacteristic->setValue(Message); 
      pTxCharacteristic->notify();
      delay(10);                                                                              // Bluetooth stack will go into congestion, if too many packets are sent
     } 
   else                                                                                       // Packets of max 20 bytes
     {   
      int parts = (Message.length()/20) + 1;
      for(int n=0;n<parts;n++)
        {   
         pTxCharacteristic->setValue(Message.substr(n*20, 20)); 
         pTxCharacteristic->notify(); 
         delay(10);                                                                           // Bluetooth stack will go into congestion, if too many packets are sent
        }
     }
   } 
}
//-----------------------------
// BLE Start BLE Classes
//------------------------------
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();
   ReceivedMessageBLE = rxValue + "\n";
//   if (rxValue.length() > 0) {for (int i = 0; i < rxValue.length(); i++) printf("%c",rxValue[i]); }
//   printf("\n");
  }  
};
//--------------------------------------------                                                //
// BLE Start BLE Service
//------------------------------
void StartBLEService(void)
{
 BLEDevice::init(BLEbroadcastName);                                                       // Create the BLE Device
 pServer = BLEDevice::createServer();                                                         // Create the BLE Server
 pServer->setCallbacks(new MyServerCallbacks());
 BLEService *pService = pServer->createService(SERVICE_UUID);                                 // Create the BLE Service
 pTxCharacteristic                     =                                                      // Create a BLE Characteristic 
     pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY);                 
 BLECharacteristic * pRxCharacteristic = 
     pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE);
 pRxCharacteristic->setCallbacks(new MyCallbacks());
 pService->start(); 
 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID); 
 pServer->start();                                                                            // Start the server  Nodig??
 pServer->getAdvertising()->start();                                                          // Start advertising
 TekstSprint("BLE Waiting a client connection to notify ...\n"); 
}
//                                                                                            //



//--------------------------------------------                                                //
//  COMMON Input from Bluetooth or Serial
//--------------------------------------------
void ReworkInputString(String InputString)
{
 if(InputString.length()> 40){Serial.printf("Input string too long (max40)\n"); return;}      // If garbage return
 for (int n=0; n<InputString.length()+1; n++)                                                 // remove CR and LF
       if (InputString[n] == 10 || InputString[n]==13) InputString.remove(n,1);
 sptext[0] = 0;                                                                               // Empty the sptext string
 
 if(InputString[0] > 31 && InputString[0] <127)                                               // Does the string start with a letter?
  { 
  switch (InputString[0])
   {
    case 'A':
    case 'a': 
            if (InputString.length() >4 && InputString.length() <30)
            {
             InputString.substring(1).toCharArray(SSID,InputString.length());
             sprintf(sptext,"SSID set: %s", SSID);  
            }
            else sprintf(sptext,"**** Length fault. Use between 4 and 30 characters ****");
            break;
    case 'B':
    case 'b':

             if (InputString.length() >4 && InputString.length() <40)
              {  
               InputString.substring(1).toCharArray(Password,InputString.length());
               sprintf(sptext,"Password set: %s\n Enter @ to reset ESP32 and connect to WIFI and NTP\n WIFI and NTP are turned ON", Password); 
              }
             else sprintf(sptext,"**** Length fault. Use between 5 and 40 characters ****");
             break;   
    case 'C':
    case 'c': 
   
             if (InputString.length() >4 && InputString.length() <30)
               {  
                InputString.substring(1).toCharArray(BLEbroadcastName,InputString.length());
                sprintf(sptext,"BLE broadcast name set: %s", BLEbroadcastName); 
              }
            else sprintf(sptext,"**** Length fault. Use between 4 and 30 characters ****");
            break;                                
    default: break;
    }
  }  
 Tekstprintln(sptext);                                   
 InputString = "";
}


@h2zero
Copy link
Owner

h2zero commented Dec 20, 2024

The call to pAdvertising->setName(BLEbroadcastName); as suggested in #772 seems not necessary because I see the name broadcasted in my app and I can connect to it.

When your phone clears its cache or you use another phone the name won't show. It only works now because phones save the information about each device in it's cache.

Your callback function signatures are out of date and need to up updated for 2.x, please see the 1.x to 2.x migration guide
Or look in the header files,

virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);

@ednieuw
Copy link
Author

ednieuw commented Dec 23, 2024

In the end the changes were simple but for an old fashioned C-programmer. pfft.

In NimBLE version 2.x.x these functions needed extra parameters in this sketch:

 void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {deviceConnected = true;Serial.println("Connected" ); };
 void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {deviceConnected = false;Serial.println("NOT Connected" );}
 void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override  

 add pAdvertising->setName(BLEbroadcastName);   after pAdvertising->addServiceUUID(SERVICE_UUID); 

Below a working sketch


/* 
 Author      : Ed Nieuwenhuys https://github.com/ednieuw , https://ednieuw.nl
 December 2024

Sketch for Bluetooth UART (universal asynchronous receiver / transmitter) communication with a BLE serial terminal.

A serial terminal can be installed on a PC (like Termite) or on a IOS or Android device
For IOS:  BLESerial nRF or BLEserial Pro   
For Android:  Serial Bluetooth Terminal

In this example entries in the serial terminal can be used to set an SSID, password and BLE broadcast name.

******
Compile with NimBLE version 2.x
******

------------------------------------------------------------------------------------------------
*/

#include <NimBLEDevice.h>     
//--------------------------------------------                                                //
// BLE   //#include <NimBLEDevice.h>
//--------------------------------------------
NimBLEServer *pServer      = NULL;
NimBLECharacteristic * pTxCharacteristic;
bool deviceConnected    = false;
bool oldDeviceConnected = false;
std::string ReceivedMessageBLE;

#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"

//----------------------------------------
// Common
//----------------------------------------
 
#define   MAXTEXT 255
char      sptext[MAXTEXT]; 
bool UseBLELongString = false;                                                                // Set to true for faster communication on IOS devices
char SSID[30];                                                                                // 
char Password[40];                                                                            // 
char BLEbroadcastName[30];                                                                    // Name of the BLE beacon

//----------------------------------------
// Setup
//----------------------------------------
void setup() 
{
Serial.begin(115200);                                                                         // Setup the serial port to 115200 baud //
 int32_t Tick = millis(); 
 while (!Serial)  
 {if ((millis() - Tick) >5000) break;}  Tekstprintln("Serial started");                       // Wait max 5 seconds until serial port is started   
 strcpy(SSID,"");                                                                             // Default SSID
 strcpy(Password,"");                                                                         // Default password
 strcpy(BLEbroadcastName,"Edsoft");
 StartBLEService();     Tekstprintln("BLE started");                                          // Start BLE service

}
//----------------------------------------
// Loop
//----------------------------------------
void loop() 
{
 CheckDevices();
}
//--------------------------------------------                                                //
// COMMON Check connected input devices
//--------------------------------------------
void CheckDevices(void)
{
 CheckBLE();                                                                                  // Something with BLE to do?
 SerialCheck();                                                                               // Check serial port every second 
}

//--------------------------------------------                                                //
// COMMON check for serial input
//--------------------------------------------
void SerialCheck(void)
{
 String SerialString; 
 while (Serial.available())
    { 
     char c = Serial.read();                                                                  // Serial.write(c);
     if (c>31 && c<128) SerialString += c;                                                    // Allow input from Space - Del
     else c = 0;                                                                              // Delete a CR
    }
 if (SerialString.length()>0) 
    {
     ReworkInputString(SerialString);                                                        // Rework ReworkInputString();
     SerialString = "";
    }
}

//--------------------------------------------                                                //
// COMMON common print routines
//--------------------------------------------
void Tekstprint(char const *tekst)    { if(Serial) Serial.print(tekst);  SendMessageBLE(tekst); sptext[0]=0; } 
void Tekstprintln(char const *tekst)  { sprintf(sptext,"%s\n",tekst); Tekstprint(sptext); }
void TekstSprint(char const *tekst)   { Serial.print(tekst); sptext[0]=0;}                          // printing for Debugging purposes in serial monitor 
void TekstSprintln(char const *tekst) { sprintf(sptext,"%s\n",tekst); TekstSprint(sptext); }


//--------------------------------------------                                                //
//  COMMON Input from Bluetooth or Serial
//--------------------------------------------
void ReworkInputString(String InputString)
{
 if(InputString.length()> 40){Serial.printf("Input string too long (max40)\n"); return;}      // If garbage return
 for (int n=0; n<InputString.length()+1; n++)                                                 // remove CR and LF
       if (InputString[n] == 10 || InputString[n]==13) InputString.remove(n,1);
 sptext[0] = 0;                                                                               // Empty the sptext string
 
 if(InputString[0] > 31 && InputString[0] <127)                                               // Does the string start with a letter?
  { 
  switch (InputString[0])
   {
    case 'A':
    case 'a': 
            if (InputString.length() >4 && InputString.length() <30)
            {
             InputString.substring(1).toCharArray(SSID,InputString.length());
             sprintf(sptext,"SSID set: %s", SSID);  
            }
            else sprintf(sptext,"**** Length fault. Use between 4 and 30 characters ****");
            break;
    case 'B':
    case 'b':

             if (InputString.length() >4 && InputString.length() <40)
              {  
               InputString.substring(1).toCharArray(Password,InputString.length());
               sprintf(sptext,"Password set: %s\n Enter @ to reset ESP32 and connect to WIFI and NTP\n WIFI and NTP are turned ON", Password); 
              }
             else sprintf(sptext,"**** Length fault. Use between 5 and 40 characters ****");
             break;   
    case 'C':
    case 'c': 
   
             if (InputString.length() >4 && InputString.length() <30)
               {  
                InputString.substring(1).toCharArray(BLEbroadcastName,InputString.length());
                sprintf(sptext,"BLE broadcast name set: %s", BLEbroadcastName); 
              }
            else sprintf(sptext,"**** Length fault. Use between 4 and 30 characters ****");
            break;                                
    default: break;
    }
  }  
 Tekstprintln(sptext);                                   
 InputString = "";
}


// ********************** NimBLE code ************************************
//-----------------------------
// BLE  CheckBLE
//------------------------------
void CheckBLE(void)
{
 if(!deviceConnected && oldDeviceConnected)                                                   // Disconnecting
   {
    delay(300);                                                                               // Give the bluetooth stack the chance to get things ready
    pServer->startAdvertising();                                                              // Restart advertising
    TekstSprint("Start advertising\n");
    oldDeviceConnected = deviceConnected;
   }
 if(deviceConnected && !oldDeviceConnected)                                                   // Connecting
   { 
    oldDeviceConnected = deviceConnected;
   }
 if(ReceivedMessageBLE.length()>0)
   {
    SendMessageBLE(ReceivedMessageBLE);
    String BLEtext = ReceivedMessageBLE.c_str();
    ReceivedMessageBLE = "";
    ReworkInputString(BLEtext); 
   }
}

//--------------------------------------------                                                //
// BLE 
// SendMessage by BLE Slow in packets of 20 chars
// or fast in one long string.
// Fast can be used in IOS app BLESerial Pro
//------------------------------
void SendMessageBLE(std::string Message)
{
 if(deviceConnected) 
   {
    if (UseBLELongString)                                                                     // If Fast transmission (for Apple IOS) is possible
     {
      pTxCharacteristic->setValue(Message); 
      pTxCharacteristic->notify();
      delay(10);                                                                              // Bluetooth stack will go into congestion, if too many packets are sent
     } 
   else                                                                                       // Packets of max 20 bytes
     {   
      int parts = (Message.length()/20) + 1;
      for(int n=0;n<parts;n++)
        {   
         pTxCharacteristic->setValue(Message.substr(n*20, 20)); 
         pTxCharacteristic->notify(); 
         delay(10);                                                                           // Bluetooth stack will go into congestion, if too many packets are sent
        }
     }
   } 
}
//--------------------------------------------                                                //
// BLE Start BLE Classes
//------------------------------
class MyServerCallbacks: public NimBLEServerCallbacks 
{
 void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {deviceConnected = true;Serial.println("Connected" ); };
 void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {deviceConnected = false;Serial.println("NOT Connected" );}
};
  
class MyCallbacks: public NimBLECharacteristicCallbacks 
{
 void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override  
  {
   std::string rxValue = pCharacteristic->getValue();
   ReceivedMessageBLE = rxValue + "\n";
  //  if (rxValue.length() > 0) {for (int i = 0; i < rxValue.length(); i++) printf("%c",rxValue[i]); }
  //  printf("\n");
  }  
};

//--------------------------------------------                                                //
// BLE Start BLE Service
//------------------------------
void StartBLEService(void)
{
 NimBLEDevice::init(BLEbroadcastName);                                                        // Create the BLE Device
 pServer = NimBLEDevice::createServer();                                                      // Create the BLE Server
 pServer->setCallbacks(new MyServerCallbacks());
 BLEService *pService = pServer->createService(SERVICE_UUID);                                 // Create the BLE Service
 pTxCharacteristic                     =                                                      // Create a BLE Characteristic 
     pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);                 
 NimBLECharacteristic * pRxCharacteristic = 
     pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE);
 pRxCharacteristic->setCallbacks(new MyCallbacks());
 pService->start(); 
 BLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID); 
 pAdvertising->setName(BLEbroadcastName);    
 pServer->start();                                                                            // Start the server  Nodig??
 pServer->getAdvertising()->start();                                                          // Start advertising
 TekstSprint("BLE Waiting a client connection to notify ...\n"); 
}
//                                                                                            //

@h2zero
Copy link
Owner

h2zero commented Dec 23, 2024

Glad you got it working. The required changes are quite minimal, also good to see you are using the override specifier as that will help catch many issues like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants