Communicate Using CC254x or NRF BLE With Arduino and Other Microcontrollers
by Ednieuw in Circuits > Arduino
424 Views, 2 Favorites, 0 Comments
Communicate Using CC254x or NRF BLE With Arduino and Other Microcontrollers
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
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:
- BLESerial Pro (nRF and CC2541 BLE)
- BLESerial Tiny (CC2541 BLE)
- BLESerial nRF (nRF BLE)
Android:
- Serial Bluetooth Terminal (CC2541 BLE)
- nRF UART v2.0 (nRF BLE)
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:
- Arduino IDE
- Library HardwareBLESerial available in the Arduino library manager.
Menu program for Arduino
BLEnRF_CC2541Menu.ino
Downloads
Installing the Software
Software
- Download the Arduino IDE
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
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
- 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
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 = ""; }