If you're exploring embedded C++ development on the STM32 Nucleo-L433RC-P board and want a modern, lightweight alternative to STM32CubeIDE, PlatformIO inside Visual Studio Code offers a fast and flexible workflow. This guide shows how to set up your environment, build your first firmware, and write clean, object-oriented code in C++ using the STM32Cube HAL framework.
Why Use PlatformIO Instead of STM32CubeIDE
STM32CubeIDE is useful for quick starts, but it’s Eclipse-based and can feel heavy and rigid. PlatformIO provides a lightweight, modern experience that integrates seamlessly into Visual Studio Code. It offers:
- Better project structure for C++ development
- Simple dependency management
- Integration with Git and CI/CD tools
- Compatibility with STM32Cube HAL for low-level control
This makes it well-suited for both prototyping and production firmware.
Prerequisites
Before you begin, make sure you have:
- STM32 Nucleo-L433RC-P development board
- Visual Studio Code installed on your system
- PlatformIO extension for VS Code
- STM32 ST-LINK USB drivers installed
Installing PlatformIO in VS Code
Open Visual Studio Code and navigate to the Extensions view. Search for “PlatformIO IDE” and install it. Once installed, you’ll see a PlatformIO alien head icon in the sidebar. Clicking it brings up the PlatformIO Home screen where you manage projects and boards.
Creating a New PlatformIO Project
Inside PlatformIO Home, click “New Project.”
Give your project a name like stm32l433-cpp-blink
. Select the board nucleo_l433rc_p
and choose the STM32Cube
framework. Click “Finish” to create the project.
PlatformIO will set up a clean directory structure:
stm32l433-cpp-blink/
├── include/ # Header files
├── lib/ # Optional libraries
├── src/ # C++ source files (place main.cpp here)
├── platformio.ini # Project configuration
This layout makes it easy to organize classes and reusable code.
Writing a Basic C++ Blink Program
Inside the src
folder, replace the contents of main.cpp
with the following code:
#include "stm32l4xx.h"
#define LED_PIN GPIO_PIN_13
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
int main(void)
{
HAL_Init();
LED_GPIO_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
while (1)
{
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN);
HAL_Delay(1000);
}
}
// Wrap the interrupt handler declarations in extern "C" to ensure they use C linkage and match the names expected by the STM32 startup code.
extern "C" {
void SysTick_Handler(void) {
HAL_IncTick();
}
void NMI_Handler(void) {
}
void HardFault_Handler(void) {
while (1) {}
}
void MemManage_Handler(void) {
while (1) {}
}
void BusFault_Handler(void) {
while (1) {}
}
void UsageFault_Handler(void) {
while (1) {}
}
void SVC_Handler(void) {
}
void DebugMon_Handler(void) {
}
void PendSV_Handler(void) {
}
}
This toggles the LED on pin PA5 every 1 second.
Configuring platformio.ini
Edit your platformio.ini
file to configure the build environment for STM32L433 with C++17:
[env:nucleo_l433rc_p]
platform = ststm32
board = nucleo_l433rc_p
framework = stm32cube
upload_protocol = stlink
build_flags =
-std=c++17
-Wall
This tells PlatformIO to build with the STM32Cube HAL and upload firmware over the ST-LINK interface.
Building and Uploading Firmware
Click the checkmark icon in the bottom status bar to build the firmware. Click the right-arrow icon to upload it to your Nucleo board.
If everything is set up correctly, the on-board LED should blink every second.
Writing a C++ Class to Control the LED
For better structure, create a Blinker
class to encapsulate LED control.
Create src/blinker.hpp
:
#pragma once
#include "stm32l4xx_hal.h"
class Blinker {
public:
Blinker(GPIO_TypeDef* port, uint16_t pin);
void toggle();
private:
GPIO_TypeDef* _port;
uint16_t _pin;
};
And src/blinker.cpp
:
#include "blinker.hpp"
Blinker::Blinker(GPIO_TypeDef* port, uint16_t pin) : _port(port), _pin(pin) {}
void Blinker::toggle() {
HAL_GPIO_TogglePin(_port, _pin);
}
Update main.cpp
to use your new class:
#include "stm32l4xx.h"
#include "blinker.hpp"
#define LED_PIN GPIO_PIN_13
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
int main(void) {
HAL_Init();
LED_GPIO_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
Blinker led(GPIOB, GPIO_PIN_13);
while (1) {
led.toggle();
HAL_Delay(500);
}
}
/// interrupt handlers as above
This keeps your logic cleaner and makes it easier to expand your project later.
Conclusion
Using PlatformIO with VS Code for STM32 Nucleo-L433 development creates a clean and modern workflow, perfect for writing modular and testable C++ firmware. Instead of relying on automatically generated code, you build your own startup and clock configuration, gaining a deeper understanding of how STM32 MCUs work.
This approach scales well for larger projects, integrates easily with Git, and gives you full control over how your firmware is organized. You're now ready to move on to more advanced projects, like using FreeRTOS, I2C sensors, or building custom drivers in C++.