The BluetoothTerminal library is designed for the Arduino framework, providing a simple and efficient way to implement serial-like communication over Bluetooth Low Energy (BLE) on microcontrollers.
Arduino Reference: https://www.arduino.cc/reference/en/libraries/bluetoothterminal/
Built on top of the ArduinoBLE library, it facilitates seamless
bidirectional data exchange between a BLE-enabled Arduino-compatible microcontroller and a central device, such
as a smartphone or tablet, using configurable BLE service and characteristic to pass and send messages ending by
configurable separator characters (\n by default).
- Supports sending messages longer than the configured characteristic value size (
20bytes by default) by splitting them into chunks and sending with a configurable delay. - Abstracts away reception of messages longer than the characteristic value size by using a buffer of configurable
length (
128bytes by default). - Provides flexibility to configure service UUID, characteristic UUID, BLE module name, and other used parameters, making it adaptable to various applications.
Whether you're developing an IoT application, a remote control system, or any other project requiring reliable serial-like communication over BLE on Arduino-compatible microcontrollers, this library offers a way to get started quickly.
Note: the implementation utilizes a singleton pattern internally due to the necessity of static methods for configuring ArduinoBLE event handlers. Despite this, the public interface of the class remains consistent with standard class usage, and the library is designed to function correctly with a single instance.
As a proof of concept, you can use the code below with the
Web Bluetooth Terminal app in your browser to test
serial-like communication over BLE. The sketch below starts sending a Hello, world! message every 5 seconds to the
connected central device and outputs messages sent from the device to the terminal.
#include <BluetoothTerminal.h>
BluetoothTerminal bluetoothTerminal;
unsigned long lastMillis = 0;
void handleConnect(BLEDevice device)
{
Serial.println("Device connected");
}
void handleDisconnect(BLEDevice device)
{
Serial.println("Device disconnected");
}
void handleReceive(const char *message)
{
Serial.print("Message received: ");
Serial.println(message);
}
void setup()
{
Serial.begin(115200);
bluetoothTerminal.enableDebug();
bluetoothTerminal.onConnect(handleConnect);
bluetoothTerminal.onDisconnect(handleDisconnect);
bluetoothTerminal.onReceive(handleReceive);
bluetoothTerminal.start();
}
void loop()
{
unsigned long currentMillis = millis();
if (currentMillis > lastMillis + 5000)
{
lastMillis = currentMillis;
if (bluetoothTerminal.isConnected())
{
bluetoothTerminal.send("Hello, world!");
}
}
bluetoothTerminal.loop();
}- BluetoothTerminal
- void enableDebug()
- void disableDebug()
- void onConnect(std::function<void(BLEDevice)> handler)
- void onDisconnect(std::function<void(BLEDevice)> handler)
- void onReceive(std::function<void(const char*)> handler)
- bool setServiceUuid(const char *uuid)
- bool setCharacteristicUuid(const char *uuid)
- void setCharacteristicValueSize(int size)
- bool setName(const char *name)
- void setReceiveBufferSize(size_t size)
- void setReceiveSeparator(char separator)
- void setSendSeparator(char separator)
- void setSendDelay(int delay)
- bool start()
- void loop()
- bool isConnected()
- bool send(const char *message)
The BluetoothTerminal class is a core component of the library that provides an interface for BLE-based serial
communication, handles the connection with the central device, and provides methods to configure BLE and send and
receive data.
Example:
BluetoothTerminal bluetoothTerminal;
void setup()
{
// ...
bluetoothTerminal.start();
// ...
}
void loop()
{
// ...
bluetoothTerminal.loop();
// ...
}Enables debug logging. When debug is enabled, the library logs every event (such as handler settings, UUID
configuration, and message transmission) into Serial. This is useful for troubleshooting and observing the behavior
of the library during development.
Optional; debug is disabled by default.
Example:
void setup()
{
// ...
bluetoothTerminal.enableDebug();
// ...
}Disables debug logging. When debug is disabled, the library does not log any events into Serial. Use this method to
silence debug logging after testing is complete or in production code.
Optional; debug is disabled by default.
Example:
void setup()
{
// ...
bluetoothTerminal.disableDebug();
// ...
}Sets a callback function to be invoked when a device connects to the BLE module.
Example:
void handleConnect(BLEDevice device)
{
Serial.println("Device connected");
}
void setup()
{
// ...
bluetoothTerminal.onConnect(handleConnect);
// ...
}Sets a callback function to be invoked when a device disconnects from the BLE module.
Example:
void handleDisconnect(BLEDevice device)
{
Serial.println("Device disconnected");
}
void setup()
{
// ...
bluetoothTerminal.onDisconnect(handleDisconnect);
// ...
}Sets a callback function to be invoked when a message ending with the receive separator (\n by default) is received
from a connected device.
Example:
void handleReceive(const char *message)
{
Serial.print("Message received: ");
Serial.println(message);
}
void setup()
{
// ...
bluetoothTerminal.onReceive(handleReceive);
// ...
}Sets the BLE service UUID. Returns true on success and false otherwise.
Optional; if not set, the default UUID ffe0 will be used. It should be called before bluetoothTerminal.start(). If
used after, the method will have no effect. The service UUID can only be set once, if the method is called again, it
will log an error, and the existing service UUID will remain unchanged.
Example:
void setup()
{
// ...
bluetoothTerminal.setServiceUuid("ffe0");
// ...
}Sets the BLE characteristic UUID. Returns true on success and false otherwise.
Optional; if not set, the default UUID ffe1 will be used. It should be called before bluetoothTerminal.start(). If
used after, the method will have no effect. The characteristic UUID can only be set once, if the method is called
again, it will log an error, and the existing characteristic UUID will remain unchanged.
Example:
void setup()
{
// ...
bluetoothTerminal.setCharacteristicUuid("ffe1");
// ...
}Sets the size of the BLE characteristic value in bytes.
Optional; if not set, 20 bytes will be used by default. It should be called before bluetoothTerminal.start(). If
used after, the method will have no effect.
Example:
void setup()
{
// ...
bluetoothTerminal.setCharacteristicValueSize(20);
// ...
}Sets the name of the BLE device that is shown when pairing. Returns true on success and false otherwise.
Optional; if not set, the default name Arduino will be visible only after pairing (according to underlying library
documentation). It should be called
before bluetoothTerminal.start(). If used after, the method will have no effect. The name can only be set once, if
the method is called again, it will log an error, and the existing name will remain unchanged.
Example:
void setup()
{
// ...
bluetoothTerminal.setName("BluetoothTerminal");
// ...
}Sets the size of the buffer used for receiving data. The buffer accumulates received data until the receive separator
(\n by default) is detected, indicating the end of a message. The buffer must be large enough to accommodate the
entire message, including the separator, before processing. If a message exceeds the buffer size, the oldest data will
be discarded to make room for new incoming data, preventing buffer overflow. Setting a buffer size that fits the
expected message lengths is essential to avoid unintended data loss.
Optional; if not set, 128 bytes will be used by default. If called in runtime, for example during loop() execution,
and there is data in the buffer, it will be discarded.
Example:
void setup()
{
// ...
bluetoothTerminal.setReceiveBufferSize(256);
// ...
}Sets the separator character used to identify the end of a received message.
Optional; if not set, \n will be used by default. Can be changed in runtime, for example during loop() execution.
Example:
void setup()
{
// ...
bluetoothTerminal.setReceiveSeparator('\n');
// ...
}Sets the separator character appended to each message sent.
Optional; if not set, \n will be used by default. Can be changed in runtime, for example during loop() execution.
Example:
void setup()
{
// ...
bluetoothTerminal.setSendSeparator('\n');
// ...
}Sets a delay in milliseconds to wait between sending chunks of a message when the total message length, including the send separator, exceeds the characteristic value size. This delay is useful in cases where some chunks are not received by the central device (the device connecting to the BLE module).
Optional; if not set, no delay will be used by default. Can be changed in runtime, for example during loop()
execution.
Example:
void setup()
{
// ...
bluetoothTerminal.setSendDelay(100);
// ...
}Initializes and starts the BLE service, making the device discoverable and ready to accept connections. Returns true
on success and false otherwise.
It is recommended to call this method after completing all other configurations, such as setting the service UUID, characteristic UUID, device name, and buffer sizes. Once start() is called, changes to these settings will not take effect. Starting the service after configuration ensures that all settings are correctly applied before the BLE service becomes active.
Example:
void setup()
{
// ...
bluetoothTerminal.start();
// ...
}Handles BLE events and should be called in the main loop() of your application.
Example:
void loop()
{
// ...
bluetoothTerminal.loop();
// ...
}Checks if a BLE device is currently connected.
Example:
void loop()
{
// ...
if (bluetoothTerminal.isConnected())
{
Serial.println("Device is connected!");
}
else
{
Serial.println("Device is not connected!");
}
// ...
}Sends a message over BLE to the connected device. If the message exceeds the characteristic value size, it is split
into chunks and sent sequentially with delay if configured. Returns true on success and false otherwise.
Example:
void loop()
{
// ...
bluetoothTerminal.send("Hello, world!");
// ...
}When debug is enabled the library logs every event into Serial like this:
[BluetoothTerminal] Debug enabled.
[BluetoothTerminal] The connect handler is set.
[BluetoothTerminal] The disconnect handler is set.
[BluetoothTerminal] The receive handler is set.
[BluetoothTerminal] The service UUID is set to "ffe0".
[BluetoothTerminal] The characteristic UUID is set to "ffe1".
[BluetoothTerminal] The characteristic value size is set to 20 bytes.
[BluetoothTerminal] The name is set to "BluetoothTerminal".
[BluetoothTerminal] The receive buffer size is set to 256 bytes.
[BluetoothTerminal] The receive separator is set to "
".
[BluetoothTerminal] The send separator is set to "
".
[BluetoothTerminal] The send delay is set to 100 milliseconds.
[BluetoothTerminal] Starting BLE service... successful.
[BluetoothTerminal] Setting up BLE service and characteristic...
[BluetoothTerminal] BLE service and characteristic were set up.
[BluetoothTerminal] Device (address "bc:d0:74:45:13:36") was connected.
[BluetoothTerminal] Sending message: "Hello, world!"... sent.
[BluetoothTerminal] Device (address "bc:d0:74:45:13:36") has written 14 bytes into the characteristic.
[BluetoothTerminal] Message received: "Hello, world!".
[BluetoothTerminal] Device (address "bc:d0:74:45:13:36") was disconnected.
When a message is received and it is longer than the receive buffer size (including the receive separator) configured, the oldest data will be discarded with the following debug message:
[BluetoothTerminal] Device (address "bc:d0:74:45:13:36") has written 20 bytes into the characteristic.
[BluetoothTerminal] Receive buffer overflow, data discarded: "Hello, world!".
- ESP32-S3: