--- 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) > 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 fans** | Driven low-side (MOSFET PWM) + tach. ⚠ They load the same 5 V the modem bursts on — mitigated with a **dedicated fan rail** (see §0a). | | 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. | --- ## 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. Two 5 V fans add **~0.3–0.5 A plus inrush**. Coincident worst case ≈ 2.8–3.0 A sits right on 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 fans their **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 fans to the main 5 V if you later upsize the supply — one board, both strategies. **Do not power fans from the *switched* (load-switch) stack 5 V** — fans 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 | **2× fan drivers + 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 (fans + temperature) ─────────────────────────────────────── PA6 FAN1_PWM TIM3_CH1 25 kHz PWM → fan-1 low-side gate PA7 FAN2_PWM TIM3_CH2 25 kHz PWM → fan-2 low-side gate PB10 FAN1_TACH EXTI fan-1 tach (open-collector, 10k pull-up to 3V3) PB12 FAN2_TACH EXTI fan-2 tach (open-collector, 10k pull-up to 3V3) 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/CH2 (PA6/PA7) do fan PWM; PB0 (TIM3_CH3) stays plain-GPIO LED. The reserved USART1 pair future-proofs the board for the [[03-Architecture-Hardware/watchdog-health-telemetry|`$PHLG` health-in-server]] upgrade without a respin. --- ## 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 ─┬─► Fan1 (+) ───┐ ┌─── Fan1 tach ─10k─► PB10 │ Fan1 (−) ─ Q2 (AO3400) ◄PWM PA6 │ (D2 SS34 flyback ∥ fan) └─► Fan2 (+) ───┘ └─── Fan2 tach ─10k─► PB12 Fan2 (−) ─ Q3 (AO3400) ◄PWM PA7 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 (e.g., **AO3401A** if fan+buck draw 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 drivers ×2 (low-side PWM + tach) — Q2/Q3 Per fan (identical for fan-1 and fan-2): ``` 5V_FAN ──► Fan(+) Fan tach ──[R_t 1k]──┬──► PBxx (EXTI) Fan(−) ──┬──► Q2/Q3 Drain │ D2/D3 SS34 ∥ fan │ (AO3400A, N-MOS) [R 10k pull-up → 3V3] (cathode→5V_FAN) │ Source ── GND PWM PAx ─[Rg 100Ω]─ Gate ──[Rgs 100k → GND] ``` - **Q2/Q3 = 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/D3 = SS34** Schottky **flyback** across each 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) | R9 10k / R10 10k (÷2) | C9 100 nF | **D5 BAT54 clamp to 3V3 + 100 Ω series**; the DFR1015 already bucks 24 V→logic, divider+clamp is belt-and-suspenders | 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, Fan1−(PWM), GND, Tach1 → fan 1 | | P7 | JST-XH 4-pos | 4 | 5V_FAN, Fan2−(PWM), GND, Tach2 → fan 2 | | 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_FAN1_PWM PA6`, `PIN_FAN2_PWM PA7`, `PIN_FAN1_TACH PB10`, `PIN_FAN2_TACH PB12`; init TIM3 CH1/CH2 PWM at **25 kHz**; attach EXTI counters on the tach pins. 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**: extend the v2.1 `TLM`/`$PHLG` record with `tair,rh,tbat,fan1_rpm,fan2_rpm,fan_pwm,fan_fault`. These are exactly the fields the `$PHLG`-to-server upgrade will carry — capture them on-unit now via the STREAM log. 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; the implementation lands in `blackpillrtkcode_v2.ino` **after** bench validation on the prototype board. --- ## 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. **Fans**: ramp PA6/PA7 PWM 0→100 %, scope the gate + fan node, confirm flyback, read **tach RPM** vs PWM; pull a 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**: 5 V **3-wire** (tach) confirmed? Size (40/50/60 mm) + 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. - [ ] **Battery**: still drop / LiFePO4 / supercap (hardware brief §6.1). Board only exposes P4 + sense; charger stays external until decided. - [ ] Confirm the **DFR1015** stays an external module (P1 IGN) or folds on-board later. - [ ] **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]]