Communicate Using CC254x or NRF BLE With Arduino and Other Microcontrollers

by Ednieuw in Circuits > Arduino

301 Views, 2 Favorites, 0 Comments

Communicate Using CC254x or NRF BLE With Arduino and Other Microcontrollers

IMG_8933.jpg

In the 1980's, when PC's were text based, communication between the user and PC took place with a keyboard and a character based menu on the screen. It was a simple method to give instructions to the program running on the PC. Between PC and instrument a serial UART communication with a cable was used.

For my Arduino projects this serial communication, in this case by air, is still the preferred way to read results from the software and send commands to the (Micro Controller Unit (MCU) via Bluetooth.

Cheap, around 5 euro's, BLE modules like the HM-10 and JDY-23 can be bought and used for this purpose. When connected to the TX&RX, ports 0 & 1, of an Arduino they can connect to a mobile phone or a Bluetooth equipped computer.

The new Arduino's, like the Nano RP2040 connect, Nano 33 IOT and MKR1010, have Bluetooth built in. But these MCU's do not use the Texas Instrument CC2541 chipset as used with the HM-10 and JDY-23 but use the the Nordic nRF chipset using the RFCOMM protocol.

With a serial UART terminal app on a Apple, Android device or Windows PC and a small program in an Arduino

This Instructable will demonstrate how to communicate with all these MCU's and chipsets.

In the coding there are examples how to deal with one character instructions, and how to dissect longer strings.

Supplies

I11-XS 6.5 IMG_7964.png

BLE CC2541 modules

  • HM-10
  • JDY-23

Arduino's

I have tested or used the following Arduino's with HM-10 or JDY-23 Bluetooth CC254x modules:

  • Arduino Uno
  • Arduino Nano
  • Arduino MEGA 2560
  • Arduino Nano Every

The Arduino's with Nordic nRF BLE:

  • MKR1010
  • Nano 33 IoT
  • Nano RP2040 Connect

For communication with a mobile:

IOS:

Android:

For communication with a PC and an USB cable connected to the Arduino:

  • Arduino IDE
  • Termite

For programming an Arduino with the Bluetooth menu program:

Menu program for Arduino

BLEnRF_CC2541Menu.ino

Installing the Software

Software


You have to install the proper boards for your Arduino. Upload the program Blink from the examples in the IDE before you reading further.

  • Install the HardwareBLESerial library from the Arduino Library Manager. (Tools -> Manage libraries -> Search for :HardwareBLESerial --> Press Install)
  • Download the BLEnRF_CC2541Menu.ino from link above and
  • Open the BLEnRF_CC2541Menu.ino in the IDE
  • Upload the program in an Arduino
// If a CC2541 module is connected to pin 0&1 the two defines below can be disabled by adding // before the #define
#define BLECC2541MOD                            // Turn on this define if a CC2541 module is connected to pin 6&7  
//#define BLEnRFMOD                             // Turn on for RP2040, Nano BLE33 , MKR1010 etc             
                                     
char VERSION[] = "Software: BLEnRF_CC2541MenuV0.0.2";        // This name is displayed in the menu so you can see what program(version) is running


                          #ifdef BLECC2541MOD
#include <SoftwareSerial.h>                        // For CC2541 Bluetooth communication
                          #endif //BLECC2541MOD
                          #ifdef BLEnRFMOD        
#include <HardwareBLESerial.h>                     // For nRF Bluetooth communication
HardwareBLESerial &Bluetooth = HardwareBLESerial::getInstance();        
                          #endif //BLEnRFMOD   



In this part of the software, in the beginning of the program, it is defined how the program will be compiled.

With #defines one can choose which part of the program will be compiled.

So let's first decide how the hardware is connected or which hardware we use.

We can choose between:

  • CC2541 modules connected to pin 0&1 and not using the 'Software serial library
  • CC2541 modules connected to pins 6&7 and be able to upload programs without disconnecting the modules
  • Using the Arduino's with built-in nRF BLE modules

The Hardware Connections

IMG_8896.JPG

CC2541 modules like the HM-10 and JDY-23

In the picture above an Arduino UNO and the front and back side of a HM-10 and a JDY-23 module are photographed. Both modules have CC2541 chipsets. The smal one is a JDY-23. The larger HM-10 is connected to the Arduino pins 6&7.

If you connect the RXD & TXD pins of a CC2541 BLE module to pin 0 and 1 you conserve two pins and some software but you can not upload programs when the module is connected. Before uploading you have to disconnect the pin 0 otherwise the BLE device blocks the upload!

If you choose for this pin 0&1 option:

  • Connect RXD from the Bluetooth module to pin 1 (TX) and TXD from the module to pin 0 (RX) of the Arduino.
  • Place two forward slashes before #define BLECC2541MOD and #define BLEnRFMOD
//#define BLECC2541MOD         // Turn on this define if a CC2541 module is connected to pin 6&7  
//#define BLEnRFMOD            // Turn on for RP2040, Nano BLE33 , MKR1010 etc             


If you have enough pins and program space choose the pin 6&7 option:

  • Connect RXD from the Bluetooth module to pin 6 and TXD from the module to pin 7 of the Arduino.
  • Place two forward slashes before #define BLEnRFMOD and remove them before #define BLECC2541MOD.


#define BLECC2541MOD             // Turn on this define if a CC2541 module is connected to pin 6&7  
//#define BLEnRFMOD              // Turn on for RP2040, Nano BLE33 , MKR1010 etc             


  • Connect 5V and GND from to module to 5V and GND on the Arduino.


MCU's with Nordic nRF

If you upload the program to boards with an Nordic nRF chipset like the ArduinoMKR1010, Nano 33 IoT or Nano RP2040 Connect. (These three Arduino's are shown at the top of this Instructable.)

  • Place two forward slashes before #define BLECC2541MOD.and remove them before #define BLEnRFMOD
//#define BLECC2541MOD     // Turn on this define if a CC2541 module is connected to pin 6&7  
#define BLEnRFMOD          // Turn on for RP2040, Nano BLE33 , MKR1010 etc             


In the program listing you can see what the defines compile and what not.

Test the Program in the Serial Monitor

2022-07-05.png
  • Open in the Arduino IDE the serial monitor (Tools -> Serial Monitor)
  • Recompile and upload the program again (or disconnect and reconnect the Arduino).

You will see that the program is compiled for a CC2541 chipset.

  • Enter an 'i' (i for Info) in the top line of the Serial monitor and press Enter.

The menu will show up in the serial monitor.


Some Arduino MCU's have trouble starting the Serial communication instantly but it starts only after a few microseconds. Too late to display the first printed lines.

The instruction: ‘while (!Serial)’ waits for the Serial port to start but as the instruction between the {bracket} state; no longer than 1000 milliseconds.

Then, as defined, the proper Bluetooth instruction code is executed.

void setup()
{                                                            
 pinMode(secondsPin,   OUTPUT );  
 Serial.begin(9600);                        // Setup the serial port to 9600 baud  
 
 int32_t Tick = millis();                   // Start the timer 
 while (!Serial)                            // Wait until serial port is started 
 {if ((millis() - Tick) >1000) break;}      // Prevents hanging if serial monitor/port is not connected
     
 Tekstprintln("\n*********\nSerial started");   
                          #ifdef BLECC2541MOD 
 Bluetooth.begin(9600);
 Tekstprintln("Bluetooth CC2541 enabled");
                          #endif  //BLECC2541MOD
                          #ifdef BLEnRFMOD        
 if (!Bluetooth.beginAndSetupBLE(menu[0])) 
      Tekstprintln("Failed to initialize HardwareBLESerial!");
 else Tekstprintln("Bluetooth nRF52 enabled");  
                          #endif //BLEnRFMOD                             
 } 

Test Bluetooth

5A9A3369-305D-4887-9569-0C18305BED4D.jpeg

In this instruction the IOS app ‘BLESerial Pro’ is used. This app is able to connect to both BLE chipsets. For Android the ones listed in Supplies can be used.

  • Start the Bluetooth app on your device
  • Choose the beacon of the micro controller unit where the menu is running
  • Send the letter ‘i’ (for info)
  • See the menu printed in the terminal window.

What Can Go Wrong

My main mistakes were:

  • Uploading with the wrong selected board in the IDE Especially the Nano 33. There is a Nano 33 BLE and a Nano 33 IOT.
  • Reversing TX and RX when connecting the module to pins0&1 or pins 6&7. Then you see in the Serial monitor that the programs runs fine but nothing on the cell phone.
  • Using a defect BLE module.
  • With the Arduino's with built-in BLE and nRF protocols I had no problems yet.

Below the complete program

// ============================================================================================
/* 


Connect the Bluetooth module GND and 5V and to pin 0 and 1 or 6 and 7 as noted in the comment of the digital pin assignment.
When connected to pin 0 and 1 two digital ports and the SoftwareSerial library are saved. Also the Bluetooth subroutine can be omitted.
This ommision is accomplished bij commenting out the #define BLECC2541MOD with two slashes // 
A disadvantage of connecting to pin 0 & 1 is that upload to the Arduino when the Bluetooth module is connected is not possible because the module
inteferes the upload.


 Author .: Ed Nieuwenhuys
 Changes.: 0.0.1 Initial program
 Changes.: 0.0.2 nRF functionality added
*/
// ============================================================================================
//                                                                                            //


// If a CC2541 module is connected to pin 0&1 the two defines below can be disabled by adding // before the #define
#define BLECC2541MOD                            // Turn on this define if a CC2541 module is connected to pin 6&7                                                  
//#define BLEnRFMOD                               // Turn on for RP2040, Nano BLE33 , MKR1010 etc             
                                     
char VERSION[] = "Software: BLEnRF_CC2541MenuV0.0.2";        // This name is displayed in the menu so you can see what program(version) is running


                          #ifdef BLECC2541MOD
#include <SoftwareSerial.h>                        // For CC2541 Bluetooth communication
                          #endif //BLECC2541MOD
                          #ifdef BLEnRFMOD        
#include <HardwareBLESerial.h>                     // For nRF Bluetooth communication
HardwareBLESerial &Bluetooth = HardwareBLESerial::getInstance();        
                          #endif //BLEnRFMOD   
//------------------------------------------------------------------------------
// PIN Assigments
//------------------------------------------------------------------------------ 


enum DigitalPinAssignments {      // Digital hardware constants 
 RX           = 0,                // Optionally onnects to Bluetooth TX
 TX           = 1,                // Optionally onnects to Bluetooth RX
 PIN02        = 2,                // Empty
 PIN03        = 3,                // Empty
 PIN04        = 4,                // Empty
 PIN05        = 5,                // Empty
 BT_TX        = 6,                // Connects to Bluetooth RX
 BT_RX        = 7,                // Connects to Bluetooth TX
 PIN08        = 8,                // Empty
 PIN09        = 9,                // Empty
 PIN10        = 10,               // Empty  
 PIN11        = 11,               // Empty
 PIN12        = 12,               // Empty
 secondsPin   = 13,               // LED on Arduino
 };
 
enum AnaloguePinAssignments {     // Analogue hardware constants ----
 EmptyA0      = 0,                // Empty
 EmptyA1      = 1,                // Empty
 EmptyA2      = 2,                // Empty
 EmptyA3      = 3,                // Empty
 SDA_pin      = 4,                // SDA pin
 SCL_pin      = 5,                // SCL pin
 EmptyA6     =  6,                // Empty
 EmptyA7     =  7};               // Empty


char     sptext[80];                                                                          // For common print purposes.  
uint32_t msTick;                                                                              // The number of millisecond ticks since we last incremented the second counter




//------------------------------------------------------------------------------
// BLUETOOTH
//------------------------------------------------------------------------------                                     
                           #ifdef BLECC2541MOD                                                // Bluetooth ---------------------
SoftwareSerial Bluetooth(BT_RX, BT_TX);                                                       // BT_RX <=> TXD on BT module, BT_TX <=> RXD on BT module
                           #endif  //BLECC2541MOD                                       


//------------------------------------------------------------------------------
// Menu
//------------------------------------------------------------------------------  
//0        1         2         3         4         5
//12345678901234567890123456789012345678901234567890  
 char menu[][42] = {                                                                          // menu[][nn]  nn is largest length of sentence in the menu 
 "My_BLE_menu",                                                                                // Beacon name of the BLE module
 "A Action A",
 "D Enter Date DDDMMYYYY (D25122021", 
 "I For this info menu",
 "R Action R",
 "T Enter time as THHMMSS (T071500)",
 "Ednieuw 07-2022" };
//  -------------------------------------   End Definitions  ---------------------------------------
//                                                                                            //
//------------------------------------------------------------------------------
// ARDUINO Loop
//------------------------------------------------------------------------------
void loop()
{
 InputDevicesCheck();                                                                         // Check for input from input devices 
 EverySecondCheck();                                                                          // This subroutine is not necessary but an example
}  
//------------------------------------------------------------------------------
// ARDUINO Setup
//------------------------------------------------------------------------------
//                                                                                            //
void setup()
{                                                            
 pinMode(secondsPin,   OUTPUT );  
 Serial.begin(9600);                                                                          // Setup the serial port to 9600 baud  
 
 int32_t Tick = millis();                                                                     // Start the timer 
 while (!Serial)                                                                              // Wait until serial port is started 
 {if ((millis() - Tick) >1000) break;}                                                        // Prevents hanging if serial monitor/port is not connected
     
 Tekstprintln("\n*********\nSerial started");   


                          #ifdef BLECC2541MOD 
 Bluetooth.begin(9600);
 Tekstprintln("Bluetooth CC2541 enabled");
                          #endif  //BLECC2541MOD
                          #ifdef BLEnRFMOD        
 if (!Bluetooth.beginAndSetupBLE(menu[0])) 
      Tekstprintln("Failed to initialize HardwareBLESerial!");
 else Tekstprintln("Bluetooth nRF52 enabled");  
                          #endif //BLEnRFMOD                             
 } 


//------------------------------------------------------------------------------
// Version info
//------------------------------------------------------------------------------
void SWversion(void) 
{ 
 unsigned int i;
 PrintLine(40);
 for (i = 0; i < sizeof(menu) / sizeof(menu[0]); Tekstprintln(menu[i++]));
 Tekstprintln(VERSION);
 PrintLine(40);
}


void PrintLine(byte Lengte)
{
 for (int n=0; n<Lengte; n++) {Serial.print(F("-"));} Serial.println(); 
}


//                                                                                            //
//------------------------------------------------------------------------------
// Update routine done every second
//------------------------------------------------------------------------------
void EverySecondCheck(void)
{
 uint32_t ms = millis() - msTick; 
 static bool Dpin;                                                                            // Only write once to improve program speed in the loop()
 if (ms > 1 && Dpin)                                                                          // With ms>5 LED is ON very short time
        {Dpin = LOW; digitalWrite(secondsPin,LOW);}                                           // Turn OFF the second on pin 13 
 if (ms > 999)                                                                                // Every second enter the loop
  {
    msTick = millis();
    digitalWrite(secondsPin,Dpin = HIGH);                                                     // Turn ON the second on pin     
   }
}


//------------------------------------------------------------------------------
// Check for input from devices
// This fubction is called from with the loop()
//------------------------------------------------------------------------------
//                                                                                            //
void InputDevicesCheck(void)
{
 SerialCheck();
                           #if defined (BLECC2541MOD)  || defined (BLEnRFMOD)
 BluetoothCheck(); 
                           #endif  //BLUETOOTHMOD / BLEnRFMOD   




}
//------------------------------------------------------------------------------
// common print routines
//------------------------------------------------------------------------------
//                                                                                            //
void Tekstprint(char const *tekst)
{
 Serial.print(tekst);   
                           #if defined (BLECC2541MOD)  || defined (BLEnRFMOD)
 Bluetooth.print(tekst);  
                           #endif  //BLUETOOTHMOD / BLEnRFMOD 
}


void Tekstprintln(char const *tekst)
{
 strcpy(sptext,tekst);
 strcat(sptext,"\n");          //sprintf(sptext,"%s\n",tekst);
 Tekstprint(sptext); 
 strcpy(sptext,""); 
}


//------------------------------------------------------------------------------
// Check for serial input
//------------------------------------------------------------------------------
void SerialCheck(void)
{
 String SerialString = "";
 while (Serial.available())
  {
   char c = Serial.read();  delay(3); 
   if (c>31 && c<127) SerialString += c;                                                   // Allow input from Space - Del
   else c = 0;
  }
 if (SerialString.length()>0)     ReworkInputString(SerialString);                         // Rework ReworkInputString();
 SerialString = "";
}
                           #if defined (BLECC2541MOD)  || defined (BLEnRFMOD)
//------------------------------------------------------------------------------
// CLOCK check for Bluetooth input
//------------------------------------------------------------------------------                           
void BluetoothCheck(void)
{ 
  String  BluetoothString = "";
  char c = 0;
                          #ifdef BLEnRFMOD 
  Bluetooth.poll();                                                                      // this must be called regularly to perform BLE updates
                          #endif //BLEnRFMOD
 while (Bluetooth.available()) 
  { 
   c = Bluetooth.read();
   Serial.print(c);
   if (c>31 && c<127) BluetoothString += c;
   else c = 0;
   delay(3);
  }
 if (BluetoothString.length()>0) 
   {   
    ReworkInputString(BluetoothString);                                                 // Rework ReworkInputString();
    BluetoothString = "";
   }
}
                           #endif  //BLECC2541MOD / BLEnRFMOD
//                                                                                            //
//------------------------------------------------------------------------------
// Constrain a string with integers
// The value between the first and last character in a string is returned between the  low and up bounderies
//------------------------------------------------------------------------------
int SConstrainInt(String s,byte first,byte last,int low,int up){return constrain(s.substring(first, last).toInt(), low, up);}
int SConstrainInt(String s,byte first,          int low,int up){return constrain(s.substring(first).toInt(), low, up);}


//------------------------------------------------------------------------------
//  Input from Bluetooth or Serial
//------------------------------------------------------------------------------
//                                                                                            //
void ReworkInputString(String InputString)
{
 InputString.trim();                                                                          // Remove trailing spaces
 if (InputString.length()>10) return;                                                         // If string is too long for some reason
 if (InputString[0] > 64 && InputString[0] <123)                                              // If the first charater is a letter
  {
  sprintf(sptext,"**** Wrong entry ****");                                                    // Default message 
  Serial.println(InputString);
  switch (InputString[0]) 
   {
    case 'A':
    case 'a':
            if (InputString.length() == 1)
              {
               sprintf(sptext,"Action A");
              }
             break;  
    case 'D':
    case 'd':  
            if (InputString.length() == 9 )
             {
              int Day, Month, Year;
              Day   = (byte) SConstrainInt(InputString,1,3,0,31);
              Month = (byte) SConstrainInt(InputString,3,5, 0, 12); 
              Year  =        SConstrainInt(InputString,5,9, 2000, 3000); 
              sprintf(sptext,"%02d-%02d-%04d",Day, Month, Year);
             }
            break;
    case 'I':
    case 'i': 
            if (InputString.length() == 1)
            {
             SWversion();
             sptext[0] = 0;                                                                      // Clear sptext    


            }
            break;


    case 'R':
    case 'r':
            if (InputString.length() == 1)
              {
               // Reset();                                                                       // Reset all settings 
               Tekstprintln("\n**** Reset to default settings ****"); 
              }
            break;
    case 'T':
    case 't':
            if(InputString.length() >= 7)  // T125500
              {  
              int Hour, Minute, Second ;           
              Hour   = (byte) SConstrainInt(InputString,1,3,0,23);
              Minute = (byte) SConstrainInt(InputString,3,5,0,59); 
              Second = (byte) SConstrainInt(InputString,5,7,0,59); 
              sprintf(sptext,"%02d:%02d:%02d",Hour, Minute, Second);


              }
              break;       
     default:
             break;
    }
  }
 Tekstprintln(sptext);                                                
 InputString = "";
}