Create your IoT gadget with Apache Mynewt and STM32 Blue Pill

The year is 2029. Humans are populating the Moon, starting at Moon Base One. Two Moon Base Operators observe something highly unusual in the crop garden of beautiful red tomatoes…

Operator 1: The sprinklers are turning on and off erratically. We have just upgraded all our STM32-BLUEST-PILL Temperature Sensing Devices with a Soil Moisture Sensor. Are we sure that the Soil Moisture Sensor is reporting the correct values to the IoT System?

Operator 2: Yes the soil moisture values are correct. Just that we are multitasking two sensors (temperature and soil moisture) so I decided to poll the temperature every minute and the soil moisture every 100 minutes.

You see this clever temperature sensing loop in my code? Every time the counter reaches 100, the code polls the soil moisture sensor, waits for the data, then resumes the temperature sensing loop…

Operator 1: So we get the soil moisture every 100 minutes? That’s absurd! Let’s change the firmware to poll the temperature every 3 minutes and the soil moisture every 5 minutes!

Operator 2: Hmmm OK. I need to make this sensing loop a little more complicated… One counter for 3 minutes and another for 5 minutes…

Operator 1: Yesterday we plugged in the Light Sensor on all our STM32-BLUEST-PILL devices right? Can we get the light sensor data now?

Operator 2: OK but I can’t find a proper software driver for the Light Sensor. I’ll hack up a simple C function to read the sensor… Done! I have just flashed all 1,000 of our STM32-BLUEST-PILL devices with the latest code for Temperature Sensor, Soil Moisture Sensor AND Light Sensor.

Operator 1: WHAT’S HAPPENING !!! Our IoT Dashboards are now totally blank! Says here… “Memory Corruption Error: Invalid pointer in Light Sensor code, core dumped”… All 1,000 sensors are dead… Our tomatoes are all EXPLODING IN FLAMES !!! NOOOOOOO !!!

Creating an IoT Device is hard… Do we have the necessary software drivers to read data correctly from the connected sensors? If the device is connected to multiple sensors, can we read multiple sensors concurrently in a safe and reliable way? Fortunately, Apache Mynewt can help!

Apache Mynewt is a free, open-source realtime operating system for microcontrollers. It has been compared with FreeRTOS and Zephyr, though Mynewt is newer than FreeRTOS and Zephyr.

Hardware needed for this tutorial: (1) Blue Pill (2) ST-Link (3) SparkFun BME280 (optional)

Mynewt runs on various microcontrollers like MIPS32, Microchip PIC32, RISC-V. In this article we’ll learn about programming and running Mynewt on the STM32 Blue Pill, one of the cheapest 32-bit microcontroller dev boards available today (only $2!)

We’ll flash and debug the Blue Pill with the ST Link V2 USB adapter. Or a compatible USB adapter.

BME280 is a popular sensor for ambient temperature, relative humidity and air pressure. In this tutorial we’ll configure Mynewt to poll the BME280 temperature sensor every 10 seconds. So just imagine we’re building a temperature sensing device with Blue Pill. BME280 is not mandatory for this tutorial.

Roadmap of the tutorial

This tutorial is packed full of Mynewt goodies but Blue Pill newbies should have no problem following the steps since we’re using Visual Studio Code. We’ll learn to…

1️⃣ Install Mynewt and the Sensor Application

2️⃣ Build Mynewt Bootloader and Application

3️⃣ Flash Bootloader and Application to Blue Pill

4️⃣ Debug Mynewt on Blue Pill

5️⃣ Configure Mynewt for BME280 Sensor

6️⃣ View Mynewt debug messages

7️⃣ Customise Mynewt with a custom debug console

8️⃣ Manage Mynewt Package Dependencies

9️⃣ Target Mynewt for multiple microcontrollers

Install Visual Studio Code and the Mynewt Sensor Application

Follow these instructions if you’re using Windows or macOS

Watch the video

1️⃣ Install Visual Studio Code for Windows or macOS from
Visual Studio Code Website

Launch Visual Studio Code

2️⃣ Click

3️⃣ After , type

Press Enter

4️⃣ For , enter

5️⃣ For Windows: Select your

For macOS: Select your Home folder

The Mynewt Sensor Application source code will be downloaded to (Windows) or (macOS)

In the Windows Ubuntu environment, this folder is accessed via the path

6️⃣ When prompted, click
. If the prompt disappears before clicking, click the bell icon at lower right

7️⃣ When prompted, click

If the prompt disappears before clicking, click the bell icon at lower right

8️⃣ Edit the configuration file Update the settings so that is uncommented and is commented out, like this…

TUTORIAL1:              1  # Uncomment Tutorial 1   #TUTORIAL2:             1  # Comment out Tutorial 2

Install Apache Mynewt for Windows

Skip this section if you’re NOT using Windows. For Ubuntu Linux, refer to these instructions.

Watch the video

1️⃣ Follow the instructions in this article to install Windows Subsystem for Linux.

The PowerShell command in the article should be run as Administrator

Right-click in the Windows menu


For the select


2️⃣ Click

When prompted, enter a simple user ID (e.g. ) without any spaces.

Enter a password of your choice.

3️⃣ Switch over to Visual Studio Code.


4️⃣ Select

When prompted, click

5️⃣ When prompted, click on the pane and enter the password from Step 2️⃣

The password only needs to be entered once.

6️⃣ The setup script will take a few minutes to download and install the build tools.

When it’s done, we should see

Exit and restart Visual Studio Code. This activates the installed extensions.

In case of problems, compare your log with this setup log.

7️⃣ Download the ST-Link USB driver from
ST-Link Driver Website (email registration required)

Unzip the downloaded file. Double-click the driver installer:

8️⃣ Install Arm Cross-Compiler and Linker for Windows from
Arm Developer Website

Look for the first Windows 32-bit Download without SHA, e.g.


Select this option at the last install step:

Install Apache Mynewt for macOS

Skip this section if you’re NOT using macOS

1️⃣ Switch over to Visual Studio Code.


2️⃣ Select

When prompted, click

3️⃣ When prompted, click on the pane and enter your macOS password

The password only needs to be entered once.

4️⃣ The setup script will take a few minutes to download and install the build tools.

When it’s done, we should see

Exit and restart Visual Studio Code. This activates the installed extensions.

Build Mynewt Bootloader and Application

We’ll now build the Mynewt Bootloader and Application that will be flashed to the Blue Pill ROM…

1️⃣ Click

Watch the video

This builds the Mynewt Bootloader, which is run first whenever the Blue Pill is restarted.

When the Bootloader Build has completed successfully, we should see

Note: If we’re building for the first time, it may fail with a missing file error. Click the build command again to complete the build.

2️⃣ Click

Watch the video

This builds the Mynewt Sensor Application, which is loaded by the Bootloader whenever the Blue Pill is restarted.

When the Application Build has completed successfully, we should see

Note: If we’re building for the first time, it may fail with a missing file error. Click the build command again to complete the build.

Create Application Image

Memory Sections in the Application. From bin/targets/bluepill_my_sensor/app/apps/my_sensor_app/my_sensor_app.elf.lst

Remember that Mynewt requires two programs to be flashed into the Blue Pill ROM: the Bootloader and our Application. How does the Bootloader discover which Application has been flashed into the ROM? That’s done through the Image Header.

The Image Header is located at the start of the Application and describes the Application (size, security options, …) to the Bootloader. The Image Header is initially blank when we built it above with . To populate the Image Header, we need to run…

Watch the video

This creates an Application ROM image file that we’ll flash to the Blue Pill in the next step.

Flash Bootloader and Application to Blue Pill

Connecting Blue Pill to ST-Link USB Debugger

1️⃣ Connect the Blue Pill and ST Link to the USB port of your computer.

Both jumpers should be set to the “0” positions

2️⃣ Set both yellow jumpers on the Blue Pill ( and near the Micro USB Port) to the positions (closer to the Micro USB Port).

3️⃣ The Bootloader is the first program that Blue Pill runs when powered up, so it needs to be flashed first. To flash the Bootloader into the Blue Pill ROM, click…

Watch the video

4️⃣ Next we flash the Application into the Blue Pill ROM, using the Application image we have created in the previous step…

Watch the video

Debug Mynewt on Blue Pill

We’re all set to execute and debug our Application!


This starts the debugger to let us inspect our Application as it runs on Blue Pill.

The debugger pauses at the line with

Click or press

The debugger pauses next at the function in , so that we may step through the execution…

Debugging Mynewt Application on Blue Pill

What will this Application do?

1️⃣ : The Application initialises the BME280 driver, which is part of the Mynewt OS. Mynewt has built-in drivers for many sensors, and we may create our own drivers too.

will only be used in the next tutorial, so let’s skip to in ...

2️⃣ : We’ll instruct the Mynewt OS to poll the BME280 sensor every 10 seconds. Mynewt is a realtime OS so we can trust it to poll multiple sensors concurrently without risk of data corruption. Which is highly critical for IoT devices!

3️⃣ : We’ll call the Mynewt Sensor Manager to fetch the object that represents our BME280 sensor. Each sensor is identified by a name assigned by Mynewt. The sensor name looks like this: .

4️⃣ : What happens after Mynewt has polled our BME280 sensor? We instruct Mynewt to call our listener function . In this demo we’ll just display the temperature on the console…

And back to ...

5️⃣ : Finally we let Mynewt start multitasking. This is the standard Event Queue loop that’s required for all Mynewt applications. It will start polling our BME280 sensor and calling our listener function every 10 seconds.

With a few lines of code we have created a working IoT sensor device that can handle multiple sensors with multitasking! Let’s verify that it really works…

In the debugger toolbar, click the Step Over button (the second button).

The debugger has paused at , which means that Mynewt has encountered a fatal error (assertion failure). Let’s check what went wrong.

In the Call Stack, click on . This the culprit function that triggered the failure…

Mynewt is attempting to initialise the BME280 driver but it failed. Which could happen if we didn’t connect a real BME280 sensor to our Blue Pill. But it could also happen if our BME280 sensor is misconfigured

Configure Mynewt for BME280 Sensor

This code in the screen above is from the Mynewt Sensor Creator. One amazing feature of Mynewt is that we don’t need to write any code to configure common sensors like BME280. How do we tell Mynewt that we have connected a BME280 sensor? Though the System Configuration File!

Here’s the System Configuration File for our Application, located at ...

BME280_OFB: 1 # Enable BME280 offboard sensor
SPI_0_MASTER: 1 # Enable port SPI1 for BME280

causes the Sensor Creator to include the BME280 driver in our Application. ( means it’s an off-board sensor, i.e. it’s not a sensor that’s integrated with the Blue Pill microcontroller)

tells the Sensor Creator to use the first SPI port (SPI1) on Blue Pill. Connecting the Application to the BME280 sensor via SPI is really that easy!

Blue Pill Pin PA4  (SS1)   → BME280 Pin !CS
Blue Pill Pin PA5 (SCK1) → BME280 Pin SCK
Blue Pill Pin PA6 (MISO1) → BME280 Pin SDO
Blue Pill Pin PA7 (MOSI1) → BME280 Pin SDI
Blue Pill Pin 3V3 → BME280 Pin 3.3V
Blue Pill Pin GND → BME280 Pin GND

That’s how we normally wire up the BME280 sensor to Blue Pill on port SPI1. Note that the CS (Chip Select) Pin is connected to PA4 on Blue Pill. There’s a slight complication… If you look at the Sensor Creator code, it assumes that the CS Pin is connected to PA3

static struct sensor_itf spi_0_itf_bme = {
.si_type = SENSOR_ITF_SPI,
.si_num = 0,
.si_cs_pin = 3 // Assumes the CS Pin is connected to PA3.

The fix is easy: Just edit the file , look for line 200, and change the CS Pin from 3 to 4

static struct sensor_itf spi_0_itf_bme = {
.si_type = SENSOR_ITF_SPI,
.si_num = 0,
.si_cs_pin = 4 // Changed to connect CS Pin to PA4.

BME280 connected to Blue Pill

Tip for Experienced Coders: You might think it’s not proper to modify the Mynewt OS files directly like this (even though it’s suggested in this article).

The cleaner way to change the configuration is to copy the function into our Application and make the changes there.

Check this tutorial for the details.

View Mynewt Debug Messages

To view debug messages generated by our Application, follow these steps…

1️⃣ After updating , stop the debugger by clicking the red square in the debugger toolbar.

2️⃣ Click

3️⃣Start the debugger: Click

4️⃣ Click

5️⃣ In the Output pane that appears, select

6️⃣ If we have connected a BME280 sensor to our Blue Pill according to the schematic above, we should see the temperature reading like this.

Our Application contains a listener function that displays the temperature after polling the BME280 sensor. To display a debug message in our Application, we just call like this...

console_printf("TMP poll data: tmp ");  

But what is this “”? How did we redirect Mynewt’s function to show messages in ? We used some Arm magic called Arm Semihosting.

Customise Mynewt with Semihosting Console

Arm Semihosting for displaying debug messages. SVC refers to the BKPT instruction. From

Arm Semihosting is a simple way to display debug messages via ST Link. When our Application executes the special Arm instruction and passes a debug message, the message is transmitted over ST Link (SWD port) to our computer.

To view the messages, we need to activate the OpenOCD Console. This is the log that we saw above. OpenOCD is the program running in the background that connects to Blue Pill via ST Link to flash and debug Blue Pill programs. So it makes sense that the Arm Semihosting debug messages are displayed in the OpenOCD Console.

However, Arm Semihosting is not supported natively in Mynewt. Mynewt’s Console Library routes debug messages to the UART port by default. This makes debugging more complicated: We need to connect Blue Pill’s UART port to our computer via a USB Serial Adapter to see the debug messages.

Fortunately Mynewt lets us customise the OS by coding our own Console Library for Arm Semihosting. The Semihosting Console Library that’s included in our Application will override the default Console Library and show debug messages in the OpenOCD Console () instead.

Select Mynewt Libraries through Package Dependencies

How do we tell Mynewt to use the Semihosting Console instead of the regular Console Library? Through the Application’s Package Configuration File located at

- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/log/stub"
- "@apache-mynewt-core/sys/stats/stub"
- "@apache-mynewt-core/hw/sensor"
- "@apache-mynewt-core/hw/sensor/creator"
- "@apache-mynewt-core/libc/baselibc"
- "libs/semihosting_console"

In we list the library packages needed by our Application. Here we declare the Sensor Library and Sensor Creator, which are needed to support BME280. The last entry is our Semihosting Console Library.

The Semihosting Console Library works only when the ST Link is connected to our Blue Pill. (The library hangs when the ST Link is disconnected) When we roll out the Blue Pill in the real world, we should disable the debug console by switching to the Stub Console Library. Change the last line of to…

    - "@apache-mynewt-core/sys/console/stub"

To switch back to the default UART Console Library, change the last line of to…

    - "@apache-mynewt-core/sys/console/full"

Target Mynewt for multiple microcontrollers

In the sections above, we examined Application configuration files that were located in two source folders: and . How are and related?

Mynewt is a portable operating system that supports many microcontrollers: Blue Pill, STM32F4-Discovery, Arduino Zero, nRF52, … So most of the Mynewt OS, Bootloader and Application code was designed to be generic to support different microcontrollers.

In order to compile this generic code to run on Blue Pill, we need to “Target” the generic code for Blue Pill. So is the generic version of the Sensor Application, and is the same application targeted for Blue Pill.

How do we target for Blue Pill? Through the Target Configuration File located at apps/my_sensor_app
target.bsp: "@apache-mynewt-core/hw/bsp/bluepill"
target.build_profile: debug

The Target Configuration File states that…

1️⃣ : Take the generic Application…

2️⃣ : And target it for the Blue Pill microcontroller. BSP refers to the Mynewt Board Support Package, which is a library that contains system functions that are specific to a microcontroller, like Blue Pill.

3️⃣ : Generate debugger info during compilation

Mynewt will let us generate Applications for multiple microcontrollers so easily… Just edit the Target Configuration File!

Here’s the folder structure for our project. We can see the generic and targeted Applications, as well as their configuration files. All Mynewt projects will follow this structure when we create projects using the command.

Mynewt project folder structure

Whats Next?

Now that we understand the fundamentals of Mynewt’s Sensor Framework, we’ll dive deep in the next tutorial and learn to send sensor data to an IoT cloud (like via ESP8266 WiFi and CoAP. We’ll be testing WiFi Geolocation — computing your location based on WiFi Access Points scanned by the ESP8266 module.

Check out the latest article on Visual Embedded Rust…

Here are the older Mynewt tutorials…

Here’s the code for this article…