Skip to content

Commit 6676428

Browse files
committed
Make installer cross-platform (Linux/macOS)
Add full Linux support and an interactive --hotspot mode to the one-line base-station installer; keep macOS support. The installer now bootstraps git and Docker on Debian/Ubuntu/Raspberry Pi OS, can enable a NetworkManager-based Wi‑Fi AP (wfr-hotspot) and installs a systemd unit for it. CI workflow updated to validate compose from the repo root and run a linux e2e job that syntax-checks the script, validates compose, runs the installer, asserts core containers, checks service ports, verifies idempotency, and ensures non-interactive hotspot behaviour. Documentation (README and MACBOOK_DEPLOY.md) and WHICH_ONE.md updated with Linux/RPi instructions and hotspot notes. Fix .env macbook DBC_HOST_PATH to be repo-root relative and add wfr-hotspot.service file.
1 parent 563b686 commit 6676428

7 files changed

Lines changed: 522 additions & 113 deletions

File tree

.github/workflows/macbook-installer-ci.yml

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
- 'universal-telemetry-software/deploy/.env.macbook'
1010
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
1111
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
12+
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
1213
- '.github/workflows/macbook-installer-ci.yml'
1314
pull_request:
1415
branches: [ main, deploy ]
@@ -18,6 +19,7 @@ on:
1819
- 'universal-telemetry-software/deploy/.env.macbook'
1920
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
2021
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
22+
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
2123
- '.github/workflows/macbook-installer-ci.yml'
2224
workflow_dispatch:
2325

@@ -65,9 +67,13 @@ jobs:
6567
6668
- name: Validate compose file
6769
run: |
68-
cd universal-telemetry-software/deploy
6970
if command -v docker &>/dev/null && docker info &>/dev/null; then
70-
docker compose -f docker-compose.macbook-base.yml config --quiet
71+
# Run from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
72+
docker compose \
73+
--project-directory "${{ github.workspace }}" \
74+
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
75+
--env-file universal-telemetry-software/deploy/.env.macbook \
76+
config --quiet
7177
echo "Compose config valid"
7278
else
7379
echo "Skipping compose validation - Docker not running"
@@ -82,20 +88,89 @@ jobs:
8288
echo "Script correctly detected Docker state"
8389
fi
8490
85-
linux-script-test:
91+
linux-e2e:
8692
runs-on: ubuntu-latest
93+
# Docker Engine and git are pre-installed on ubuntu-latest runners.
94+
# install_linux_deps() detects them and skips the apt-get paths.
95+
# Images are pulled from ghcr.io (public) and stack runs in-band.
96+
timeout-minutes: 20
8797
steps:
8898
- uses: actions/checkout@v4
8999

90-
- name: Verify script detects non-macOS
100+
- name: Syntax check
101+
run: bash -n universal-telemetry-software/deploy/install.sh
102+
103+
- name: --help exits 0
104+
run: bash universal-telemetry-software/deploy/install.sh --help
105+
106+
- name: Compose config validates against PR branch files
107+
# Runs from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
91108
run: |
92-
chmod +x universal-telemetry-software/deploy/install.sh
93-
output=$(bash universal-telemetry-software/deploy/install.sh 2>&1) || true
94-
echo "$output" | grep -q "requires macOS" && echo "Correctly rejects Linux" || echo "Unexpected output"
109+
docker compose \
110+
--project-directory "${{ github.workspace }}" \
111+
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
112+
--env-file universal-telemetry-software/deploy/.env.macbook \
113+
config --quiet
114+
echo "Compose config valid"
115+
116+
- name: Python compose structural check
117+
run: python3 universal-telemetry-software/deploy/ci-validate-compose.py
118+
119+
- name: Run full installer end-to-end
120+
# Clones ~/wfr-base-station from the main branch, pulls images, starts stack.
121+
# Uses sudo docker compose (as install.sh does on Linux) — fine on GHA runners.
122+
run: bash universal-telemetry-software/deploy/install.sh
123+
124+
- name: Assert all core containers are running
125+
run: |
126+
docker ps
127+
docker ps --format '{{.Names}}' | grep -q daq-telemetry || { echo "daq-telemetry not running"; exit 1; }
128+
docker ps --format '{{.Names}}' | grep -q daq-redis || { echo "daq-redis not running"; exit 1; }
129+
docker ps --format '{{.Names}}' | grep -q daq-pecan || { echo "daq-pecan not running"; exit 1; }
130+
echo "All 3 core containers are up"
95131
96-
- name: Validate compose file structure
132+
- name: Wait for Pecan on port 3000
97133
run: |
98-
python3 universal-telemetry-software/deploy/ci-validate-compose.py
134+
echo "Polling http://localhost:3000 ..."
135+
for i in $(seq 1 30); do
136+
if curl -sf http://localhost:3000 -o /dev/null; then
137+
echo "Pecan is serving on port 3000"
138+
exit 0
139+
fi
140+
echo " attempt $i/30 — retrying in 3 s"
141+
sleep 3
142+
done
143+
echo "Pecan did not become ready within 90 s"
144+
docker logs daq-pecan || true
145+
exit 1
146+
147+
- name: Wait for telemetry status page on port 8080
148+
run: |
149+
echo "Polling http://localhost:8080 ..."
150+
for i in $(seq 1 30); do
151+
if curl -sf http://localhost:8080 -o /dev/null; then
152+
echo "Status page is serving on port 8080"
153+
exit 0
154+
fi
155+
echo " attempt $i/30 — retrying in 3 s"
156+
sleep 3
157+
done
158+
echo "Telemetry status page did not become ready within 90 s"
159+
docker logs daq-telemetry || true
160+
exit 1
161+
162+
- name: Idempotency — re-run installer over existing clone
163+
run: bash universal-telemetry-software/deploy/install.sh
164+
165+
- name: --hotspot skips gracefully in non-interactive CI
166+
# stdin from /dev/null makes [[ ! -t 0 ]] true → prints skip message, returns 1.
167+
# The outer script still reaches print_success and exits 0.
168+
run: |
169+
output=$(bash universal-telemetry-software/deploy/install.sh --hotspot </dev/null 2>&1) || true
170+
echo "$output"
171+
echo "$output" | grep -q "Not an interactive terminal" \
172+
|| { echo "Expected hotspot-skip message was not found in output"; exit 1; }
173+
echo "Non-interactive hotspot skip verified"
99174
100175
url-accessibility:
101176
runs-on: ubuntu-latest

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66

77
Comprehensive telemetry and data acquisition system for real-time monitoring of formula racing vehicle performance. This system captures CAN bus data from the vehicle, transmits it to a base station, and visualizes it through an interactive web dashboard.
88

9-
> 🚀 **Quick Start — macOS Base Station**
9+
> 🚀 **Quick Start — Base Station (macOS or Linux)**
1010
> ```bash
1111
> curl -fsSL https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh | bash
1212
> ```
13+
> **Linux with Wi-Fi hotspot for pit devices (RPi at track):**
14+
> ```bash
15+
> curl -fsSL https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh | bash -s -- --hotspot
16+
> ```
1317
> [Full setup guide →](./universal-telemetry-software/deploy/MACBOOK_DEPLOY.md)
1418
1519
![pecan dashboard](pecan/docs-assets/PECAN-Dashboard.jpg)

universal-telemetry-software/deploy/.env.macbook

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
REMOTE_IP=10.71.1.10
1010
ENABLE_TIMESCALE_LOGGING=auto
1111
TIMESCALE_TABLE=WFR26test
12-
DBC_HOST_PATH=./example.dbc
12+
DBC_HOST_PATH=./universal-telemetry-software/deploy/example.dbc
1313
RELAY_TOKEN=
1414
CLOUDFLARED_CONFIG=./cloudflared/config.yml
1515
CLOUDFLARED_CREDENTIALS=./cloudflared/credentials.json

universal-telemetry-software/deploy/MACBOOK_DEPLOY.md

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
1-
# MacBook Base Station Setup
1+
# Base Station Setup (macOS / Linux)
22

3-
Local telemetry stack for a MacBook base station. The default startup is minimal:
3+
Local telemetry stack for a MacBook or Linux base station (including Raspberry Pi 4B). The default startup is minimal:
44
telemetry receiver, Redis, and the Pecan dashboard.
55

66
TimescaleDB writes, local media services, and the Cloudflare tunnel are opt-in
77
Docker Compose profiles.
88

99
## Prerequisites
1010

11-
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed and running
12-
- Car RPi on the same network, or use simulation mode
11+
**macOS:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed and running.
12+
13+
**Linux / Raspberry Pi 4B:** Nothing pre-installed — the installer bootstraps git and Docker Engine automatically. Requires Debian/Ubuntu or Raspberry Pi OS (apt-get).
14+
15+
Car RPi on the same network, or use simulation mode.
1316

1417
---
1518

1619
## One-Command Install (Recommended)
1720

18-
**Requires macOS + Docker Desktop (one-time install from docker.com).**
19-
2021
```bash
2122
curl -fsSL https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh | bash
2223
```
2324

25+
**Linux with Wi-Fi hotspot for pit devices** (RPi 4B at track — ethernet to car radio, Wi-Fi AP for laptops/tablets):
26+
27+
```bash
28+
curl -fsSL https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh | bash -s -- --hotspot
29+
```
30+
2431
That's it. The script:
25-
1. Verifies Docker Desktop is running
26-
2. Clones the repo to `~/wfr-base-station/` (or updates if already present)
27-
3. Pulls the latest images
28-
4. Starts the stack
32+
1. **macOS:** Verifies Docker Desktop is running
33+
2. **Linux:** Installs git and Docker Engine if missing, enables Docker on boot
34+
3. Clones the repo to `~/wfr-base-station/` (or updates if already present)
35+
4. Pulls the latest images
36+
5. Starts the stack (auto-restarts on reboot via `restart: unless-stopped`)
37+
6. **`--hotspot` (Linux):** Prompts before enabling — switches `wlan0` to AP mode (`WFR-Base`); pit crew opens Pecan at `http://10.42.0.1:3000`. **Warning:** disconnects Wi-Fi SSH/internet if no other adapter is present.
2938

3039
Subsequent updates: run the same command again.
3140

41+
**Pecan runs in the browser on pit devices** — the base station only serves static files and forwards telemetry. Open Pecan from a laptop or tablet on the LAN, not in Chromium on the Pi itself.
42+
3243
---
3344

3445
## Manual Setup
@@ -71,7 +82,7 @@ All configuration is done through `deploy/.env.macbook`. Key variables:
7182
| `REMOTE_IP` | `10.71.1.10` | Car RPi IP address |
7283
| `ENABLE_TIMESCALE_LOGGING` | `auto` | Auto-start writer when the TimescaleDB profile is running |
7384
| `TIMESCALE_TABLE` | `WFR26test` | Season table name (no `_base` suffix — added automatically) |
74-
| `DBC_HOST_PATH` | `./example.dbc` | Path to DBC file |
85+
| `DBC_HOST_PATH` | `./universal-telemetry-software/deploy/example.dbc` | Path to DBC file (relative to repo root) |
7586
| `RELAY_TOKEN` | blank | Optional relay token |
7687
| `CLOUDFLARED_CONFIG` | `./cloudflared/config.yml` | Private tunnel config path for `--profile tunnel` |
7788
| `CLOUDFLARED_CREDENTIALS` | `./cloudflared/credentials.json` | Private tunnel credentials path for `--profile tunnel` |
@@ -128,6 +139,43 @@ docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.
128139

129140
**TimescaleDB not writing:** Start with `docker compose --profile timescale ... up -d`. In `auto` mode, telemetry probes the configured database at boot and starts the writer only when it is reachable. Verify the `WFR26test_base` table exists: `psql postgresql://wfr:wfr_password@localhost:5432/wfr -c "\dt"`
130141

142+
## Network setup
143+
144+
### macOS
145+
146+
Set IP `10.71.1.20` on the USB-C ethernet adapter connected to the car radio base.
147+
148+
Via GUI: System Settings → Network → USB-C Ethernet → Configure IPv4 → Manually → IP: 10.71.1.20 / Subnet: 255.255.255.0
149+
150+
Via CLI:
151+
```bash
152+
networksetup -listallhardwareports
153+
sudo networksetup -setmanual '<interface>' 10.71.1.20 255.255.255.0
154+
ping -c 3 10.71.1.10
155+
```
156+
157+
### Linux / Raspberry Pi
158+
159+
Set IP `10.71.1.20` on the ethernet interface connected to the car radio (USB-ethernet or onboard).
160+
161+
```bash
162+
ip -br link # find interface name
163+
164+
# NetworkManager (RPi OS / Ubuntu)
165+
sudo nmcli con mod '<connection-name>' ipv4.method manual \
166+
ipv4.addresses 10.71.1.20/24 ipv4.gateway ''
167+
sudo nmcli con up '<connection-name>'
168+
169+
# Or temporary (resets on reboot)
170+
sudo ip addr add 10.71.1.20/24 dev eth0
171+
172+
ping -c 3 10.71.1.10
173+
```
174+
175+
With `--hotspot`, the installer **asks for confirmation** before switching `wlan0` to AP mode (`WFR-Base` / `wfr-racing`). This disconnects any Wi-Fi client connection (including SSH over Wi-Fi) and removes internet on Wi-Fi unless another adapter (e.g. ethernet) is connected. Pit devices connect and open `http://10.42.0.1:3000`. The ethernet car link at `10.71.1.20` is independent.
176+
177+
---
178+
131179
## Windows / WSL2 — Limited Support
132180

133181
The base station stack is designed for macOS and native Linux. Windows support is limited:

universal-telemetry-software/deploy/WHICH_ONE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ Use these first:
1919
| Target | File / doc | Notes |
2020
|--------|------------|-------|
2121
| Car RPi | `CAR_DEPLOY.md` + `car-telemetry.service` | Native systemd, no Docker/Redis on the car |
22-
| MacBook base | `docker-compose.macbook-base.yml` | Default LAN stack with optional TimescaleDB, media, and tunnel profiles |
23-
| RPi base | `docker-compose.rpi-base.yml` | Lightweight base station, no local TimescaleDB persistence |
22+
| MacBook / Linux / RPi 4B base | `docker-compose.macbook-base.yml` + `install.sh` | One-command curl install; optional `--profile timescale`, `--hotspot` on Linux |
23+
| RPi base (manual, deprecating) | `docker-compose.rpi-base.yml` | Lightweight ephemeral base, host networking; no one-click installer |
2424

2525
---
2626

0 commit comments

Comments
 (0)