Introduction
Embedded systems development has traditionally been dominated by low-level languages like C and Assembly. However, with the rise of more modern and secure programming languages like Rust, developers now have the opportunity to bring the benefits of these languages to the embedded domain. In this article, we will explore how to leverage Rust and the Apache Mynewt operating system to program the nRF52 series of microcontrollers from Nordic Semiconductor, using the Visual Studio Code (VSCode) development environment.
Prerequisites
Before we dive into the coding process, ensure that you have the following prerequisites installed on your development machine:
Additionally, you will need a nRF52 development board, such as the nRF52840 DK or the nRF52832 DK.
Setting up the Development Environment

1. Install the Required Extensions for VSCode
Open Visual Studio Code and navigate to the Extensions view by clicking on the square icon on the left sidebar or by pressing Ctrl+Shift+X
(Windows/Linux) or Cmd+Shift+X
(macOS). Search for and install the following extensions:
rust-lang.rust
(Rust Language Server)binarycodeddata.ionide-installer
(Ionide-fsharp)ms-vscode.cpptools
(C/C++ Tools)
2. Clone the Apache Mynewt Repository
Next, we need to clone the Apache Mynewt repository, which contains the operating system and the necessary tools for building and flashing our Rust-based firmware.
Open a terminal or command prompt and navigate to a directory where you want to clone the repository. Run the following command:Copy code
git clone https://github.com/apache/mynewt-core.git
This will create a mynewt-core
directory containing the Mynewt codebase.
3. Install the Mynewt Tool
The Apache Mynewt project provides a command-line tool called newt
for managing and building Mynewt projects. To install the newt
tool, follow these steps:
- Navigate to the
mynewt-core
directory:
Copy code
cd mynewt-core
- Run the installation script:
Copy code
# On Linux or macOS ./install.sh # On Windows install.sh
This script will install the newt
tool and other required dependencies.
4. Create a New Mynewt Project for nRF52
With the newt
tool installed, we can now create a new Mynewt project for our nRF52 board. Run the following command, replacing project_name
with your desired project name:Copy code
newt install -v newt newt create project_name
This will create a new project directory with the specified name and populate it with the necessary files and directories.
5. Target the nRF52 Board
Next, we need to set the target for our project to the nRF52 board. Run the following command, replacing project_name
with your project name:Copy code
cd project_name newt target create nrf52_board newt target set nrf52_board app=@apache-mynewt-core/apps/blinky newt target set nrf52_board bsp=@apache-mynewt-core/hw/bsp/nrf52 newt target set nrf52_board build_profile=debug
These commands create a new target called nrf52_board
, set the application to the blinky
example (which blinks an LED), set the board support package to the nRF52 BSP, and set the build profile to debug
.
6. Build and Flash the Firmware
With the project configured, we can now build and flash the firmware to our nRF52 board. Connect your board to your development machine via USB and run the following commands:Copy code
newt build nrf52_board newt create-rev -repo=repo_name versions newt run nrf52_board 0.0.0 [tgt.app-rev=@repo_name/apps/blinky@0.0.0]
Replace repo_name
with a name for your local repository (e.g., mynewt-project
). These commands build the firmware, create a versioned repository, and flash the firmware to the connected board.
If everything is set up correctly, you should see the LED on your nRF52 board blinking.
Developing with Rust and Apache Mynewt
Now that we have our development environment set up and a working example, let’s explore how to develop Rust applications for the nRF52 using Apache Mynewt.
1. Add Rust Support to Your Project
Apache Mynewt supports Rust applications out of the box. To add Rust support to your project, run the following command:Copy code
newt install -v rust
This command installs the necessary Rust components and dependencies for your project.
2. Create a Rust Application
Let’s create a new Rust application for our project. Run the following command, replacing app_name
with your desired application name:Copy code
newt pkg new -r rust-app@0.0.0 app_name
This command creates a new package for your Rust application with the specified name and version (0.0.0 in this case).
3. Edit the Rust Application Code
Navigate to the apps/app_name/src
directory within your project. You should see a main.rs
file, which is the entry point for your Rust application. Open this file in Visual Studio Code and modify the code as desired.
Here’s an example of a simple Rust application that blinks an LED:
rustCopy code
use kernel::allocator; use kernel::component::Component; use kernel::mem; use kernel::sysinit; use mynewt; use nrf52; struct HelllmnrApp { sysinit_comp: kernel::component::Component<'static>, } impl kernel::sys::App for HelllmnrApp { fn prepare_start(&'static self) { self.sysinit_comp.init_components(); } fn start(&'static self) { let led_pin = nrf52::pins::p0_pin::P0_13.make_output(); loop { led_pin.set(); kernel::sys::os::delay_ms(500u32); led_pin.clear(); kernel::sys::os::delay_ms(500u32); } } } fn main() { let app = HelllmnrApp { sysinit_comp: kernel::component::init_mpu(), }; kernel::os::main(&app as &dyn kernel::sys::App); }
This code sets up a Rust application that blinks an LED connected to pin P0.13 on the nRF52 board. You can modify the code to implement your desired functionality.
4. Build and Flash the Rust Application
To build and flash your Rust application, follow these steps:
- Set the target for your Rust application:
Copy code
newt target set nrf52_board app=@repo_name/apps/app_name
Replace repo_name
with the name you used earlier for your local repository, and app_name
with the name of your Rust application.
- Build and flash the firmware:
Copy code
newt build nrf52_board newt create-rev -repo=repo_name versions newt run nrf52_board 0.0.0 [app.loader=bootloader@0.0.0]
These commands build the firmware, create a versioned repository, and flash the firmware to the connected board.
Your Rust application should now be running on your nRF52 board!
Debugging with Visual Studio Code

Visual Studio Code provides excellent support for debugging Rust applications. To debug your Rust application running on the nRF52 board, follow these steps:
- Install the
CodeLLDB
extension in Visual Studio Code. This extension provides a debug adapter for the LLDB debugger, which is used for debugging Rust applications. - Create a new file called
launch.json
in the.vscode
directory of your project. If the.vscode
directory doesn’t exist, create it. - Paste the following content into the
launch.json
file and modify theprogram
path to match the path to your Rust application’s ELF file:
jsonCopy code
{ "version": "0.2.0", "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug Rust Application", "program": "${workspaceFolder}/bin/targets/nrf52_board/app/apps/app_name/app_name.elf", "cwd": "${workspaceFolder}", "preLaunchTask": "build", "postLaunchTask": "monitor" } ] }
Replace app_name
with the name of your Rust application.
- Create a new file called
tasks.json
in the.vscode
directory of your project and paste the following content:
jsonCopy code
{ "version": "2.0.0", "tasks": [ { "type": "shell", "label": "build", "command": "newt build nrf52_board", "problemMatcher": [] }, { "type": "shell", "label": "monitor", "command": "newt run nrf52_board 0.0.0 [app.loader=bootloader@0.0.0]", "problemMatcher": [] } ] }
- Connect your nRF52 board to your development machine via USB.
- In Visual Studio Code, open the Debug view by clicking on the bug icon on the left sidebar or by pressing
Ctrl+Shift+D
(Windows/Linux) orCmd+Shift+D
(macOS). - Select the “Debug Rust Application” configuration from the dropdown list.
- Set any desired breakpoints in your Rust code.
- Click the green “Start Debugging” button or press
F5
to start the debugging process.
Visual Studio Code will now build your Rust application, flash it to the nRF52 board, and start the debugging session. You can step through your code, inspect variables, and take advantage of the full debugging capabilities provided by Visual Studio Code and the LLDB debugger.
Frequently Asked Questions (FAQ)
- Q: Can I use Rust for bare-metal programming on the nRF52? A: Yes, you can use Rust for bare-metal programming on the nRF52. Apache Mynewt provides a lightweight and modular operating system that allows you to develop Rust applications that run directly on the hardware without the need for a full-fledged operating system.
- Q: How does Apache Mynewt compare to other embedded operating systems like FreeRTOS or Zephyr? A: Apache Mynewt is a lightweight and modular operating system specifically designed for embedded systems and IoT devices. It offers a similar feature set to FreeRTOS and Zephyr but with a focus on security, modularity, and support for various programming languages, including Rust. Mynewt’s modular architecture allows you to include only the necessary components, resulting in a smaller memory footprint.
- Q: Can I use other programming languages besides Rust with Apache Mynewt? A: Yes, Apache Mynewt supports multiple programming languages, including C and C++, in addition to Rust. However, Rust offers several advantages in terms of memory safety, concurrency, and performance, making it a compelling choice for embedded systems development.
- Q: How do I handle hardware peripherals like I2C, SPI, or UART in Rust with Apache Mynewt? A: Apache Mynewt provides a Hardware Abstraction Layer (HAL) that abstracts the low-level hardware details and exposes a consistent API for interacting with peripherals like I2C, SPI, and UART. You can use the Rust bindings for the HAL to access and control these peripherals from your Rust code.
- Q: Can I use third-party Rust crates (libraries) in my Apache Mynewt project? A: Yes, you can use third-party Rust crates in your Apache Mynewt project. However, it’s important to ensure that the crates are compatible with the embedded environment and the specific target architecture (in this case, nRF52). Some crates may rely on features or dependencies that are not available or supported in the embedded context.
Conclusion
In this article, we explored how to set up a development environment for coding the nRF52 microcontrollers using Rust and the Apache Mynewt operating system on Visual Studio Code. We covered the installation process, project setup, building and flashing firmware, developing Rust applications, and debugging with Visual Studio Code.
Using Rust for embedded systems development offers several advantages, including memory safety, concurrency support, and improved performance. Combined with the lightweight and modular Apache Mynewt operating system, Rust provides a powerful and secure foundation for developing applications for the nRF52 and other embedded platforms.
While this article focused on the nRF52 and Apache Mynewt, the principles and tools demonstrated can be applied to other microcontrollers and embedded operating systems that support Rust development. As the embedded systems landscape continues to evolve, languages like Rust and development environments like Visual Studio Code will play an increasingly important role in enabling more secure, efficient, and maintainable embedded software development.