STM32 MCU: PlatformIO Setup
Set up with your favorite AI
Gemini & other AI
Copy the setup prompt into your AI. Work through each step together, from checking your kit to getting it running.
Copy the prompt, then paste it into Gemini’s message box.
View prompt
Introduction
Section titled “Introduction”This guide follows the Japanese STM32 PlatformIO guide and uses VS Code with PlatformIO to build a program that prints Hello World once per second. STM32CubeProgrammer uploads it through a USB leaf. You do not need Arduino IDE or its board package for this workflow.
Install the latest stable software from the official distribution sites. The STM32CubeProgrammer version pins in older instructions are no longer required for this workflow.
Required items
Section titled “Required items”- STM32 MCU leaf (AP03 / STM32L452REI6) and a USB leaf
- A data-capable USB cable
- A Windows, macOS or Linux computer with an internet connection
For Basic Kit 2, replace the assembled AVR MCU with the STM32 MCU leaf before programming.
Install the tools
Section titled “Install the tools”- Install Visual Studio Code for your OS and CPU.
- Open Extensions in VS Code, find PlatformIO IDE published by PlatformIO, and install it. Wait for its initial setup to finish and restart VS Code if requested.
- Install STM32CubeProgrammer from ST. Locate its command-line executable; you will use its actual path in
upload_command.
PlatformIO Core and its serial monitor are included in the IDE extension. If prompted for Python, follow PlatformIO’s Python installation instructions; Linux also requires python3-venv. See the PlatformIO IDE documentation for setup details.
Python is prepared automatically on supported operating systems. Add Japanese Language Pack for Visual Studio Code if you want a Japanese interface, or Teleplot for graphs.
| OS | STM32CubeProgrammer command-line tool |
|---|---|
| Windows | bin/STM32_Programmer_CLI.exe under the installation folder |
| macOS | Contents/Resources/bin/STM32_Programmer_CLI inside STM32CubeProgrammer.app |
| Linux | bin/STM32_Programmer.sh under the installation folder |
Use a local project path with simple ASCII characters, such as C:/PlatformIO/Projects on Windows. Open the command terminal through PlatformIO → Quick Access → Miscellaneous → PlatformIO Core CLI. To change the default folder for new projects, run the following with your actual path and restart VS Code:
pio settings set projects_dir "C:/PlatformIO/Projects"This does not move existing projects. See the projects_dir documentation.
Simple paths help avoid path-handling problems. User folders and OneDrive locations differ between computers, so check the actual destination before copying an example. On macOS and Linux, replace the quoted path with the absolute path to your chosen folder.
Quick start
Section titled “Quick start”1. Create the project and install its board definition
Section titled “1. Create the project and install its board definition”Use the Leafony AP03 board definition, which contains Leafony’s pin assignments and serial configuration. It differs from the Leafony Systems AP03 definition registered with PlatformIO.
- Create an empty folder named
STM32_Hello_World_Pjtand open it with File → Open Folder in VS Code. - Download the board definition repository using Code → Download ZIP and extract it.
- Create
boards,variants/STM32L4xxandsrcunder the project folder. - Copy
LEAFONY_AP03.jsonintoboards. Copy the entireLEAFONY_AP03directory intovariants/STM32L4xx. Match filename capitalization exactly. - Create
platformio.iniin the project root andmain.cppinsidesrc.
STM32_Hello_World_Pjt/├── platformio.ini├── boards/│ └── LEAFONY_AP03.json├── variants/│ └── STM32L4xx/│ └── LEAFONY_AP03/│ ├── PeripheralPins.c│ ├── PinNamesVar.h│ ├── generic_clock.c│ ├── ldscript.ld│ ├── variant_generic.cpp│ ├── variant_generic.h│ ├── variant_LEAFONY_AP03.cpp│ └── variant_LEAFONY_AP03.h└── src/ └── main.cppPlatformIO reads the definition from the project’s boards directory. You do not need to overwrite files in .platformio/packages or .platformio/platforms. For another project, copy boards and variants and use the configuration below.
2. Identify the serial port
Section titled “2. Identify the serial port”With USB disconnected, assemble the STM32 MCU and USB leaves as shown in the connection example. Connect the USB leaf to your computer, then run:
pio device listThe connected port disappears and reappears when you unplug and reconnect the cable.
| OS | Example port |
|---|---|
| Windows | COM3 |
| macOS | /dev/cu.usbserial-XXXXXXXX |
| Linux | /dev/ttyUSB0 |
If no port appears, check the cable, leaf connections and USB driver. Refer to the FAQ. On Linux, also follow PlatformIO’s serial port permissions instructions.
3. Configure platformio.ini
Section titled “3. Configure platformio.ini”The following example is for Windows on COM3. Replace the port and executable path with those on your computer.
[env:leafony_ap03]platform = ststm32board = LEAFONY_AP03framework = arduinoboard_build.variants_dir = variants
upload_protocol = customupload_port = COM3upload_command = "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI.exe" -c port=$UPLOAD_PORT br=115200 -w "$SOURCE" 0x08000000 -v
monitor_port = ${this.upload_port}monitor_speed = 115200On macOS, replace upload_port and upload_command with the following, using the actual port name:
upload_port = /dev/cu.usbserial-XXXXXXXXupload_command = "/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/Resources/bin/STM32_Programmer_CLI" -c port=$UPLOAD_PORT br=115200 -w "$SOURCE" 0x08000000 -vOn Linux, use the absolute installation path and actual serial port. This example uses the account name user:
upload_port = /dev/ttyUSB0upload_command = "/home/user/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer.sh" -c port=$UPLOAD_PORT br=115200 -w "$SOURCE" 0x08000000 -v| Setting | Purpose |
|---|---|
[env:leafony_ap03] | Names the build environment and output folder |
board = LEAFONY_AP03 | Selects boards/LEAFONY_AP03.json |
board_build.variants_dir | Selects the folder containing the pin definitions |
upload_protocol = custom | Runs upload_command for uploads |
$UPLOAD_PORT, $SOURCE | Substituted by PlatformIO with the port and firmware path |
0x08000000 | Flash start address |
-v | Verifies the uploaded data |
monitor_port | Serial monitor port; the upload port in this example |
monitor_speed | Serial baud rate; must match Serial.begin() |
Leave $UPLOAD_PORT and $SOURCE unchanged. See the PlatformIO upload command reference and STM32CubeProgrammer CLI manual.
4. Write the program
Section titled “4. Write the program”Save the following as src/main.cpp:
#include <Arduino.h>
void setup() { Serial.begin(115200);}
void loop() { Serial.println("Hello World"); delay(1000);}5. Build
Section titled “5. Build”Run PlatformIO → PROJECT TASKS → leafony_ap03 → General → Build, or run this command in the project folder:
pio run -e leafony_ap03The first build downloads the compiler and framework and can take several minutes. Check for SUCCESS at the end of the log and the generated file .pio/build/leafony_ap03/firmware.bin. If the project is not recognized, reopen the folder containing platformio.ini.
If several projects or build environments are open, check that the selected target is leafony_ap03 in STM32_Hello_World_Pjt.
6. Upload to the STM32 MCU
Section titled “6. Upload to the STM32 MCU”- Close any application using the serial port, including the serial monitor and Teleplot. Press
Ctrl+Cin PlatformIO’s monitor terminal to stop it. - Set the STM32 MCU switch to Program and check that its LED is on.
- Press Reset to enter programming mode.
- Run PROJECT TASKS → leafony_ap03 → General → Upload, or:
pio run -e leafony_ap03 -t upload
Check the STM32CubeProgrammer log for successful connection, programming and verification. A verification success message may read Download verified successfully; wording depends on the tool version. If there is a timeout or verification error, close other port users, reset in Program mode and upload again, even if the overall log says SUCCESS.
7. Check Hello World
Section titled “7. Check Hello World”- Set the switch back to Run and check that the LED turns off.
- Press Reset to start the program.
- Run PROJECT TASKS → leafony_ap03 → General → Monitor, or:
pio device monitor -e leafony_ap03The following output should appear once per second:
Hello WorldHello WorldHello WorldUse an existing sample
Section titled “Use an existing sample”Download a project from the Leafony STM32 PlatformIO samples. Open the individual folder containing its platformio.ini, rather than the entire repository.
Apply the same boards, variants and upload settings used above. Preserve sample-specific lib_deps and build_flags. Replace old upload commands referencing Arduino15/.../STM32Tools/1.4.0/... with the path to your installed STM32CubeProgrammer.
For example, when using STM32_Hello_World_Pjt, place boards and variants inside the project and apply this page’s platformio.ini settings. Review the existing sample settings before carrying them over.
Review library compatibility before updating packages. pio pkg update updates dependencies within the version constraints in platformio.ini; see the command reference.
Adjust the version constraints as needed, then run the update command in the project folder.
Libraries
Section titled “Libraries”Add libraries to lib_deps inside the [env:...] section you use. The Hello World example needs no additional libraries.
If lib_deps already exists, append to its list. Add only the libraries needed for the leaves and functions you use.
lib_deps = adafruit/Adafruit Unified Sensor adafruit/Adafruit BusIO https://github.com/Leafony/TBGLibGit URLs require Git. Restart VS Code after installing it and check that git --version works in the terminal.
| Leaf or function | Libraries |
|---|---|
| BLE | TBGLib |
| 4-Sensors | Adafruit Unified Sensor, Adafruit BusIO, HTS221, ClosedCube OPT3001, Adafruit LIS3DH |
| LCD | ST7032 |
| RTC on RTC & microSD | RTClib |
| STM32 internal RTC / low power | STM32duino RTC, STM32duino Low Power |
| LTE-M | LteLeafV4 |
| Wi-Fi | WiFi101Leafony |
| LoRa / signed communication | arduino-LoRa, arduino-tca9536, SparkFun ATECCX08a |
Library versions in older samples
Section titled “Library versions in older samples”ST STM32 20.0.0 moved to STM32 Arduino Core 3.0.0. Follow the STM32RTC requirements and STM32LowPower requirements: use the 2.x library lines with Core 3.x, and 1.x with Core 2.x.
; For STM32 Arduino Core 3.xlib_deps = stm32duino/STM32duino RTC@^2.0.0 stm32duino/STM32duino Low Power@^2.0.0Check the framework-arduinoststm32 entry under PACKAGES in the build log to identify your Core. Do not use the AVR MsTimer2 library on STM32; use HardwareTimer instead.
Even if an older sample specifies a version such as @1.2.0, check each library’s README against the Arduino Core used by your project.
Set the CPU frequency to 16 MHz
Section titled “Set the CPU frequency to 16 MHz”The Leafony board definition normally runs the CPU at 80 MHz.
- Download Leafony’s
leafony_tools .cppand save it assrc/leafony_tools.cpp. Remove the space before.cppfrom the downloaded filename. - Add the following flag to the active environment. If
build_flagsalready exists, append the flag to it.
build_flags = -D CPUCLOCK_LOW- Replace
main.cppwith the following, rebuild and upload. Return the switch to Run and reset. The monitor should print16000000.
#include <Arduino.h>
void setup() { Serial.begin(115200);}
void loop() { Serial.println(HAL_RCC_GetHCLKFreq()); delay(1000);}Remove -D CPUCLOCK_LOW, rebuild and upload to return to 80 MHz. Changing board_build.f_cpu alone does not switch the actual hardware clock.
Serial plotter
Section titled “Serial plotter”Install Teleplot for VSCode. Upload this example, return the switch to Run and reset:
#include <Arduino.h>
void setup() { Serial.begin(115200);}
void loop() { Serial.print(">seconds:"); Serial.println(millis() / 1000.0f); delay(100);}Close PlatformIO’s serial monitor. From the VS Code command palette run teleplot: Start teleplot session, choose the serial port and baud rate 115200, then click Open. Check that seconds appears in the graph.
Teleplot uses newline-terminated >name:value messages. Serial.println() supplies the newline. Close Teleplot’s port before uploading another program. See also the STM32 Teleplot example.
Port an Arduino IDE sketch
Section titled “Port an Arduino IDE sketch”src/main.cpp follows normal C++ rules. Add #include <Arduino.h>, declare functions before use, and add required libraries to lib_deps.
#include <Arduino.h>
void printMessage();
void setup() { Serial.begin(115200); printMessage();}
void loop() {}
void printMessage() { Serial.println("Hello World");}Upload with the STM32CubeProgrammer GUI
Section titled “Upload with the STM32CubeProgrammer GUI”- Build and locate
.pio/build/leafony_ap03/firmware.bin. If you renamed the environment, use its folder name instead. - Close other serial port users, switch to Program and press Reset.
- Open STM32CubeProgrammer and connect using the following settings.
| Setting | Value |
|---|---|
| Connection | UART |
| Port | The STM32 MCU serial port |
| Baud rate | 115200 |
| Parity | Even |
| Data bits | 8 |
| Stop bits | 1 |
| Flow control | OFF |
- Open Erasing & Programming. Select
firmware.binand start address0x08000000. - Enable Verify programming, click Start Programming, and check that verification succeeds.
- Click Disconnect, return the switch to Run, press Reset, and check output in PlatformIO’s serial monitor.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check |
|---|---|
UnknownBoard or missing variant files | File paths, capitalization and board_build.variants_dir |
pio command not found | Reopen the PlatformIO Core CLI terminal |
| Upload tool not found | Actual installation path and quotes around paths containing spaces |
| Cannot open the port | Port name, other applications holding the port, and Linux permissions |
| Connection times out | Program switch, Reset before upload, USB cable and port name |
| No output after upload | Switch back to Run, Reset, and monitor_port |
| Garbled serial output | Match monitor_speed to Serial.begin(); this example uses 115200 |