How To Add Bluetooth to your Zephyr powered nRF9160 Feather
Learn how to easily add Zephyr's Bluetooth stack to the nRF9160 Feather.
One of the cool things about Zephyr is its modularity. It’s also one of things that makes development on the platform difficult. This is especially true if you’re not use to the recursive nature of CMake or how to configure it in the first place!
Despite these setbacks, I was able to configure the nRF9160 Feather to be used as a fully fledged Bluetooth device! In this post you’ll see some of important steps and learn from my experience of starting “from scratch.” That way you don’t have to do it all yourself!
So without further ado, let’s begin!
An RTOS With Some Serious Features
Let’s start with an easy example. Then we can wade into something more.. complicated.
Does you project have USB? Add a few lines in your prj.conf
and voilà, presto change-o, and you have a USB Console!
Don’t believe me?
I took a Particle Xenon I was using and got searching through the Zephyr repository. I searched for maybe a minute or two until I found the correct definitions. Fortunately. there were a few other samples in the Zephyr repo that use the same features. That made the search much easier!
You can add this to nearly any project. Just make sure that your device has USB and that it’s definied in the Device Tree. For now we’ll assume you’re using a nRF52840.
CONFIG_CONSOLE=y
CONFIG_USB=y
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Xenon"
CONFIG_USB_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_LINE_CTRL=y
CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM_0"
CONFIG_USB_UART_DTR_WAIT=y
Compiling and flashing is done with these guys:
west build -b particle_xenon
west flash
Then, when I plug in and start the session using screen
I get some nice output:
Things don’t always go as planned though.
The CONFIG_USB_UART_DTR_WAIT
does lock the processor from executing until the shell session has been opened. The peripheral doesn’t show up otherwise. (not exactly sure why…)
With the above being said, the Zephyr stack is fraught with hidden APIs and documentation that is sorely lacking in some areas. Even the features I found above area not in the official Zephyr documention. You should be prepared to hit some potholes as you go along. (I’ve been hitting them for the past few weeks if that gives you any indication!)
Fortunately, Nordic has been leading the charge in making sure that anything related to Nordic devices is as stable as possible.
The nRF9160 Feather Does Some Tricks Too
The nRF9160 is a fantastic little System in Package. One thing is missing though: Bluetooth. Fortunately, there’s an easy way to add it with Zephyr that works quite well. As you’ll see in the next steps, the picture I pain’t of Zephyr above isn’t all bad.
Looking for the example code
Examples are always a great place to start in Zephyr. The one we can start using almost immediately is the /nrf/samples/nrf9160/lte_ble_gateway
sample. This sample does everything that someone would want to do in order to connect a nRF9160 to a Bluetooth capable device.
You’ll need to have the full ncs
(Nordic’s Connect SDK) setup in order to get to it though. You can learn more about getting setup here. You can also look at my earlier post about it as well.
Modifying the Device Tree definition
Taking a look at the boards
folder withing the sample you can see that there is an .overlay
file. This helps point which UART interface will be used for the HCI communication. The one for the nRF9160 development kit looks something like this:
/ {
chosen {
zephyr,bt-uart=&uart2;
};
};
&uart2 {
compatible = "nordic,nrf-uarte";
current-speed = <1000000>;
status = "okay";
tx-pin = <18>;
rx-pin = <17>;
rts-pin = <21>;
cts-pin = <19>;
};
The first part, is about choosing which interface to use. In this case they assigned the zephyr,bt-uart
interface to uart2
. They’ve then re-defined uart2
below. I say re-defined because every board has a top level .dts
(aka a Device Tree) file.
Then mapping to the pins that are connected in hardware, they’ve set the speed, what driver to use and more.
The one i’m using for the nRF9160 Feather is not too disimilar.
/ {
chosen {
zephyr,bt-uart=&uart2;
};
};
&uart2 {
compatible = "nordic,nrf-uarte";
current-speed = <1000000>;
status = "okay";
tx-pin = <24>;
rx-pin = <23>;
rts-pin = <29>;
cts-pin = <30>;
};
You can see i’ve only changed the pin assignments. These are set to match the TX/RX/CTS/RTS pins on the Feather.
Adding to your app configuration
The next thing you’ll have to do, as I alluded to in the first section of this post is to edit your prj.conf
file. If you look at the original, the section related to Bluetooth is the most important:
# Enable Bluetooth stack and libraries
CONFIG_BT=y
CONFIG_BT_H4=y
CONFIG_BT_WAIT_NOP=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_GATT_DM=y
CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_BT_SCAN_UUID_CNT=1
CONFIG_UART_2_NRF_FLOW_CONTROL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
As you become familiar with Zephyr, the more likely you’ll be able to recognize which definitions do what. You can also use west build -t menuconfig
and then hit the /
button to search for definitions.
Side note: If your program fails to pass the first stage of compilation, you’ll have to fix that before entering the configuration menu.
Let’s say we want more information about CONFIG_BT_SCAN
. Type BT_SCAN
into the search box.
Then press enter
and then press ?
. This will give you all the necessary information you need about the option in question.
I’ve used this method numerous times to determine what’s what. Usually the descriptions here are brief so you may not always find what you’re looking for!
The most important defintions that tell Zephyr we’re using HCI UART are:
CONFIG_BT_H4=y
and
CONFIG_UART_2_NRF_FLOW_CONTROL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
Without them compliation would bork or your code would simply not work. I spent a good chunk of time trying to figure out why my setup wasn’t working. I’ll get to that in a second! First though, you’ll have to install the hci_uart
sample to a companion device. In my case i’m using a Particle Xenon.
A companion
Next, jump on over to the /ncs/zephyr/samples/bluetooth/hci_uart
example. We’ll have to do some similar Device Tree finagling here. As of this writing, Xenon is not in the boards
folder. So we can add a particle_xenon.overlay
with the Bluetooth to UART interface defined:
/* SPDX-License-Identifier: Apache-2.0 */
/ {
chosen {
zephyr,bt-c2h-uart=&uart0;
};
};
&uart0 {
compatible = "nordic,nrf-uarte";
current-speed = <1000000>;
status = "okay";
tx-pin = <8>;
rx-pin = <6>;
rts-pin = <34>;
cts-pin = <33>;
};
This matches specifically to the complement of the Feather’s TX/RX pins. If you choose to use a different Feather board as the main processor, you can leave this unchanged!
Then matching most of the other .conf
files, here’s what particle_xenon.conf
looks like:
CONFIG_GPIO=y
CONFIG_MAIN_STACK_SIZE=1024
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512
CONFIG_BT_WAIT_NOP=y
CONFIG_BT_MAX_CONN=16
CONFIG_BT_TINYCRYPT_ECC=n
CONFIG_BT_CTLR_DTM_HCI=y
CONFIG_BT_CTLR_ASSERT_HANDLER=y
# Use UART0 for HCI
CONFIG_UART_0_NRF_FLOW_CONTROL=y
This example uses the Zephyr Link Layer implementation. If you’d like to use the Nordic one, you can add these definitions to particle_xenon.conf
.
# Link Layer
CONFIG_BT_LL_NRFXLIB_VS_INCLUDE=y
CONFIG_BT_LL_NRFXLIB_DEFAULT=y
Remember, a .conf
file is changing application specific definitions. If it’s defined in the board definitions, this file will override it!
Then compiling and flashing is as simple as these two commands:
west build -b particle_xenon -p
west flash
In my case I had connected a nRF52DK to the Xenon in question. As long as there are no other J-Link or programmers it shouldn’t prompt you to choose a programmer. Here’s a picture of the setup:
This should look farmilar to those who have seem my guides before.
Fire it up!
Programming the nRF9160 Feather in a similar way you are now ready to add the connections. I’ve used a Feather tripler to make the connections easy. I did cut a few traces like the Enable pin since the enable pin on the nRF9160 Feather is active low.
I also cut the 3.3V that way each board can regulate its own supply. The Xenon is powered by the 5V pin on the nRF9160 Feather.
You can also see the green wire I used to interconnect two pins. This was for controlling reset of the Xenon. This turned out to be critical because without it UART would get stuck! I only figured this out after a very confusing hardware and firmware debug session. I did eventually figured it out. Only after hitting the rest button at the perfectly right time though! You live and learn, right?
If you’ve done things correctly, you’ll get something nice like this:
Now that you have one half of the equation done, you can integrate it with almost any device. The lte_gatreway
sample I mentioned before is used with Nordic’s Thingy52. That’s a great starting point expecially if you have one handy!
Another Tip for Debugging Bluetooth
There’s one more tool you may find yourself using during your Zephyr development: the Bluetooth shell.
Located at ncs/zephyr/tests/bluetooth/shell
, this sample allows you to control a Bluetooth device over a shell session by using a human readable commands. It’s a great sanity check to make sure your device is working in the way you want.
I ran this first on a nRF52840 development kit. So my build and flash commands looked like this:
west build -b nrf52840dk_nrf52840 -p
west flash
Once programmed, you can open the session like so:
screen /dev/tty.usbmodem0006838206631 115200
If you’re on Windows, you can use a program like Putty to accomplish the same thing.
Then you can start advertising in normal mode using the following input:
bt init
bt advertising on
Likewise, on a separate device you can scan using:
bt init
bt scan on
You can connect, disconnect, send data and more. To see all of the commands simply type bt
in the command prompt.
There are little gems like this scattered throughout the Zephyr and Nordic repos. Thanks to some helpful Nordic engineers for the tip on using this tool. Without it, I would have never found it!
More coming soon!
More coming soon especially about nRF9160 Feather developments. Including updates about Proto 3!
The boards should be at my doorstep this coming week. So i’ll be sure to update everyone on the status of the new active GPS circuit. If that checks out, things may be looking good for launch! Make sure you subscribe to stay up to date on the project!
Originally posted on jaredwolff.com
Designing the nRF9160 Feather in collaboration with Hackster.io and GroupGets. https://www.hackster.io/launch