For a project I am working on, I need to achieve serial communication over Bluetooth, from my Linux laptop to an Arduino board. I already did, using an Arduino Uno connected to a Bluetooth HC-05 SPP module via RFCOMM sockets, thanks to this document: An Introduction to Bluetooth Programming written by Albert Huang.
Last week, in order to switch to Bluetooth Low Energy (and because I wanted a smaller board), I switched to an Arduino Nano ESP32. But I did not figure out how to enable serial communication over BLE.
I already took a look at the ESP32 Basics: Bluetooth Classic tutorial, but the Arduino Nano ESP32 is based on an ESP32-S3, which does not support the Bluetooth classic (only BLE). I also took a look at this page Utilisation du Bluetooth Low Energy avec bluez (in french), but I could not extract anything useful. I also took a look at the Bluez documentation, but I could not figure what to do.
Here is the code currently uploaded on my Arduino board:
/**
* Bluetooth LE Serial Simple Example
*
* Outputs the text "Hello!" to the Bluetooth LE Serial port every second.
*
* Avinab Malla
* 24 July 2022
**/
#include <BleSerial.h>
BleSerial ble;
void setup()
{
//Start the BLE Serial
//Enter the Bluetooth name here
ble.begin("BleSerialTest");
}
void loop()
{
//The usage is similar to Serial
ble.println("Hello!");
delay(1000);
}
And here is the code I tried to connect to it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <sys/socket.h>
int main()
{
struct sockaddr_hci addr;
int sock;
char *bta = "34:85:18:7B:3C:05";
if ((sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
perror("socket() failure");
return errno;
}
memset(&addr, 0, sizeof(addr));
addr.hci_family = AF_BLUETOOTH;
addr.hci_dev = hci_devid("34:85:18:7B:3C:05");
addr.hci_channel = HCI_CHANNEL_CONTROL;
int status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
if (status < 0) {
perror("connect() failure");
return errno;
} else {
printf("bind() success\n");
}
char msg[65536] = {0};
ssize_t n;
while (1) {
if ((n = recv(sock, msg, sizeof(msg), 0)) < 0) {
perror("recv() failure");
} else {
printf("Received %ld bytes\n", n);
}
}
close(sock);
return EXIT_SUCCESS;
}
Currently, all it does is displaying bind() success
and hang. When I first tried with connect
instead of bind
, it stops after displaying connect() failure: Operation not supported
.
Unfortunately while the Bluetooth specification has a standard serial profile for classic Bluetooth (SPP over RFCOMM), it does not have a standardized BLE serial profile. This is because when BLE was developed, it was not aimed to be a serial replacement (given that it is supposed to be "Low Energy"), but rather a wireless protocol that sends small amount of data every once in a while (e.g. sensor data). This means that if you want to achieve serial communication via BLE then you have to create your own application, whis is not an easy task as it requires thorough understanding of serial communication in general and BLE GATT client/server architecture. Plus, it will be a time consuming development effort as you will have to implement the BLE serial communication from scratch.
That being said, if you are still willing to invest the time and effort into developing this, then I recommend starting with this:-
Part 1: Arduino Nano ESP32 (GATT server)
You want to implement a GATT server on your Arduino Nano. The GATT server will have at least two characteristics, named Tx and Rx. Tx is a characteristic with the NOTIFY property (because data will be sent to the remote device), and Rx is a characteristic with the WRITE property (which means that the remote device will be able to write to it). Now whenever you input data via the Arduino board, your code needs to read that data and send the data as a notification via the Tx characteristic. Similarly, whenever data is received on the Rx characteristic, it needs to be displayed on the screen. NOTE: The example that hcheung linked to seems to be doing this part of the implementation.
Part 2: Linux Laptop (GATT client)
On the Linux laptop, you have to implement a GATT client to talk to the GATT server. Your code needs to 1) connect to the remote GATT server (Arduino Nano ESP32), and 2) enable notifications on the remote Rx characteristic. Then whenever you write data through the keyboard, your program needs to capture that data and send it via BLE (as a GATT client WRITE operation) onto the remote Tx characteristic on the Arduino Nano ESP32. Similarly, when you receive data as notifications from the Arduino Nano, that data needs to be displayed on the screen.
You can expand the program to do flow control via two new RTS/CTS characteristics on the Arduino Nano, but this can be a bonus step after you successfully achieve the above.
Some useful links on the subject:-