Version control for scientific microcontrollers with git hooks
In our lab we are using microcontrollers (mainly arduinos) for a variety of tasks. These tasks can be as simple as forwarding a serial command to BNC to trigger a stimulation device, or waiting for button presses to measure reaction time. Often these tasks depend on settings (e.g. a threshold for analog-read, pulse-width in microseconds, or which BNC out to trigger).
It is impractical to hard-code such settings into the firmware. By using a protocol for the serial interface to the arduino, one can change settings at runtime. Furthermore, this allows to request the current settings, and receive them from the device..
Especially in a scientific environment, where we do rapid development and source-code changes often, one can easily lose track of the current version installed. Being able to requesting information about the current version of firmware can therefore be quite practical.
Semantic versioning and compile-time
Defining a variable using semantic versioning (e.g. const String versionInfo = "'v0.0.1 - EdgyExample'";
)
or the __TIME__
and __DATE___
macros allow us do aggregate information
about the firmware and construct a json-string. Then, whenever we send the
enquiry to the device (we like to use ASCII 0x05), it answers with
this string (see the arduino-code). From the PC side,
we can then request the current version, and parse the received
byte stream (see python-code). This approach is probably
already feasible and sufficient for most use cases, especially if we keep a linear
chain of commits.
There are some gaps though. First, there is no clear link between the version defined in in the string and the version checked out. Git tags have to be kept manually in line with the string. Additionally, compile-time and date only indicate the time of the last compile, not the actual version which has been checked out.
Hooking the Hash
That means that if we roll back to an earlier version, __TIME__
and __DATE__
are not as informative anymore. But if we keep our source-code under version
control, say git, why not just use the information from there? Is there a way to
put the hash of the current commit into the source-code before it is compiled
and downloaded?
Obviously, the json-string could be expanded, and we can included a field for the hash.
This could be achieved by creating a file, say hash.h which defines const char * hash =
"9a99cd8...";
and including it with #include hash.h
.
But we would have to create and fill the hash.h
file.
Ideally this should be be done automatically. Git hooks to the rescue!
The first step is writing a script that creates such
a file. Git hooks reside under .git/hooks
. The next step is therefore calling this script from .git/hooks/post-commit
and .git/hooks/post-checkout
. This can be done by just replacing those files with the script. But consider that hooks are themselves not under version control. Because we develop on multiple computers, this can be impractical. Therefore, i like to create a folder hooks
in the root directory of the repository, which contains the shell script called create-hash
that actually creates the hash, two shell-scripts called post-commit
and post-checkout
which are the prototypes for the hooks, and an install-script that just copies the hook-scripts to
.git/hooks
. This allows me to keep the hooks under version control, and only requires to install once on each computer.
Conclusion
This is not bullet-proof though, as the hooks are only executed after you commit or checkout. That means if you hack away on your code, compile and download it, without committing it first, it still uses the old hash. Which makes total sense - as the new one was not created yet. Additionally, i like to .gitignore
the hash-file, as every commit and checkout alters the files.
Code on the arduino
|
|
Python interface
|
|
Bash to create hash-file
can be used for post-commit
and post-checkout
in .git/hooks
|
|