IOT


iPad

So I made an app for a camper-van… It has seen daily use for close to a year and honestly I’m quite impressed. It’s based on MQTT protocol, which provides good ability to handle power and network outages and includes state persistence and restoration. The stack is quite lean for the amount of features and has been able to grow well while maintaining reasonable complexity.
After some experimentation with existing solutions like OpenHAB, Home Assistant, ESPHome and even Apple Home via Homebridge I came to the irresponsible decision to try and do it myself. While these open source projects are undeniably easier to get started with, I was finding that the added layer of abstraction and use of DSLs made debugging rather tricky, beyond certain scale.

Requirements

Features:

Technical:

Architecture

The core of the system is a single board computer running an open source broker Mosquitto. The SBC also provides a dedicated WiFi access point with static IP allocation, to allow over the air updates. The main functions are split between few dedicated clients, to provide some separation of concerns. The Internet connectivity if provided by Mikrotik’s LtAP mini. With the recent addition of MQTT support in RouterOS, I’m additionally able to collect GPS, and LTE information by writing few simple scripts

flowchart TB
    subgraph Raspberry Pi
        direction TB
        B{Mosquitto</br>Broker}
        C(Database</br>Client)
        D[(SQLite)]
        E(Automations</br>Client)
        F(Notifications</br>Client)
    
        B -->|Logs| C
        C -->|Graph Data| B
        C === D
        B <--> E
        B -->|Alarms</br>Plausability checks| F
    end
    X("📱 iOS App") <-..->|TX Control</br>RX State| B
    Y(ESP32 Based Clients:</br>Solar, GPS/LTE, Cameras, Lights, Ventilation etc.) <-..->|TX State</br>RX Control| B

S.S.0.T.

Since a lot is happening asynchronously, it’s important to have a single source of truth. What it means in practice is that clients stateless, and does not retain readings for later publishing. They simply receive commands, update external devices, and report the state to the broker. Clients also fetch the last retained state from broker on startup.
This, combined with last will feature ensures that broker has up to date view of the system.

Data Flow

Here’s a simple toy example to illustrate dataflow.
Rounded boxes represent clients while squared ones contain topics:

flowchart LR
	A("🎚️ Dimmer")
	B[room/light</br><i>retained topic</i>]
	C("💡 Light")
	A <--> B
	B --> C

Naive Approach - Here the dimmer has two way binding to the room/light topic and the Light client is simply consuming this state. While this setup mostly works, there are many cases where the state of the light could get out of sync. It could be dropped message or multiple instances of dimmer controlling the same light.

flowchart LR
	D("🎚️ Dimmer")
	E[set/room/light</br><i>not retained</i>]
	F("💡 Light")
	G[room/light</br><i>retained topic</i>]
	D --> E
	E --> F
	F --> G
	G --> D

Unidirectional Data Flow - MQTT Topics work on publish/subscribe principle and this makes them a two way street. To achieve unidirectional data flow my approach was separating the command topics (not retained) from those containing state (retained). Any client might only read or write to a certain topic. Now the state of the light originates at it’s physical location - the light client. This also prevents potential feedback loops.

Clients

iPad

Most clients were implemented using ESP development boards. The one above is responsible for managing the battery. It’s built on a standard 100x100mm prototype PCB using common brake-out boards. The client has a serial connection to the solar charge controller using Victron’s VE.Direct for reporting solar readings. It also measures battery current using a shunt. The state of charge is retained in the broker, for preservation between reboots.
Since I’m using LiFePO4 batteries that would get damaged, if charged below freezing - the client also operates a heating pad using PWM based on temperature readings.

Conclusion

While I could go into way more detail, hopefully this article provides a decent high level overview of one of many ways to approach building a system like this. Overall the process has been a remarkable learning experience and my go-to justification for learning new things. This project involved broad range or technologies and tools such as 3d printing, PBC design/assembly, Rust, C++, Time Series Databases, SwiftUI, etc.
While it certainly has been a significant time investment, if feel that system like this is generic enough with sufficiently wide range of applications to justify time spent. (Or at least that’s what I tell myself)

Comic XKCD Comic