Communicating over UART0 with HCI uart BLE dongle (cross post)

(this is a cross post from Communicating over UART0 with HCI uart BLE dongle since I originally, mistakenly, posted it on Balena Cloud)


I am trying to use the BluePy Scanner on a Raspberry Pi 3 B+ with a nRF52840-Dongle programmed as a Bluetooth: HCI-uart.

I am able to get the Bluepy Scanner code to work with the onboard BLE, but I am so far unable to get it to communicate with the Bluetooth HCI dongle that is attached to the UART0. I have connected UART0_TXD (GPIO14), UART0_RXD (GPIO15), UART0_CTS (GPIO16), and UART0_RTS (GPIO17).

How do I go about attaching a HCI ble device to the RPi’s UART and then use it in my python scanner program?

The nRF52840-Dongle has the following default UART settings.

  • Baudrate: 1Mbit/s
  • 8 bits, no parity, 1 stop bit
  • Hardware Flow Control (RTS/CTS) enabled

Following is my bluepyScanner code.

from bluepy.btle import Scanner, DefaultDelegate

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
            print("Discovered device", dev.addr)
        elif isNewData:
            print("Received new data from", dev.addr)

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(10.0)

for dev in devices:
    print("Device {} ({}), RSSI={} dB".format(dev.addr, dev.addrType, dev.rssi))
    for (adtype, desc, value) in dev.getScanData():
        print("  {} = {}".format(desc, value))
        with open('bluepyscanlog.txt', 'a') as the_file:
            the_file.write("{}={}\n".format(desc, value))

And the Dockerfile.template for bluepyScanner.

FROM balenalib/%%BALENA_MACHINE_NAME%%-python:3-stretch-run

# enable container init system.
ENV INITSYSTEM on

# use `install_packages` if you need to install dependencies,
# for instance if you need git, just uncomment the line below.
# RUN install_packages git
RUN pip install --upgrade pip
RUN apt-get update && apt-get install -yq --no-install-recommends build-essential 
RUN install_packages \
    build-essential \
    bluez \
    python-dbus \
    python-dev \
    libglib2.0-dev

# Set our working directory
WORKDIR /usr/src/bluepyScanner

# Copy requirements.txt first for better cache on later pushes
COPY requirements.txt requirements.txt

# pip install python deps from requirements.txt on the resin.io build server
RUN pip3 install -r requirements.txt

# This will copy all files in our root to the working  directory in the container
COPY . ./

# Enable udevd so that plugged dynamic hardware devices show up in our container.
ENV UDEV=1

# main.py will run when container starts up on the device
CMD ["python3","-u","src/bluepyScanner.py"]

The only item in requirements.txt is

bluepy

Following is my docker-compose.yml

version: '2.1'

services:
    bluepy_scanner:
        privileged: true
        restart: always
        build: ./bluepy_scanner
        devices:
            - "/dev/ttyS0:/dev/ttyS0"
        network_mode: host
        cap_add:
            - NET_ADMIN
        labels:
            io.balena.features.supervisor-api: '1'
            io.balena.features.dbus: "1"
            io.balena.features.kernel-modules: "1"
            io.balena.features.firmware: "1"
            io.balena.features.balena-api: "1"
    wifi-connect:
        build: ./wifi-connect
        network_mode: "host"
        labels:
            io.balena.features.dbus: '1'
        cap_add:
            - NET_ADMIN
        environment:
            DBUS_SYSTEM_BUS_ADDRESS: "unix:path=/host/run/dbus/system_bus_socket"

I have set the following Device configurations.

I am trying to use the BluePy Scanner on a Raspberry Pi 3 B+ with a nRF52840-Dongle programmed as a Bluetooth: HCI-uart.

I am able to get the Bluepy Scanner code to work with the onboard BLE, but I am so far unable to get it to communicate with the Bluetooth HCI dongle that is attached to the UART0. I have connected UART0_TXD (GPIO14), UART0_RXD (GPIO15), UART0_CTS (GPIO16), and UART0_RTS (GPIO17).

How do I go about attaching a HCI ble device to the RPi’s UART and then use it in my python scanner program?

The nRF52840-Dongle has the following default UART settings.

  • Baudrate: 1Mbit/s
  • 8 bits, no parity, 1 stop bit
  • Hardware Flow Control (RTS/CTS) enabled

Following is my bluepyScanner code.

from bluepy.btle import Scanner, DefaultDelegate

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
            print("Discovered device", dev.addr)
        elif isNewData:
            print("Received new data from", dev.addr)

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(10.0)

for dev in devices:
    print("Device {} ({}), RSSI={} dB".format(dev.addr, dev.addrType, dev.rssi))
    for (adtype, desc, value) in dev.getScanData():
        print("  {} = {}".format(desc, value))
        with open('bluepyscanlog.txt', 'a') as the_file:
            the_file.write("{}={}\n".format(desc, value))

And the Dockerfile.template for bluepyScanner.

FROM balenalib/%%BALENA_MACHINE_NAME%%-python:3-stretch-run

# enable container init system.
ENV INITSYSTEM on

# use `install_packages` if you need to install dependencies,
# for instance if you need git, just uncomment the line below.
# RUN install_packages git
RUN pip install --upgrade pip
RUN apt-get update && apt-get install -yq --no-install-recommends build-essential 
RUN install_packages \
    build-essential \
    bluez \
    python-dbus \
    python-dev \
    libglib2.0-dev

# Set our working directory
WORKDIR /usr/src/bluepyScanner

# Copy requirements.txt first for better cache on later pushes
COPY requirements.txt requirements.txt

# pip install python deps from requirements.txt on the resin.io build server
RUN pip3 install -r requirements.txt

# This will copy all files in our root to the working  directory in the container
COPY . ./

# Enable udevd so that plugged dynamic hardware devices show up in our container.
ENV UDEV=1

# main.py will run when container starts up on the device
CMD ["python3","-u","src/bluepyScanner.py"]

The only item in requirements.txt is

bluepy

Following is my docker-compose.yml

version: '2.1'

services:
    bluepy_scanner:
        privileged: true
        restart: always
        build: ./bluepy_scanner
        devices:
            - "/dev/ttyS0:/dev/ttyS0"
        network_mode: host
        cap_add:
            - NET_ADMIN
        labels:
            io.balena.features.supervisor-api: '1'
            io.balena.features.dbus: "1"
            io.balena.features.kernel-modules: "1"
            io.balena.features.firmware: "1"
            io.balena.features.balena-api: "1"
    wifi-connect:
        build: ./wifi-connect
        network_mode: "host"
        labels:
            io.balena.features.dbus: '1'
        cap_add:
            - NET_ADMIN
        environment:
            DBUS_SYSTEM_BUS_ADDRESS: "unix:path=/host/run/dbus/system_bus_socket"

I have set the following Device configurations.

Hi

  • If you set priviledged: true, you don’t need to give individual access to the devices. See more about this on our documentation page here - https://www.balena.io/docs/learn/develop/hardware/
  • As I understand, you are using a device that has BLE capabilities which is connected over serial. The python script that you are using assumes that the bluetooth capabilities are on the device that is running the python script - and not the external device over serial. Depending on what you want to do, and what firmware is running on the device. Now the example that you shared, from zephyr - says that you are using the HCI UART example on the Nordic device. In that case, you will have to attach that device to your host i.e. the Raspberry Pi. I am trying to find the code that does this in the stuff that you have shared - are you sure you have attached the device?

I think attaching the device is my problem. When I tested the Nordic HCI USG example it will automatically attach and be seen as hci1 and I can change the python code to use it. I realize that the USB will auto mount, unlike ttyS0, this is just to evidence that the dongle is functional.

However, I don’t know what I am missing to attach the HCI UART dongle.

From the command line, when I try

btattach -B /dev/ttyS0 -P bcm -S 1000000 -R &

and I get the following message:

Attaching Primary controller to /dev/ttyS0
Switched line discipline from 0 to 15
Device index 1 attached
Failed to open HCI user channel
No controller attached

If I have btmon running in a parallel terminal when I run the above btattach command I see the following messages.

Bluetooth monitor ver 5.50
= Note: Linux version 4.19.118 (armv7l) 0.443459
= Note: Bluetooth subsystem version 2.22 0.443469
= New Index: B8:27:EB:EE:6C:BD (Primary,UART,hci0) [hci0] 0.443471
= Open Index: B8:27:EB:EE:6C:BD [hci0] 0.443473
= Index Info: B8:27:EB:EE:6C:BD (Broadcom Corporation) [hci0] 0.443474
@ MGMT Open: bluetoothd (privileged) version 1.14 {0x0001} 0.443477
@ MGMT Open: btmon (privileged) version 1.14 {0x0002} 0.443734
= New Index: 00:00:00:00:00:00 (Primary,UART,hci1) [hci1] 37.475844
= Open Index: 00:00:00:00:00:00 [hci1] 37.475897
= Index Info: 00:00:00:00:00:00 (Broadcom Corporation) [hci1] 37.475906
< HCI Command: Reset (0x03|0x0003) plen 0 #1 [hci1] 37.476137
@ RAW Open: hciconfig (privileged) version 2.22 {0x0003} 37.499233
@ RAW Close: hciconfig {0x0003} 37.499623
@ RAW Open: hciconfig (privileged) version 2.22 {0x0003} 37.538559
@ RAW Close: hciconfig {0x0003} 37.540069
= Index Info: 00:00:00:00:00:00 (Broadcom Corporation) [hci1] 47.608248
= Close Index: 00:00:00:00:00:00 [hci1] 47.608294
= Delete Index: 00:00:00:00:00:00

I wonder if I am having an issue setting up hardware flow control (CTS/RTS) for the uart.

Will setting up the hardware flow control automatically enable the alternate functions for GPIO16 (CTS0) and GPIO17 (RTS0), or do I need to do additional steps to enable the alternate functions? (see section 6.2 on page 102 of https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf)

Following is the information from my attempt to try attaching the HCI UART dongle, without specifying the protocol, (mentioned in the Using the Controller with BlueZ portion of the following page: Bluetooth: HCI UART — Zephyr Project Documentation). Note: I do have to change the port from their example from /dev/ttyACM0 to /dev/ttyS0.

After a fresh restart, I open a terminal running btmon.
Initially I see the following messages.

Bluetooth monitor ver 5.50
= Note: Linux version 4.19.118 (armv7l)
0.046288
= Note: Bluetooth subsystem version 2.22
0.046294
= New Index: B8:27:EB:EE:6C:BD (Primary,UART,hci0)
[hci0] 0.046296
= Open Index: B8:27:EB:EE:6C:BD
[hci0] 0.046297
= Index Info: B8:27:EB:EE:6C:BD (Broadcom Corporation)
[hci0] 0.046299
@ MGMT Open: bluetoothd (privileged) version 1.14
{0x0001} 0.046301
@ MGMT Open: btmon (privileged) version 1.14
{0x0002} 0.046484

In a parallel terminal I run

btattach -B /dev/ttyS0 -S 1000000

and I get the following output

root@71946ab:~# btattach -B /dev/ttyS0 -S 1000000
Attaching Primary controller to /dev/ttyS0
Switched line discipline from 0 to 15
Device index 1 attached

When I go back to look at btmon I see the following new messages:

= New Index: 00:00:00:00:00:00 (Primary,UART,hci1)
[hci1] 178.155977
= Open Index: 00:00:00:00:00:00
[hci1] 178.156094
< HCI Command: Reset (0x03|0x0003) plen 0
#1 [hci1] 178.156155
@ RAW Open: hciconfig (privileged) version 2.22
{0x0003} 178.174166
= Close Index: 00:00:00:00:00:00
[hci1] 188.448245
= Open Index: 00:00:00:00:00:00
[hci1] 188.448407
< HCI Command: Reset (0x03|0x0003) plen 0
#2 [hci1] 188.448446
= Close Index: 00:00:00:00:00:00
[hci1] 198.688228
@ RAW Close: hciconfig
{0x0003} 198.688945
@ RAW Open: hciconfig (privileged) version 2.22
{0x0003} 198.740648
= Open Index: 00:00:00:00:00:00
[hci1] 198.740695
< HCI Command: Reset (0x03|0x0003) plen 0
#3 [hci1] 198.740732
= Close Index: 00:00:00:00:00:00
[hci1] 208.928263
@ RAW Close: hciconfig
{0x0003} 208.928954

The btattach command never give a message indicating a failure even though from btmon it seems that 3 Reset commands to the HCI UART device have failed.

I also never see the hci1 device listed in the output from bluetoothctl list or hciconfig status

Hi, can you use the miniuart-bt overlay? This should free up the uart for you to use

I have “miniuart-bt=on” set in the DT overlays, but it doesn’t seem to work.

I wonder if this issue is, at least in part, due to issues in setting GPIO Alternate functions. With that in mind, and referencing page 102 of the BCM2835 ARM Peripherals document

  1. For ttyS0 uart, I am assuming it it use TXD0/RXD0, and not TXD1/RXD1.
  2. Does the “uart-ctsrts” DT overlay change the alternate functions of the GPIO16/GPIO17 to be CTS/RTS respectively, or do I need to use a script like the following rpirtscts (From Raspberry Pi 3 Hardware Flow Control

Can you use the webterminal to cat and then paste here the contents of /mnt/boot/config.txt please?