SEGGER_RTT Gives You the Debug Output You Wished Arduino Had!
Learn how to use some of the lesser known SEGGER J-Link features to ease your Arduino debugging!
Sometimes, you're faced with a debugging dilemma.
Even a simple MCU project can sometimes take a serious number of Serial.print(...) statements for you to figure out where your flow went wrong.
Sometimes, it's nice just to get a little insight into the inner operations of your MCU.
Sometimes, you might not even have the spare I/O for a debug UART — heaven forbid...
For those of us reading who are more used to working with vendor IDEs and debugging probes, this is all going to be old news - I'm not going to show you anything new here.
But — if you're tinkering in the Arduino world of 32-bit Cortex MCUs, and wanting to get a bit of a better grok at what's going on under the hood, those of us using a SEGGER J-Link probe have a free debug interface that they might not be aware of — SEGGER_RTT; Real Time Transfer.
RTT can be readily employed as a bi-directional communications link — a lighter weight alternative to the occasionally cumbersome and resource heavy Arduino Serial class, for speedy transfer of data between your application and the host machine.
I'm going to demonstrate how to get it going — with a minimum of effort — in an Arduino sketch targeting a Nordic Semiconductor nRF52840-based BLE application.
Prerequisites
If you want to play along with this write-up, we're going to need to get a few things sorted out first. Here's a rundown of what we'll need.
- A recent Arduino build (I'm using 1.8.13)
- You'll need to have the nRF5 Arduino Core — courtesy of SandeepMistry — installed.
This Arduino BSP will get you from A to BLE in no time at all!
With a well established list of pre-defined boards, and generic templates available, it's easy to define any Nordic nRF5 module you might have — thanks to the flexible pin I/O mapping that happens internally within the MCU.
This makes targeting the myriad modules available an easy task indeed!
- The BLEPeripheral Library
Available in the Arduino Library manager (Ctrl + Shift + I), simply search "BLEPeripheral," and install.
- A Nordic Semiconductor board for either an nRF51 or nRF52 device — with an SWD header.
I'm using this little breakout as the first one I had to hand — the module mounted is an nRF52840.
- A SEGGER J-Link Debug Probe
I'm using myJ-Link-EDU Mini for this example, as I'm a foolhardy risk taker, and run my Arduino environment from within an Ubuntu instance, hosted on WSL2.
For the hobbyist, it's a pretty invaluable tool and it has worked flawlessly on my absolute hackjob of a host setup — over usbip no less! — well... a lot of other tools haven't been so hardy.
(USB support for WSL2 is an upcoming article in itself, but for now, SEGGER — I've got a full-price Plus pod, I just don't want to brick it. Hope this is all coolandstuff, yeah?)
- The SEGGER_RTT source files
Available as a zip file direct from SEGGER here, this is the source that gets us up and running with the RTT protocol.
Grab those, and stick a copy of them somewhere you can remember for later.
Turn up the volume on the Arduino compilation process!
First thing first, a lot of errors are pretty easy to solve, provided you have the right information — and when you're going into uncharted territory, the more information the better.
Pull up the preferences window of the Arduino IDE (Ctrl + ,), and check both boxes after the line asking "Show verbose output during:"
Compiler warnings can be pretty handy to have, as are line numbers and code folding, I have no idea why the last two aren't enabled by default!
Bootstrap the BSP
In the same preferences window, click the icon to the right of the text box that lists the "Additional Board Manager URLs:"
In the following pop-up window, enter the link to the json file, that tells Arduino where to find the nRF5 Core.
https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
And click OK, and once more to get back to the IDE window. Next, navigate to the Tools -> BoardsManager option.
Arduino will spend a little time sorting out the new information from the JSON file we've just told it about, and once it's happy, the Boards Manager listing will present the Nordic Semiconductor nRF5 Boards package, ready for you to click Install.
Once complete, the window will update to show Installed, next to the Boards entry. There — you can now target nearly any Nordic nRF51/52 MCU from within Arduino — and boost your BLE skills a few XP levels!
Setting up your nRF Target
With the nRF5 Core installed, let's pick our board. For the sake of demonstrating the practical nature of SEGGER_RTT, I'm going to pick a Generic nRF52 Target, as I don't need to define any pins to use the interface — all we need is the SWD debug interface and the BLE radio.
The soft touch...
So, one slight gotcha. Before such a complex SoC as a Cortex-M4 MCU, with a full BLE radio stack can spool up, some housekeeping has to be taken care of.
Before our Arduino code gets the green light to jump to setup(), the MCU needs to initialize itself, and all it's complex peripherals.
Nordic handles this with something called a SoftDevice. In essence, it's a bootstrapper / bootloader blob that sets up the MCU, handles certain errata, and provides the interface to the radio front end.
We can take care of this, given you've followed the instructions provided in the nRF5 Git Wiki on how to add the required IDE add-on, that handle the flashing of the SoftDevice that is required by your specific nRF5x device.
You'll want to grab the required SoftDevice files (S110, S130 & S132 v2.0.1), as — from memory — the current licensing prevents the distribution of these blobs with the nRF5 Core itself. No issue, we can just grab the from Nordic directly using the links listed here.
Get flashy!
I'll leave the SEGGER "how-to" to the experts, but I'll assume you've got the J-Link software suite installed, and have verified that you can talk to your J-Link debug probe flavor of choice. First up, check you can see the J-Link option in the Programmer menu of the IDE.
Next up, make sure you've got the right SoftDevice selected for your target. Ι'm using an nRF52, so I want S132 selected!
And finally, we can flash our SoftDevice firmware blob! Click Tools -> Burn Bootloader!
And it will fail. So hard.
You remeber how I suggested turning up the Arduino output verbosity?
Well, this is where we learn why all that extra noise coming from the Arduino output window can be pretty helpful. To demonstrate what's going on, I've forced the following screenshots to look for the S110 firmware, as that's one I don't use.
If I try to burn the S110 SoftDevice, let's look at the error output for some clues as to why it's failed so badly.
The line highlighted, starting "Error:" reads:
Error: couldn't open /root/.arduino15/packages/sandeepmistry/hardware/nRF5/0.7.0/cores/nRF5/SDK/components/softdevice/s110/hex/s110_nrf51_8.0.0_softdevice.hex
And, of course! We've grabbed those SoftDevice files, but haven't put them where the core is told to look for them, so the flashing procedure fails as it has no hex file to load.
Not to fret — it's an easy fix! We want to navigate to "softdevice" folder, where the core expects to find the various SoftDevice files.
In the example here, looking at the error, we can see it is looking in where I have the BSP installed:
/root/.arduino15/packages/sandeepmistry/hardware/nRF5/0.7.0/cores/nRF5/SDK/components/softdevice/
(Yes, I'm running Arduino as root - don't @ me.)
Pick apart your error output untill you can figure out where your SoftDevice folder is. For Windows users, that might be something like:
C:\Users\[USERNAME]\AppData\Local\Arduino15\packages\sandeepmistry\hardware\nRF5\0.7.0\cores\nRF5\SDK\components\softdevice
At the worst case, you might have to search for a folder called "softdevice"...
Once you find it, you'll see the following 4 folders, conveniently named.
Looking at the S110 folder — the one we are missing here — we can see that the enclosed hex folder is somewhat devoid of any .HEX files!
If we take a peek in the S110 SoftDevice Zip archive that we pulled from Nordic earlier, we see the following file structure — we want to copy the HEX file into the hex folder that we've just located, within the nRF5 core.
Bear with me — simply drag and drop the appropriate HEX files from the Nordic downloads, into the appropriate softdevice hexfolder.
For the S132 device, as I'm using, that ends up looking like this! It could be a good idea to make sure all three SoftDevice files are in place!
With the files where the core expects them to be, you can now breathe (several) sighs of relief!
Go back to Tools -> Burn Bootloader, and sit back and watch the output flow as the SoftDevice is flashed. This process can take up to a minute or more, so don't panic — if it doesn't fail instantly, it's a good sign!
Lets check we can compile an example.
So, let's see where we're at.
We've got a nearly limitless supply of BLE-enabled, Arduino-capable chips to play with now and plenty of code examples.
Let's open up the "serial" example, from the BLEPeripheral library example files.
First up, we're going to change the pin numbers passed to the BLESerial Initialization call to "-1." This tells the BLEPeripheral library we're working with the on-board BLE radio.
Now is a good time to hit compile, and check everything builds. You might see an error like the following:
root/Arduino/libraries/BLEPeripheral/src/nRF51822.h:11:12: fatal error: ble_gatts.h: No such file or directory
11 | #include <ble_gatts.h>
| ^~~~~~~~~~~~~
This can happen when poking around with different board types. Each time you change the selected nRF device in the board menu, you must make sure to re-select the required SoftDevice. Here, I've forgotten to do that, as you can see with the "None," following the "Generic nRF52," at the bottom of the IDE window.
If we go back and make sure that we have the required SoftDevice selected (S132 here), hit compile again, and wait for the much desired successful return from the compiler.
Wahoo! We've got a BLE UART to Serial UART bridge, that takes up 7% of our available space. Woah.
Next up, let's check it all works — before we start adding in the final, much simpler RTT files.
We're using a J-Link debugger, so, we can now use this to upload our compiled firmware, including the SoftDevice, all in one. Select "Sketch -> Upload Using Programmer," or hit (Ctrl + Shift + U).
Watch that output flow again, and wait for the **Verified OK** — we've done it!
If that's not the case, it's worth perhaps double checking the compatibility of the SoftDevices.
In my case, I found actually had to fall back to "pretending" I was an nRF51, to flash s130 SoftDevice, and then re-upload using the programmer.
But there's our little SoC broadcasting it's services in the nRFConnect Android app.
Right — it's about time for some RTT!
Hit Ctrl + K, to bring up the folder where the sketch files are located.
That should show you the root folder of the current Arduino window Sketch.
You'll see your main .ino listing, and the two extra files used by BLESerial.
Now, we're going to go back to the SEGGER_RTT file Zip archive, and copy the contents of the RTT folder contained within that archive, to our sketch folder.
Next, we add the following code to the top of our "serial" sketch.
extern "C"{
#include "SEGGER_RTT.h"
}
This tells the Arduino IDE that the files being included are plain C, rather than the C++ the compile expects — allowing it them to be properly compiled!
And now, how to actually use RTT?
The very first lines of your setup function in the sketch should look like:
void setup() {
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
SEGGER_RTT_Init();
// custom services and characteristics can be added as well
BLESerial.setLocalName("UART");
SEGGER_RTT_ConfigUpBuffer will give us a stream from the target, up to the host machine. It's buffer index 0, that's all we need to know at the moment.
SEGGER_RTT_Init(); does just as it suggests.
With these two lines in place, hit compile again, to verify that everything links properly.
Now, let's test out the whole kaboodle.
I'm going to modify the "loopback" function in the sketch, to echo out the received BLE data to the RTT terminal.
Orignal code:
And our updated function, with the added RTT call to pass the data up the pipe.
If we uncomment the call to "loopback();" in the main loop, we can now UploadUsing Programmer again.
That's it. Now we can show off what all this work has been in aid of!
From the J-Link Software folder, find "JLinkRTTViewer," and run it.
From the file menu, click Connect, and you'll see a window that should automatically grab the required settings from the attached J-Link Pod.
Click "OK," and you'll see the log window show that we're successfully talking over RTT!
Now, we've hooked the loopback function to be the part of our application that sends data to RTT. So, let's jump back onto the nRFConnect Toolbox, and send some chars over the UART.
Type it in, hit "SEND," and almost instantly, RTT Viewer will spawn a new terminal window (Terminal 0), and lo-and-behold, look what's printed there!
We've done it! From zero to using a professional debug method, that while admittedly isn't quite stepping through breakpoints, is a lot more versatile and more robust than the standard Arduino Serial core.
The more intrepid will quickly see from looking at the RTT source files how to configure multiple, bi-directional pipes, allowing for fast, solid, and reliable data transfer in and out of your application on-the-fly as it's running. That opens up huge possibilities for working with image data, etc, while tweaking algorithms.
For once, this is one of my own write-ups, so, er, yeah, I guess check me out on Twitter (@tomfleet), and I'll be sure to update when this all gets put up into a gist / repo!