Supporting services running on the Raspberry Pi.
This repository contains four systemd services that supplement the main deskradar application. They handle LCD display output, web-based configuration, and boot-time network setup. The code is deployed to /opt/deskradar-pi-services/ on the Pi.
| Service | Port | Role |
|---|---|---|
deskradar-lcd | 8010 | HTTP API → I2C LCD display |
deskradar-configurator | 5000 | Web UI for settings and WiFi |
deskradar-bootstrap | — | One-shot: resolves MatrixPortal IP at boot |
deskradar | — | Managed by this repo's service file; code lives in /opt/deskradar/ |
A FastAPI server at port 8010 that accepts text messages and renders them to a 16×2 character LCD connected via I2C.
POST /display
Content-Type: application/json
{
"line1": "BAW123 /",
"line2": ""
}
Both fields are optional. Each line is truncated to 16 characters. Consecutive identical messages are silently skipped (no redundant I2C writes).
Response (new message):
{"status": "ok", "displayed": {"line1": "BAW123 /", "line2": ""}}
Response (no change):
{"status": "ok", "skipped": true, "reason": "no change"}
| Device | I2C Bus | Address | Role |
|---|---|---|---|
| LCD1602 | 1 | 0x7c | 16×2 character LCD display |
| SN3193 | 1 | 0x6B | RGB LED backlight controller |
The LCD library (lcd/lib/LCD1602.py) operates the display in 4-bit mode, 2 lines, 5×8 font. It supports custom characters in CGRAM (used for a backslash glyph). The SN3193 backlight controller supports per-channel PWM brightness and breathing mode.
lcd/app/main.py — FastAPI applicationlcd/lib/LCD1602.py — LCD1602 and SN3193 I2C driversA Flask application at port 5000 that provides a browser-based UI for changing DeskRadar settings and switching WiFi networks. Reachable at http://deskradar.local:5000 when on the same network.
| Method | Path | Purpose |
|---|---|---|
| GET | / | Home page. Shows a form to join a WiFi network (enter SSID + password). |
| POST | /switch-to-lan | Connects to the specified WiFi network. On failure, restores the AP and reboots. |
| GET | /config | Config editor. Reads /etc/deskradar/config.json and renders a form. |
| POST | /config | Saves updated config values and restarts the main deskradar service. |
The form exposes all user-facing config fields. Three fields are intentionally hidden from the UI (GET_URL, LCD_SEND_URL, MATRIX_HTTP_URL) as they are managed by the boot process or rarely need manual changes.
The config form includes a browser geolocation button to automatically populate LAT and LON, and a color picker for CLOSEST_COLOUR (stored as an RGB dict internally).
nmcli device wifi connectwifi.txt on the CIRCUITPY volume at /media/deskradar/ — so the MatrixPortal S3 also joins the new network on its next bootdeskradarAP access point and rebootsCIRCUITPY, CIRCUITPY1, etc. The configurator searches /media/deskradar/ for any matching mount.Saving config does not write files directly — the configurator calls update_config.py as a subprocess, passing new values as JSON on stdin. That subprocess writes /etc/deskradar/config.json and then runs sudo systemctl restart deskradar. This separation allows the subprocess to run with elevated privileges without the configurator needing root access.
configurator/app.py — Flask routes and form handlingconfigurator/update_config.py — Config write + service restart subprocessconfigurator/templates/base.html — Shared layoutconfigurator/templates/home.html — WiFi formconfigurator/templates/config.html — Config editor formconfigurator/setup_mdns.sh — Initial mDNS setup (run once)configurator/config.json — Default configuration valuesA one-shot Python script that runs once per boot. Its job is to locate the MatrixPortal S3 on the network and write its IP address into the config file before the main app starts.
deskradar-portal.local via mDNS (socket lookup), retrying every 2 seconds for up to 30 seconds totalMATRIX_HTTP_URL = http://<ip>:80 to /etc/deskradar/config.jsonSet the environment variable DESKRADAR_BYPASS_BOOT=true to skip mDNS resolution (useful for development or when the MatrixPortal is unreachable).
scripts/boot_check.pyRun once during initial Pi setup:
bash scripts/setup_ap.sh
Creates a NetworkManager connection profile:
| Setting | Value |
|---|---|
| Connection name | deskradarAP |
| SSID | deskradar |
| Password | deskradaradsb |
| Pi IP address | 10.42.0.50/24 |
| Mode | AP (shared IPv4 — acts as DHCP server) |
| Priority | 999 (lowest priority; LAN connections take precedence) |
The script is idempotent — it exits cleanly if the profile already exists.
Run once during initial Pi setup to make the Pi reachable as deskradar.local:
bash configurator/setup_mdns.sh
This script:
avahi-daemondeskradar/etc/sudoers.d/deskradar-nmcli allowing the service user (default: pi) to run nmcli without a passwordcd /opt/deskradar-pi-services pip install -r requirements.txt bash install_services.sh
install_services.sh copies service files from services/ to /etc/systemd/system/ and enables all four services to start on boot.
| Path | Contents |
|---|---|
/opt/deskradar-pi-services/ | This repository |
/opt/deskradar/ | Main deskradar application |
/etc/deskradar/config.json | Runtime configuration file |
/etc/systemd/system/deskradar*.service | Service unit files |
| Package | Used by |
|---|---|
| flask | Configurator web UI |
| waitress | Configurator WSGI server |
| python-dotenv | Configurator env vars (.env → AP_NAME) |
| requests | Boot check (LCD status posts) |
| fastapi | LCD service API |
| uvicorn | LCD service ASGI server |
| smbus2 | I2C communication (pure Python) |
| smbus | I2C alternative (hardware-native) |