Lup Yuen Lee 李立源
Mar 26 · 17 min read

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
View → Command Palette

3️⃣ After >, type
git clone
Press Enter

4️⃣ For Repository URL, enter

https://github.com/lupyuen/stm32bluepill-mynewt-sensor

5️⃣ For Windows: Select your
Local Disk C: drive

For macOS: Select your Home folder

The Mynewt Sensor Application source code will be downloaded to C:\stm32bluepill-mynewt-sensor (Windows) or $HOME/stm32bluepill-mynewt-sensor (macOS)

In the Windows Ubuntu environment, this folder is accessed via the path /mnt/c/stm32bluepill-mynewt-sensor

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

7️⃣ When prompted, click
Open Workspace

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

8️⃣ Edit the configuration file targets/bluepill_my_sensor/syscfg.yml. Update the settings so that TUTORIAL1 is uncommented and TUTORIAL2 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 Windows PowerShell in the Windows menu

Select Run As Administrator

For the Linux Distribution,
select Ubuntu

Click Get

2️⃣ Click Windows → Ubuntu

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

Enter a password of your choice.

3️⃣ Switch over to Visual Studio Code.

Click Terminal → Run Task

4️⃣ Select
[0] Install Apache Mynewt

When prompted, click Continue Without Scanning The Task Output

5️⃣ When prompted, click on the Terminal 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 Done!

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)
Click Get Software

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

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.
gcc-arm-none-eabi-...-major-win32.exe
Click Download

Select this option at the last install step:
"Add path to environment variable"


Install Apache Mynewt for macOS

Skip this section if you’re NOT using macOS

1️⃣ Switch over to Visual Studio Code.

Click Terminal → Run Task

2️⃣ Select
[0] Install Apache Mynewt

When prompted, click Continue Without Scanning The Task Output

3️⃣ When prompted, click on the Terminal 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 Done!

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
Terminal → Run Task →
[1] Build bluepill_boot

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 Done!

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
Terminal → Run Task →
[2] Build bluepill_my_sensor

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 Done!

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 Build bluepill_my_sensor. To populate the Image Header, we need to run…

Terminal → Run Task →
[3] Image bluepill_my_sensor

Watch the video

This creates an Application ROM image file my_sensor_app.img 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 (BOOT0 andBOOT1 near the Micro USB Port) to the 0positions (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…

Terminal → Run Task →
[4] Load bluepill_boot

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…

Terminal → Run Task →
[5] Load bluepill_my_sensor

Watch the video


Debug Mynewt on Blue Pill

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

Click Debug → Start Debugging

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

The debugger pauses at the line with LoopCopyDataInit

Click Continue or press F5

The debugger pauses next at the main() function in apps/my_sensor_app/src/main.c, so that we may step through the execution…

Debugging Mynewt Application on Blue Pill

What will this Application do?

1️⃣ sysinit(): 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.

start_network_task() will only be used in the next tutorial, so let’s skip to start_sensor_listener() in apps/my_sensor_app/src/listen_sensor.c...

From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/master/apps/my_sensor_app/src/listen_sensor.c#L26-L50

2️⃣ sensor_set_poll_rate_ms(): 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️⃣ sensor_mgr_find_next_bydevname(): 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: bme280_0.

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

And back to apps/my_sensor_app/src/main.c...

5️⃣ os_eventq_run(): 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 __assert_func(), which means that Mynewt has encountered a fatal error (assertion failure). Let’s check what went wrong.

In the Call Stack, click on sensor_dev_create(). 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 syscfg.yml System Configuration File!

Here’s the System Configuration File for our Application, located at apps/my_sensor_app/syscfg.yml...

syscfg.vals.TUTORIAL1:
BME280_OFB: 1 # Enable BME280 offboard sensor
SPI_0_MASTER: 1 # Enable port SPI1 for BME280

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

SPI_0_MASTER 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!

Connecting BME280 to Blue Pill. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/master/bme280spi.fzz
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

#if MYNEWT_VAL(SPI_0_MASTER) && MYNEWT_VAL(BME280_OFB)
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.
};
#endif

The fix is easy: Just edit the file repos/apache-mynewt-core/hw/sensor/creator/src/sensor_creator.c, look for line 200, and change the CS Pin from 3 to 4

#if MYNEWT_VAL(SPI_0_MASTER) && MYNEWT_VAL(BME280_OFB)
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.
};
#endif
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 config_bme280_sensor() 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 sensor_creator.c, stop the debugger by clicking the red square in the debugger toolbar.

2️⃣ Click Terminal → Run Task → [2] Build bluepill_my_sensor

3️⃣Start the debugger: Click Debug → Start Debugging

4️⃣ Click View → Output

5️⃣ In the Output pane that appears, select Adapter Output

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 console_printf() like this...

console_printf("TMP poll data: tmp ");  
console_printfloat(tmp);
console_printf("\n");

But what is this “Adapter Output”? How did we redirect Mynewt’s console_printf() function to show messages in Adapter Output? 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 https://developer.arm.com/docs/dui0471/k/what-is-semihosting/what-is-semihosting

Arm Semihosting is a simple way to display debug messages via ST Link. When our Application executes the special Arm instruction bkpt #0xAB 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 Adapter Output 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 (Adapter Output) 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 pkg.yml Package Configuration File located at apps/my_sensor_app/pkg.yml

pkg.deps:
- "@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 pkg.yml 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 libs/semihosting_console 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 pkg.yml to…

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

To switch back to the default UART Console Library, change the last line of pkg.yml 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: my_sensor_app and bluepill_my_sensor. How are my_sensor_app and bluepill_my_sensor 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 my_sensor_app is the generic version of the Sensor Application, and bluepill_my_sensor is the same application targeted for Blue Pill.

How do we target bluepill_my_sensor for Blue Pill? Through the target.yml Target Configuration File located at targets/bluepill_my_sensor/target.yml

target.app: apps/my_sensor_app
target.bsp: "@apache-mynewt-core/hw/bsp/bluepill"
target.build_profile: debug

The bluepill_my_sensor Target Configuration File states that…

1️⃣ apps/my_sensor_app: Take the generic my_sensor_app Application…

2️⃣ @apache-mynewt-core/hw/bsp/bluepill: 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️⃣ debug: 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 stm32bluepill-mynewt-sensor 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 newt new command.

Mynewt project folder structure

Lup Yuen Lee 李立源

Written by

Techie and Educator in IoT 物聯網教師

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade