6. ESP32 NODE NETWORK (Tier 0)
The controller is an ESP32-S3 in a DIN-rail industrial enclosure, running on 7-36V DC direct ("automotive") β wires to 12V truck power, no buck needed. ESPHome-ready. Tier 0 is the always-on, crash-proof I/O + safety layer; it never sleeps and survives anything the Linux boxes do (graceful degradation, Β§4.4).
rev 3.6 β FINALIZED: digital-output controller + remote serviceable relay panel. After surveying the field (Waveshare, KinCony KC868/Pi5-ARM, Erqos, Norvi, Olimex), the owner's serviceability instinct won: don't put soldered relays on the controller (a dead onboard relay = down a channel / board surgery). Instead the controller has digital outputs that TRIGGER an external automotive relay panel with socketed, swappable Bosch relays. A dead relay = a 10-second, $3 plug-in swap. The controller has nothing that switches a load, so nothing on it wears out. Option B DECIDED (rev 3.31, Β§6.5): TWO boards β a FRONT/cab board and a BED/camp board, Ethernet between them, each its own OTA ESPHome firmware (the bed is a real subsystem β combiner + tent + 3+ light sets + growth).
THE CONTROLLER β Waveshare ESP32-S3-POE-ETH-8DI-8DO, ALWAYS-ON (start with ONE):
β’ 8 DIGITAL OUTPUTS β Darlington transistor, optocoupler-isolated, 500mA sink,
built-in freewheeling diode β drive Bosch relay coils DIRECTLY (no extra flyback diode)
β’ 8 DIGITAL INPUTS β optocoupler isolated (door, ignition, reverse, dash button, sensors)
β’ Power: ALWAYS-ON 12V via 7-36V screw terminal (NOT PoE β PoE dies with the camera switch)
β’ Ethernet: W5500 (wired DATA link to the always-on switch; frees WiFi/BT radio for BLE proximity)
β’ Also onboard: RS485 + CAN (unused for now β see CAN note) ; ESP32-S3, ESPHome
β’ NO onboard relays β nothing on the controller to fail or wear
Jobs: fire DO triggers Β· read DIs Β· LVD + thermal cutoff (ADC) Β· proximity Β· send WOL/power control.
Publishes to / subscribes from the Pi 3 B+ MQTT broker (Pi holds the smart wake logic).
THE SWITCHING β external automotive relay/fuse panel (the swappable muscle):
β’ A relay/fuse block β rev 3.23 part: True MODS 60/80A 5-pin kits (interlocking sockets gang into
the panel; 87a left empty; observe 85/86 polarity if diode-suppressed) + a separate ATO fuse block
(the kit has no fuses). Bussmann RTMR remains the integrated alternative.
β’ Each DO output β one relay coil (pin 85); coil pin 86 β panel-local 12V; DO sinks coil to ground
β’ Carries the real load current (30-40A capable) + integrates the fuses
β’ SERVICEABLE: dead relay β pull + plug a $2-5 Bosch in, ~10 sec, no board touched
β’ Handles high-current/inrush/inductive loads (light bar 18A, compressor) β no separate relay needed;
the panel relays ARE the relays, sized per load
Trigger wiring: DOβpanel = 20-22 AWG, or owned SOLID Cat5 (24 AWG, one Cat5 = 8 triggers).
β οΈ Secure solid Cat5 every ~12in + strain-relieve both ends (solid fatigues at flex points
under vibration); use stranded only where it crosses something that moves (door jambs).
Load wiring (relayβload): sized to load β 16 AWG β€5A / 14 AWG long / 12 AWG 18A bar β FUSED.
WROOM-32: RETIRED to spare. No always-on CAN node is needed (wake = DI wires; CAN = OptiPlex, see Β§8).
SENSOR node: folded into the controller's spare DIs + ADC (cab temp, battery voltage) unless it strains it.
Wake word ("Hey JARVIS") is NOT on any ESP32. Porcupine runs on the OptiPlex 5080 once it is awake. Hardware wake (from hibernate) is handled by door/proximity events routed through Pi 3 B+ β WOL. This eliminates the need for a dedicated wake-word ESP32 node.
Programming β single unified ESPHome firmware. One chip, one YAML codebase. ESPHome handles all of it: DO triggers (output/switch), DI (binary_sensor), LVD (ADC + threshold + hysteresis), the on-road light interlock (reads CAN speed via the OptiPlex over MQTT, or a DI), BLE proximity, and the WOL/power-control magic packet (small UDP lambda). None needs hard-real-time. (You don't "keep" the factory firmware β ESPHome replaces the throwaway demo; the unerasable ROM bootloader makes re-flash/recovery foolproof. OTA updates push over the wired network from the OptiPlex at the 3am window β Β§5.4.)
ESP32-S3 "AI" = TinyML β tiny neural nets (KB-few MB) via ESP-DL / TF-Lite Micro / microWakeWord. Good for: wake word, simple sound/vibration classification, binary present/absent decisions. CANNOT run an LLM, analyze camera video, or do face/object recognition β LLM work is the OptiPlex's (β Fireworks for heavy reasoning); camera object-detection is deferred to the Pi 5 + Hailo-8L (~1yr out).
REJECTED controller options (rev 3.6 survey):
- Onboard-relay boards (Waveshare 8DI-8RO-C, KinCony A8v3/A16) β soldered relays = a dead one is board surgery / down a channel. Lost to the DO + swappable-panel approach.
- KC868-H32BS (32-ch, $260) β overkill + overpriced for ~12-14 loads.
- ARM boards (KinCony Pi5R/Pi5M, Raspberry Pi CM5) β powerful (full Linux, runs any program) but relays/safety belong on a crash-proof microcontroller, not a Linux box that can hang/corrupt. If ever used, only with an ESP32 watchdog + ESP32-held battery cutoff. Brain stays the owned Pi 3 B+.
- ESP32-S3-RS485-CAN gateway β WiFi-only, no Ethernet β fails the wired-first rule.
- MrDIY ESP32 CAN Shield ($56) / Waveshare Pi Zero relay HAT ($38) β redundant / too weak.
6.1 Manual overrides & RS485 Modbus IO expansion (rev 3.10)
Goal: add physical override switches (and any extra inputs) without burning the 8DI-8DO's 8 DI, which are already spoken for (door, ignition, reverse, dash button, cab-temp/batt-V).
The expansion path β RS485 Modbus RTU. The 8DI-8DO has an onboard RS485 port. Hang a Modbus RTU IO module on that 2-wire bus; the 8DI-8DO is the master and polls it (up to 247 devices on one bus). Each added module brings its own DI/DO β so a cab switch panel lives on the RS485 module, and the main board's 8 DI stay free.
- Module choice by environment:
- Isolated industrial Modbus IO module β for harsh / long / engine-bay runs (optoisolated, 12V-tolerant inputs, screw terminals).
- Cheap $4 TTL module (R4PIN08-class, jumper-selectable 8DI / 8DO / 4-4 / etc.) β fine for a short-run cab panel only. β οΈ Its inputs are 3.3/5V TTL β wire each switch as a dry contact to GROUND, never to 12V (12V destroys it). Non-isolated bare board β keep it in the cab, not under the hood.
Two manual-switch wiring patterns (different tradeoffs):
- DI-input switch (stays in sync). Switch β a DI (on the main board or the RS485 module). The ESP32 reads it and fires the output, so voice and the physical switch agree on state and either can toggle. β best when the ESP32 is alive. β does nothing if the ESP32 is dead.
- Parallel-at-the-coil bypass (fail-safe). A switch in parallel with the relay coil's trigger (pin 85 β ground), so flipping it energizes the relay regardless of the ESP32. β works even if the ESP32 is dead. β the ESP32 doesn't know you did it (state desyncs) β use only for true limp-home loads.
Dead-relay limp-home. Because switching is on the swappable Bosch panel (Β§6), a dead relay = a $3 plug-in swap. Carry a spare Bosch relay; for a no-spare situation, a gutted-relay bypass plug (dead relay shell with 30β87 jumpered, or a toggle across them) hard-closes that load by hand.
6.2 RS485 switch panel + stateful illuminated switch (rev 3.11)
Goal: a cab switch panel where each manual switch agrees with voice/app state, run home on ONE cable instead of a wire per switch. Three companion wiring sheets live in
projects/carputer/(and the dropzone): **control-wiring-optionA-modbus.pdf,control-wiring-optionB-dualboard.pdf,switch-led-stateful.pdf**.
The panel is a Modbus slave behind the switches, not wires home. RS485 is a transport (differential serial carrying Modbus RTU) β a bare switch can't speak it. Put a fixed-function Modbus RTU IO module at the panel; wire toggles to its terminals (short local runs); the 8DI-8DO polls it as master over one cable. A TTLβRS485 adapter (e.g. H1-HBB0150-10) or an industrial repeater/isolator are bus plumbing only β neither reads a switch; don't substitute them for the IO module.
- Confirmed part: Waveshare Modbus RTU IO 8CH (SKU 26244, ~$24). 8 isolated DI (PNP/NPN β accepts dry-contact OR switched-12V, both optoisolated β so no TTL "ground-only" landmine) + 8Γ DO (500mA Darlington open-drain, drives Bosch coils directly). 7β36V, DIN-rail, addr 1β255, cascadable, resettable fuse + TVS. This is the Β§6.1 module, now nailed down β prefer it over the $4 TTL board for anything past a trivial cab run.
- Optional sibling on the same bus: Modbus RTU Analog Input 8CH (~$25). 8Γ 12-bit 0β10V / 4β20mA β for air-tank PSI, tank levels, extra temps. Different Modbus address, same two wires. Future/optional (pack-V already comes from the WonVon shunt).
- One cable = ~4 conductors, not 2. The module needs power where it's mounted: one pair RS485 A/B, one pair 12V+GND β one run of owned solid Cat5 covers it, spare pairs for later. 120Ξ© termination at the bus ends for long/fast runs; short cab runs are forgiving. Daisy-chain multiple panels (front + bed) on the one bus, each its own address.
Stateful illuminated switch (the "button + voice agree, LED shows truth" pattern). State lives in the Waveshare as the single source of truth β so use a MOMENTARY pushbutton with an INDEPENDENT LED (4 wires: 2 contact + 2 LED). Not a latching switch β a mechanical latch would hold a position voice can't change and lie about state.
- Contact:
NO β DIx,COM β GND. Each press = a pulse β Waveshare toggles the load's state. - LED:
+12V β (series resistor if the LED isn't 12V-rated) β LED+,LEDβ β DOy; the DO sinks when state = ON so the lamp tracks state. The DI and the LED-driving DO both live on the panel module (short, local); the load relay's DO can be anywhere. - Result: button press or voice toggles the one state; that state drives both the load relay and the switch LED β so button and voice always agree and the LED never lies. ESPHome: momentary
binary_sensoron_press: switch.toggle; a template switch whose on/off actions set both DOs; voice/app call the same switch. Seeswitch-led-stateful.pdf.
Example part + 2-switch wiring: DMWD 12mm metal anti-vandal momentary, 4-pin, halo-ring LED, 12-24V, IP66 (~$2/ea in a 5-pack β buy the MOMENTARY, not latching, version). Its 12-24V LED has a built-in resistor β LED+ β 12V, LEDβ β DO, no external resistor; verify pin polarity with a meter (markings are tiny). Contact is only 2A-rated but that's irrelevant β it carries signal to a DI, never load current. Per switch: NOβDIx, COMβGND, LED+β12V rail, LEDββDOx; share the 12V + GND rails; panel jumpers 22-24 AWG (Cat5 strands fine, mA only). Debounce in ESPHome. See switch-panel-2sw-dmwd.pdf (2Γ = Rock + Fog).
Gesture commands (momentary + software = free). Because the button is momentary and state is in software, ESPHome can layer behaviors: short press = toggle (on_click); press-and-hold = on for 10 min then auto-off (hold β script + delay); multi-click = extra actions (on_multi_click, e.g. 4-press = rock + fog together). β οΈ Color-change on 2-press is NOT possible with the DMWD (single fixed-color LED) β that needs an RGB-ring switch (separate R/G/B = 3 DO per switch; on/off = 7 colors, or PWM on the Waveshare's native pins for full mixing). One placement nuance: multi-click timing is sharper on the Waveshare's NATIVE DI (instant edge) than on the polled Modbus module (~100-300ms poll blurs fast clicks) β so put gesture switches on a native DI, simple on/off overrides on the module.
β DECIDED (rev 3.31): OPTION B β two clusters, by LOCATION. Two Waveshare ESP32-S3-POE-ETH-8DI-8DO boards (~$43 each): a FRONT/cab board and a BED board, talking over one Ethernet (Cat5e) run. Picked because the bed is a real subsystem, not "a battery" β rooftop tent on racks, 3+ rack light sets, tent lights, a tailgate hangout zone, and unknown future additions. A local bed board gives short local wiring, local camp control (a tailgate switch panel wired right to it), keeps the safety-critical combiner control loop local (no truck-length bus in it), keeps the bed working even if the cabβbed cable is cut, and leaves spare DO/DI + an RS485 port for "the more things later." Firmware is a non-issue: each board runs its own small ESPHome YAML, OTA over the network (flash once via USB, all later updates push wirelessly); you only touch firmware to add physical hardware or change core logic β scenes/schedules/automations that combine existing outputs live in the Flask/PWA layer (no reflash). See Β§6.5 for the cluster split + bed board I/O map.
Combiner timing (still relevant): the sequence is second-scale (~15s ΞV converge, 0.5s step gaps) and the 125A fuse + BMS are the fast protection, not the ESP32. With Option B the combiner runs locally on the bed board (native DO β RLY-O5/RLY-PC coils, bed-V via the DC Monitor on the bed board's RS485) β so the control loop never crosses the truck-length link at all.
6.3 ESP32 reboot behavior & firmware boot rules (rev 3.22)
During any reboot (watchdog, brownout, crash, and EVERY OTA update) the DO opto-drivers go dark β every board-driven relay drops. What that means per subsystem:
- Combiner: fail-safe + self-healing. Contactor + pre-charge open β banks isolate. Mid-COMBINED, the contactor breaks under load (it's rated for that); the front bank carries everything via the trunk for the ~5-15s boot, then the state machine re-reads the ADCs (ΞVβ0 β band 1) and recombines. (Sheet 6.)
- Modbus-module loads (lights): unaffected β the module is independently powered and holds its DO states while the master reboots; lights don't blink. Check/configure its comms-watchdog (if present) deliberately.
- Pi / Switch A / Cudy: unaffected (direct fused feeds, not relay-gated).
- β οΈ O1 = the real casualty: the converter relay dropping is a hard power cut to a RUNNING OptiPlex (fs-corruption risk β the Β§5.4 deep-storage cut is safe only because hibernation happens first). Latching is the fix, but it can't go in the Bosch panel β a latching relay needs set + reset coils, more terminals than the 5-pin ISO socket has, so there's no "latching Bosch relay" to drop in. Two real paths:
- (A, recommended) Standalone 12V 10A MAGNETIC latching relay module β "ZERO POWER HOLD" β outside the panel. The key spec phrase when shopping is literally "magnetic latching β¦ zero power hold": it's a true magnetic-latching relay (mechanical hold, zero standby current). β οΈ Do NOT grab the cheap "touch / MCU / push-to-start" toggle modules that flood the search β those have an onboard chip drawing continuous standby current (defeats 0W) and a single toggle input. β₯10A contacts (O1 peak ~8A); avoid 1-2A bare latching relays (HFD2/MEC β too small). Drive: ideally a board with SET + RESET inputs (two DOs force known states). If it's a single-toggle board (common), it still works but add a read-back (below). CONFIRMED part: Electronics-Salon MD-D262T/12V (single relay, DIN housing; internal = TE/Schrack RT424F12 2-coil bistable). Control terminals V+ / S / R, both active-LOW β a perfect match for the open-drain DOs:
- β
CONFIRMED EQUIVALENT β ALREADY OWNED β use this one (rev 3.46): Electronics-Salon D-151 / MD-D151S/12V (internal relay = Hongfa HF115F-L/12-2ZS4L2). Datasheet-verified June 2026: bistable/latching (Hongfa lists HF115F-L as a "miniature high power latching relay";
2Z=DPDT,L2=dual-coil latching) and S/R both active-LOW (control spec: low 0-0.5V / high 2.5-24V; "R low β reset action") β exactly the two things this section requires, so it's functionally identical to the MD-D262T (Hongfa relay inside instead of the TE/Schrack RT424F12). the owner owns this β it IS the O1 part; do NOT also buy the MD-D262T or a bare RT424F12. Wire identically (V+/S/R below). Sanity-check the specific unit before install: pulse S, disconnect VCC, confirm the contacts hold (a latching relay holds with the coil dead; a monostable can't). V+β 12V Β·Sβ DO_a (pulse low ~300ms = ON) Β·Rβ DO_b (pulse low ~300ms = CUT)- Switch X (the muscle):
Cxβ 12V,Sxβ converter 12V (SET=on, RESET=cut) - Switch Y (free read-back):
Cyβ 12V,Syβ DI7 (true mechanical state β more reliable than sensing 19V; a contact can't mislead) - Firmware: ~300ms pulse (datasheet min 30ms); NEVER pull S and R low at the same time (hard rule);
restore_mode: ALWAYS_OFFon both DOs + read DI7 on boot to learn state (don't pulse on boot). - Specs that fit: ~50mA for ~300ms per change, then ZERO (true 0W hold); 15A making (converter inrush OK); β40-85Β°C; AgNi contacts. Holds through reboot/OTA; dead-ESP32 leaves the 5080 running (itself an alarm).
- (B, no special part) Wire a standard panel relay NC (normally-closed): power flows when the coil is unpowered β a reboot keeps the 5080 ON. Tradeoff: cutting for deep storage needs the coil held energized (~1W) β not true 0W (pair with the rotary/manual cut for long storage). NC = reboot-safe but ~1W; module = both.
- $0 interim regardless: only OTA the ESP32 while the OptiPlex is hibernated.
Firmware boot rules (must be in the YAML):
restore_mode: ALWAYS_OFFon O5/O6 (and O3) β never restore relay state from flash; the battery may have been swapped while powered off. Re-derive from ADCs every boot.- Force-merge confirmation does not persist across reboot β fresh confirm required.
- ~10s boot-settle delay before energizing anything (also prevents relay-chatter in brownout reboot loops); never energize O5 until the ADCs are read and banded. O3 additionally gets min/max-pulse-width guards so a boot glitch can't fake a power-button press.
6.4 Waveshare module reference β solutions on tap (rev 3.30)
The point: because the build already runs an RS485 Modbus bus off the controller, almost any future sense/control need is a $20-27 DIN-rail module on the same two wires β give it a Modbus address and poll it. All are 7-36V, isolated (optocoupler + TVS + resettable fuse), rail-mount, cascade up to 247. Keep this table as the "what solves X" lookup.
| Need | Module (Waveshare) | SKU | $ | Notes |
|---|---|---|---|---|
| Switches + LEDs (dash panel) | Modbus RTU IO 8CH | 26244 | 24 | 8 isolated DI (PNP/NPN, dry-or-12V) + 8Γ 500mA DO. Selected for the dash panel (Β§6.1/Β§6.2). |
| Bank voltage sense (combiner ΞV) | Modbus RTU DC Monitor | 33931 | 27 | 4-ch 16-bit 0-36V; current Β±8A only (not main banks β keep the shunt); needs common GND. Selected (Β§5.9). |
| Analog sensors (PSI/level/temp) | Modbus RTU Analog Input 8CH | 25821 | 25 | 12-bit 0-10V / 4-20mA (NOT >10V β divider or DC Monitor for battery V). 4-20mA air-tank PSI, tank floats, temp probes. |
| Dimmable / RGB LED | Modbus RTU PWM Output 4CH | 33921 | 22 | 1Hz-200kHz PWM signal (<30mA) β drive a MOSFET gate for the power. Only if dimming is revived (was dropped rev 3.6). |
| 0-10V driver dimming | Modbus RTU Analog Output 8CH | 26419 | 20 | 0-10V / 4-20mA out β for LED drivers / fan controllers that take a 0-10V dim input. |
| More relays over the bus | Modbus RTU Relay 8CH / Latching (C) | 25739/30460 | 29/39 | If you want bus-driven relays somewhere instead of the Bosch panel (latching = holds on power loss). |
| Always-on LAN switch | Industrial 5-port Gigabit switch (DIN) | β | 25 | Candidate for "Switch A" (the always-on LAN) β DIN-mount matches the rail. |
| OptiPlex USB expansion | Industrial USB hub (metal/DIN, 4-port) | β | 15-18 | The powered hub for Β§4.8 (CAN/mic/phone stay direct). |
| GPS (24/7) | L76K GPS HAT / module | β | 13-23 | Put on the always-on Pi (not OptiPlex USB) β time + geo even while the 5080 sleeps. |
| Parked CAN watch | 2-CH CAN HAT for Pi | 17912 | 22 | Only if you later want CAN while parked (default = CAN on OptiPlex when awake, Β§8). |
| Split/isolate the RS485 bus | Isolated RS485 Hub (4-port) | 32247 | 18 | Branch the bus (cab vs bed) with isolation + signal relay if it grows. |
| OptiPlex talks Modbus direct | USB-TO-RS485 (C) | 34620 | 12 | Lets the 5080 poll the Modbus modules itself when awake (bypass the ESP32 master). |
| Small DC load current | (DC Monitor, above) | 33931 | β | Β±8A bidirectional, 0.8mA res β for a <8A circuit, not the banks. |
6.4.1 OWNED β Modbus RTU IO 8CH (SKU 26244): chip-level reference (rev 3.47)
Identified June 2026 from the owner's own board photos (
projects/carputer/photos/waveshare-modbus-rtu-io-8ch/, 15 shots) + Waveshare wiki / botnroll specs. This is the dash-panel expansion module of Β§6.1/Β§6.2 β NOT the brain. It has no ESP32, no Ethernet, no PoE: it's an **STM32-based RS485 Modbus RTU *slave*** that hangs off the ESP32-S3-POE-ETH-8DI-8DO's RS485 bus (master). Don't confuse the two boards.
Chip inventory (silk-screen β part β role):
| Marking on board | Part | Role |
|---|---|---|
STM32F0β¦C8T6 (48-pin LQFP) | ST STM32F0-series MCU | Runs the Modbus RTU slave firmware; reflashable via the SWD header |
EAS 8.000 | 8.000 MHz crystal | MCU clock |
SP485EE K18110 (SOIC-8) | MaxLinear/Sipex SP485EE | Half-duplex RS485 transceiver |
TI β¦5C531BXH / ULN2803A (SOIC-18) | TI ULN2803A Darlington array | The 8 DO drivers β open-drain/low-side sink, 500mA & 5β36V per ch (the row of R500 through-hole resistors = per-channel) |
EL Q3H4 β¦ / EL Q3H7 β¦ (SOIC) Γseveral | Everlight EL3H4 / EL3H7 optocoupler arrays | Galvanic isolation β DI input side + DO output side (full I/O isolation) |
MP1?2 9447 (QFN) + 100 inductor + ELOM SS34 + 606k 10Β΅F 50V | MPS buck regulator + Schottky + bulk cap | Step-down from the 7β36V input to internal 3.3/5V rails |
K-CUT B0505LS-1W (brick) | B0505S-1W isolated DC-DC (5Vβ5V, 1W) | "Unibody power isolation" β powers the isolated RS485/field side |
WEIDIA blue disc + E517/EJON (SMA, diode mark) + JF/712 | GDT/MOV + TVS + clamp diodes | RS485 600W surge + 15kV ESD protection cluster |
060 050 SMD | PPTC resettable fuses | Input + bus over-current protection |
Headers / jumpers β and the DIP-switch question:
- **4-pin SWD header
3V3 / SWCLK / GND / SWIO** (bottom edge) β for reflashing the STM32. Leave unused in normal operation. - **Yellow 2-pin
120Rjumper β RS485 termination enable (fits 120Ξ© across A/B). Install it only on the last device on the bus**; remove on mid-bus modules. - β οΈ There is NO DIP switch on this board. Device address (1β255) and baud are set in software via Modbus register writes β they are not switch-selectable. Default out of the box: address 1, 9600 8N1 (baud options 4800β256000).
Wiring notes that matter for THIS build:
- **DI are isolated, 5β36V, NPN or PNP β unlike the cheap $4 TTL module Β§6.1 warns about, these inputs CAN take 12V directly** β dash switches can switch +12V or to-ground. Built-in debounce.
- DO are open-drain sink (ULN2803), 500mA, 5β36V β they pull a load to ground (low-side), exactly like the ESP32's native DO β fine for triggering Bosch-relay coils (<500mA) for the lighting DOs (M-DO1β¦M-DO6, Β§5.6). For anything >500mA, the DO triggers a relay; it never carries the load.
- Status LEDs: 8Γ DI + 8Γ DO channel indicators, plus RXD / TXD / STA.
6.5 Tier-0 architecture β TWO CLUSTERS (Option B, rev 3.31)
Two Waveshare ESP32-S3-POE-ETH-8DI-8DO boards (~$43 ea), split by LOCATION, talking over one always-on Ethernet (Cat5e) run. Each is 12V-fed (NOT PoE β must survive the camera switch sleeping), each runs its own small ESPHome firmware (OTA over the network), each publishes to / subscribes from the Pi 3 B+ MQTT broker.
FRONT board (cab) βββββββββββββββ βββββββββββββββ BED board (bed/camp)
OptiPlex power (O1 latching), β Ethernet β COMBINER (contactor + pre-charge),
accessory cut, camera/PoE gate, β (Cat5e, MQTT) β 3+ rack light sets, tent lights,
front lighting, wake/sleep, βββββ Switch A βββββ€ tailgate camp switch panel,
dash switch panel (Modbus IO) β (always-on) β DC Monitor (bed-V) on local RS485,
+ DC Monitor / sensors on its β β + SPARE DO/DI + RS485 for growth
own short RS485 β β Powered from the BED BUS (live via trunk)
Why two (the decision): the bed is a genuine second subsystem (rooftop tent, racks, 3+ light sets, hangout zone, unknown future loads), not "a removable battery." A local bed board = short local wiring, local camp control, the safety-critical combiner loop stays local (no truck-length bus in it), the bed keeps working if the cabβbed link is cut, and spare I/O for later. The old "WROOM-32 = the 2nd board reserve" idea is retired β the 2nd board is a 2nd Waveshare in the bed; the WROOM-32 stays a bench spare.
BED board I/O map (starting point β adjust as the camp zone fills):
- DO1 β RLY-O5 (Bosch) β contactor coil Β· DO2 β RLY-PC β pre-charge Β· DO3-5 β 3 tent/camp rack light sets (rearward, each β a relay) Β· DO6 β tent lights Β· DO7 β WATER PUMP (planned β see the pump note below) Β· DO8 β SPARE (fridge / fan / awning / lighting-scene loads later)
Water pump (shower / cleaning) β DO7 (rev 3.57). 12V diaphragm pump (Shurflo-class, 4β7A, buy one with a built-in pressure switch so it self-stops when the sprayer closes) β DO7 β relay β 16 AWG fused ~10A off the bed bus. Manual switch = one position on the tailgate Modbus panel using the Β§6.2 stateful pattern (momentary + LED, state in firmware) β so the switch, voice, and PWA all toggle the same state and the LED never lies. Firmware: auto-off timer ~10β15 min (battery + pump protection) + low-battery shed (non-critical load). Voice control from OUTSIDE the truck, three tiers: (1) $0 now β the Β§4.5 PWA pump button on truck WiFi/Tailscale, plus an Android Assistant/Tasker routine β HTTP to Flask/MQTT ("pump on" by phone voice, anywhere); (2) cab XVF3800 through open windows/door β often works at ~5m, bonus not plan; (3) the shower answer β a tailgate ESP32-S3 VOICE SATELLITE (ESPHome voice-assistant node, ~$15β50) streaming wake-word audio to the OptiPlex pipeline: hands-free with wet soapy hands, mounted under the rack lip / near the Β§6.5.1 control box, mic port down. No rework at any tier β DO7 doesn't care which client commands it; switch/phone/cab-voice/satellite are all just clients of the same state.
- DI1 β combiner override Β· DI2 β bed-V read-back if used Β· DI3-8 β camp switches / spare (or run the camp switches on a tailgate Modbus IO 8CH so the DI stay free + the switch LEDs get DO β the Β§6.2 stateful-switch pattern, in the bed)
- RS485 β DC Monitor (bed bank V) + future bed Modbus modules Β· Ethernet β Switch A (front) Β· CAN β spare (inverter telemetry later?) Β· Power β bed bus, 7-36V terminal
- Combiner runs its full state machine locally here (Β§5.9 / sheet 6); fail-safe NO contactor + the same reboot rules (Β§6.3).
FRONT board: OptiPlex power, accessories, camera/PoE gate, wake logic, the dash Modbus IO panel, and the KC front-bumper light rack (forward-facing β on-road interlock Β§5.7 #4 + diode-OR manual KC switch fail-safe) + any front rock/underbody. It no longer carries the combiner (that moved to the bed). (The bed board's "3 rack light sets" = the rearward TENT/CAMP lights, not the KC front rack.)
6.5.1 Bed enclosures β control box (waterproof) vs power section (SEPARATE)
Two separate bed enclosures (the owner's call, confirmed rev 3.52):
- CONTROL BOX = Lost Creek medium watertight case (IP67 Pelican-class). Holds the low-current logic only: ESP32-S3 bed board, bed Modbus IO 8CH, DC Monitor, pre-charge relay (RLY-PC), a small fuse block, terminal strips. Pull the pluck foam and mount everything on a backplate / DIN rail β never float modules on foam (vibration).
- POWER SECTION = separate (bracket or its own vented box): contactor, bed +/β bus bars, 2β4 AWG battery / inverter / MPPT / trunk cables. Kept OUT of the control box on purpose β fat cables = big holes, high current = heat, battery cable beside Cat5e = CAN/RS485 noise. The combiner state machine still runs on the bed ESP32; only a few thin control wires cross to the contactor.
Pass-throughs β consolidate to ~3 holes (each hole = a leak risk; group by function, use multi-hole glands):
| Gland | Carries | Note |
|---|---|---|
| G1 (multi-hole) | 12V/GND control power IN + combiner control OUT (contactor coil, pre-charge, bed-V sense) | both head to the power area β share one multi-hole gland |
| G2 (multi-hole) | Ethernet-to-front + tailgate switch harness + RS485 | 2β3Γ Cat5e through one 2/3-hole gland |
| G3 | all 6 rack/tent light circuits, bundled | one multi-core β waterproof junction near the lights β fan out (NEVER 6 separate holes) |
| + breather | Gore/PTFE pressure-equalization vent (tiny) | MANDATORY β a sealed case heat-cycling in the bed pulls a vacuum and sucks water past the seals; the breather prevents it |
Rules: glands on the bottom/side face, never the lid (lid stays a clean gasket seal); drip loops below each gland; match gland to cable OD (loose = leak); strain-relief inside. Components are β40β+65/85Β°C rated and only a few watts, so thermally fine once the combiner heat stays out (reason #2 above). Optional for a removable box: waterproof bulkhead connectors (RJ45 coupler, Anderson SB50) to disconnect at the wall without unsealing.
β Wiring sheets B-revision COMPLETE (rev 3.55): all of sheets 1β6 redrawn to the two-cluster architecture (combiner + bed lights on the BED board; DC Monitor V-sense; D-151 for O1; True Mods boxes; compressor out) in the clearer Sheet-8 layout style (strict LR columns, orthogonal routing), and Sheet 8 (
full-wiring-8-bed-box) covers the Β§6.5.1 enclosures + gland plan.