--- tags: [project-drogheda, hardware, watchdog, pcb, thermal, fans, schematic, v3] project: Project Drogheda author: Helgen CTO date: 2026-06-21 status: Active — design for review; bench-validate before fab supersedes_section: hardware-respec-and-pcb-brief.md §4 (watchdog carrier detail) --- # HBOX Watchdog Carrier PCB — v3.0 Design (Watchdog + Smart-Fan Thermal) > ⚠️ **Current electrical state (2026-06-24): see > [[03-Architecture-Hardware/watchdog-electrical-design-state|watchdog-electrical-design-state.md]] > (CANONICAL).** Since this doc, the DFR1015 folded **on-board** (GX16 pin 3 = raw ignition), the > **battery was re-added** (LTC4040 + NTC temp-qualified charging on the 1S4P pack), and **reset + > 4-LED gauge + power-cutoff** were added. Where this doc disagrees with the canonical state, the > canonical state wins. (Also: 1 fan, not "two" — see the 1-fan update.) > The production PCB for the STM32F411 watchdog, **expanded** to drive two > extraction fans and monitor internal temperature/humidity. This is the > schematic-capture spec: every sub-circuit, every value, every part, the pin > map, the firmware deltas, and the layout/fab path. Pairs with: > [[03-Architecture-Hardware/watchdog-pcb-v3-bom|v3 BOM (xlsx)]] · > [[03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide|KiCad + JLCPCB guide]] · > [[04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2|firmware v2.1]] · > [[03-Architecture-Hardware/watchdog-schematic|as-built watchdog]]. **Decisions driving this revision (2026-06-21, Leo):** | # | Decision | Consequence | |---|---|---| | 1 | **Scope = watchdog + thermal *carrier*** | Blackpill plugs on via 2×20 headers (reuse v2.1 firmware, no MCU bring-up). Mean Well SD-15B-05 and the battery stay **external modules**. Lowest-risk first board. | | 2 | **5 V fan ×1** | One extraction fan, low-side (MOSFET PWM) + tach. ⚠ Loads the same 5 V the modem bursts on — mitigated with a **dedicated fan rail** (§0a). Needs a **passive filtered inlet** to pull air through, and has **no redundancy** — a fan failure falls back to conduction + thermal derate (tach-fault flags it). | | 3 | **I²C temp + humidity** | SHT40 (board ambient + RH) + TMP102 probe (battery/modem hot spot) on one I²C bus. | | 4 | **JLCPCB with assembly** | Parts chosen from JLC/LCSC stock; we output Gerbers + BOM + CPL. Footprints biased to JLC-placeable + hand-reworkable. | | 5 | **Live telemetry ($PHLG): bench-test first, NOT built into v3** (2026-06-22) | The 4G modem keeps its proven home on the F9P **XBee socket = UART2**; there is **no spare UART** to tap (see §2a). v3 ships health on the **USB STREAM log** (already works). PA9/PA10 header is **reserved/DNP** only. The forwarding question is settled by an isolated **bench experiment** (§7a), not by a board change. | --- ## 0. Read this first — two engineering caveats ### 0a. 5 V fans must NOT share the RTK stack rail The Mean Well **SD-15B-05** gives **5 V @ 3 A**. The RTK+4G stack already peaks **~2–2.5 A on LTE-TX bursts** — the brownout the whole redesign is fixing. One 5 V fan adds **~0.15–0.25 A plus inrush**. Coincident worst case ≈ 2.7 A still nears the supply limit, and **fan inrush + a modem burst at the same instant is exactly the sag that drops the link.** So the board gives the fan its **own 5 V** from a small **24→5 V buck** tapped off the cabinet 24 V (which is already inside the HBOX). A **0 Ω jumper (JP1)** lets you instead bridge the fan to the main 5 V — with a single ~0.2 A fan that's more defensible than with two, but the dedicated rail stays the safe default. **Do not power the fan from the *switched* (load-switch) stack 5 V** — it must run when hot even while the stack is power-cycling. ### 0b. Fans + IP67 is a real tradeoff — own it **Moving air in/out of the box breaks the IP67 seal.** A fan only helps if it *exchanges* HBOX/cabinet air with outside air; that path is an opening. The honest position for the v3 mechanical: - Ventilation drops the rating from **IP67 → ~IP54/IP55** at best, *with* filtered, hooded, downward-facing louvers. In a **salty, humid, dusty port** that is a maintenance item (filters clog/corrode). - Keep every electronic mitigation we already have: **conformal-coat the PCB** (acrylic/urethane), keep a **Gore pressure-vent** for condensation, and treat the fans as **assist**, not the whole thermal solution. **Conduction to the metal plate (hardware brief §2b) still does the heavy lifting**; fans buy the last few °C. - The firmware runs the fans **only when temperature demands it** (closed loop, §7), minimising the hours the vents are actively pulling dirty air. > If ICAVE can accept IP54 with filter maintenance, fans are fine. If they require > IP67, the answer is conduction + sunshade, not fans. **This board supports both** — > populate or depopulate the fan section. Confirm the rating target with ICAVE. --- ## 1. What the close look at the watchdog found The as-built watchdog (`watchdog-schematic.md`) + v2.1 firmware is **sound logic on fragile hardware**. Keep the brains, fix the body. **Keep (proven):** - The **high-side power gate** concept (PA1 → stack 5 V), **fail-safe OFF** at boot via the gate pull-up. This is the heart of 4G/NTRIP recovery — don't change its behaviour, only its build quality. - Three **sense dividers** (battery PA0, 5 V PA4, ignition PA5) and the v2.1 recovery state machine (backoff + LIMP + NMEA-silence + IWDG + MCU-thermal). - USART2 (PA2/PA3) to the F9P UART1 for NMEA read + UBX rate. **Fix (the liabilities):** | Liability | Root issue | v3 board fix | |---|---|---| | 2N3904 + IRF9540N + pull-up, hand-wired | vibration intermittents; IRF9540N is **not** logic-level (marginal gate drive from a 5 V swing) | SMD discrete high-side switch (**AO3401A** logic-level P-FET + **MMBT3904**) + **PPTC** overcurrent; optional eFuse upgrade | | LED + dividers on Dupont | mechanical | all on-board, 0603, conformal-coatable | | `1.5 kΩ "????"` mystery R | undocumented | resolved on this schematic (it was the **LED series R**; dividers are 10k/10k — see §6) | | MCU temp only | heat casualties are the **18650/modem**, not the die | **external** I²C sensors at the hot spot (§5) | | No thermal *actuation* | firmware can derate but can't cool | **1 fan driver + closed-loop control** (§4, §7) | | No SWD/header bring-out | can't debug a sealed unit | **SWD + USB + reserve-USART1** headers (§6) | --- ## 2. Expanded STM32F411 pin map (v2.1 pins kept; 6 added) ``` STM32F411 "Blackpill" (mounted on 2×20 headers) ── existing (firmware v2.1, unchanged) ─────────────────────────────────── PA0 BAT_SENSE ADC1_IN0 18650 ÷2 divider PA1 PWR_CTRL GPIO out → RTK-stack load switch enable (HIGH = ON) PA2 GNSS_TX USART2_TX → simpleRTK2B UART1 RX (UBX rate cmds) PA3 GNSS_RX USART2_RX ← simpleRTK2B UART1 TX (NMEA read) PA4 5V_SENSE ADC1_IN4 stack 5 V ÷2 divider (vehicle-power present) PA5 IGN_SENSE GPIO in ignition via DFR1015 (INPUT_PULLDOWN) PB0 EXT_LED GPIO out external status LED (low-side MOSFET) PC13 LED GPIO out onboard LED (active-LOW) ── NEW for v3 (fan + temperature) ──────────────────────────────────────── PA6 FAN_PWM TIM3_CH1 25 kHz PWM → fan low-side gate PB10 FAN_TACH EXTI fan tach (open-collector, 10k pull-up to 3V3) PA7 (spare) TIM3_CH2 reserved — 2nd fan PWM if ever needed PB12 (spare) EXTI reserved — 2nd fan tach if ever needed PB6 I2C1_SCL I2C1 SHT40 + TMP102 (4.7k pull-ups to 3V3) PB7 I2C1_SDA I2C1 SHT40 + TMP102 ── reserved (do not populate now; future in-band $PHLG telemetry relay) ─── PA9 USART1_TX header → future: inject $PHLG into modem uplink PA10 USART1_RX header ← future: F9P UART2 NMEA tap PA13 SWDIO / PA14 SWCLK SWD debug header PA11 USB_DM / PA12 USB_DP Blackpill micro-USB = STREAM log capture ``` No conflicts: TIM3_CH1 (PA6) does fan PWM; PB0 (TIM3_CH3) stays plain-GPIO LED. The reserved USART1 pair (H3) **future-proofs** the board for the [[03-Architecture-Hardware/watchdog-health-telemetry|`$PHLG` health-in-server]] upgrade without a respin — but in v3 it is **left unpopulated; no relay is wired** (see §2a + the decision row above). **PA10 caveat:** PA10 is USART1-RX *and* a known Blackpill DFU-entry pin — a load/float here can block flash mode. Keep H3 unpopulated, or fit a **~22 kΩ pull-up on PA10 to 3V3**, so this board never inherits the flaky-flashing gremlin. ### 2a. WeAct Black Pill header pinout — what lands on J1/J2 The module mounts on two 1×20 headers. From the WeAct **MiniF4x1Cx V3.0** schematic (`MiniF4x1Cx_V30 SchDoc.pdf`, headers P1/P2), the physical pin order is (pin 1 = VBAT end): | Pin | J1 = WeAct **P1** | carrier net | J2 = WeAct **P2** | carrier net | |--:|---|---|---|---| | 1 | VBAT | — | PB12 | — (spare) | | 2 | PC13 | — (onboard LED) | PB13 | — | | 3 | PC14 | — | PB14 | — | | 4 | PC15 | — | PB15 | — | | 5 | NRST | — | PA8 | — | | 6 | PA0 | **BAT_SENSE** | PA9 | **USART1_TX** (reserve) | | 7 | PA1 | **PWR_CTRL** | PA10 | **USART1_RX** (reserve, +Rdfu 22k) | | 8 | PA2 | **GNSS_TX** | PA11 | — (USB) | | 9 | PA3 | **GNSS_RX** | PA12 | — (USB) | | 10 | PA4 | **5V_SENSE** | PA15 | — | | 11 | PA5 | **IGN_SENSE** | PB3 | — | | 12 | PA6 | **FAN_PWM** | PB4 | — | | 13 | PA7 | — (spare) | PB5 | — | | 14 | PB0 | **EXT_LED** | PB6 | **I2C_SCL** | | 15 | PB1 | — | PB7 | **I2C_SDA** | | 16 | PB2 | — (BOOT1, pulled low on-module) | PB8 | — | | 17 | PB10 | **FAN_TACH** | PB9 | — | | 18 | 3V3 | **3V3** (LDO out) | 5V | **MAIN5V** (in) | | 19 | GND | **GND** | GND | **GND** | | 20 | 5V | **MAIN5V** (in) | 3V3 | **3V3** (LDO out) | **Consequences (caught from the real pinout):** - **SWD (PA13/PA14) is NOT on the 2×20** — it's the module's separate 4-pin SWD header. → **H1 dropped**; flash/debug via the Blackpill **USB-C**. - **5V pin = power IN, 3V3 pin = LDO OUT.** `MAIN5V` feeds the 5V pin; the module's 3V3 LDO output *is* the carrier `3V3` rail (sensors + I²C/tach pull-ups). - **BOOT1 (PB2)** is pulled low on the module — no carrier action needed. - USB (PA11/PA12) is on the header but unused (we flash via the USB-C connector). ### 2b. Why there is no spare UART (the telemetry reality) The simpleRTK2B **Pro** breaks out **both** F9P UARTs, but only one is free: | F9P port | Header pins | Used by | Free? | |---|---|---|---| | **UART1** | TX1 / RX1 | **watchdog** (NMEA read + UBX rate) — PA2/PA3 | taken | | **UART2** | TX2 / RX2 **and the XBee socket** | **4G NTRIP Master** (server uplink) | **no — shared bus** | `TX2` = F9P-UART2-TX = the **modem's DIN** (the wire the modem reads and forwards to the server). The F9P already drives it, so the watchdog can't simply "connect a second wire" to send `$PHLG` — that's two transmitters on one line. Getting health to the server therefore needs a **relay** (interpose the MCU between F9P-UART2 and the modem), which is a deliberate architecture change **deferred out of v3**. To *learn whether it's even possible*, run the isolated bench experiment in **§7a** — it touches no PCB. --- ## 3. Block diagram ```text Cabinet 24 V ──GX16-5(pin1/2)──► P1 [24V_IN] │ ├─► reverse-pol P-FET (Q10) + TVS (D10, SMBJ33A) ─► 24V rail │ │ ├──────────────────────────────► FAN buck in │ (ignition 24V ─GX16 pin3─► DFR1015 ext module ─► P1[IGN] ─► PA5 divider) │ Mean Well SD-15B-05 (EXTERNAL, 24→5 V 3 A) ──► P2 [5V_IN] ─► MAIN 5V rail MAIN 5V ─► Blackpill 5V pin (onboard LDO → 3V3) MAIN 5V ─► high-side load switch (Q1 AO3401A / U1 eFuse opt) ─EN◄PA1─► SW_5V ─► P3 simpleRTK2B + 4G MAIN 5V ─► PA4 sense divider 18650 / PowerBoost (EXTERNAL) ──► P4 [BAT+ , BAT_SENSE] ─► PA0 divider FAN rail: 24V ─► U2 buck (MP1584EN) ─► 5V_FAN (JP1: alt-bridge to MAIN 5V) 5V_FAN ───► Fan (+) ┌─── Fan tach ─10k─► PB10 Fan (−) ─ Q2 (AO3400) ◄PWM PA6 ┘ (D2 SS34 flyback ∥ fan) I²C1 (PB6/7) ─┬─ SHT40 (U3, 0x44) board ambient temp + RH └─ TMP102 (U4, 0x48) on P5 pigtail → battery/modem hot spot PB0 ─► Q4 (AO3400) ─► status LED (D1) SWD/USB/USART1 headers (H1/H2/H3) ``` --- ## 4. Sub-circuit detail (capture this exactly) ### 4.1 24 V input protection (light — the cabinet does the heavy lifting) The cabinet already has the Phoenix SPD + breaker, so the board needs only: - **Q10** reverse-polarity P-MOSFET in the 24 V high side. **⚠ Corrected (review Finding 1): AO3401A (±12 V Vgs) and SI2305 (−20 V Vds) are NOT rated for a 24 V rail and would fail.** Use **Vds ≥ −40 V, Vgs ≥ ±20 V** — e.g. **IRFR5305** (−55 V/±20 V, DPAK; verify LCSC stock); the proto can use a TO-220 **IRF9540** (−100 V/±20 V). Add a **gate→source Zener clamp DzG ≈ 15 V** (A→Q10.G, K→Q10.S) **with Rrp 10 k gate→GND**: the gate sits ~9 V, Vgs ≈ −15 V (in rating, fully on). Buck input current is ~0.2 A — the part is sized by *rating*, not current. - **D10** TVS **SMBJ33A** (33 V standoff) 24 V→GND, plus **C10** 100 µF/50 V electrolytic + **C11** 100 nF bulk/decoupling. ### 4.2 Dedicated fan 5 V buck (U2) — see caveat §0a - **U2 = MP1584EN** (SOP-8, 4.5–28 V in, up to 3 A) set to **5.0 V** out. - FB divider for 5 V: **R20 = 100 kΩ top**, **R21 = 19.1 kΩ bottom** (Vout = 0.8 V·(1+R20/R21) ≈ 5.0 V — verify against the MP1584 datasheet FB = 0.8 V). - **L2 = 10 µH** (≥1 A sat), **C22 = 22 µF** in, **C23 = 22 µF** out, freewheel Schottky per datasheet (SS34), EN tied to 24V-rail-derived enable. - **JP1**: 0 Ω / solder-jumper. **Open** = fans on U2 (recommended). **Closed** = fans bridged to MAIN 5 V (only if SD-15B-05 is upsized; never both). - Keep U2's switching node small; **keep this buck away from the GNSS antenna feed** (RF cleanliness — see §8). > Simpler alternative if you'd rather not lay out a buck on the first spin: drop U2, > close **JP1**, and **upsize the external 5 V supply to a 5 V/5 A** unit so the rail > has true headroom. The board supports it; it's a BOM/jumper choice, not a respin. ### 4.3 RTK-stack high-side load switch (Q1 trio — proven, SMD) Keeps the as-built topology (so **firmware PA1 logic is unchanged**) but in solid, sourceable SMD, plus a resettable fuse for the missing current limit: ``` MAIN 5V ──[F1 PPTC 3A]──┬───────────────[R2 10k]──┐ │ │ Q1 AO3401A Source Gate ── Q5 MMBT3904 Collector (P-MOS) │ │ Drain ──► SW_5V ─► P3 stack Q5 Emitter ── GND │ PA1 ──[R1 1k]── Q5 Base ───────────────┘ ``` - PA1 **HIGH** → MMBT3904 on → P-FET gate low → **SW_5V ON**. PA1 LOW → R2 holds gate high → **OFF** (fail-safe at boot). Identical to as-built. - **AO3401A** is logic-level and switches fully from the 5 V gate swing (the old **IRF9540N was not** — a real latent bug we're removing). - **F1 PPTC** (3 A hold / ~6 A trip, e.g., **MF-MSMF300**) gives overcurrent protection without a hard-to-source eFuse, and **cannot permanently latch a healthy unit out** (it self-resets) — satisfies the "never black-hole" invariant. - **C3 = 47 µF** bulk on SW_5V (modem-burst reservoir). - **Optional upgrade footprint U1**: pin-compatible-ish current-limit load switch / eFuse (e.g., **AP2553W6-7** SOT-23-6, EN active-high, FLAGn). Lay the footprint *and* the discrete; populate one. Verify LCSC stock at order; **default = discrete**. ### 4.4 Fan driver ×1 (low-side PWM + tach) — Q2 Single extraction fan: ``` 5V_FAN ──► Fan(+) Fan tach ──[R_t 1k]──┬──► PB10 (EXTI) Fan(−) ──┬──► Q2 Drain │ D2 SS34 ∥ fan │ (AO3400A, N-MOS) [R 10k pull-up → 3V3] (cathode→5V_FAN) │ Source ── GND PWM PAx ─[Rg 100Ω]─ Gate ──[Rgs 100k → GND] ``` - **Q2 = AO3400A** logic-level N-MOSFET (SOT-23): full-on from 3.3 V PWM, 5.7 A / 30 V — vast margin over a <0.5 A fan. - **D2 = SS34** Schottky **flyback** across the fan (motor is inductive — protects the FET at PWM turn-off). Cathode to 5V_FAN, anode to drain. - **Rg 100 Ω** gate series (tames edges/EMI), **Rgs 100 kΩ** gate→GND pull-down = **fans OFF until the MCU drives PWM** (fail-safe; consistent with the ethos — if the MCU is dead the stack is also unpowered, so there's nothing to cool). - **Tach**: 3-wire fan's open-collector tach → **10 kΩ pull-up to 3.3 V** + **1 kΩ** series into the EXTI pin. 2 pulses/rev → MCU computes RPM and flags a stalled/failed fan. **Use 3-wire (or 4-wire) fans so tach exists.** PWM is on the **ground leg**, so on a 3-wire fan the tach is valid only while PWM is high — firmware samples RPM during a brief 100 % window (§7). *(4-wire fans avoid this entirely; if you can source 5 V 4-wire, drive their PWM pin from PAx via a 2N7002 open-drain and leave power always-on — cleaner tach. 5 V 4-wire fans are rarer, hence 3-wire default.)* ### 4.5 Status LED (unchanged topology, SMD) `3V3 ─[R3 1k]─►|D1 LED|─► Q4 (AO3400) drain; source→GND; PB0 ─ gate, [10k → GND] pull-down.` (Resolves the "1.5 kΩ ????" — it was this series resistor; 1 kΩ is the clean value for a 3.3 V drive.) ### 4.6 Sense conditioning (filtered; protect the ignition input) | Net | Divider | RC filter | Protection | |---|---|---|---| | PA0 BAT (3.0–4.2 V) | R5 10k / R6 10k (÷2) | C5 100 nF | — | | PA4 5V (≈5 V) | R7 10k / R8 10k (÷2) | C7 100 nF | — | | PA5 IGN (from DFR1015) | **none — direct** | C9 100 nF | **Corrected (Finding 2): Rig 100 Ω series + D5 BAT54 clamp to 3V3, NO ÷2.** PA5 is a *digital* input (`INPUT_PULLDOWN`); the DFR1015 logic level reaches PA5 undivided (a ÷2 would drop it below VIH). Internal pulldown = LOW when ignition off; clamp caps ≤3.3 V. | ADC stays ≤3.3 V with margin (verified in firmware-review). Firmware `DIVIDER_RATIO = 2.0` matches the 10k/10k legs. --- ## 5. Temperature + humidity sensing (I²C1) | Ref | Part | Addr | Where | Why | |---|---|---|---|---| | U3 | **SHT40-AD1B-R2** (DFN-4) | 0x44 | on-board, near an air-path edge | board-ambient **temp + RH** (RH flags condensation risk — IP/coastal) | | U4 | **TMP102AIDRLR** (SOT-563) | 0x48 (ADD0→GND) | on **P5 pigtail** to the 18650/modem | the **actual heat casualty** spot — the data the thermal RCA needs | - One bus, **4.7 kΩ pull-ups** (R30/R31) on SCL/SDA to 3.3 V. Both 3.3 V parts. - 100 nF decoupling each. SHT40 wants a small keep-out/slot so it reads air, not board. - Firmware reads all three temps (SHT40, TMP102, **plus** the existing MCU-internal) → triangulated thermal picture and the fan-control input (§7). --- ## 6. Connectors & headers (all keyed/locking) | Ref | Connector | Pins | Net | |---|---|---|---| | P1 | GX16-5 (HBOX↔cabinet, **keep**) | 5 | 24V_IN, GND, IGN, (spare ×2) | | P2 | Molex Micro-Fit 3.0, 2-pos | 2 | 5V_IN from SD-15B-05, GND | | P3 | JST-XH 4-pos | 4 | SW_5V, GND, GNSS_TX(PA2), GNSS_RX(PA3) → simpleRTK2B | | P4 | JST-XH 2-pos | 2 | BAT+ (to PA0 divider), GND (18650/PowerBoost) | | P5 | JST-XH 4-pos | 4 | 3V3, GND, SDA, SCL → TMP102 probe pigtail | | P6 | JST-XH 4-pos | 4 | 5V_FAN, Fan−(PWM), GND, Tach → fan | | P8 | JST-XH 2-pos | 2 | EXT_LED+, EXT_LED− (panel LED) | | H1 | 1×4 0.1″ | 4 | SWD: 3V3, SWDIO, SWCLK, GND | | H2 | (Blackpill micro-USB) | — | STREAM log capture / flashing | | H3 | 1×3 0.1″ **DNP** | 3 | reserve USART1: PA9, PA10, GND (future $PHLG) | | — | 2× 2×20 female headers | 40 | Blackpill seats here | > **Pin-1 / keying:** silkscreen pin-1 arrows + the GX16 keyway; JST-XH is already > keyed. Label every connector net on silk (DFM/serviceability requirement). --- ## 7. Firmware deltas (spec only — bench-test before any field flash) The v2.1 `.ino` changes are **small and additive** (the ethos: bench first; the sandbox can't build STM32). New globals + functions, no state-machine surgery. 1. **Pins**: add `PIN_FAN_PWM PA6`, `PIN_FAN_TACH PB10` (PA7/PB12 stay spare); init TIM3 CH1 PWM at **25 kHz**; attach an EXTI counter on the tach pin. 2. **I²C temps**: `Wire.begin()` on PB6/PB7; read SHT40 (0x44, temp+RH) and TMP102 (0x48). Add `tAirC`, `rhPct`, `tBatC` to telemetry. Keep MCU-internal as cross-check. 3. **Closed-loop fan curve** (input = `max(tAirC, tBatC)`): - `< 45 °C` → fans **off** (0 % — minimise vent hours / dust ingress). - `45–70 °C` → **linear 30 → 100 %** PWM. - `≥ 70 °C` → **100 %**. - **Hysteresis 3 °C** on the off→on edge (no chatter). This *replaces nothing* — the existing `THERM_WARN_C 75` derate-to-30 s still stands as the last resort. 4. **Tach fault**: once/30 s, command a fan to **100 % for ~300 ms**, count tach edges, compute RPM. `0 RPM while commanded > 0` → latch `FAN_FAULT`, log `EVT`, raise it in telemetry. A dead fan in a hot box must be **visible**, not silent. 5. **Telemetry (on-unit only in v3)**: extend the v2.1 `TLM` record with `tair,rh,tbat,fan_rpm,fan_pwm,fan_fault`, captured to the **USB STREAM log**. These are the same fields a future `$PHLG`-to-server sentence would carry — but **v3 does NOT push them live to the server** (no UART relay; see §2a + decision row 5). Live telemetry is gated on the bench experiment below. 6. **IWDG-safe**: fan/tach/I²C work is non-blocking; keep petting the dog. I²C read uses a short timeout so a stuck sensor can't hang the loop. Pin map and control law live here; **implemented in v2.3** (`blackpillrtkcode_v2.ino`) — closed-loop fan + I²C SHT40/TMP102 + tach fault, with MCU-temp fallback. **Bench-test before any field flash** (the sandbox can't build STM32; the logic was host-verified). ### 7a. The one bench experiment that decides live telemetry (no PCB change) > **✅ RESULT — 2026-06-22: PASS (no extra hardware needed).** Enabled `$PUBX` (a u-blox > **proprietary** `$P…` talker — same class + checksum format as `$PHLG`) on the F9P > **UART2** and watched the server `udp/5010` capture: `$PUBX,00,…` arrives **end-to-end, > intact**, alongside GGA/RMC. → **The 4G NTRIP Master forwards proprietary `$P…` > sentences, so `$PHLG` health-in-server is confirmed viable.** No XBee-breakout test > needed. Remaining work = the **UART2 relay** (interpose the watchdog to inject `$PHLG`) > \+ backend parser — a deliberate later board, still out of v3 scope. > > *Incidental (flagged for GNSS follow-up):* the same capture caught two live units — > bench **H0014 (Querétaro) at SPS (GGA quality 1), RSSI −93**, and field > **H0017 (Veracruz/ICAVE) at DGPS (quality 2, RMC mode `D`, diff base id 0133), RSSI −51**. > Per Leo the bench is in a **basement** (poor sky + weak cellular) — which alone explains > the bench SPS, since **RTK fix needs open sky (carrier-phase) regardless of > corrections/config**. But note H0017 has *good* sky+signal and still only reaches **DGPS, > not RTK (4/5)** → the **corrections pipeline** (free rtk2go caster + RAM-only F9P config) > is a *separate* ceiling from antenna placement. One **open-sky test with a proper caster + > FLASH-saved config** separates the two. (Reinforces the v2.1 SPS-thrash guard: a healthy > basement/field unit may legitimately sit at SPS/DGPS — do **not** power-cycle it.) Settles "does the 4G NTRIP Master forward a proprietary `$P…` sentence?" by **isolating the modem** — the F9P is not involved, so there's no bus collision: 1. Remove the **4G NTRIP Master** from the F9P XBee socket; mount it on a bench/XBee adapter with **3.3 V power, the SIM, and the LTE antenna**. 2. Wire **Blackpill PA9 (USART1 TX) → modem DIN** (the `TX2`/F9P-UART2-TX net in the socket) and **modem DOUT → Blackpill PA10 (RX)** *or* a USB-UART so a PC can watch it. 3. Match the modem's UART baud (ArduSimple default **115200**, 3.3 V). Let it register. 4. Emit `$PHLG,2.1,RUN,LIMP,1,12,34192,4.01,5.02,63,71,2,1,420,IWDG*CS\r\n` and watch the **server `tcpdump`**. - **Appears →** modem forwards proprietary sentences → `$PHLG` is viable → *then* plan a relay-capable board. - **Doesn't →** modem whitelists talkers → fall back to a `$GPTXT,HLT,…` subtype (special-cased by **field 1** so it never collides with the cell-id parse). Either way, **v3 ships unchanged** — this experiment only informs a *later* decision. --- ## 8. Layout, stackup & DFM (for a relative beginner — the rules that matter) - **Stackup**: **2-layer is acceptable** for this mostly-DC/low-speed board and is cheaper; **4-layer** (GND + power inner planes) is better for the GNSS RF and the buck noise — recommended if budget allows. Either way: **solid ground pour** on a full layer. - **Star-ground the power**: fan return currents (PWM, pulsed) and the modem-burst return must **not** share a thin trace with the ADC/sensor grounds. Bring power grounds to the pour at one stitching region; keep sensor/MCU ground quiet. - **Buck (U2) discipline**: tiny switch-node copper, input cap **right at** the IC, ground the cap to the pour with vias. **Keep U2 and the fan-PWM traces away from the GNSS antenna pigtail / P3** — switcher harmonics degrade the ZED-F9P front-end (= the watchdog's own "link health" signal). - **Trace widths** (1 oz Cu): 24 V_IN and 5 V rails ≥ **0.5 mm** (fan + stack current); SW_5V to P3 ≥ **0.8 mm** (modem bursts); signals 0.25 mm fine. - **Thermal**: copper pour + vias under Q1/Q10/U2 tabs. Mounting holes positioned to let the board **bolt to the cabinet metal plate** (conduction path, hardware brief §2b). - **Creepage**: nothing >24 V here, so standard clearances are ample; still keep the 24 V input section tidy. - **Conformal coat** after assembly (mask connectors/headers/SHT40 slot) — humidity + vibration joint stability. - **JLC specifics**: 0603 passives (hand-reworkable *and* machine-placeable), SOT-23 actives, 1.6 mm FR-4, HASL or ENIG. Add **fiducials** if you want tight placement. Put the JLC order number on silk (free, helps reorders). --- ## 9. Bring-up & test plan (prototype first) 1. **Bare-board power**: 24 V in, confirm **5V_FAN = 5.0 V**, reverse-polarity FET blocks a flipped input, TVS not hot. 2. **Load switch**: toggle PA1 → SW_5V on/off; confirm OFF at reset; PPTC holds the measured stack current, trips on a deliberate short. 3. **Fan**: ramp PA6 PWM 0→100 %, scope the gate + fan node, confirm flyback, read **tach RPM** vs PWM; pull the fan → confirm `FAN_FAULT`. 4. **Sensors**: I²C scan finds 0x44 + 0x48; heat the TMP102 probe → fan ramps per curve. 5. **Power budget (the §0a check)**: with **JP1 open**, scope MAIN 5 V during an LTE burst **and** a fan ramp simultaneously — confirm the stack rail no longer sags. Repeat with JP1 closed to *demonstrate* the coupling you're avoiding. 6. **Full stack**: mount Blackpill + F9P + 4G, run v2.1+fan firmware, watch the STREAM log for temps/RPM, confirm RTK recovery behaviour is unchanged. 7. Only then: pilot 2–3 boards in real cabinets before the fleet (mirrors the August-30-unit ramp). --- ## 10. Open questions / decisions - [ ] **IP rating target** with ICAVE: IP67 (no vents → conduction only) vs IP54/55 (filtered louvers + fans). Determines whether the fan section is populated. - [ ] **Fan choice (×1)**: 5 V **3-wire** (tach), size (40/50/60 mm), + a **filtered passive inlet** so the single extraction fan can pull air through. Measured static-pressure need through the chosen duct/filter. Source 2–3 candidates. - [ ] **U2 buck vs upsize supply** (§4.2): lay the buck (default) or go 5 V/5 A external + JP1-closed. Recommend laying the buck for rail isolation. - [x] **Battery — RESOLVED (Finding 3): default = dropped (vehicle-powered only)** — removes the worst thermal/safety liability and makes the connection list correct as-is (MAIN5V = SD-15B-05 powers the MCU; **PA4 senses the vehicle-side 5 V**; P4 = sense-only, battery DNP). *If ICAVE needs parked tracking*, fit LiFePO4/supercap on P4 **and move the PA4 sense tap to the SD-15B-05/P2 (vehicle) node**, not the battery-backed rail. - [x] ~~Confirm the **DFR1015** stays external or folds on-board~~ → **RESOLVED 2026-06-24: folds ON-BOARD.** GX16 pin 3 now carries raw machine ignition (12–24 V) into the on-board DFR1015 → Rig 100 Ω → D5 BAT54 clamp 3V3 → PA5 (digital, no divider). - [ ] **Load switch**: discrete (default, sourceable) vs eFuse U1 — verify AP2553 LCSC stock at order; otherwise ship discrete. ## Related - [[CLAUDE|Master Index]] · [[03-Architecture-Hardware/watchdog-schematic|as-built watchdog]] · [[03-Architecture-Hardware/hardware-respec-and-pcb-brief|hardware re-spec + PCB brief]] · [[03-Architecture-Hardware/watchdog-health-telemetry|health telemetry ($PHLG)]] · [[03-Architecture-Hardware/BOM-master|BOM master]] · [[03-Architecture-Hardware/watchdog-pcb-v3-bom|v3 PCB BOM (xlsx)]] · [[03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide|KiCad + JLCPCB guide]]