Include a Git revision in firmware with PlatformIO

Posted on 20 May 2021 in Mechatronics

How to include a Git revision in your firmware at build time with PlatformIO.

Note

This article is a comprehensive write up of two PlatformIO issues the author responded to on GitHub.com in late 2020:

PlatformIO supports the generation of dynamic build flags. This feature allows the creation of build flags by executing a user defined script. The script should output one or more gcc -D options to standard output. For example:

-D SRC_REVISION="b4837070c4b10d37acfbf0283b758d38e739a670"

The -D option allows the defination of a macro. From the gcc(1) man page:

-D name
Predefine name as a macro, with definition 1.
-D name=definition
The contents of definition are tokenized and processed as if they appeared during translation phase three in a #define directive. In particular, the definition will be truncated by embedded newline characters.

Project structure

This example uses a typical PlatformIO project structure:

.
├── src
│   └── main.cpp
├── define-git-revision.py
└── platformio.ini

Configuration file

The script to run is defined by the build_flags option in platformio.ini. Scripts are indicated by prepending them with a !.

[platformio]
description = Include Git revision in firmware with PlatformIO

[env:esp32dev]
board = esp32dev
framework = arduino
monitor_speed = 115200
platform = espressif32
build_flags =
    !python3 define-git-revision.py

Download platformio.ini.

User defined script

The following Python script define-git-revision.py will set two predefined macros:

  1. SRC_REVISION containing the Git revision, for example 97c333a4b3c732523e09742318d7acc52b33dbcc, and
  2. SRC_STATE defining the state of the working tree as either clean or dirty.
#!/usr/bin/env python3
"""Git revision and working tree state"""

import subprocess

REVISION = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip()

if subprocess.check_output(["git", "diff", "--stat"]).strip() == "":
    STATE = "clean"
else:
    STATE = "dirty"


print("-D SRC_REVISION='\"{}\"'".format(REVISION.decode("UTF-8")))
print("-D SRC_STATE='\"{}\"'".format(STATE))

Download define-git-revision.py.

Main.cpp

The predefined macros may be used in src/main.cpp as follows:

#ifndef UNIT_TEST

#include <Arduino.h>

#ifndef SRC_REVISION
#define SRC_REVISION "(revision not defined)"
#endif

#ifndef SRC_STATE
#define SRC_STATE "(state not defined)"
#endif

void setup() {
    Serial.begin(115200);
}

void loop()
{
    Serial.println(SRC_REVISION);
    Serial.println(SRC_STATE);
    Serial.println();

    delay(1000);
}

#endif

Download main.cpp.

Output

Example output on the PlatformIO device monitor:

> Executing task: platformio device monitor <

--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on /dev/ttyUSB0  115200,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
b4837070c4b10d37acfbf0283b758d38e739a670
dirty

b4837070c4b10d37acfbf0283b758d38e739a670
dirty