--- tags: [project-drogheda, hardware, watchdog, connections, review, firmware, verification] project: Project Drogheda author: Helgen CTO date: 2026-06-22 status: Review — read before populating the protoboard or releasing Gerbers reviews: watchdog-connections.xlsx (128-row from→to list) against: blackpillrtkcode_v2.ino v2.2 · watchdog-pcb-v3-design.md --- # Watchdog Connection List — Justification & Firmware Cross‑Check > Every connection in [[03-Architecture-Hardware/watchdog-connections|`watchdog-connections.xlsx`]] > justified — **where it goes, how it's wired, why** — and checked **against the actual > firmware** (`blackpillrtkcode_v2.ino` v2.2). This pass found **3 issues to fix before fab** > (one is a part‑rating error) and confirms the rest match the code. Read §5 first if you're > short on time. --- ## 1. Method Three sources had to agree: the **connection list** (the wires), the **firmware** (what each pin actually does), and the **design doc** (the intent + values). I walked every Blackpill pin in the firmware, found its net in the connection list, and confirmed the sub‑circuit on the other end does what the code assumes. Discrepancies are called out as **findings (§5)**. Legend: ✅ wired *and* used by firmware today · 🟡 wired, **firmware pending** (v3 thermal, spec'd in design §7 but not yet coded) · ⚪ system/spare · 🔴 issue to fix. --- ## 2. Firmware ↔ connection cross‑check (the core table) | Blackpill pin | Firmware v2.2 (`#define` / use) | Connects to (list) | Status | Match | |---|---|---|---|---| | PA0 | `PIN_BAT_SENSE` → `analogRead`×`DIVIDER_RATIO 2.0` | R5(10k)→BAT+, R6(10k)→GND, C5 | ✅ | ÷2 matches the ×2 in code | | PA4 | `PIN_5V_SENSE` → `analogRead`×2.0; `V5V_ON 4.2/OFF 3.8` | R7(10k)→MAIN5V, R8(10k)→GND, C7 | ✅ | ÷2 correct; **see Finding 3 (tap point)** | | PA5 | `PIN_IGN_SENSE` → `digitalRead`, `INPUT_PULLDOWN` | R9/R10(÷2) from DFR1015, D5 clamp, C9 | ✅ | digital — **see Finding 2 (level)** | | PA1 | `PIN_PWR_CTRL` → `OUTPUT`, HIGH=stack ON | R1(1k)→Q5 base → Q1 P‑FET | ✅ | HIGH→NPN→gate low→P‑FET on ✓ | | PA2 | `GNSS` TX (USART2); floated on power‑off | P3.3 → simpleRTK2B UART1 RX | ✅ | TX→RX ✓ | | PA3 | `GNSS` RX (USART2); floated on power‑off | P3.4 ← simpleRTK2B UART1 TX | ✅ | RX←TX ✓ | | PB0 | `PIN_EXT_LED` → `OUTPUT` (HIGH=on) | Q4 gate; Rled(10k)→GND; D1+R3 | ✅ | active‑high via N‑MOS ✓ | | PC13 | `PIN_LED` → `OUTPUT`, active‑LOW (`!ext`) | onboard (internal) | ✅ | mirrors PB0 inverted ✓ | | PA6 | — (not in firmware) | Rg→Q2 fan gate (PWM) | 🟡 | wired per design §7; **code pending** | | PB10 | — | fan tach → EXTI | 🟡 | code pending | | PB6 | — | I²C1 SCL → SHT40+TMP102 | 🟡 | code pending | | PB7 | — | I²C1 SDA → SHT40+TMP102 | 🟡 | code pending | | PA7 | — | NC (TIM3_CH2 spare) | ⚪ | reserved 2nd fan | | PB12 | — | NC (EXTI spare) | ⚪ | reserved 2nd tach | | PA9 | `RELAY` TX *iff* `ENABLE_PHLG_RELAY` (default **0**) | H3.1 (DNP) → modem DIN | ⚪ | off in v3 ✓ | | PA10 | `RELAY` RX *iff* relay flag | H3.2 (DNP) ← F9P UART2 TX; Rdfu 22k→3V3 | ⚪ | off in v3; 22k = DFU‑safe ✓ | | PA13/PA14 | SWD (core) | H1.2 / H1.3 | ⚪ | debug | | PA11/PA12 | USB CDC (`Serial` = STREAM log) | micro‑USB | ✅ | log + flash | | PC14/PC15, PB2, BOOT0, NRST | boot/clock/reset | PB2→GND, BOOT0 button→3V3 | ⚪ | **Finding 2b** (BOOT pins) | **No pin is double‑assigned**, and no ADC/timer/UART peripheral collides: PA0/PA4 = ADC1, PA2/PA3 = USART2, PA6 = TIM3_CH1 (PB0 is TIM3_CH3 but used as plain GPIO so no PWM clash), PB6/PB7 = I²C1, PB10/PB12 = EXTI, PA9/PA10 = USART1. ✅ --- ## 3. Sub‑circuit justifications (where it goes, how, why) ### 3.1 Power input — 24 V → rev‑pol → TVS → fan buck - **P1 (GX16‑5)** brings the cabinet's **clean, regulated 24 V** + GND + isolated ignition. Keyed/locking because the #1 field failure was vibration on Dupont. The cabinet already has the SPD + breaker, so the board only needs **reverse‑polarity + a modest TVS** — not a full protection front‑end. - **Q10** sits in the 24 V high side as a reverse‑polarity gate: source to the load (24 V rail), drain to the input, **gate to GND** so a correctly‑polarised input turns it fully on (low drop) and a reversed input leaves it off. **🔴 This is where Finding 1 lives — the part rating is wrong for 24 V.** - **D10 (TVS)** clamps residual transients 24 V→GND; **C10 (100 µF)** is the input reservoir. - **U2 (MP1584)** bucks the protected 24 V to **5V_FAN** — a *dedicated* rail so the fan's inrush never sags the 5 V the modem bursts on (design §0a). **R20/R21** set 5.0 V. **JP1** can bridge 5V_FAN↔MAIN5V but stays **open** by default. ### 3.2 The 5 V rails + RTK‑stack load switch - **MAIN 5 V** enters at **P2** from the external Mean Well **SD‑15B‑05**. It powers the Blackpill's 5 V pin (→ onboard LDO → 3V3) and the load switch. - **Load switch (PA1‑driven):** `setPower(true)` drives **PA1 HIGH** → **R1(1k)** into the **Q5 (NPN)** base → Q5 conducts → pulls the **Q1 (P‑MOSFET)** gate toward GND → Vgs negative → Q1 **on** → **SW_5V** feeds the simpleRTK2B + 4G stack via **P3**. PA1 LOW → **R2(10k)** holds the gate at MAIN5V → Q1 **off**. This gives the firmware‑critical property: **stack OFF at boot/reset** (the gate pull‑up defines the off state before the MCU asserts) — the whole fail‑safe doctrine depends on it. **F1 (PPTC)** adds self‑resetting overcurrent (never black‑holes a healthy unit); **C3 (47 µF)** rides out LTE bursts on SW_5V. - **Why PA2/PA3 float on power‑off:** `setPower(false)` sets them to `INPUT` so the MCU can't back‑power the switched rail through a GPIO while the stack is down — the connection (PA2/3 → P3 UART) is correct precisely because the firmware manages this. ### 3.3 Sense dividers — PA0, PA4, PA5 - **PA0 battery (✅):** `vBat = analogRead/4095 · 3.3 · 2.0`. The **10k/10k ÷2** is exactly the `DIVIDER_RATIO 2.0` the code multiplies back, so 3.4–4.2 V → 1.7–2.1 V at the pin (no clipping). Drives `BAT_CRITICAL 3.40 / BAT_RECOVERY 3.70` (sleep/wake). - **PA4 5 V / vehicle‑present (✅, but Finding 3):** same ÷2; `v5v` compared to `V5V_ON 4.2 / V5V_OFF 3.8`. The catch is **what node it taps** — it must be the *vehicle‑side* 5 V (the SD‑15B‑05 output, which disappears when the machine is off), not a battery‑backed rail. - **PA5 ignition (✅, but Finding 2):** firmware does `digitalRead(PA5)==HIGH` with `INPUT_PULLDOWN`. So the chain only has to land a clean **logic HIGH** when ignition is on — and the ÷2 may pull it too low (Finding 2). D5 clamps PA5 ≤3V3; C9 debounces with the firmware's 750 ms. ### 3.4 Fan driver + tach (🟡 firmware pending) - **PA6 → Rg(100R) → Q2 gate**, **Q2 (logic‑level N‑MOS)** low‑side: drain = Fan(−) at **P6.2**, source = GND. **Rgs(100k)** gate→GND = **fan OFF until the MCU drives PWM** (fail‑safe, and it means an un‑programmed board can't spin the fan). **D2** flyback across the fan (K→5V_FAN, A→Fan(−)) catches the inductive kick at PWM turn‑off. - **Tach:** open‑collector fan tach → **Rtp(10k) pull‑up to 3V3** + **Rts(1k)** series → **PB10** (EXTI). Because PWM is on the **ground leg**, the tach is only valid while PWM is high — the (pending) firmware must read RPM in a brief 100 % window (design §7.4). - **Status today:** the v2.2 firmware does **not** drive PA6 or read PB10. So a freshly‑flashed board is electrically correct but **the fan won't move and tach won't read until the fan/temp code lands** (design §7). That's intended sequencing, not a wiring error — see Finding 4. ### 3.5 Temperature / humidity (🟡 firmware pending) - **PB6/PB7 = I²C1** to **U3 SHT40 (0x44)** and **U4 TMP102 (0x48, ADD0→GND)**, **R30/R31 (4.7k)** pull‑ups to 3V3. Two different addresses → one bus, no clash. U4 rides a **P5 pigtail** to the battery/modem hot spot (the actual heat casualty). Same status as §3.4: wired, code pending. ### 3.6 Status LED — PB0 / PC13 - Firmware drives **PB0 HIGH = LED on** through **Q4** (N‑MOS low‑side; LED + R3 from 3V3 to Q4 drain), with **Rled(10k)** holding the gate off at boot. **PC13** (onboard) mirrors it **inverted** (`digitalWrite(PIN_LED, !ext)`) because it's active‑LOW. The proto simplification (PB0 →330Ω→ LED →GND) keeps the **same HIGH=on polarity** — consistent with the code. ### 3.7 Reserved relay + debug/boot - **PA9/PA10 (H3, DNP):** only touched if `ENABLE_PHLG_RELAY=1`; default 0, so the default build ignores them. **Rdfu 22k on PA10→3V3** keeps DFU flashing reliable (PA10 is a bootloader‑sensitive pin) and is harmless on USART1 RX (idle‑high). **H1** = SWD. **BOOT0** button→3V3 = DFU; **PB2 (BOOT1)→GND** = boot‑from‑flash — see Finding 2b. --- ## 4. What I checked against the code, line‑for‑line - `analogRead(PIN_BAT_SENSE/PIN_5V_SENSE) … · DIVIDER_RATIO 2.0` ⇄ both dividers are 10k/10k (÷2). **Consistent.** - `digitalRead(PIN_IGN_SENSE)` + `INPUT_PULLDOWN` ⇄ PA5 is a **digital** input → the divider must deliver a solid HIGH (Finding 2), and the internal ~40 k pulldown sits in parallel with R10. - `digitalWrite(PIN_PWR_CTRL,HIGH)=on` ⇄ NPN→P‑FET gate‑pull‑down topology gives HIGH=on, **OFF at reset.** **Consistent.** - `setPower(false)` floats PA2/PA3 ⇄ UART pins go to the F9P only (no rail back‑feed). **Consistent.** - `PIN_EXT_LED` active‑high / `PIN_LED` active‑low ⇄ Q4 low‑side + onboard inverted. **Consistent.** - `ENABLE_PHLG_RELAY` default 0 ⇄ PA9/PA10 are DNP. **Consistent.** - No firmware references to PA6/PA7/PB6/PB7/PB10/PB12 (grep = 0) ⇄ those are 🟡 pending. **Consistent with "design ahead of code, by intent."** --- ## 5. Review findings — ✅ ALL CORRECTED 2026‑06‑22 > **Resolution summary** (applied across the design doc, BOM, connection list, and firmware): > **F1** — Q10 → a **−40 V/±20 V P‑FET** (IRFR5305; proto IRF9540) **+ a 15 V gate‑source > Zener (DzG)** + Rrp, so Vgs stays in rating. **F2** — ignition **÷2 removed**; the DFR1015 > logic level now reaches PA5 direct (Rig + clamp) → solid HIGH. **F3** — **battery resolved: > dropped (vehicle‑only)**; PA4 senses the vehicle‑side 5 V and the list is correct as‑is > (LiFePO4/supercap + moved sense‑tap documented if parked tracking is later required). > **F4** — the fan/temp firmware is now **implemented in v2.3** (closed‑loop fan + I²C SHT40/ > TMP102 + tach fault, MCU‑temp fallback). **F2b** — boot pins verified. Detail below is the *why*. ### Original findings (retained for the record — now corrected) ### 🔴 Finding 1 — Reverse‑polarity FET (Q10) is over‑stressed on 24 V *(blocking)* With source at the 24 V rail and gate at GND, **Vgs ≈ −24 V**. **AO3401A is rated ±12 V Vgs** (and −30 V Vds) — **it will be destroyed.** The design's alternate **SI2305 is −20 V Vds — also unsuitable** for a 24 V rail with transients. **Fix:** pick a P‑FET rated **Vds ≥ −40 V and Vgs ≥ ±20 V** (e.g., DMP3017SFG‑class), **and add a gate→source Zener clamp (~15 V) with the existing series resistor** so Vgs can never exceed the rating. Update Q10 in the BOM + the connection list (add `DzG` Zener: A→Q10.G, K→Q10.S). ### 🔴 Finding 2 — Ignition sense (PA5) may never read HIGH *(blocking if DFR1015 ≤ 3.3 V)* PA5 is a **digital** input. The DFR1015 conditions 24 V→logic, then the **÷2** halves it again; the **internal pulldown (~40 k ∥ R10 10 k ≈ 8 k)** shifts the ratio to ≈0.44. If the DFR1015 outputs **3.3 V**, PA5 sees **≈1.4 V — below VIH**, and `ignitionOn` stays false (breaking the 1 Hz/30 s rate switch *and* the brownout exit). **Fix (pick one):** (a) set DFR1015 to **~5 V** and keep the ÷2 (PA5 ≈ 2.2 V, solid HIGH); or (b) **drop R9/R10** and feed the DFR1015 output **directly** to PA5 through the 100 Ω series + D5 clamp (cleanest if DFR1015 ≤ 3.3 V). Confirm the measured DFR1015 output on the bench. ### 🔴 Finding 3 — `5V_SENSE` (PA4) must tap the *vehicle* side, and depends on the battery call "Vehicle‑power present" only works if PA4 senses a node that **drops when the machine is off** — i.e. the **SD‑15B‑05 output**, *before* any battery hold‑up. The connection list assumes the **battery is dropped** (MAIN5V = SD‑15B‑05 powers the MCU directly; PA4 senses MAIN5V; P4 is sense‑only). **If the 18650/PowerBoost is kept**, the MCU runs off the battery‑backed rail and **PA4 must instead tap the SD‑15B‑05/P2 node** (not the backed rail) — otherwise `v5v` reads ~5 V forever, `vehiclePower` never clears, and the cell drains. **Action:** close the **battery decision** (design §6.1). Drop‑battery → list is correct as‑is. Keep‑battery → add the PowerBoost between P2 and MAIN5V and move the PA4 tap to the vehicle side. ### 🟡 Finding 4 — Thermal pins are wired ahead of the firmware *(expected, track it)* PA6 (fan), PB10 (tach), PB6/PB7 (I²C) are in the list per design §7 but **not in v2.2**. A flashed board is correct but **inert on cooling** until the fan‑curve + I²C‑temp code is added (after the bench bring‑up). Don't be surprised when the fan doesn't spin on first power‑up. **Action:** implement design §7 (I'll do it once you confirm the temp sensor you bought). ### 🟡 Finding 2b — Boot pins (housekeeping) Confirm **PB2 (BOOT1) → GND** and **BOOT0** only high via its button — a stray pull‑up on either makes flashing flaky (matches the DFU notes). The list already shows PB2→GND and the **22 k on PA10**; just verify on the board. ### ✅ Everything else Load switch logic, both ADC dividers, the float‑on‑power‑off, LED polarity, the relay being default‑off, and the I²C addressing all **match the firmware**. No pin conflicts. --- ## 6. Verdict The connection list is **sound and matches the code** for the watchdog core (sensing, load switch, LED, UART, power‑off behaviour). **All review findings are now corrected (§5)** — the Q10 FET rating + 15 V gate Zener, the ignition direct‑feed, the battery decision (dropped / vehicle‑only with PA4 on the vehicle side), and the **v2.3 fan/temp firmware**. The design doc, BOM, connection list, and firmware are consistent and **Gerber‑ready**. For the **breadboard bring‑up**, none of this changes the bench steps: use a bench supply (correct polarity, so Q10/D10 are skipped anyway), confirm the ignition logic level with a meter, and the watchdog — now including the smart‑fan loop — comes up exactly as the firmware expects. ## Related - [[03-Architecture-Hardware/watchdog-connections|connection list (xlsx)]] · [[03-Architecture-Hardware/watchdog-pcb-v3-design|v3 design (sub‑circuits)]] · [[03-Architecture-Hardware/watchdog-protoboard-bringup|protoboard bring‑up]] · `04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino` (v2.2)