Setting up STM32F103 (blue pill) with + PlatformIO + STM32Duino on USB

I actually lol'd reading back the title of this post, but this is exactly what I just did today: I spent about 5 hours today and finally managed to make an STM32 blue pill board work through USB. Prepare for usability hell.

There are a couple of tutorials about how to set up STM32 boards but most of them are either for Windows and/or for Arduino and/or using an FTDI programmer. What I could not find is how to make this board work for my specific setup, which is:

  • Board: STM32F103C8T6, AKA the blue pill board. Yes when it comes to STM32's there are one hundred million different boards you can choose from and you must be this specific. (Note: I have a proper board with 1.5kΩ resistor on the USB port so I don't need to hack this to make USB work properly.)
  • OS: macOS
  • Programmer: ST Link V2, but I want to be able to upload my code to the board via USB for convenience.
  • IDE: PlatformIO
  • Platform: Arduino/STM32Duino

If you don't have the exact same setup then it's probably not worth continue reading.


In order to make the above config work you need to install a couple of things first:

  1. Homebrew to install some CLI tools
  2. dfu-util which is a tool for uploading/downloading firmwares via USB. To install, run brew install dfu-util in the terminal
  3. STM32CubeProgrammer, a "cross-platform" (ie. Java 🤮) application that lets you program bootloader to STM32 boards via ST Link or other tools. When you download it for macOS you'll find an app in the zip which – even if you go through the usual macOS security shitshow – obviously won't open. In order to install it you need to right click on the app/"Show package contents", then go to Contents/MacOs where you'll find an executable called "SetupSTM32CubeProgrammer-2_7_0_macos". Right click/"Open" will get you to the installer. Very user friendly.
    Alternatively you can use STLink for macOS if you're okay with using CLI tools.

Step 1: USB bootloader

For Arduino or ESP32 boards it's pretty straightforward to upload the code from PlatformIO through USB: plug in board, upload code, that's it. Is it the same for STM32 boards? Hell no! As it turned out, by default the blue pill cannot be programmed via USB because there's no USB bootloader on the chip. (What's a bootloader?)

Follow these steps to get the USB bootloader to the STM32 blue pill.

Step 1.1: Get the USB bootloader

Download this file, which is the actual bootloader binary that you'll need to flash on the chip. Note that this specific binary will only work for boards with their built-in LED being connected to PC13. See there on the bottom-ish right-ish part of the blue pill, it says "PC13". That means that the LED right besides it is connected to PC13.

If you have a different or fake STM32F103 then you can download a different binary from the same Github repo.

You'll need a programmer to flash the bootloader to the board. I used an ST Link which you need to connect with jumper wires like this:

Just to make sure: the SWDIO (2), GND (4), SWCLK (6) and 3.3V (8) pins on the ST Link have to be connected to the corresponding pins on the board.


There are 2 jumpers on the blue pill. [If the USB port is on the left] the top jumper is BOOT0 and the bottom one is BOOT1. In order to be able to upload the bootloader the BOOT0 jumper should short the center and the right pin, ie. set to logical HIGH or 1.

Set BOOT0 jumper for flashing the bootloader

Cool, now you can plug in the ST Link to your computer's USB port.

Step 1.3: Flash the bootloader

Open STM32CubeProgrammer. Make sure that ST Link is selected in the dropdown on the right, then click on "Connect" and then the download icon on the right. Because why not call the whole upload procedure "download"??! Anyways...

Next, browse the binary file you just downloaded in Step 1.1 then click on "Start Programming" to flash the bootloader.

If all goes well then the bootloader should be on the board now. Don't disconnect the ST Link just yet.


Before disconnecting the ST Link set back the BOOT0 jumper to LOW – ie. short the left and the center pins – to get back to normal upload mode. Press the reset button on the blue pill.

Now you can click on "Disconnect" on the right panel of the STM32CubeProgrammer app to disconnect the blue pill from the computer. Unplug the ST Link from the computer and then disconnect the blue pill from the ST Link.

Note that the bootloader takes up about 20kB from the available 64kB flash memory of the blue pill.

Step 2: Set up PlatformIO

I assume you know what PlatformIO is. If not, then look it up and start using it instead of the Arduino IDE. It's 1000% better.

Step 2.1: Create a new project

Go to PlatformIO home and create a new project with the settings below. Make sure that the name of the project doesn't contain any special characters or spaces. If it does then dfu-util won't find the project files and USB won't work.

Step 2.2: Edit platformio.ini

As soon as the project is created, there will be a file called platformio.ini in the root directory of the project. This file contains the settings for the project, things like which board to use and build parameters etc. The default platformio.ini config file that PlatformIO generates for the project looks like this:

platform = ststm32
board = genericSTM32F103C8
framework = arduino

Unfortunately this didn't work for me. After digging a lot in various forums, obscure STM32 sites and the PlatformIO docs I ended up with the following platformio.ini config file, which seems to be working:

platform = ststm32
board = bluepill_f103c8
framework = arduino
upload_protocol = dfu
upload_port = /dev/cu*

build_flags = 
    ; enable USB serial

So let's see what's what:

  • platform = ststm32 – this specifies that we're going to use the ST32Duino platform, which is backed by ST Microelectronics so hopefully has good support and community
  • board = bluepill_f103c8 – according to PlatformIO docs, this is the correct board name for the blue pill
  • framework = arduino – yes, we want to use the Arduino framework
  • upload_protocol = dfu – this is the setting that enables USB and that makes PlatformIO use dfu-util to upload the programs to the blue pill
  • upload_port = /dev/cu* – this will scan all serial ports and look for the board. Without this I couldn't make the whole thing work, I always got an error to specify the upload_port
  • build_flags – these flags enable USB serial communication so you can use functions like printing things on the console etc.

Step 3: Test

I tested the whole thing with the classic blinky example but there are several others on the PlatformIO examples page.

Important notes

  1. Here's a great article about what you should know about STM32 when getting started.
  2. Which port does the blue pill use? If you either list the contents of the /dev/ directory in terminal or open the Arduino IDE and open the ports menu, then you'll see that there's a port called /dev/cu.usbmodemXXXXXXX. where XXX... is a random identifier That's the port for the blue pill. If you can't see such a port then the board is not recognised by the system and wouldn't work via USB. This happened to me too and usually I had to flash the bootloader and upload some code in PlatformIO again to make it work.
  3. You can list all DFU devices in the terminal using dfu-util --list. However right after flashing the bootloader and connecting the blue pill via USB, this command returned no devices for me so the system didn't "see" my blue pill baord. To make the devices available through USB I had to upload some code first to the board through PlatformIO. Once I did that, I could list the devices with dfu-util and I could re-upload programs to my board via USB.
  4. H'Alarm! This whole thing feels very-very shaky with a lot of potential points of failure! This is understandable, given the blue pill is not even an official board by ST. For example sometimes after changing the platformio.ini file, the system wouldn't even recognize the board anymore. In these cases I had to re-flash the bootloader and try to re-upload the code (ie. go through the whole procedure again). If I find this setup to be annoyingly unstable then I may just go back to using ST Link to upload and debug.
  5. I haven't uploaded anything else than the blinky example to my blue pill board yet, so I have no idea how this whole thing will work out. I'll keep this post updated with my new findings.