IoT CONFERENCE Blog

IoT for web developers: From zero to firmware

May 22, 2019

In part one of Andreas Schmidt’s IoT series, we started with PlatformIO, an open source ecosystem for IoT development. In part two, Andreas Schmidt walks us through the first steps of using it from zero to running firmware.

In the first part of this series, we could already read between the lines. Software and firmware development for embedded devices – and in our case, constrained devices with low performance – can be very complex. We can especially see this in C/C++ where developers are confronted with challenges at the bottom of the development stack. Cross compilers and linkers, libraries, include paths and build/flash tools need to be selected and installed, often depending on the underlying hardware. A cross-toolchain for an Arduino with Atmel AVR processor is not the same as the one for ARM Cortex M. Wouldn’t it be great to have a tool that takes care of this?

PlatformIO does exactly this work for the developer; It currently knows 24 development platforms for embedded (e.g. Atmel AVR, Espressif8266,…), 15 embedded frameworks (e.g. Arduino, ARM mbed,…), over 470 boards (“Arduino UNO”, “NodeMCU ESP8266”,…) and thousands of libraries and example. That should make for an easy start!

Integrated into the development environment

PlatformIO (PIO) can be installed and operated in different flavors. On the one hand, PIO Core allows you to do everything from the command line. This is useful for developers who feel comfortable on the shell level. It is also very handy for use with cloud IDEs, but we’ll talk about this in a later part of our IoT series.

PIO comes with integrations for various IDEs, and the rest of the series uses Visual Studio Code from Microsoft. It is very popular in the developer community due to its open architecture regarding extensions. A click on the icon for extensions in the left menu line and entering “platformio” in the search field leads with the first hit to the official PIO-VS code extension, after a click on “Install”, it is available.

IoT Figure 1: Installation of PIO plugins within VSCode

The core of PlatformIO is based on Python 2.7, which is part of the standard repertoire under MacOS and Linux. Windows users may need to reinstall Python, but this is automated by the PIO installer and well documented on the PlatformIO website. Furthermore, the installation of C-compilers like clang under MacOS may be necessary.
After starting VSCode you will be welcomed with the PIO Home Screen, at least as long as you have selected the checkmark “Show at startup” (Figure 2).

IoT Figure 2: All Functions within PlatformIO Homescreen

The home screen combines all the functionalities offered by PlatformIO Backend: Manage your account data (optional), search for boards, libraries, platforms, search for connected devices and manage projects.

Boards & frameworks

For our training workshops as well as for the following examples of this series, we will use the board “NodeMCU 1.0” with an ESP8266 processor. The ESP8266 is a very cheap WiFi-SoC from Chinese company Espressif Systems, which support the espressif-own embedded framework. It also has a port of the Arduino framework, so most of the “sketches” known from Arduino also work with this board. Communication is possible with its built-in WiFi connectivity, IoT solutions with MQTT, CoAP or HTTP.

You can use the Board Explorer (Figure 3, click on the “Boards” icon in PIO Home) to see if your own board is supported by PlatformIO. With over 470 boards currently supported, the probability of this is fairly high.

IoT Figure 3: Finding one’s own board

After entering the board name you can quickly find your own board. The table shows the supported platforms, frameworks and some technical data of the board. Each board has a board ID, which is visible on the “+” icon after opening the entry. This board ID becomes relevant for project creation and can be copied to the clipboard.

A similar search for embedded platforms is also available by clicking on “Platforms”. The cross-linking between boards, platforms and frameworks allows you to check which implementation options you have. Links lead to manufacturer documentation or source code repositories (Figure 4)

IoT Figure 4: Explore Embedded Frameworks

First example project

Normally, you create a new project via the PIO Project Wizard. After clicking on “New Project” in the home screen, the wizard opens and asks for the minimum conditions required for creating a project: Which board should the project work for, and with which embedded framework should it work? The first choice determines the compiler toolchain to be installed.

For ESP8266, this is the toolchain for the xTensa processors (the ESP8266 is a Tensilica xTensa) with the so-called Esptool for flashing. The latter choice of framework determines where the framework code is to be downloaded from. In our case, it is from the Github repository of Espressif System. PlatformIO will automatically install the toolchain and frameworks in a user’s local home directory and make them available in both the IDE and the command line. This is an enormous simplification and acceleration, especially when changing boards or platforms!

To start as quickly as possible with functional code, we select an example by clicking on “Project Examples”. PIO has examples of different platforms, and the Arduino area is abundant (Figure 5).

IoT Figure 5: Selection of a sample template

The examples are sorted by platform. As a simple starter example, we select the example “arduino-blink” in the category “Espressif8266”. A click on “Import” creates the project and opens it in the VSCode Explorer.
For PlatformIO, a project is located in a directory containing all sources, dependent libraries, settings for the selected framework/board (“environment”) and the compiled binary code. A suitable .gitignore is included that excludes directories with binary files or local IDE settings.

The source code “Sketch” can be found in src/main.cpp. The Arduino framework is implemented in C++ and provides classes with a degree of abstraction for many functionalities that make it easier to get started. Even those who have not yet programmed or not programmed much in C/C++ should get along well here. (And, yes, behind every C statement there needs to be a semicolon 🙂
Listing 1 shows the generated example:

 
#include "Arduino.h"
void setup() {
  // initialize LED digital pin as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
  // turn the LED on (HIGH is the voltage level)
  digitalWrite(LED_BUILTIN, HIGH);
  // wait for a second
  delay(1000);
  // turn the LED off by making the voltage LOW
  digitalWrite(LED_BUILTIN, LOW);
   // wait for a second
  delay(1000);
}

The sketch is divided into two sections. The setup() function is called once when the board is initialized. It contains code that makes certain board preparations. In the example, the I/O port for the LED (“LED_BUILTIN”) permanently installed on the board is set to “OUTPUT” via “pinMode” – to be able to switch the LED.

The loop() method is called continuously by the Arduino Framework, which expects the function not to block for a long time but to return to be called again. In the example, “digitalWrite” sets the LED to “HIGH” or “LOW” to switch it on and off. (Tip: the permanently installed LED on the NodeMCU is inverted, HIGH switches it off, LOW switches it on.) In between, wait with “delay”.
At this point, we extend the sketch by a program output on the serial interface, so an “embedded logging in simple”. The matching C++ class in the Arduino framework is called “Serial” and is predefined. It offers several functions for output from the first serial port wired to the USB cable on most (Arduino-compatible) boards.

 
void setup() {
  // initizalize Serial with 9600 bps
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // send something..
  Serial.print(".");
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
(…) 

Listing 2 shows the extensions. In the setup() function the serial port is initialized with 9600 bits/s (Serial.begin); in the loop() function we send a dot every time the function is entered.

Compile, flash, monitor

The next step is to bring the code on the board to life. The PIO extension displays several action buttons in the lower left corner of the PC code icon bar (Figure 6). The names of the functions can be displayed by hovering from left to right: compilation problems, PIO home, build (compile/link without flashing), upload (build with flashing), upload to remote device (flashing over a network), clean (corresponds to a make clean), test, run task, serial monitor and terminal.

IoT Figure 6: Action buttons within the command pane

By clicking on the arrow (“Upload”) the current project is compiled, linked to a firmware binary and uploaded to the device connected via USB, i.e. written to the flash memory there. PlatformIO recognizes the connected boards by their signature. They can also be displayed on the PIO home screen at any time by clicking on “Devices”. Under Linux and MacOS, the ports are identified with a file in /dev (e.g. /dev/cu.SLAB-USBtoUART or /dev/ttyUSB0). Under Windows they are managed as the known COM:xx ports. As long as only one board is connected, no further query is required. PIO simply uses this one board for the upload command.

IoT Figure 7: Compile and Upload

Figure 7 shows the file platformio.ini in the upper text window. It contains the definition of the environments, the combinations of board and framework, together with specific settings. The example contains several environments. For our example, we remove everything except the section [env:nodemcuv2]. A tip for the ESP8266 at this point is to add the line “upload_speed = 921600” to the end; this substantially accelerates the flashing process.

After clicking the Upload button in the icon bar, a terminal window opens and shows the compile, link and upload process. After successful flashing, the board starts flashing according to the sketch. How do we get our debug output? A click on the connector icon (Serial Port Monitor) opens another terminal window with an instance of miniterm, which opens with the USB port and the basic settings 9600bit/s 8N1. As in the sketch, you can see a dot on the output every 2 seconds.

Build system

PlatformIO provides its user with a very good abstraction to the underlying build system. As a developer you don’t have to deal with makefiles, link settings and other things, because PIO covers many build steps by using SCons. In the following article, the parts under the hood will be examined more closely.

To access the various functions of the build system from the IDE, simply click on the “Run-Tasks” icon. It opens a list of tasks that basically correspond to the targets of the build systems, e.g. “Clean” to delete build artifacts. Command line friends can open a terminal in the icon bar and use the “pio run –target clean” command to do the same.

IoT Figure 8: Compile and Upload

Bottom line

The steps shown here are sufficient to take the first steps with your own embedded board away from manufacturer-specific development environments. PlatformIO offers access to numerous development frameworks and boards. SCons has a very powerful build system with many functions. We will go over SCons in greater detail in the next article in the IoT series. These include going over all the work that is necessary in distributed teams for modern, agile development; integration in code repositories; continuous integration and delivery environments; as well as the possibility to test code automatically.