At first step, we need to create a new project using PlatformIO Home Page (to open this page just press Home icon on the toolbar):

On the next step, we need to select STNucleo-F401RE as a development board, STM32Cube as a framework and a path to the project location (or use the default one):

Processing the selected project may take some amount of time (PlatformIO will download and install all required packages) and after these steps, we have a fully configured project that is ready for developing code with STM32Cube framework.

Now we can build the project. To compile firmware we can use next options:
Build option on the ProjectTasks menu, Build button on PlatformIO Toolbar, using Command Palette View:CommandPalette>PlatformIO:Build, using Task Menu Tasks:RunTask...>PlatformIO:Build or via hotkeys cmd-alt-b/ctrl-alt-b:

If everything went well, we should see the successful result in the terminal window:

To upload the firmware to the board we can use next options:
Upload option on the ProjectTasks menu, Upload button on PlatformIO Toolbar, using Command Palette View:CommandPalette>PlatformIO:Upload, using Task Menu Tasks:RunTask...>PlatformIO:Upload or via hotkeys cmd-alt-u/ctrl-alt-u:

PIO Unified Debugger offers the easiest way to debug your board. To start debugging session you can use Startdebugging option in PlatformIOQuickAccess menu, Debug:Startdebugging from the top menu or hotkey button F5:

We need to wait some time while PlatformIO is initializing debug session and when the first line after the main function is highlighted we are ready to debug:

We can walk through the code using control buttons, set breakpoints, see peripheral registers, add variables to Watchwindow:

Also, we need to create a new folder test where the tests and custom test_transport implementation (described next) will be located:

We will use USART2 on STNucleo-F401RE board because it’s directly connected to the STLink debug interface and in OS it can be visible as a Virtual Com Port, so we don’t need any additional USB-UART converter. To implement the custom test_transport we need to create two files unittest_transport.h and unittest_transport.c and put them in the test_dir in the root folder of our project. In these files we need to implement the next four functions:

Now we need to add some test cases. Tests can be added to a single C file that may include multiple tests. First of all, we need to add three default functions: setUp, tearDown and main. setUp and tearDown are used to initialize and finalize test conditions. Implementations of these functions are not required for running tests but if you need to initialize some variables before you run a test, you use the setUp function and if you need to clean up variables you use tearDown function. In our example, we will use these functions to accordingly initialize and deinitialize LED. main function acts as a simple program where we describe our test plan.

Let’s add a new file test_main.c to the folder test. Next basic tests for blinking routine will be implemented in this file:

test_led_builtin_pin_number ensures that LED_PIN has the correct value

Now we are ready to upload tests to the board. To do this we can use Test option from the Project Tasks menu, Tasks:RunTask...>PlatformIOTest option from the top menu or Test button on PlatformIO Toolbar:

After processing we should see a detailed report about the testing results:

Congratulations! As we can see from the report, all our tests went successfully!