Embedded Development that Doesn't Suck: PlatformIO - ECE-180D-WS-2024/Wiki-Knowledge-Base GitHub Wiki
Nicholas Nhien - 1303 words. Excludes code snippets, HTML tags, additional resources, and sources
Embedded firmware development lies in a unique space between software and hardware engineering. Embedded engineers are closer to their hardware than software engineers yet are still comfortably removed from the chaotic world of Verilog. This poses an outstanding challenge for toolchains that aim to cater to the needs of embedded engineering. On the one hand, toolchains that attempt to hide too much from the user risk frustrating those who demand flexibility. On the other, toolchains that expose too much risk overwhelming users who are used to creature comforts provided by high-level abstractions. Designing a toolchain that satisfies the needs of all users requires a fine balancing act.
PlatformIO [1] is a toolchain for embedded C/C++ development that succeeds in this balancing act. The development team supports a VSCode extension that is the primary and most straightforward way of using the toolchain. A CLI that provides more flexibility than the VSCode extension is also available. The development environment feels familiar to an intermediate to advanced-level software engineer. PlatformIO supports many common embedded platforms like Arduino and ESP32. A list of supported embedded platforms and the corresponding libraries can be found in the PlatformIO registry [2], which acts as a package dependency management solution similar to tools like pip
and cargo
.
We guide the reader through writing, compiling, and flashing firmware utilizing the Arduino framework; the process will feel familiar to programmers coming from Arduino IDE. However, note that PlatformIO can take advantage of other frameworks like Espressif's IoT Development Framework [3]. For an industry example of PlatformIO being used with ESP-IDF, refer to Tidbyt's hardware development kit [4].
Before getting into the technicalities of PlatformIO, it’s important to discuss why PlatformIO is our toolchain of choice over others such as Arduino IDE. While Arduino IDE is easy to use and popular with lots of user support, it lacks many key features that more experienced developers would want. Some of the downsides of the Arduino IDE include:
- A lack of a debugger that can insert break statements and track variable values over the course of the program.
- An inability to automatically determine what USB port a microcontroller is connected to.
- No coding assistance features such as autocomplete or built-in reference to write code faster or catch errors.
- No integration with code repositories such as GitHub. [6]
PlatformIO includes all of these features that the Arduino IDE lacks.For a full overview of the feature differences between Arduino IDE and PlatformIO, one can reference this diagram here:
PlatformIO provides two interfaces: VSCode extension and CLI. The VSCode extension includes the CLI.
The following steps are pulled straight from PlatformIDE's official guide for convenience.
- Install VSCode
- Open the extension manager
Figure 1. VSCode's extension manager icon found in the sidebar
-
Search for "platformio ide"
-
Install "PlatformIO IDE"
Figure 2. Searching for PlatformIDE in the extension manager
A new tab will appear in the same sidebar where the extension manager was found. This is where the GUI controls for PlatformIO IDE are found.
Figure 3. PlatformIO IDE's icon found in the sidebar
If the reader is developing on Linux, they must add some udev
rules to allow PlatformIO IDE to upload firmware to connected boards. Follow PlatformIO's guide to complete this configuration [5].
We will write and upload a simple firmware using the Arduino framework that blinks an LED on a board implementing the ESP32 platform.
To create a new project,
- Open the PlatformIO extension
- Select "Create New Project"
- In the new VSCode tab that opens, select "New Project"
- Under "Name" name the project something descriptive. This guide assumes "blink" was chosen.
- Under "Board" search for the physical hardware you are developing for. If you purchased the board specified by the syllabus, the corresponding board will be "SparkFun ESP32-S2 Thing Plus"
- Under "Framework" use "Arduino"
- Change the project location if desired
- Select "Finish"
After PlatformIO finishes the initial setup, a new directory will be added to your VSCode workspace in the explorer tab. This directory contains your new project. The file structure reflects good C++ development practices. It's best practice to respect the following important locations:
lib/
: Contains the source code for libraries manually managed by the developer. One might utilize git
's submodules to automatically track changes to libraries not provided by PlatformIO's registry. Typically this directory will be left untouched. In most use cases, libraries provided by PlatformIO's registry are used.
includes/
: To be populated by .h
and/or .hpp
files containing constants, function headers, etc. During the development process, VSCode will, by default, search for function headers and constants defined here when providing auto-completion suggestions. Similarly, when building your firmware, PlatformIO will automatically search for function headers and constants defined in header files located here.
src/
: To be populated by .c
and/or .cpp
files containing implementations of functions defined in the header files located in includes/
platformio.ini
: The project configuration file. More advanced project configuration can be done here, such as specifying different build environments, the communications protocol to be used when uploading firmware to a board, the target device file to use when uploading firmware,
Note: The reader familiar with Arduino IDE might note that PlatformIO does not abstract away the more complicated nuances of the project structure. If this is intimidating, the reader can do everything in
src/main.cpp
. Everything will be as if the reader were using Arduino IDE. They still get VSCode's syntax highlighting, auto-completion, and extensions as nice additions.
Open src/main.cpp
and replace the default contents with the following:
#include <Arduino.h>
void setup() {
// put your setup code here, to run once:
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
Connect your Arduino to your computer using a cable that supports data transfer.
Note: Not all USB cables are created equal. Some do not support data transfer - if the subsequent steps fail, you likely have a cable that can only transmit power.
Open PlatformIO's VSCode controls and select "Upload and Monitor"
Figure 4. Location of "Upload and Monitor" in PlatformIO's VSCode controls
Once the firmware build and upload is complete, the light on your board will blink!
Libraries are pieces of code written to solve a frequently encountered problem and released to a community of developers. Libraries are often used to implement communications protocols like MQTT or enable developers to interface with hardware on the board like WiFi chips. Developers use libraries written by other developers so they don't have to reinvent the wheel.
PlatformIO's registry provides a plethora of libraries. In the following sections, we will add the ArduinoJson
library as a dependency for our project and demonstrate how to use it in our firmware implementation. ArduinoJson
provides a simple-to-use interface to serialize data structures in JSON format.
Open the PlatformIO VSCode controls. Under the "Quick Access" pane, select "Libraries"
Figure 5. Location of "Libraries" under "Quick Access"
Search for "arduinojson"
Figure 6. ArduinoJson's entry in the PlatformIO registry
Click on "ArduinoJson". Select "Add to Project". Under "Select a project", select our "blink" project.
Figure 7. Adding ArduinoJson as a dependency to the blink
project
Select "Add"
Once finished, the library will have been added to your project. You can confirm this by inspecting platformio.ini
. A new option named lib_deps
will have been added with a single entry.
Figure 8. ArduinoJson's entry in platformio.ini
The following is a toy example of using the ArduinoJson
library in our firmware implementation. Each time we change the state of the LED_BUILTIN
pin, we will change the state of a variable. We then serialize this variable into a JSON object and print it to the serial monitor. In a real implementation, the serialized object can be transmitted to another computer using a communications protocol like Bluetooth or MQTT.
In src/main.cpp
, use the following code:
#include <Arduino.h>
#include <ArduinoJson.h>
boolean led_on;
JsonDocument board_state;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial);
pinMode(LED_BUILTIN, OUTPUT);
led_on = false;
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED_BUILTIN, HIGH);
led_on = true;
board_state["led_on"] = led_on;
serializeJsonPretty(board_state, Serial);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
led_on = false;
board_state["led_on"] = led_on;
serializeJsonPretty(board_state, Serial);
delay(500);
}
A major difference is the addition of #include <ArduinoJson.h>
. Note that lib/
is still empty; when PlatformIO builds the firmware binary, it checks platformio.ini
for library dependencies and automatically links them. The library source is located in ${projectRoot}/.pio/libdeps/
.
Note: If the
libdeps/
directory does not exist, try building the project first.
Now that we have gone over an example PlatformIO project, here are some tricks that can help you improve your own PlatformIO application.
If you would like to manage your projects, libraries, platforms, or profile information, PlatformIO’s Home interface provides a comprehensive and user-friendly dashboard.
How to use:
There is no installation for PlatformIO Home, as it is already installed on the PlatformIO IDE and CLI. To launch PlatformIO Home, users have two options. The first option is to press the PlatformIO IDE Home button in the VSCode toolbar, as shown below:
The second option is to enter the command $pio home
in the terminal and then open any browser to the page http://127.0.0.1:8008/.
This will bring you to PlatformIO home page:
PlatformIO is compatible with popular linters such as ‘cpplint’ and ‘clang-tidy’. These linters can help automatically check a project’s code for both programmatic and stylistic errors. To add the linter that you want to you PlatformIO project, simply add the linter to the platformio.ini
file like so:
[env:myenv]
platform = espressif32
board = esp32dev
framework = arduino
extra_scripts = pre:lint.py
Multiple environments can be defined in PlatformIO to manage different configurations. This would account for different boards or build flags. To define multiple environments, add the following code per environment to the platformio.ini
file:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
[env:uno]
platform = atmelavr
board = uno
framework = arduino
Q: Can I use PlatformIO with other IDEs besides VSCode?
A: Yes, PlatformIO can be integrated with other IDEs such as Atom and CLion. Additionally, it can also be used purely from the command line.
Q: How do I update PlatformIO and its packages?
A: You can update PlatformIO by running $pio update
in the terminal. This command updates the core and all installed platforms and libraries.
Q: How can I add custom libraries that are not available in the PlatformIO registry?
A: You can add custom libraries by placing them in the lib/
directory of your project. Alternatively, you can specify a Git URL or local path in the lib_deps
section of the platformio.ini
file.
For other questions, it would be best to refer to common support forums such as the PlatformIO community page and Stack Overflow QnA website.
PlatformIO arms software engineers with a valuable tool if they wish to explore the world of embedded systems by providing a familiar interface to work with hardware. It introduces hardware engineers to the amenities that software engineers have enjoyed for the past 20 years. Although we have only scratched the surface of PlatformIO's capabilities, we already have a glimpse of the power offered. Below are additional resources if the reader wants to survey additional use cases.
- Standalone CLI Installation
-
Supported Embedded Platforms: Below are a couple of popular platforms
- Espressif 32 (ESP32): Platform supporting wireless connectivity, multicore boards, and immense configuration flexibility. PlatformIO enables developers to embed binary data, adjust partition tables, and more when compiling firmware for this platform.
- Atmel SAM: Platform supporting a wide range of boards designed by Adafruit, Arduino, and other vendors.
- Integration with automated software delivery pipelines (CD)
- Espressif IoT Development Framework (ESP-IDF)
- Source Code
[2] https://registry.platformio.org/
[3] https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html
[4] https://github.com/tidbyt/hdk
[5] https://docs.platformio.org/en/latest/core/installation/udev-rules.html