Blue Pill to Black Magic Probe

Turning the Blue Pill development board into a Black Magic Probe

Similar projects worth following
The Black Magic Probe is a ARM Cortex debug adapter featuring a builtin GDB server, providing an easy way to debug embedded controllers.
This project describes how to build a Black Magic Probe with readily available and budget friendly development boards, while also using nix for easier toolchain- , dependency- and build-management.

This project provides step by step for tuning a Blue Pill into a Black Magic Probe with the help of only an USB-UART converter. Additionally we will take a look at some files containing nix expressions for building and flashing the bootloader and firmware.

Black Magic Probe is an excellent project developed and maintained by a great open source community. 1BitSquared and Adafruit sell “the Black Magic Probe”, which comes with features such as level shifters and standard debugger headers. I recommend checking it out, since it makes a great tool in the tool belt of any embedded engineer.

The goal of this project is to provide a more affordable, albeit less feature rich, equivalent tool to engineers working on a tight budget or in a pinch.

  • 1 × Blue Pill STM32F103C8T6 development board
  • 1 × USB UART Adapter e.g. FTDI or CP210x

  • Building Upstream Firmare with Nix

    marble09/03/2022 at 18:12 0 comments

    Thankfully, nixpkgs already contain blackmagic as a package, which also already builds firmware images. So all we need to do to build these is run 

    nix-build '<nixpkgs>' -A blackmagic

    This gives us a result directory, which is a symbolic link to the built package. Since '<nixpkgs>' means the build is based on the system nix channel, chances are that the pre-built package is fetched from a build server.

    This also means that we get the Black Magic version that is pinned in the system channel package description

      firmwareVersion = "v1.7.1-415-gc4869a5";
      src = fetchFromGitHub {
        owner = "blacksphere";
        repo = "blackmagic";
        rev = "c4869a54733ae92099a7316954e34d1ab7b6097c";
        hash = "sha256-0MC1v/5u/txSshxkOI5TErMRRrYCcHly3qbVTAk9Vc0=";
        fetchSubmodules = true;

    If we now wanted to build a different version of Black Magic, we can start by writing a nix-expression that essentially does the exact same thin we were doing in the command line.

    { pkgs ? import <nixpkgs> { }
    , ...
    with pkgs;

    This expression defines a function that takes an attribute set as the parameter and returns the blackmagic package. The only attribute in the parameter is the package repository, which defaults to importing the system package channel. The with pkgs; part is syntax sugar letting nix automatically search in pkgs for names that are not defined in the default environment, like just blackmagic is this case. The alternative would be writing pkgs.blackmagic .

    The problem with this is, that the system channel is not fixed, which means that this expression is dependent on an external state and thus not reproducible.

    The fix for that is importing a pinned version of the nixpkgs repository.

    { pkgs ? import
        (builtins.fetchGit {
          name = "nixos-22.05-2022_08_27";
          url = "";
          ref = "refs/heads/nixos-22.05";
          rev = "f11e12ac6af528c1ba12426ce83cee26f21ceafd";
        { }
    , ...

    The value in rev can be obtained by running

    git ls-remote nixos-22.05

    We now can make changes to the blackmagic by using the overrideAttrs function. The first thing we can do is change the version to the current Black Magic main branch.

    blackmagic.overrideAttrs (oldAttrs: rec {
      version = "unstable-2022-09-01";
      firmwareVersion = "v1.8.0-639-g3e8f296d";
      src = fetchFromGitHub {
        owner = "blacksphere";
        repo = "blackmagic";
        rev = "3e8f296dd59a1edbbd666c534ae4c046cd369d54";
        sha256 = "0vh0359ya3ajslk0h9i3qz37b9wc7ww63yxy2h08ngghnj6xq82i";
        fetchSubmodules = true;

    The sha256 hash is obtained by running nix-buil with a wrong hash first and then using the reported actual hash.

    The first problem during the build is that version.h is missing. This is due to a breaking change in the Black Magic repository which demand that we make changes to the build.

      postPatch = oldAttrs.postPatch + ''
        # Prevent calling out to `git' to generate a version number:
        substituteInPlace src/Makefile \
          --replace '$(shell git describe --always --dirty --tags)' '${firmwareVersion}'

    The next problem is that the common platform is not a proper target, but contains a This confuses the build script. One solution is to copy and fix the build script.

    for platform in platforms/*/platform.h; do
      probe=$(basename "$(dirname "$platform")")
      make_platform "$probe"

    This also requires us to state the usage of the script in our override, which is the exact same code as in the package, but will reference our instead due to file loation. If you wanted more clarity, you could use a different name.

      buildPhase = ''
        runHook preBuild
        ${} ${./}
        runHook postBuild

    When now running nix-build we get the result symlink again, containing the new package contents.

View project log

  • 1
    Install the Toolchain

    Install gcc-arm-embedded, stm32flash and dfu-util.

    Most GNU/Linux distributions provide this as a package. E.g. on distributions with APT like Debian you can run

    sudo apt install gcc-arm-none-eabi stm32flash dfu-util
  • 2
    Download the blackmagic Firmware Repository
    git clone --recursive
    cd blackmagic
  • 3
    Build the Firmware
    make PROBE_HOST=swlink

View all 5 instructions

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates