Browse Source

little change to section V, big refactoring of section VI, todo: section VI-2

master
Sven Reissmann 7 years ago
parent
commit
87ebaa6777
  1. 7
      5-concept.tex
  2. 72
      6-1-build_and_deploy.tex
  3. 41
      6-1-multidevice_build_structure.tex
  4. 44
      6-2-automatic-deployment.tex
  5. 0
      6-2-update_mechanism.tex
  6. 14
      6-implementation.tex

7
5-concept.tex

@ -36,12 +36,7 @@ Device specific code is organized in a sub-folder for each device type.
To build the software, a \textit{Makefile} \cite{make} is used, which provides a simple way for reproducible builds. To build the software, a \textit{Makefile} \cite{make} is used, which provides a simple way for reproducible builds.
Whenever a new build process is started, the build system scans for all device specific folders and calls the original build process for each of them. Whenever a new build process is started, the build system scans for all device specific folders and calls the original build process for each of them.
After the build of the firmware has finished, the build system also creates a file for each device type, containing the build version and cryptographic signatures of the corresponding binary images. After the build of the firmware has finished, the build system also creates a file for each device type, containing the build version and cryptographic signatures of the corresponding binary images.
To avoid interferences between different build environments on developers computers and roll out new versions as quickly as possible, the code has been integrated into a continuous integration (CI) system.
The CI, which is based on \textit{drone} \cite{drone} and provided as part of the hackerspace infrastructure, allows to execute commands on each version published into the \textit{Git} repository.
In the specific case, whenever a new version gets checked-in into the release branch, the \textit{Makefile} is used to build the binary firmware files and publish them to the firmware repository server.
Therefore, a \textit{drone} configuration file as shown in Listing~\ref{lst:drone} has been added to the source code as \texttt{.drone.yml}.
To avoid interferences between different build environments, and to roll out new versions as quickly as possible, the code has been integrated into a continuous integration (CI) system.
\subsection{Device setup and flash layout}\label{flashlayout} \subsection{Device setup and flash layout}\label{flashlayout}

72
6-1-build_and_deploy.tex

@ -0,0 +1,72 @@
\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.

41
6-1-multidevice_build_structure.tex

@ -1,41 +0,0 @@
\subsection{Multi-Device build infrastructure}
\subsubsection{Framework integration}
The framework is build to keep control over the application life-cycle.
It ensures that the device unspecific code is executed at the right time and provides an API for device specific functionality.
The framework specifies a simple interface, 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.
\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}
\subsubsection{Build system}
The \textit{Makefile} is build 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.
Another \textit{Makefile} has been created which scans a project subdirectory and uses each directory in there as container for device specific code.
For each of these directories, the other \textit{Makefile} is called and the subdirectories name is used as \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.

44
6-2-automatic-deployment.tex

@ -1,44 +0,0 @@
\subsection{Automatic deployment and roll-out}
%The source code of the \textit{ESPer} project is published into a \textit{GIT} source code repository which is provided by the hackerspace.
%To avoid interferences between different build environments on developers computers and roll out new versions as early as possible, the code has been integrated into a continuous integration (CI) system.
%The CI, which is based on \textit{drone}\cite{drone} and provided as part of the hackerspace infrastructure, allows to execute commands on each version published into the \textit{GIT} repository.
%Therefore a \textit{drone} configuration file as shown in Listing~\ref{lst:drone} has been added to the source code as \texttt{.drone.yml}.
\begin{sloppypar}
As shown in the configuration Snippet, the build environment includes some special settings.
First, the \texttt{CONFIG=maglab} option lets the build system use \texttt{Configurion.mk.maglab} instead of the default configuration file.
This configuration file is stored inside the repository, too.
To keep the WiFi password secret, it is not written down in the configuration, but must be specified in the environment.
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.
At last, the firmware version is configured to be made out of the first 8 letters of the \textit{GIT} commit hash, which uniquely identifies a version of the source code.
\end{sloppypar}
\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 files and the version files) of all devices are copied to the machine running the home automation controller software into a directory served by a HTTP server.
The used configuration file references this server as source of updates.

0
6-3-update_mechanism.tex → 6-2-update_mechanism.tex

14
6-implementation.tex

@ -1,11 +1,9 @@
\section{Implementation} \section{Implementation}
Implementing \textit{OTA} updates under the given requirements involves three different components, which interact closely.
Implementing \textit{OTA} updates under the given requirements involves multiple components, which interact closely.
The continuous integration system is in charge of building the firmware from source, calculating cryptographic signatures, and publishing the built binary images.
The deployment infrastructure provides resources for downloading the binary firmware images and triggering the update on all devices.
Finally, the implementation of the update mechanism as a part of the firmware running on the embedded device is responsible for downloading and installing the updates.
The first component is the build system which is in charge of building the firmware from source and publishing the built binary images.
Second, the deployment provides infrastructure for downloading the binary firmware images and triggering the update on all devices.
Finally, the implementation of the update mechanism as part of the firmware running on the embedded device which is responsible for downloading and checking the updates and installing them.
\input{6-1-multidevice_build_structure}
\input{6-2-automatic-deployment}
\input{6-3-update_mechanism}
\input{6-1-build_and_deploy}
\input{6-2-update_mechanism}
Loading…
Cancel
Save