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.
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.
No comments yet! Start the discussion at forum.golioth.io