nRF52 Development using Visual Studio Code [macOS]

VS Code + nRF52

Introduction

When it comes to IDEs and advanced text editors we, developers, have so many choices… probably too many!

But that’s not necessarily a bad thing.

Having these many choices can be overwhelming, and if you’re a tools geek like me, you end up spending hours and hours just exploring and trying out new tools 🤦

But once in a while, you come across a tool that you end up liking so much that you’re happy spending these hours learning it 😉and for me, Visual Studio Code has become one of those tools.

In the past, I’ve published posts about how to set up nRF52 development for Netbeans and Segger Embedded Studio. Today, I will be walking you through a similar tutorial for setting up Visual Studio Code for nRF52 development on macOS.

So, why use a tool that was created with embedded programming in mind for nRF52 development?

Well, there’s a lot to like about VS Code even in the case of embedded development:

  • It’s free and open-source
  • Availability and growing number of extensions
  • Extreme flexibility and customization
  • Multi-platform support
  • Usability across multiple projects and for different chipsets

In this post, we will go through:

Setup Steps

Download and Installation

The first step you need to do is download the following:

Downloading Xcode and installing it is simple and can be done from the Mac App Store. After you install it, you need to make sure you have Xcode Command Line Tools installed. Here’s a great tutorial on how to do that:

https://www.embarcadero.com/starthere/berlin/mobdevsetup/ios/en/installing_the_xcode_command_line_tools_on_a_mac.html

Once you’ve downloaded the nRF5 SDK, place it in a folder of your choice and make note of the location (/Users/mafaneh/nRF52/ in my case):

Downloading and installing the GNU Arm Embedded Toolchain is also as simple as extracting the package (gcc-arm-none-eabi-8-2019-q3-update-mac.tar.bz2 in my case) into a folder of your choice. Make sure you make note of this folder as well.

Once you’ve downloaded VS Code, extract the .zip package and move the Application binary to your Applications folder. Don’t run it just yet.

Setting up the development environment

Before we run VS Code, we need to perform a few configuration steps to make development easier.

Edit the file named <NRF5 SDK>/components/toolchain/gcc/Makefile.posix and change the GNU_INSTALL_ROOT to point to the location of your GNU Arm Embedded Toolchain installation:
Makefile.posix

Next, we’ll be creating a shell script to launch VS Code with the appropriate environment variables defined. This allows us to define the locations of the SDK and Arm Toolchain in one place rather than in multiple places in the VS Code workspace, tasks, and configurations.

You can place this script anywhere on your machine. I named mine nRF_VSCode.sh and placed it on the Desktop for easy access. Here are the contents of the file:
VS Code shell script

The next step is to make the shell script executable so you can launch it from the Finder (by double-clicking). Open up a Terminal window in the same folder and run the following command
chmod +x <shell_script_name>

Now we’re ready to launch VS Code via our shell script by double-clicking the shell script file:
VS Code shell script launch

You should see the VS Code window appear:


VS Code Launch

Next, we need to install the C/C++ Extension within VS Code. Navigate to the Extension tab highlighted in the screenshot:


Extensions tab

Alternatively, you could access the Extensions by navigating to the following menu:


Extensions menu

Search for “C/C++” and install the official Extension provided by Microsoft:

C/C++ Extension

Creating the Workspace

Now we’re ready to create our VS Code workspace and add the nRF52 example application to it.

To do this, we’ll first start by selecting the “Add Folder to Workspace” from the File menu.


Add Folder to Workspace

Next, navigate to the folder for the example labeled “ble_app_blinky” located in the nRF5 SDK folder at <nRF5 SDK>/examples/ble_peripheral/ble_app_blinky, then click “Add”.

Now, let’s save the Workspace and name it. You can do this by accessing the “Save Workspace As” menu item under the File menu.


Save Workspace As

Make sure you save this Workspace to the example’s folder at <nRF SDK>/examples/ble_peripheral/ble_app_blinky/.

At this point, your Explorer window should include the ble_app_blinky folder and look something like this:

Unfortunately, this is only half the battle… If you open main.c for example, you’ll see that the header files are not recognized:

Compiler Settings

Intellisense is the (really useful) feature of VS Code which provides autocompletion functionality and allows us to jump to functions, variable definitions, and header files very easily. It’s one of the features that make VS Code closer to an IDE than a simple text/code editor.

In order to get this working, we need to configure the C/C++ Compiler settings.

First, launch the Command Palette by pressing ⇧⌘P or F1 (or from the View menu).

Start typing “C/C++: Edit Conf…” and you should find the command “C/C++: Edit Configurations (JSON)“:


C/C++ Configurations

This will create a new c_cpp_properties.json file local to your Workspace. Initially, you’ll have something like this:

c_cpp_properties

We’ll make changes to the include path and the compiler setting. We’ll include the GNU Arm Embedded include path and a couple of paths from the nRF SDK.

Notice that we’re using environment variables here in the form ${env:...}. The two environment variables we’re using are GNU_GCC and nRF_SDK, which we previously defined in our VS Code launcher shell script.

{
    "configurations": [
        {
            "name": "nRF52840 DK",
            "includePath": [
                "${workspaceFolder}/**",
                "${env:GNU_GCC}/arm-none-eabi/include",
                "${env:nRF_SDK}/modules/**",
                "${env:nRF_SDK}/components/**"
            ],
            "defines": [
                "BOARD_PCA10056",
                "CONFIG_GPIO_AS_PINRESET",
                "INITIALIZE_USER_SECTIONS",
                "FLOAT_ABI_HARD",
                "NRF52",
                "NRF52840_XXAA",
                "NRF_SD_BLE_API_VERSION=6",
                "S140",
                "SOFTDEVICE_PRESENT",
                "SWI_DISABLE0"
            ],
            "macFrameworkPath": [
                "/System/Library/Frameworks",
                "/Library/Frameworks"
            ],
            "compilerPath": "${env:GNU_GCC}/bin/arm-none-eabi-gcc",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

Now we’re ready to configure the build process.

Building and Compiling the Sample Application

VS Code includes the concept of Tasks, which can be used to invoke external commands and configure automated operations. We’ll be using Tasks to define our different build operations.

You can learn more about VS Code Tasks here: https://code.visualstudio.com/docs/editor/tasks

The different Tasks are defined in a tasks.json file. To create this file, you simply navigate to the menu named Terminal then run Configure Tasks.

You’ll be presented with the following drop-down option:

Click “Create tasks.json file from template“. Then choose “Others“:

VS Code will automatically create a tasks.json file for you:

This is where we’ll be defining our build operations. For our purposes, we’ll be defining the following tasks:

  • A build task: for building our nRF52 application.
  • A clean task: for cleaning up and removing any previously built objects and binaries.
  • A flash task: for flashing the application binary to the nRF52 development board.
  • A flash_softdevice task: for flashing the SoftDevice to the nRF52 development board.
  • An sdk_config task: for launching the CMSIS Configuration Wizard which is used for accessing the sdk_config.h file.

Let’s go ahead and define each of these tasks:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "make",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "clean",
            "type": "shell",
            "command": "make clean",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "problemMatcher": []
        },
        {
            "label": "flash",
            "type": "shell",
            "command": "make flash",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "group": "build",
            "problemMatcher": []
        },
        {
            "label": "flash_softdevice",
            "type": "shell",
            "command": "make flash_softdevice",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "problemMatcher": []
        },
        {
            "label": "sdk_config",
            "type": "shell",
            "command": "make sdk_config",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "problemMatcher": []
        },
        {
            "label": "erase",
            "type": "shell",
            "command": "make erase",
            "options": {
                "cwd": "${workspaceFolder}/pca10056/s140/armgcc"
            },
            "problemMatcher": []
        }
    ]
}

A few important notes about the tasks:

  • The current working directory (cwd) is set to point to the pca10056/s140/armgcc/ folder.
  • The Make targets such as clean, flash, flash_softdevice, erase, and sdk_config are all already defined in the file pca10056/s140/armgcc/Makefile.
  • The build task is the default VS Code build task for this Workspace. This makes it easier to invoke by running the Run Build Task command under the Terminal menu:
    Run Build Task

Now, we’re ready to confirm that our header files are recognized in our source files such as main.c.

We can also now compile our application by running the Build Task.

We can see that the build process completed successfully.

Note: You may notice a number of “Problems listed when you have various source files open. This seems to be a known issue with VS Code not being able to parse some of the definitions and macros in Arm GCC header files. The good thing is that this should not affect any of the functionality, and only some of the Intellisense parsing.

Flashing the Sample Application to the Development Kit

Now, we can flash our development kit with the application. But before we do that, we need to first flash it with the SoftDevice.

It’s a good idea to erase the development kit before flashing the SoftDevice and application. We do so by running the erase task:

Once we’ve erased the development kit, we can now flash it with the SoftDevice via the flash_softdevice task:

Now, flash the application to the development kit via the flash task:

At this point, we should have our application running on the development kit. We can confirm that by running a BLE mobile app such as nRF Connect or LightBlue.

Next Steps: Debugging using Visual Studio Code

Naturally, the next step would be to debug in real-time and connect to the J-Link interface on the development kit, all in Visual Studio Code.

If you’re interested in learning more about how to set up real-time debugging in VS Code, in addition to a complete video tutorial covering all the steps in this post, then check out the new Bluetooth Developer Academy.

By joining the Bluetooth Developer Academy, you will get access to a growing library of courses and tutorials covering (some to be added over time):

  • The basics of Bluetooth Low Energy.
  • Development environment setup for various platforms (e.g. Nordic nRF52, Dialog Semiconductor DA1469x, ESP32, etc.)
  • Advanced BLE topics such as high-speed mode (2M PHY), long-range mode (Coded PHY), OTA firmware update (DFU), and more.
  • Bluetooth beacons.
  • Bluetooth mesh.
  • Keeping up with the latest Bluetooth Low Energy standard features and enhancements.

The Academy also features a thriving community of Bluetooth experts, developers, and innovators. You’ll get to connect and interact with leaders in the Bluetooth space and learn from their experience and knowledge, as well as get advice and answers to your toughest Bluetooth challenges.

In the community, you will find:

  • Discussions around new features such as long-range mode (Bluetooth 5.0) and direction-finding (Bluetooth 5.1).
  • Discussions around the capabilities of different BLE sniffers.
  • Comparisons of BLE support and restrictions in iOS and Android.
  • Various technical questions and answers to these questions.
  • Listing of Bluetooth-related job openings.
  • And much more!

Summary & Closing

In this post, we went over how to set up and configure Visual Studio Code to develop applications on the Nordic nRF52 series development kit.

We covered:

“Learn The Basics of Bluetooth Low Energy EVEN If You Have No Coding Or Wireless Experience!"

Don't miss out on the latest articles & tutorials. Sign-up for our newsletter today!

Take your BLE knowledge to the next level.

If you’re looking to get access to full video courses covering more topics, then check out the Bluetooth Developer Academy.

As part of all the courses within the Academy, you’ll also be able to download the full source code to use as a reference or use within your own application.

By joining the Bluetooth Developer Academy, you will get access to a growing library of video courses.

The Academy also features access to a private community of Bluetooth experts, developers, and innovators. You’ll get to connect and interact with me and other experts in the Bluetooth space, learn from others’ experiences and knowledge, and share yours as well.

So, what are you waiting for?? Join today!