BLE Serial: Binding Bluetooth 4.0 modules to virtual serial ports

The Bluetooth 4+ (low energy) to UART modules are getting increasingly popular, but there was still no proper tool to use them with normal Linux e.g. desktop or RaspberryPi, like rfcomm bind on the old 2.0 bluetooth. So i finally wrote BLE Serial and this post is a introduction. I will go over the installation, usage and slightly into to differences from the newer Bluetooth protocol.

Installation

The software is written completely in Python and packaged as module, so it can be easily installed with pip:

pip install ble-serial
pip install git+https://github.com/edwios/bluepy.git@10f1cee90afb416f139949b86b491e4cfa98c886

If you are wondering why the second command is required: It depends on the bluepy library, but unfortunately there are bugs in the original version and there was no development since a year, so it is important to specifically install this fork with a few fixes.

Now you should have 2 new scripts: ble-scan and the main ble-serial.

Finding devices

First make sure the bluetooth adapter is enabled, for example with bluetoothctl power on, then the scan function can be used (note: root is required for this step):

# ble-scan
Discovered device: 20:91:48:4c:4c:54 -> UT61E - JK
...
Found 2 devices!

Device 20:91:48:4c:4c:54 (public), RSSI=-58 dB
    01: Flags = 06
    ff: Manufacturer = 484d2091484c4c54
    16: 16b Service Data = 00b000000000
    02: Incomplete 16b Services = 0000ffe0-0000-1000-8000-00805f9b34fb
    09: Complete Local Name = UT61E -  JK
    0a: Tx Power = 00

Device ...

The output is a list of the recognized nearby devices. After the MAC address it prints out the device name, if it can be resolved.

If there are devices not found it might help to increase the scan time. All discoverable devices must actively send advertisements, to save power the intervall of this can be quite slow, so try for example 30 seconds then.

optional arguments:
  -h, --help            show this help message and exit
  -t SEC, --scan-time SEC
                        Duration of the scan in seconds (default: 5.0)
  -d, --deep-scan       Try to connect to the devices and read out the service/characteristic UUIDs (default: False)

On Bluetooth 2.0 there was a “serial port profile”, with 4.0 BLE there is unfortunately no standardized mode anymore, every chip manufacturer chooses their own ID to implement the features there.

'0000ff02-0000-1000-8000-00805f9b34fb', # LithiumBatteryPCB adapter
'0000ffe1-0000-1000-8000-00805f9b34fb', # TI CC245x (HM-10, HM-11)

Some usual IDs are included in ble-serial, these will be tried automatically if nothing is specified. You might skip this part and start directly with the connection.

To find the correct ID otherwise i added a deep scan option, it will go through the devices and show all provided interfaces. This scan can take long, especially if there are many devices in the area, so only use it if you want to find the right write characteristic ID.

# ble-scan -d
  Device ...
  ...
  Service: 00001800-0000-1000-8000-00805f9b34fb
    Characteristic: 00002a00-0000-1000-8000-00805f9b34fb READ 
    Characteristic: 00002a01-0000-1000-8000-00805f9b34fb READ 
    Characteristic: 00002a02-0000-1000-8000-00805f9b34fb READ WRITE 
    Characteristic: 00002a03-0000-1000-8000-00805f9b34fb READ WRITE 
    Characteristic: 00002a04-0000-1000-8000-00805f9b34fb READ 
  Service: 00001801-0000-1000-8000-00805f9b34fb
    Characteristic: 00002a05-0000-1000-8000-00805f9b34fb INDICATE 
  Service: 0000ffe0-0000-1000-8000-00805f9b34fb
    Characteristic: 0000ffe1-0000-1000-8000-00805f9b34fb READ WRITE NO RESPONSE NOTIFY 

Now in addition to the previous output there are all characteristics listed, grouped into services. The characteristics in the first service starting with 00002a are not interesting in this case, because they are standard values (for example the device name), if you want to know more look at this list.

After the (U)ID the permissions are listed. We are searching for a characteristic that allows writing = sending to the device, the only candidate in here is 0000ffe1-0000-1000-8000-00805f9b34fb (spoiler: a HM-11 module again).

Connecting a device

The ble-serial tool itself has a few more options:

  -h, --help            show this help message and exit
  -v                    Increase verbosity (logs all data going through)
  -d DEVICE, --dev DEVICE
                        BLE device address to connect (hex format, can be seperated by colons)
  -w WRITE_UUID, --write-uuid WRITE_UUID
                        The GATT chracteristic to write the serial data, you might use "scan.py -d" to find it out
  -l FILENAME, --log FILENAME
                        Enable optional logging of all bluetooth traffic to file
  -p PORT, --port PORT  Symlink to virtual serial port (default = /tmp/ttyBLE)

Only the device address is always required:

$ ble-serial -d 20:91:48:4c:4c:54
21:02:55.823 | INFO | virtual_serial.py: Slave created on /tmp/ttyBLE -> /dev/pts/8
21:02:56.410 | INFO | interface.py: Connected device 20:91:48:4c:4c:54
21:02:56.909 | INFO | interface.py: Receiver set up
21:02:56.909 | INFO | __main__.py: Running main loop!

This log shows a successful start, the virtual serial port was opened on /dev/pts/8, the number at the end changes, depending on how many pseudo terminals are already open on the system. In addition it creates automatically a symlink to /tmp/ttyBLE, so you can easily access it there always on the same file, the default can be changed with the -p/--port option.

Now it is possible to use any serial monitor program, just connect to that port, baud rate etc. does not matter, it will work with any value (ignored it is only virtual). The software acts as transparent bridge, everything that is sent to that virtual port gets transmitted to the BLE module and comes out of the TX pin there. Same in the other direction, everything that the BLE module receives on the RX pin gets transmitted to the PC and shows up in the virtual serial port. This makes also possible to add ble module to create a wireless serial connection with existing hard/software.

As mentioned before, the start might fail because the ID is not in the list, then you have can manually specify the correct characteristic ID like this:

$ ble-serial -d 20:91:48:4c:4c:54 -w 0000ffe1-0000-1000-8000-00805f9b34fb

Also there is an option to log all traffic on the link to a text file:

$ ble-serial -d 20:91:48:4c:4c:54 -l demo.txt
cat demo.txt
2019-12-09 21:15:53.282805 <- BLE-OUT: 48 65 6c 6c 6f 20 77 6f 72 6c 64
2019-12-09 21:15:53.491681 -> BLE-IN: b0 b0 b0 b0 b0 b0 3b b0 b0 b0 ba b0 0d 8a
2019-12-09 21:15:53.999795 -> BLE-IN: b0 b0 b0 b0 b0 b0 3b b0 b0 b0 ba b0 0d 8a

As always, i hope it was helpful. If you encounter problems, please use the issue tracker on GitHub.