Boost the functionality of the nR9160 Feather With Bluetooth on Zephyr!

Jared Wolff · 2020.6.20 · 8 Minute Read · engineering · hardware · 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:

images/Screen_Shot_2020-06-20_at_4.57.41_PM.png

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

images/Untitled.png

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.

images/Screen_Shot_2020-06-20_at_3.55.07_PM.png

Side note: If your program fails to pass the first stage of compilation, you’ll have to fix that before entering the configuration menu.

images/Screen_Shot_2020-06-20_at_3.55.13_PM.png

Let’s say we want more information about CONFIG_BT_SCAN. Type BT_SCAN into the search box.

images/Screen_Shot_2020-06-20_at_3.57.01_PM.png

Then press enter and then press ?. This will give you all the necessary information you need about the option in question.

images/Screen_Shot_2020-06-20_at_3.57.06_PM.png

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:

images/Untitled_1.png

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.

images/Untitled_2.png

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:

images/Screen_Shot_2020-06-20_at_4.26.56_PM.png

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.

images/Untitled_3.png

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!

images/Untitled_4.png

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!

Last Modified: 2020.6.20

Subscribe here!

You may also like

The nRF9160 Feather Connects!

This week, the nRF9160 Feather got some more attention. I even got it to roll over. 😇 To top it all off, my hardware validation list is looking good! In this post i’ll discuss the…

Zephyr Running on The nRF9160 Feather

In this post, i’ll be outlining my first experiences with Zephyr and nRF Connect SDK. If you’re not familiar, Zephyr is an up and coming RTOS for embedded devices. It brings things…

Designing the nRF9160 Feather

Whenever I design a board, I optimize as much as possible. Layer count, part count and even part location can play a big role in how much assemblies cost later down the road. In…