4. SYSTEM ARCHITECTURE β€” THREE TIERS

TIER 0 β€” ESP32 CONTROLLER (always-on, ~0.5-1W) β€” rev 3.6
└── Waveshare ESP32-S3-POE-ETH-8DI-8DO: 8 digital OUTPUTS (trigger external Bosch relay panel)
    + 8 DI (ignition/door/reverse wires) + LVD/thermal + BLE proximity + WOL; wired Ethernet.
    NO onboard relays (swappable Bosch panel does the switching). WROOM-32 retired. (start with 1 board)

TIER 1 β€” RASPBERRY PI 3 B+ (always-on, ~3-5W) β€” OWNED
β”œβ”€β”€ Mosquitto MQTT broker (all devices connect here)
β”œβ”€β”€ Wake coordinator: phone proximity + door events β†’ WOL β†’ OptiPlex
β”œβ”€β”€ ffmpeg recording of the **Wolfbox dashcam only** (its own 5GHz hotspot) β€” NOT the perimeter cams
β”‚      (rev 3.3: perimeter cams are recorded by the OptiPlex while awake β€” see Β§7.2. A Pi 3 B+ (1GB RAM,
β”‚       USB-2-attached 100Mbit NIC) should not also carry multi-stream perimeter recording: it is the
β”‚       wake-critical broker, and that path must not be starved. Keep its camera duty to the one Wolfbox feed.)
β”œβ”€β”€ ESPresense phone proximity detection
β”œβ”€β”€ Tailscale client
└── NO audio, NO mic, NO Porcupine β€” voice is entirely OptiPlex's domain

TIER 1 β€” DELL OPTIPLEX 5080 MICRO (in truck 24/7, hibernates when parked) β€” THE BOSS
β”œβ”€β”€ i7-10700T CPU-only (no discrete GPU), 32GB RAM
β”œβ”€β”€ Ollama (Qwen3 4B), Porcupine (wake word "Hey JARVIS"), whisper.cpp (STT), TTS (Piper/Kokoro)
β”œβ”€β”€ PipeWire: S24 Ultra BT A2DP β†’ OptiPlex β†’ AUX β†’ Uconnect; auto-ducts music on TTS
β”œβ”€β”€ reSpeaker XVF3800 USB mic array β†’ Porcupine β†’ whisper.cpp β†’ Qwen3 β†’ TTS β†’ Uconnect speakers
β”œβ”€β”€ SH-C31G (CAN via USB), python-can
β”œβ”€β”€ Cameras (Phase 1): records + restreams while awake (no parked 24/7 record)
β”œβ”€β”€ Local-vs-Fireworks router: easy queries local, heavy reasoning β†’ Fireworks API
└── Wakes via: door/proximity β†’ Pi 3 B+ MQTT β†’ WOL

[ TIER 1.5 β€” Pi 5 + Hailo-8L β€” ~1 YEAR OUT: becomes always-on camera appliance ]

4.1 Network (rev 3.4 β€” Cudy owned, likely the single box)

The Cudy LT18 runs OpenWrt, has WiFi 6, and OpenWrt can run Tailscale + subnet routing β€” so the Cudy probably does all three jobs (LTE internet + LAN router/WiFi + Tailscale), and the GL.iNet may not be needed at all.

Cudy LT18 (OWNED β€” Keepgo SIM, OpenWrt, WiFi 6, traffic-locked LTE,
           Tailscale SUBNET ROUTER advertising the truck LAN)
β”œβ”€β”€ OptiPlex 5080 (192.168.1.50) β€” runs its OWN Tailscale client
β”œβ”€β”€ Pi 3 B+ (192.168.1.40) β€” broker/PWA β€” runs its OWN Tailscale client
β”œβ”€β”€ (PoE switch) β†’ cameras (.101-108), Waveshare (.110)  ← reached via SUBNET ROUTE (no client)
β”œβ”€β”€ WROOM-32 (.111, if deployed)                          ← reached via SUBNET ROUTE
└── WiFi β†’ Phoenix (.120), S24 Ultra (own client)
   [ small unmanaged switch (~$15) or the PoE switch = the wired backbone if Cudy ports are few ]

Tailscale model (the key concept): being connected to the Cudy does NOT put a device on the tailnet. Two ways in: (a) the device runs its own Tailscale client (OptiPlex, Pi, phone β€” clean named 100.x addresses), or (b) a subnet router on the LAN advertises 192.168.1.0/24 so the dumb devices that can't run a client (cameras, ESP32, relay board) become reachable through it. Run both: subnet router on the Cudy for the dumb stuff + clients on the smart boxes. ⚠️ **Verify the Cudy can install the Tailscale opkg package** (vendor OpenWrt builds can be locked / short on flash β€” may need extroot). If it can't cleanly β†’ that's the only reason to buy the GL.iNet (its selling point is one-click Tailscale + subnet router in the GUI). Security: a subnet router exposes the whole truck LAN to your tailnet β€” incl. the auth-less Mosquitto broker. Lock it with tailnet ACLs so only your devices reach it.

Static IPs on OptiPlex, Pi, Waveshare, cameras. Local works fully offline. Cudy LTE locked to: OpenRouter API (rev 3.34 β€” swapped from Fireworks) + Tailscale + security JPEG uploads. S24 Ultra on truck WiFi only β€” music via BT to OptiPlex, not through cell.

4.2 Display Strategy

NOW (rev 3.3): no dedicated tablet purchase β€” internet is the Cudy LT18 (not a tablet tether), and the dev screen is whatever's on hand (S24 Ultra browser, or a spare tablet you already own) β†’ Chrome β†’ Flask over the Cudy WiFi. SavvyCAN no longer runs "on a tablet via RDP to the Z440" (Z440 shelved) β€” see Β§8 for the rev-3 CAN-discovery host (SavvyCAN-Linux on the OptiPlex, or do discovery at home on the Dell Precision before install).

LATER β€” head unit (rev 3.33, FULL market survey + decision below).

Interface boxes (NAViKS/PAC/Lockpick): REJECTED. ~$300-400 for video-IN only β€” touch can't be captured from the 2014 QNX Uconnect (hardware wall, unsolved for a decade). At that price, a full-Android head unit (touch) is the better buy. Mayton AutoPro X also rejected (requires factory Android Auto, which a 2014 RAM lacks).

4.2.1 Head-unit decision (rev 3.33; purchase config locked rev 3.35) β€” βœ… Phoenix Automotive direct, S7865 8GB/128GB Android 14

rev 3.35 update β€” purchase locked. Bought direct from Phoenix Automotive (phoenixautomotiveinc.com), NOT via the AmericanTrucks/Navos channel β€” saves ~$267 and skips the AT-forced $150 Midas install. Actual cart total: $695.20 (regular $789.99 β†’ sale, then code 2026 for ~12% off). Variant: S7865 8GB RAM / 128GB ROM / Android 14 / "2013-2018 w/ floor center console w/ Original Auto AC" / Silver-Chrome trim / NO 4G LTE (Cudy LT18 provides cell per Β§4.1, second cellular radio = wasted hardware). All rev-3.33 reasoning below still holds β€” the doc's $962 was a stale AT ceiling, manufacturer-direct is cheaper. See Β§9.4 for the OptiPlex layered-on-top CAN-write voice-control plan that this unit is designed to coexist with (Phoenix handles AC touchscreen baseline; OptiPlex adds voice control of HVAC via parallel B-CAN write).

The market splits into three camps (the organizing insight):

  1. Name-brand "digital media receivers" β€” Alpine, Pioneer, Kenwood, Sony, JVC. ALL closed CarPlay/Android-Auto appliances. None runs open Android, none installs a browser, none takes touch to an external device. The "Android" in Android Auto only means it projects a phone, not that it runs Android. Switching brands = different audio/styling, not a different capability. (Stinger HEIGH10 is the same closed camp β€” no Play Store.)
  2. Open-Android head units (Phoenix, Linkswell/Navos, Atoto, Dasaita, etc.) β€” run real Android + Play Store β†’ install Chrome β†’ native touch on the OptiPlex UI, no phone. The only camp that fits this build.
  3. CarPlay AI Box dongle (Ottocast/Carlinkit, ~$150-250, Android 12/13) β€” the bridge: plug into a Camp-1 unit's USB/CarPlay, runs full Android, touch passes back through the CarPlay channel. But its video rides the compressed CarPlay pipe (soft + laggy) β€” wrong for a unit you live in 99% of the time. Only relevant if someone insists on a closed name-brand head unit.

Why a name-brand (e.g. Alpine Halo11) loses for THIS build: even though the Halo11 has an HDMI input (and "video in motion," no parking-brake issue) + iDatalink Maestro + great audio, it cannot show the OptiPlex UI with touch β€” HDMI-in is view-only (no touch back to the source), and the unit can't run a browser. Touch would require the flaky phone-over-Android-Auto sideload bridge (Google keeps breaking it). Paying ~$1,700 + ~$200 AI-box to degrade the everyday carputer UI through a compressed pipe is backwards. Name-brands win only on audio fidelity + build + factory integration β€” none of which is the 99% use (OptiPlex UI + streaming + touch).

The key money insight β€” HDMI-in is a LUXURY here, not a requirement. The Β§4.5/Β§9.2 dashboard is a Flask/WebSocket web app by design β†’ it renders perfectly in any open-Android unit's native Chrome over the Cudy WiFi, with full touch. HDMI-in only matters for showing the raw OptiPlex desktop (non-web). Since the whole UI is web-based, the native-browser path covers the real need β€” and HDMI-in is the only reason the big Linkswell units cost ~$1,640-2,225 instead of ~$960.

Price ladder (AmericanTrucks = authorized "Navos" retailer β†’ real warranty/support, unlike Amazon/eBay grey-market which Linkswell refuses to support directly):

UnitScreenSpecsHDMI-inPrice
Phoenix-by-Navos 13" βœ…12/13"A13, 6GB/128GB❌$962
Phoenix-by-Navos 13"12/13"A13, 12/256❌$1,049
Linkswell Gen VI 12.1"12.1"A11, QCM6125, 4/64+$130 adapter$1,640
Linkswell XL 15.6"15.6"A11, QCM6125, 8/128+$130 adapter$1,945 (+install β‰ˆ $2,225)

βœ… DECISION: Phoenix Automotive by Navos 13", 6GB/128GB, Android 13 β€” ~$962 from AmericanTrucks. Big vertical screen, newer Android than the Linkswell (A13 vs A11), runs the Flask UI in native Chrome w/ touch, retains factory backup cam + steering-wheel + climate. Saves ~$1,260 vs the XL β€” money that belongs in the carputer guts (battery, OptiPlex power, MPPT) per the buy-what-you-need-first rule.

Chip note: Phoenix "S7865" = Unisoc UIS7862S (the enthusiast-favorite car SoC β€” 2Γ—A75+6Γ—A55, biggest modding/firmware community, best app compat). Linkswell uses Qualcomm QCM6125 (~Snapdragon 665 β€” comparable, often more stable). For a thin client the OptiPlex drives, either is fine; favor RAM/Android-version over chip splitting-hairs.

⚠️ Gates before buying ANY unit (one is a real risk):

  1. CANBUS coexistence β€” the #1 risk. Every Linkswell/Navos page warns it "may not be compatible with other aftermarket CANBUS devices… tuners, OBD devices." This truck has a tune and the build plans OBD/CAN reading (Β§8, OptiPlex on the SH-C31G). Confirm with the AT Sales Tech (610-990-4561) that the unit coexists with the tune + a CAN/OBD tap before ordering.
  2. AC type β€” match Auto / Manual 4-speed / Manual 7-speed to the dash; send a photo of the OEM radio + A/C panel.
  3. Internet β€” the unit goes online via the Cudy WiFi (Β§4.1); its own LTE/SIM is unneeded.
  4. Audio-in path β€” Phoenix has no HDMI/AUX line-in (factory AUX often lost): OptiPlex audio (TTS/voice/media) reaches the radio via Bluetooth (aptX) or USB, not a wire. (The Linkswell route would carry audio over HDMI instead.) Decide this when wiring.

⚠️ Sourcing (corrected) β€” AmericanTrucks FORCES a $150 Midas install on this Phoenix item (real cost ~$1,112), and it's a special order, 2-3 wk lead; the AT listing's feature blurb is even wrong-copy (shows a Ford Mustang unit β€” fitment checker still passes, but confirm real specs with the AT Sales Tech). Two buy paths: (a) AT/Navos = better support but forced pro-install (brief Midas on the tune/CAN, or sort the CANbus box after β€” the head unit talks to the OptiPlex over WiFi, so the carputer integration is mostly decoupled from the dash install); (b) Phoenix Automotive direct (phoenixautomotiveinc.com, code 2026) = self-install + cheaper, but Phoenix's direct support is the spotty one the forums flag. For a hands-on builder who must control the CAN integration, direct + self-install likely fits better.

4.3 Multi-Screen Control (tablet + radio simultaneously)

Each screen is an independent browser client. One always-open kiosk browser per screen; voice navigates it via targeted WebSocket push β€” no new windows, no touch needed, no radio interaction.

Each screen registers with a name: ?screen=radio / ?screen=tablet
"Hey Truck, 4-camera view on the radio" β†’ only radio switches
Tablet stays on gauges, untouched.
"on the radio" / "on the tablet" / "on the dash" = target selector

You can run gauges on the tablet while voice-switching the radio from reverse cam to a 4-cam grid β€” each addressed separately.

4.4 Tier Responsibilities & The Always-On Side (rev 3.4 β€” core design)

Organizing rule: "Does it need to be true while the OptiPlex is asleep?" Yes + beyond pin-level β†’ Pi. Only matters while driving β†’ OptiPlex. A hardware reflex β†’ ESP32. Heavy reasoning / away-delivery β†’ Fireworks / Hetzner.

LayerPowerOwns (primary)Notes
ESP32 (Waveshare Γ—2)always-on, 12V-fedRelays + safety reflexes; LVD + thermal cutoff; lights logic (auto-off, on-road interlock); proximity; WOL Β· bed board also runs the combiner + camp loads locallyOption B (Β§6.5): FRONT + BED boards, Ethernet between, each its own OTA ESPHome. The hands + safety reflexes. No mic (voice is OptiPlex). Relays fail-safe open on power loss. WROOM-32 = bench spare only now.
Pi 3 B+always-on (~2-3W)Mosquitto broker + wake-coordinator (dongle-vs-intrusion logic, lockout, quiet hours) + status/control PWA (§4.5) + telemetry log (SQLite) + high-level schedules + Tailscale foothold + MQTT→Hetzner bridge + Wolfbox pullThe always-on Linux brain. Holding the smarts here keeps the single ESP32 lean (the owner's goal). Reliability hygiene: boot from USB-SSD or read-only rootfs, clean 12V buck, Waveshare-signaled clean shutdown. Keep its primary (broker+wake) at 100%; don't pile heavy video on it.
OptiPlex 5080hibernates parkedVoice (Porcupine→whisper→Qwen3 4B→TTS), local-vs-cloud router (OpenRouter, rev 3.34), full CAN decode (gear/RPM/trans-temp — only meaningful engine-running), camera record/restream while awake, RAG, rich interactive Flask UIThe ears + small brain. Wakes on event/dongle; sleeps otherwise.
OpenRouter / Venice / HetznercloudHeavy reasoning (OpenRouter Qwen3 235B; Venice for privacy-lane queries); store-and-forward alert relay to phone (Hetzner)All only over cell.

Graceful degradation (every tier degrades DOWN, never up): the ESP32 keeps its safety reflexes (LVD, manual switches, direct WOL) independent of the Pi β€” so a dead Pi costs you the smart layer (alerts, status, schedules) but never battery protection or basic relay control. A dead OptiPlex costs voice/cameras/RAG but the Pi+ESP32 still run the truck's 24/7 side.

Why the Pi stays (and isn't redundant): its broker is featherweight, but it's the always-on host for the things that must work while the OptiPlex sleeps β€” the PWA, alert orchestration, the wake decision, logging. Compute is a non-issue (Mosquitto runs on a Pi Zero); the only real work is the reliability hygiene above. Pi 4 (4GB, ~$60) = FUTURE upgrade β€” its USB-2 100Mbit NIC + 1GB RAM are the weak points; upgrade when the 3 B+ shows strain under broker+PWA+Wolfbox, not before. (Keep the future Pi 5 + Hailo as a separate camera box β€” don't fold cameras onto the safety-bus Pi.)

4.5 Phone Control β€” the PWA on the Pi (rev 3.4)

Your S24 becomes the truck's remote. The Pi hosts a PWA (web app that "installs" to the S24 home screen β€” feels native, no app store, no maintenance):

Layer reachedAvailable whenWhat you get
Pi (24/7)always, 5080 offstatus + relay toggles + wake button
OptiPlex (woken)after wakelive cameras, voice, gauges, RAG

Access β€” same page, two paths: on truck WiFi (Cudy) β†’ Pi's local IP, instant, zero cell needed; away β†’ over Tailscale at the Pi's 100.x (toggle lights / wake the truck from across town). Lock it down: PIN/password-gate the control actions; reachable only on truck-WiFi + tailnet, never public. (A simple PIN is enough for a local truck page β€” no need for full Authelia/SSO here. High-power relay actions still get the Β§9.2 confirmation gate.)

4.6 Wake vs Alert β€” two separate triggers (rev 3.4)

The single most important split on the 24/7 side: "wake me" and "alert me" are different events.

Keyring dongle > phone proximity (decided): a dedicated BLE tag (iBeacon/nRF, coin cell, months) has a fixed identifier + steady RSSI and is literally tied to the act of going to drive. Phone proximity is unreliable β€” Android randomizes its BLE MAC, battery-optimization kills background BLE, BT may be off. ~$5-10. Dongle primary, phone optional backup.

GPS does NOT sense your approach. The truck's GPS knows where the truck is (stationary when parked) β€” it can't tell you're walking up. The BLE dongle is what detects you. "Ready before you open the door" comes from dongle range + a shallow sleep state: parked <2hr the OptiPlex is in S3 (wakes 2-5s β€” see Β§5.4), so it's up before you reach the handle. GPS's role is the longer-range phone-geofence wake (your phone crosses a radius around the truck's parked spot β†’ sends a wake; needs truck+phone online) plus find-my-truck / geo-stamp / offline clock. Dongle = offline approach-wake; GPS-geofence = the "I have signal" early-wake bonus.

Parked alert flow = wake-OptiPlex-per-event (chosen over an always-on camera, to protect winter solar):

door/alarm DI (ESP32) β†’ Pi wake-coordinator classifies:
   β”œβ”€ dongle present  β†’ "owner returning" β†’ normal wake, NO alert
   └─ no dongle + door β†’ "intrusion" β†’ Pi fires TEXT alert via Hetzner immediately,
                          AND WOLs the OptiPlex β†’ it grabs frames β†’ pushes JPG via Hetzner β†’ sleeps

Hetzner = store-and-forward alert relay, NOT a bus middleman (the owner's original design, and correct). The local MQTT bus stays 100% in the truck. But your phone can't receive a push straight from the truck β€” a server must hold the alert + JPG and retry until your phone ACKs (across cell dead zones). Self-hosting that on your Hetzner box (ntfy or similar) keeps the alerts + intrusion JPGs private. Tiered by cost: ESP32 text alert (~1W, always) β†’ on-demand live-view/JPG via OptiPlex wake. (An always-on camera for automatic JPG-every-event is the ~15W tier β€” deliberately NOT chosen; see Β§5.2 winter margin.)

4.7 Displays & Media Routing β€” 5080 as media hub (rev 3.12)

Model: the OptiPlex 5080 is the media hub β€” ingest a source once, fan it out to many screens. "Brain in control, even if the brain is a group of sub-components." Companion sheet: **media-routing-5080hub.pdf** (sources β†’ 5080/go2rtc β†’ screens).

Two planes, kept separate. Control (lights, relays, gauges) = MQTT + Flask/PWA β€” local, instant, needs no internet. Media (video) = go2rtc restream β€” slight lag, parked use. Don't run control through the media path or vice-versa. (go2rtc is the restreamer already bundled with Frigate, Β§7.2 β€” zero new stack.)

Using the S23's UNLIMITED data (the key trick). The phone's hotspot/tether is capped (~5GB) β€” useless for video. The phone's on-device data is unlimited, so the phone itself must be the streamer, and you move the picture, not the data:

The fan-out. Ingest the S23 once via scrcpy β†’ the 5080 becomes the distribution point for the phone's unlimited pipe β†’ go2rtc re-publishes it (and the cameras, and local YouTube) as WebRTC to every browser screen. So 5080 + S23 β†’ Phoenix works (restreamed over Cudy WiFi, parked) β€” the dash shows the phone's YouTube without touching the metered SIM.

Screens.

Caveats. Latency stacks per hop (scrcpy + re-encode + WebRTC β‰ˆ 0.3-1.5s) β†’ great for watching parked, not twitch-interactive; control stays on the direct PWA (instant). Re-encode = CPU + watts (UHD 630 QuickSync helps) β€” a parked-movie load, mind deep-winter battery (Β§5.2). Carriers often throttle video resolution on "unlimited" (480/720p) even when data is uncapped; YouTube over DeX/scrcpy is DRM-fine.

Data split by who's present: phone on-device unlimited = media/bulk when you're at the truck (via scrcpy/DeX); Cudy/Keepgo SIM = always-on automation (Fireworks, Tailscale, alerts) when the phone isn't there. The Cudy can prefer the phone hotspot when in range and fall back to its SIM.

4.8 USB / Peripheral Port Map (rev 3.14)

The 5080 Micro has enough USB β€” the trick is the right allocation, not more ports. Standard layout: front 1Γ— USB-C + 1Γ— USB-A; rear 4Γ— USB-A (2Γ— 3.2 Gen1 + 2Γ— USB 2.0) + DP + HDMI + RJ45 (+ optional flex port) β‰ˆ 5 USB-A + 1 USB-C + built-in BT 5.2. (Reseller listings may label it "USFF" β€” the 5080 has no USFF variant; Dell makes only Tower / SFF / Micro. This unit is confirmed Micro by teardown β€” single SO-DIMM, 64GB max, 1.1L brick. Internal teardown notes: Β§9 RAM, Β§7.3 storage, Β§13 RTC.)

Allocation rule β€” critical/real-time on the board, tolerant on a hub:

**Get a powered hub, not a dock.** Powered is the key word (a vehicle browns out bus-powered hubs); an industrial/DIN wide-temp hub fits the build. A dock is the wrong tool here: the 5080 has no Thunderbolt, so docks fall back to driver-heavy DisplayLink for extra video (flaky on Linux), and they funnel everything β€” including real-time CAN β€” through one USB-C = a single point of failure. You already have rear DP+HDMI+Ethernet natively. (Reserve the "dock" concept for the removable bed screen, where dock/undock is the actual job β€” see Β§4.7.)

BIOS for the parked-power build: DISABLE Deep Sleep Control β€” Deep Sleep cuts the 5VSB that Wake-on-LAN needs, and WOL from S4 hibernate is the parked-wake path (Β§5.4/Β§13); the small extra S4 standby draw is the accepted cost of WOL, and the deep-storage tier cuts power entirely via the ESP32 anyway (so standby draw is moot there). Also DISABLE Smart Power On (USB-wake) β€” not needed (you wake via WOL + the O3 power-pulse) and it risks spurious USB wakes draining the bank while parked. Enable WOL/PXE. (Earlier draft here mistakenly said enable Deep Sleep β€” corrected; that would kill WOL.)

Verify on the actual unit: (1) does the front USB-C do DisplayPort-Alt-Mode? Many OptiPlex Micro front-C ports are data/PD only β€” no video; if so, a USB-C touch monitor's video comes from DP/HDMI with touch on a separate USB-A (2 cables, not one). (2) BT β€” confirmed: Intel AX201 (Wi-Fi 6 + BT 5.2 + BLE) is installed β†’ use the built-in BT for S24 A2DP audio and BLE proximity; drop the planned USB BT dongle (redundant; keep one only as a Linux-flakiness fallback). The front USB-C header is also a tidy spot for the SH-C31G CAN adapter if you're not using USB-C for a monitor/DeX.


β€Ή 3. CARPUTER β€” HARDWARE TO PURCHASE5. POWER ARCHITECTURE (the keystone) β€Ί