You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
4.8 KiB

\subsection{Build infrastructure and automatic deployment}
The CI system, which is based on \textit{drone} \cite{drone} allows to execute commands, whenever a new version is published into the \textit{Git} repository.
A corresponding \textit{drone} configuration file has been added to the source code as \texttt{.drone.yml} (Listing~\ref{lst:drone}).
Within this configuration file, settings relevant to the build process are provided to the build environment.
First, the \texttt{CONFIG=maglab} option lets the build system use an additional configuration file (\texttt{Configurion.mk.maglab}), which is stored inside the framework repository and provides information, such as the WiFi SSID.
To keep the WiFi password secret, it is not written down in the configuration file.
Instead, to include secrets into a build process while allowing to keep the configuration public, \textit{drone} allows to encrypt these with a repository specific key.
Using this method, the password is stored as \texttt{.drone.sec} file inside the repository from where it is injected into the build environment.
Also noticeable in Listing~\ref{lst:drone} is the firmware version, which is configured to be the first 8 letters of the \textit{Git} commit hash uniquely identifying a version of the source code.
\begin{lstlisting}[language=,
caption={The \textit{drone} configuration for the \textit{ESPer} project.},
label=lst:drone, basicstyle=\ttfamily\scriptsize]
build:
image: maglab/sming
environment:
- CONFIG=maglab
- WIFI_PWD=$$WIFI_PWD
- VERSION=$${COMMIT:0:8}
commands:
- make clean
- make
publish:
sftp:
host: eddie.maglab.space
username: esper
files:
- dist/*
destination_path: './'
when:
branch: master
\end{lstlisting}
For deployment, only the master branch is considered.
After a successful build, all distribution files (the binary firmware and meta-data files) of all device types are copied to the repository server, where they are served by a HTTP server.
The configuration file (\texttt{Configurion.mk.maglab}) references this repository server as the source for updates.
Support for multiple devices of different type is implemented in both, the \textit{ESPer} framework itself and the build system.
The framework keeps control over the application life-cycle.
It ensures that device unspecific code is executed at the right time and provides an API for device specific functionality.
For this, a simple interface is specified by the framework, which must be implemented by each device.
A single function \texttt{Device* getDevice()} must be defined exactly once in each device specific folder.
To implement this interface, a static instance of \texttt{Device} is created and returned.
Each \texttt{Device} is populated with device specific \texttt{Feature} instances.
While the \texttt{Feature}-API leverages common runtime polymorphism to share functionality between features, the initial \texttt{Device} creation uses compile-time polymorphism, which reduces the need for memory management and increases performance by avoiding virtual function tables.
Listing~\ref{lst:create_device_socket} shows the complete device specific code used for a simple power socket, which is mainly confined to the device type and its capabilities (i.e., the number of GPIO pins).
\begin{lstlisting}[caption={Device specific code for a socket driver.},
label=lst:create_device_socket, basicstyle=\ttfamily\scriptsize]
#include "Device.h"
#include "features/Socket.h"
Device device:
constexpr const char NAME[] = "socket";
constexpr const uint16_t GPIO = 12;
OnOffFeature<NAME, GPIO, false, 1> socket(&device);
Device* getDevice() {
return &device;
}
\end{lstlisting}
The actual compilation of the source code is mainly controlled using two \textit{Makefiles}.
The main \textit{Makefile} is built to accept a parameter for device type identifiers called \texttt{DEVICE}, and to create its whole output inside a subdirectory specific to the device type.
An additional \textit{Makefile} scans a project subdirectory and uses each directory in there as a container for device specific code.
For each of these directories, the main \textit{Makefile} is called and the subdirectories name is used as the value of the \texttt{DEVICE} parameter.
By splitting the build and recompiling the framework each time before intermixing it with the device specific code, the device type identifier can be used inside the shared framework code.
While building a devices firmware, the meta-information file used during updates is also created and stored beside the binary firmware image.
For development, each device can be build separately by using the device type identifier as \textit{Makefile} target.
In addition, the prefix \texttt{/flash} can be used to flash a specific firmware.