Drogheda/03-Architecture-Hardware/watchdog-connections-rationale.md

258 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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
> ⚠️ **Current electrical state (2026-06-24): see
> [[03-Architecture-Hardware/watchdog-electrical-design-state|watchdog-electrical-design-state.md]]
> (CANONICAL).** The PA5 ignition row's "R9/R10 (÷2)" is **superseded** — there is **no divider**
> (DFR1015 **on-board** → Rig 100 Ω → D5 BAT54 clamp → PA5 digital). EXT_LED (J1/14) was a real
> disconnect, now fixed. Reset, 4-LED gauge, power-cutoff, and the LTC4040 battery backup were added.
> 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 — **PCB only; the PROTO borrows PA7 for the LM35 (ADC1_IN7, 2026-07-03)** |
| 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 — **implemented on PROD 2026-07-02** (`R7.1` → `VEH5V`)
"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.
---
## 7. Additions 2026-07-02 — Block B / H / I per-connection rationale
> The production capture session (Blocks **B** LTC4040 backup · **H** Modo-B bypass relay ·
> **I** NTC→PB1) added rows **129–193** to `watchdog-connections.xlsx`. Specs live in
> [[03-Architecture-Hardware/watchdog-production-schematic-delta|watchdog-production-schematic-delta.md]]
> §4/§5b/§5c; this section records the **why** per connection, same as §3 did for the core.
> Battery status here supersedes §5's "battery dropped" resolution text — the pack was
> **re-added 2026-06-24** (canonical state doc §3.3) and **Finding 3 is now implemented on
> PROD** (PA4 divider top `R7.1` re-tapped to `VEH5V`, 2026-07-02).
| Connection | How it's wired | Why |
|---|---|---|
| **PB1 NTC divider — 1 % top (R42)** | `3V3 → R42 10 k 1 % → PB1`; NTC 10 k (P12, at the hot spot) → GND | The top resistor is the *reference half* of the thermometer — its tolerance adds directly to the reading. A 5 % top can shift the computed temperature by several °C right at the 45–70 °C fan/charge decision band; 1 % keeps divider error below the NTC's own spread. |
| **PB1 filter — C24 100 nF** | `PB1 → C24 → GND` at the pin | ADC **source impedance**: the F411's sample cap needs a low-impedance source, and a 10 k/10 k divider alone is too stiff for a clean conversion. C24 is the local charge reservoir (and RC noise filter for the NTC pigtail). |
| **K1 relay, not an analog mux** | F9P TX2 → **K1 NC** → 4G DIN; PB5 → R40 1 k → Q6 base (R41 10 k → GND); D6 flyback; coil from SYS5V | **Fail-closed with no 3V3.** A mux is a powered semiconductor — it dies with the rail (or with the Blackpill removed). Relay **contacts are metal**: de-energized, NC keeps F9P TX2 → 4G DIN closed with the board unpowered or the MCU absent. Default-OFF base pull-down means the MCU must *actively* insert itself. Invariants #2/#3 on the diagnostic path. |
| **CHRG / FAULT 100 k pull-ups → 3V3** | `U6.CHRG → PB3`, `U6.FAULT → PB4`, each 100 k up to **3V3** | Open-drain status outputs need pull-ups, and these signals are **MCU-scope telemetry only** — nothing but the Blackpill reads them. Riding the MCU's own 3V3 rail means no leakage path into a dead/absent module; charging itself never depends on them. |
| **F2/F0 chemistry straps → VSYS, not 3V3** | JP2 (`F2`) / JP4 (`F0`) → MAIN5V(VSYS); JP3 (`F1`) → GND ⇒ 4.00 V Li-ion | **Chemistry selection must survive an absent MCU.** 3V3 is the Blackpill's on-module LDO output — pull the module and 3V3-referenced straps float while the charger keeps running (undefined charge voltage = safety hazard). VSYS is present whenever the LTC4040 can charge at all. |
| **CHGOFF 100 k pull-down** | `U6.CHGOFF → PA8` (`CHRG_EN`) + 100 k → GND | **Charge enabled by default**: MCU dead/absent ⇒ CHGOFF held low ⇒ the pack still charges, and the LTC4040's **hardware NTC still temperature-qualifies** every charge. The MCU's drive HIGH (TMP102/PB1 hot) is an *additional* cutoff lever, never the only guard. |
| **PFO — DNP / test point only** | `U6.PFO` → TP + DNP 100 k pull-up | The **PA4 divider already senses VEH5V** (vehicle-power-present, `V5V_ON/OFF` thresholds in firmware). Wiring PFO to a GPIO would duplicate that signal and burn a pin; the TP keeps it probe-able at bring-up. |
| **OVSNS shorted to VIN** | short at U6 (OVP FET + divider unpopulated) | **OVP unused by design**: VIN is the SD-15B-05's *regulated 5 V* inside the same cabinet — there is no plausible overvoltage source between them, and the 24 V side already carries SPD + reverse-polarity protection. Datasheet-sanctioned disposition for an unused OVSNS. |
- [[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)