Using STM32CubeMX and PlatformIO

It helps a lot, thanks! In the meanwhile via Bash for windows I tried the gcc-arm-none-eabi and I can compile makefile projects without any problem. I think I should write a rule for flashing, then I am good to go :wink:

Hello! Thank you very much for posting your STM32 CubeMX configuration steps. I followed the steps, and generated my projects; however when I tried to open the project in PlatformIO (by clicking “Open Project”), I am getting the following error:

How could I deal it with it from here?

Thank you!

I suspect you missed the

Now copy the headers and source files under the “src” folder in the Platformio IDE and compile.

step :wink:

It looks like he created a project in VSCode/PlatformIO first and then copied the STM32CubeMX generated files into the /src project source folder. Since you’ve got the project, but don’t have it configured for platformio, you can initialise the project folder by using the VSCode/PlatformIO terminal (PlatformIO alien head icon on the sidebar, look for new terminal down the bottom), changing to the directory you have the project installed in, and then running

platformio init --ide vscode -b genericSTM32F103CB

with the correct board specified after the -b parameter. If you’re not sure what to put there, look it up here and get the board id on the documentation page for the chip/board you’re using.

Technically, you may even only need to create the platformio.ini and put the correct framework and board settings in it for it to work…

Hello,
I am sorry, I have kind of given up on the STM32 cube way. It is generating way too much of a bloated binary with the simplest of code. I have chosen to move to libopencm3.

I am sorry I am unable to be of much help to you beyond this. :frowning:

Thank you very much, the script works for me with this config:

[platformio]
src_dir = Src

[env:disco_f407vg]
platform = ststm32
board = disco_f407vg
framework = stm32cube
extra_scripts = pre:copyMiddlewares.py
build_flags = -I Inc

Note that build_flags = -I Inc must be used instead of include_dir = Inc, otherwise building of middleware module from lib folder fails if it needs C headers from Inc

1 Like

Another way is to use config library.json. Then you don’t need any prescript. You only set all “Inc” subfolders in library.json.

platformio.ini:

[platformio]
src_dir = Src
lib_dir = Middlewares/ST

[env:disco_f407vg]
platform = ststm32
board = disco_f407vg
framework = stm32cube

Create library.json in Middlewares/ST/STM32_USB_Host_Library and set this content

{
    "name": "STM32_USB_Host_Library",
    "version": "0.0.0",
    "build": {
        "flags": [
            "-I $PROJECT_DIR/Inc",
            "-I Core/Inc",
            "-I Class/CDC/Inc"
        ]
    }
}

I checked that if I regenerate code in STM32CubeMX, library.json isn’t lost.

1 Like

With all the wonderful hints from this discussion, I’ve come up with something that works pretty well for me until PlatformIO can natively support CubeMX Middlewares. Here are the steps I do at the moment…

PlatformIO with CubeMX middleware

  1. Start CubeMX project with required target device, adjust and add middleware, Makefile toolchain (anything else works too), Copy only the necessary library files, then Generate Code - see Images (1), (2)

  2. VSCode : Open folder, F1 > PlatformIO : New terminal

  3. platformio init --board nucleo_f103rb --project-option "framework=stm32cube"

  4. Close folder, open same folder (to get around PlatformIO not recognising project)

  5. Delete PlatformIO generated include folder, and CubeMX Drivers folder

  6. Adjust platformio.ini and add custom prebuild-include.py (only required if you are are using Middlewares)

platform.ini

[platformio]
src_dir = ./
include_dir = Inc/

[env]
src_filter = +<Src/>
extra_scripts = pre:prebuild-include.py
prebuild_include = Middlewares

[env:nucleo_f103rb]
platform = ststm32
board = nucleo_f103rb
framework = stm32cube

prebuild-include.py

from os import path, walk
import SCons.Errors

Import('env')
try:
    import configparser
except ImportError:
    import ConfigParser as configparser

config = configparser.ConfigParser()
config.read('platformio.ini')
prebuild_include = config.get('env', 'prebuild_include').split()

source_paths = set()
header_paths = set()
for included in prebuild_include:
    if not path.exists(included):
        raise SCons.Errors.UserError(
            "prebuild-include.py could not find path '%s'" % included)
    for root, dirs, files in walk(included):
        for file in files:
            if file.endswith(('.c', '.cpp', '.s')):
                source_paths.add(root.replace('\\', '/'))
            if file.endswith(('.h', '.hpp')):
                header_paths.add(root.replace('\\', '/'))

if not 'SRC_FILTER' in env:
    env['SRC_FILTER'] = ['+<Src/>']
env['SRC_FILTER'] += ['+<%s/>' % source_path for source_path in source_paths]

if not 'BUILD_FLAGS' in env:
    env['BUILD_FLAGS'] = []
env['BUILD_FLAGS'] += ['-I' + header_path for header_path in header_paths]

“It works for me”, but I hope this helps out anyone else in the same situation, or at the least provide some additional clues until the day PlatformIO automatically detects and uses CubeMX Middleware. :wink:

Aside: Out of date PlatformIO CubeMX drivers

The framework-stm32cube CubeMX LL and HAL drivers are very out of date now for a lot of the devices (last updated Nov 30, 2018). Pity this can’t be automatically generated via some PlatformIO build/release pipeline every time ST releases updates, or at least on a button click for PlatformIO project maintainers. I wish I could help here, but I couldn’t even find the source for it - and I tried very hard :frowning: .

Currently am forced to do the following (not recommended - absolutely dirty flaky hack)

  1. Find device firmware location in CubeMX (might need to generate code to trigger the download first) - see Images (3)
  2. Delete all the existing files in PlatformIO <packages_dir>\framework-stm32cube\<Fx>\* (see platformio.ini :: platformio)
  3. Copy Drivers, Utilities, package.xml, Release_Notes.html from CubeMX location to PlatformIO location - see Images (4)

Note: This might not work as I’m wilfully ignoring the existing PlatformIO <packages_dir>\framework-stm32cube\platformio\ldscripts, and have only tried it with the STM32F103 (but, will be trying STM32F401 today).

Images

Sorry about this, but had to combine into one image since I cannot post more than one for the first post!


Matthew

2 Likes

Hey guys.

I’m having trouble using the code generated by CubeMX inside PIO, because of STM32Cube version mismatch. The one included in STSTM32 platform in PIO is outdated.

@rwx has a way to “hack” the new drivers inside PIO, but that was 4 months ago. Is this still the best way to do this now?

Thanks! :slight_smile:

1 Like

Hi,

based on the script by rwx I created a script that collects the necessary information automatically from the STM32CubeIDE .cproject file and as a backup a manual script that relies heavily on the script by rwx. Looking forward to comments. Here is the link:

Cheers, Joachim

1 Like

Hi @jbaumann
Excellent. If I understand correctly, with your script, we will be able to convert all applications and samples coming with SMT32CubeMX to plaformio project domain and do everything we can do with platformio (for example, transfer to another IDE or transfer to another board, etc.) Is it true?

Since the STM32CubeIDE uses the same CubeMX implementation this should be possible as long as the same .cproject file for eclipse is generated.

But if this is not the case, then creating the same CubeMX information with STM32CubeIDE should still be possible.

Cheers, Joachim

1 Like

A missing puzzle piece to this may also be STM HAL drivers are out of date · Issue #439 · platformio/platform-ststm32 · GitHub. The current STM32Cube HAL is very much out of date and results in compilation error some times. I have generated a new version of framework-stm32cube with the very-latest versions (built through a script), but it needs testing, see issue. @valeros if you see this, please consider updating with the tool I posted in the issue :smiley:

Ok, I didn’t know that the libraries provided by Platformio are so outdated. My experiments focused on STM32F4 boards and these seem to be not as badly out of sync as the rest…

I now created a new script that extracts as much information from the .project and .cproject file as possible, turns off as much of the additional “intelligence” of platformio as manageable, and then lets it build with only the libraries provided by CubeMX.

Caveat: You must create you project with the option “Add necessary files as reference in the toolchain project configuration file” because this is the only way my script can extract the needed information.

Here is the link to the script: pio_and_stm32cubeide/automatic_cubemx at main · jbaumann/pio_and_stm32cubeide · GitHub

Looking forward to your comments/feedback.

Cheers, Joachim

1 Like

(yeah, old thread, I know, but it highlights the issues involved to get this working …)

I can’t figure out how to make PIO + FreeRTOS + LwIP play nice together on STM32. There’s a makefile-based version which works out of the box on github (effortlessly!), but every attempt to bring it into PIO’s world has failed me so far (FWIW, I’d rather not go through the STM32Cube code generator, which leads to unmaintainable code).

I’ve tried using the FreeRTOS / LwIP libs inside .platformio (fails to compile) and I’ve tried mincrmatt12’s PIO repos (see 6696, 6751, and github, after adapting them from F2 to F7).

So far, it’s a bit like herding cats. If there’s a working example of running PIO + FreeRTOS + LwIP on any STM32 board, I’d love to hear about it …

After quite literally weeks worth of attempts trying to just manually copy paste files from cubeMX into the src and include and library folders in platformio with zero luck, this technique finally got my project to build, and bonus points because it allowed me to maintain the cubeMX structure which will allow me to regenerate the generated code if i ever want to add more peripherals.

I do have another question, I’m looking to add some C++ to my project and just changing main.c to main.cpp resulted in a bunch of errors (which don’t come up in cubeIDE), so I was wondering if anyone knows what else is required to make the project C++ compatible after the successful import into PIO?

Actually bonus points if I can easily just convert the entire project into C++

Nice to read that the script helped you.

The script currently is simply looking for the C resources, C++ resources are currently ignored. If you look into line 161 of the script you can see that it only matches the C Compiler include paths.
Could you post the errors you get so that I could determine whether that is the only problem?
If so, I could try and add this functionality to the script (even though I cannot promise when I have the spare time for that…)

Cheers, Joachim

changing main.c to main.cpp and main.h to main.hpp yields the following result. Note that doing the same in cubeIDE works without issue.

Processing nucleo_h723zg (platform: ststm32; board: nucleo_h723zg)
--------------------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
SETUP_CUBEMX: Using the following source directories: 'Core, Middlewares, Drivers'
SETUP_CUBEMX: Using the following build flags: '-mthumb, -mcpu=cortex-m7, -mfpu=fpv5-d16, -mfloat-abi=hard'
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/nucleo_h723zg.html
PLATFORM: ST STM32 (16.1.0) > ST Nucleo H723ZG
HARDWARE: STM32H723ZGT6 550MHz, 320KB RAM, 1MB Flash
DEBUG: Current (stlink) On-board (stlink) External (blackmagic, cmsis-dap, jlink)
PACKAGES:
 - toolchain-gccarmnoneeabi @ 1.70201.0 (7.2.1)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Compiling .pio\build\nucleo_h723zg\src\Core\Src\freertos.o
Core\Src\freertos.c:23:10: fatal error: main.h: No such file or directory

************************************************************** 
* Looking for main.h dependency? Check our library registry!   
*
* CLI  > platformio lib search "header:main.h"
* Web  > https://registry.platformio.org/search?q=header:main.h
*
**************************************************************

 #include "main.h"
          ^~~~~~~~
compilation terminated.
Compiling .pio\build\nucleo_h723zg\src\Core\Src\main.o
Compiling .pio\build\nucleo_h723zg\src\Core\Src\stm32h7xx_hal_msp.o
Compiling .pio\build\nucleo_h723zg\src\Core\Src\stm32h7xx_hal_timebase_tim.o
Compiling .pio\build\nucleo_h723zg\src\Core\Src\stm32h7xx_it.o
Compiling .pio\build\nucleo_h723zg\src\Core\Src\syscalls.o
Compiling .pio\build\nucleo_h723zg\src\Core\Src\sysmem.o
*** [.pio\build\nucleo_h723zg\src\Core\Src\freertos.o] Error 1
Compiling .pio\build\nucleo_h723zg\src\Core\Src\system_stm32h7xx.o
Core\Src\stm32h7xx_hal_msp.c:22:10: fatal error: main.h: No such file or directory

**************************************************************
* Looking for main.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:main.h"
* Web  > https://registry.platformio.org/search?q=header:main.h
*
**************************************************************

 #include "main.h"
          ^~~~~~~~
compilation terminated.
Core\Src\stm32h7xx_it.c:21:10: fatal error: main.h: No such file or directory

**************************************************************
* Looking for main.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:main.h"
* Web  > https://registry.platformio.org/search?q=header:main.h
*
**************************************************************

 #include "main.h"
          ^~~~~~~~
compilation terminated.
*** [.pio\build\nucleo_h723zg\src\Core\Src\stm32h7xx_hal_msp.o] Error 1
*** [.pio\build\nucleo_h723zg\src\Core\Src\stm32h7xx_it.o] Error 1
Core\Src\main.cpp:55:1: sorry, unimplemented: non-trivial designated initializers not supported
 };
 ^
Core\Src\main.cpp:55:1: sorry, unimplemented: non-trivial designated initializers not supported
In file included from Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_rcc.h:27:0,
                 from Core\Inc/stm32h7xx_hal_conf.h:247,
                 from Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal.h:29,
                 from Core\Inc/main.hpp:30,
                 from Core\Src\main.cpp:20:
Core\Src\main.cpp: In function 'void SystemClock_Config()':
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_pwr.h:295:14: warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}'
       UNUSED(tmpreg);                                                          \
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_def.h:69:27: note: in definition of macro 'UNUSED'
 #define UNUSED(x) ((void)(x))
                           ^
Core\Src\main.cpp:175:3: note: in expansion of macro '__HAL_PWR_VOLTAGESCALING_CONFIG'
   __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core\Src\main.cpp: In function 'void MX_GPIO_Init()':
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_rcc.h:1460:48: warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}'
                                         UNUSED(tmpreg); \
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_def.h:69:27: note: in definition of macro 'UNUSED'
 #define UNUSED(x) ((void)(x))
                           ^
Core\Src\main.cpp:329:3: note: in expansion of macro '__HAL_RCC_GPIOE_CLK_ENABLE'
   __HAL_RCC_GPIOE_CLK_ENABLE();
   ^~~~~~~~~~~~~~~~~~~~~~~~~~
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_rcc.h:1468:48: warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}'
                                         UNUSED(tmpreg); \
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_def.h:69:27: note: in definition of macro 'UNUSED'
 #define UNUSED(x) ((void)(x))
                           ^
Core\Src\main.cpp:330:3: note: in expansion of macro '__HAL_RCC_GPIOF_CLK_ENABLE'
   __HAL_RCC_GPIOF_CLK_ENABLE();
   ^~~~~~~~~~~~~~~~~~~~~~~~~~
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_rcc.h:1428:48: warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}'
                                         UNUSED(tmpreg); \
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_def.h:69:27: note: in definition of macro 'UNUSED'
 #define UNUSED(x) ((void)(x))
                           ^
Core\Src\main.cpp:331:3: note: in expansion of macro '__HAL_RCC_GPIOA_CLK_ENABLE'
   __HAL_RCC_GPIOA_CLK_ENABLE();
   ^~~~~~~~~~~~~~~~~~~~~~~~~~
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_rcc.h:1476:48: warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}'
                                         UNUSED(tmpreg); \
Drivers\STM32H7xx_HAL_Driver\Inc/stm32h7xx_hal_def.h:69:27: note: in definition of macro 'UNUSED'
 #define UNUSED(x) ((void)(x))
                           ^
Core\Src\main.cpp:332:3: note: in expansion of macro '__HAL_RCC_GPIOG_CLK_ENABLE'
   __HAL_RCC_GPIOG_CLK_ENABLE();
   ^~~~~~~~~~~~~~~~~~~~~~~~~~
*** [.pio\build\nucleo_h723zg\src\Core\Src\main.o] Error 1
================================================== [FAILED] Took 1.26 seconds ==================================================

If you look at the first error, you can see that the build misses the file main.h, that is referenced by freertos.c. I know it is a preference of many to use .hpp as the header extension (I personally still prefer .h), but try first to rename main.hpp back to main.h.

Cheers, Joachim

I had that originally, but then had other errors. I did actually figure it out after the fact. What I ended up needing to do was rename main.c to main.cpp as well as rename freertos.c to freertos.cpp. I also just had to remove a C-style struct from main which solves the sorry, unimplemented: non-trivial designated initializers not supported error and after that it did build successfully.

To be 100% honest with you I’m not super sure what your script does but things seem to be working now so I won’t ask too many questions :sweat_smile:. I assume that I shouldn’t have any problems from here adding more files and expanding now that the base project builds correctly.

Also a note for others who might be using some of the newer STM chips, I would recommend removing the framework line from your platformio.ini file, since with this import strategy you have your HAL library files bundled in already and it’s better to use the one packaged with cubeMX since you won’t run into strange issues with mismatched versions that way.

Fantastic to hear that.

The script actually scrapes all information regarding the build, including all used files, include files and directories, build options and so on from different files in which STM32CubeIDE hides/keeps them. It then uses these informations to manipulate the build state of the PlatformIO build so that it uses the correct libraries.

As long as STM doesn’t change the data location and as long as PIO doesn’t change the build process this script will hopefully continue to work.

I already suggest to remove the framework reference in the comment directly above it. I tested both and the only difference was that the potentially older libraries got downloaded. But they are not used because my script overwrites the whole library path…

Cheers, Joachim