Skip Navigation

Architecture of CarPi

In this post I would like to outline the architecture of CarPi.

Skip table of contents

Requirements

Before we go into details, let’s first outline the requirements to CarPi. CarPi shall

Hardware

CarPi went through quite a few iterations. While I will highlight some key changes that came along over the years this is the current setup:

Software

CarPi is built on the best-supported OS for the hardware, Raspberry Pi OS, a derivate of Debian. That means: Everything that runs on (ARM) Linux will run here.

In terms of software development, most of the components are built using Python. Python is fairly easy to build software on the Raspberry Pi which performs decently. However some components will be written in TypeScript running on Node.js. More on that later.

Since I am also running my personal Nextcloud server, I can leverage an already existing server software called PhoneTrack. PhoneTrack is intended to track smartphones or other devices with a mobile internet connection and an app that periodically sends the current coordinates back to the server, which is exactly what I want. It also keeps track of past locations I’ve been to so I can create a movement profile. And since all this data is stored on my personal server, I don’t have to worry about a third-party (ab)using this information.

Architecture

CarPi uses what’s called a “Micro service architecture” meaning there isn’t a single CarPi application that does everything but rather smaller individual applications that do one thing specifically. This makes the whole system more robust but requires some form of inter-service communication. For this I have chosen the memory database Redis since it supports a simple PubSub system and persistency is not required so it will not tax the SD card with unnecessary write operations. With Redis, all individual components can publish values in a fire-and-forget manner and other components can listen for (subscribe to) certain values they are interested in. This is called the “Redis Data Bus” (inspired by the CAN Bus). We’ll be talking about the protocol and other technical details later.

Speaking of components: Every component should start automatically once the system is powered on. It should also automatically recover if it ever fails. Therefore all components are registered as daemons, or services, using Debian’s default init system systemd. Any service will come with a service file and a configuration file making it easy to start, restart or stop single components.

One of the requirements states that the collected data should be accessible via a smartphone. Since the used LTE dongle supports creating a WiFi hotspot, I can simply assign a static IP to the Pi and connect my phone to the same surfstick via said WiFi hotspot. To expose the data, I’m going to use a simple webservice written in TypeScript / JavaScript based on Express and a user interface written in Angular as a Single Page Application. This should make the app fairly easy to run on the Pi in terms of resources since the webservice only needs to provide data collected via the Redis Data Bus and the UI can do the heavy lifting on the phone itself.

Libraries

Because quite a few components will be written over and over again, these will be segmented out into their own packages and imported as libraries. The Python package manager pip allows pulling from Git repositories, making this relatively painless to develop. Plus by creating automated pipelines these packages can be exported as packages and uploaded to pip so they can just be installed.

To make sure that components don’t influence each other in terms of dependencies, each service will run in its own virtual environment.

Final Thoughts

This project has been in the making for quite some time and several prototypes have been built with different approaches. However the above described one has proves the most stable and most reliable out of all of the tested methods.

In the next post I will start talking about the more technical details of inter-service communication and how Redis’ PubSub features can be leveraged from all components.