The ESP32 HCI makes any Zephyr board a Bluetooth gateway

Bluetooth is everywhere, or soon will be…especially on Golioth! Adding Bluetooth connectivity opens up a wealth of new user interaction possibilities. It’s inexpensive, low-power, and easy to add, especially with projects that use Zephyr. Today we’ll look at adding Bluetooth to a design based on the Nordic nRF91 cellular modem by using the Espressif ESP32-C3.

This is possible because the Bluetooth Host Controller Interface (HCI) provides a standard for communicating between the two chips, even though one is running Zephyr and the other is running ESP-IDF. For this particular example we chose the chips because they are already in place on the Golioth Aludel Elixir hardware. But the same approach can be used as long as the Bluetooth radio has an HCI firmware available. This means we have opened up another avenue to build a Bluetooth gateway. In fact, we did that originally by using the HCI interface to turn the nRF52840 into a Bluetooth interface on the nRF9160-DK as part of our early access program for Bluetooth-to-Cloud functionality of Golioth.

Hardware Setup

The main processor will talk to the Bluetooth chip via UART. In our case we’re are not using hardware flow control, we only have TX and RX lines. This will work, but we need to keep it in mind when preparing the firmware.

Block diagram of an nRF9160dk connecting to an ESP32c3 through two lines labeled RX and TX

Building the HCI Firmware for the ESP32-C3

Espressif has already done the hard work for use. We start from the ESP-IDF UART HCI Controller sample app. While the README clearly states “RTS and CTS lines of UART1 are required.”, this is actually something that can be configured. So let’s set up the UART on this application to match our needs.

Edit the main.c file to match the pin mapping for the ESP32-C3:

#define GPIO_UART_TXD_OUT  (7)
#define GPIO_UART_RXD_IN   (6)
#define GPIO_UART_RTS_OUT  (-1)
#define GPIO_UART_CTS_IN   (-1)

Note that I’ve used -1 for RTS and CTS. These are the hardware flow control pins that we don’t have and setting to this value is how you indicate “not connected” to ESP-IDF. However, this will cause a little trouble with another macro so let’s update those lines by removing the RTS/CTS bits:

#define GPIO_OUTPUT_PIN_SEL  (1ULL<<GPIO_UART_TXD_OUT)
#define GPIO_INPUT_PIN_SEL   (1ULL<<GPIO_UART_RXD_IN)

The final customization we need is to disable hardware flow control in the configuration. Add the following to the sdkconfig.defaults file:

CONFIG_EXAMPLE_HCI_UART_FLOW_CTRL_ENABLE=n

Now just build and flash the firmware:

idf.py set-target esp32c3
idf.py build
cd build
esptool.py --chip esp32c3 merge_bin -o merged-flash.bin @flash_args

On the Aludel Elixir, we use a programming header that is normally unpopulated and a USB-to-UART plug/cable to program the binary into the ESP32-C3.

Building Main Firmware with HCI Support

Zephyr includes host support to use an HCI controller. It may be enabled on any Bluetooth application by adding devicetree nodes for the UART, and enabling it in Kconfig.

First, add a pinctrl mapping to your overlay file:

&pinctrl {
    esp_uart_default: esp-at-uart-default {
        group1 {
            psels = <NRF_PSEL(UART_TX, 0, 25)>,
                <NRF_PSEL(UART_RX, 0, 31)>;
        };
    };

    esp_uart_sleep: esp-at-uart-sleep {
        group1 {
            psels = <NRF_PSEL(UART_TX, 0, 25)>,
                <NRF_PSEL(UART_RX, 0, 31)>;
            low-power-enable;
        };
    };
};

Next add a node for the UART using the pinctrl mapping from the previous step. This new node also needs to be set as the zephyr,bt-hci chosen.

/ {
    chosen {
        zephyr,bt-hci = &bt_hci_uart;
    };
};

&uart1 {
    status = "okay";
    current-speed = <115200>;
    pinctrl-0 = <&esp_uart_default>;
    pinctrl-1 = <&esp_uart_sleep>;
    pinctrl-names = "default", "sleep";

    bt_hci_uart: bt_hci_uart {
        compatible = "zephyr,bt-hci-uart";
        status = "okay";
    };
};

With the overlay file in place we need to tell Zephyr to use the HCI controller. It is also important that we remember to disable hardware flow control since we don’t have RTS/CTS lines in this setup:

CONFIG_BT_HCI_ACL_FLOW_CONTROL=n
CONFIG_BT_HCI=y
CONFIG_BT_LL_SW_SPLIT=n

For demonstration purposes I used the above setup to build the samples/bluetooth/observer app from the Zephyr tree.

cd zephyr/samples/bluetooth/observer/
west build -p -b aludel_elixir/nrf9160/ns .
west flash

By opening a serial connection to the device we see the sample app simply logs any Bluetooth devices it finds to the console:

*** Booting nRF Connect SDK v3.0.1-9eb5615da66b ***
*** Using Zephyr OS v4.0.99-77f865b8f8d0 ***
Starting Observer Demo
Started scanning...
Exiting main thread.
Device found: C9:DC:04:7C:AE:75 (random) (RSSI -65), type 0, AD data len 20
Device found: 33:02:84:13:7B:BE (random) (RSSI -98), type 3, AD data len 31
Device found: F4:BC:DA:35:5E:68 (public) (RSSI -100), type 0, AD data len 27
Device found: 2B:EB:AE:20:87:95 (random) (RSSI -95), type 3, AD data len 31
Device found: C0:70:4C:D8:D4:00 (random) (RSSI -52), type 0, AD data len 25
Device found: 19:47:44:63:14:9B (random) (RSSI -66), type 0, AD data len 0
Device found: F0:EF:86:F5:BD:AE (random) (RSSI -99), type 0, AD data len 14

Putting It All Together

Logging mac addresses from Bluetooth nodes isn’t all that compelling. The real reason for all of this work is to use the Golioth Aludel Elixir hardware as a Bluetooth-to-Cloud gateway. This device can now send data from Bluetooth devices that otherwise have no network connectivity to the Golioth cloud. While our Bluetooth-to-Cloud features are still in private access, you can try them out simply by telling us you want early access.

Mike Szczys
Mike Szczys
Mike is a Firmware Engineer at Golioth. His deep love of microcontrollers began in the early 2000s, growing from the desire to make more of the BEAM robotics he was building. During his 12 years at Hackaday (eight of them as Editor in Chief), he had a front-row seat for the growth of the industry, and was active in developing a number of custom electronic conference badges. When he's not reading data sheets he's busy as an orchestra musician in Madison, Wisconsin.

Post Comments

No comments yet! Start the discussion at forum.golioth.io

More from this author

Related posts

spot_img

Latest posts

A Remote Shell for Embedded IoT Devices

Golioth's Remote Shell uses Remote Procedure Calls (RPCs) and a custom Zephyr shell backend to enable an interactive, web-based shell experience from anywhere in the world.

Enabling Bluetooth-to-Cloud on the Arduino Nicla Sense ME

Here's how easy it is to add a Bluetooth device to the cloud. In just a few minutes, we built a BLE-GATT example for the Arduino Nicla Sense ME board and had it sending data to the cloud.

Hardware MVP: Why “Start Ugly” Beats Perfect in the AI Age

Building a connected product? Start ugly. Duct tape a dev board to your prototype. Don't worry about enclosures. Focus on the data you need and...

Want to stay up to date with the latest news?

Subscribe to our newsletter and get updates every 2 weeks. Follow the latest blogs and industry trends.