commit fd7b26b35739e86726565755ac54851b6958837c Author: Leopoldo Rodríguez Date: Sun Jun 21 23:39:49 2026 -0600 Initial commit (Hub backfill) diff --git a/.claude/skills/project-drogheda/SKILL.md b/.claude/skills/project-drogheda/SKILL.md new file mode 100644 index 0000000..9f163e1 --- /dev/null +++ b/.claude/skills/project-drogheda/SKILL.md @@ -0,0 +1,49 @@ +--- +name: project-drogheda +description: Load Project Drogheda context — the Helgen HBOX GPS-RTK tracker fleet deployed at Hutchison Ports ICAVE (Veracruz). Use whenever Leo works on anything Drogheda/HBOX-related: the simpleRTK2B / u-blox ZED-F9P, the 4G NTRIP Master, the Blackpill STM32 watchdog (blackpillrtkcode.ino), PowerBoost/LiPo power, the IP65 enclosure, retranslator.py / NTRIP caster / Wialon, the field failures and thermal issues, the watchdog PCB design, or any document/diagram/firmware destined for the Project-Drogheda folder — even if "Drogheda" isn't named. +--- + +# Project Drogheda — HBOX RTK Tracker + +You are helping **Helgen Technologies** with **Project Drogheda**: a fleet of +vehicle-mounted high-precision **GPS-RTK trackers (HBOX)** deployed on heavy +equipment at **Hutchison Ports ICAVE, Veracruz, Mexico**, streaming RTK-corrected +position to the client's **Wialon** portal. + +## Always do first +1. Read `CLAUDE.md` in the project root — it is the source of truth for hardware, + firmware, backend, deployment, people, and the current crisis. +2. Read `PLAN.md` for current priorities and open tasks. +3. For failures/diagnosis, read `06-Testing-Validation/RCA-2026-06-field-failures.md` (keep it a + ranked differential — don't over-anchor on "heat" before the cheap backend/ + SIM/caster checks are ruled out). + +## System in one breath +simpleRTK2B (u-blox ZED-F9P) + 4G NTRIP Master pull RTCM3 over LTE and emit NMEA; +a Blackpill STM32 **watchdog** manages power (vehicle/ignition/LiPo) and restarts +the stack when the link drops; `retranslator.py` filters NMEA (anti-jump + speed) +and forwards to Wialon. Power: 12–24 V → PowerBoost 1000C (+LiPo) → switched 5 V. + +## Hard-won facts (don't relearn these) +- **NMEA GGA quality:** 0 none · 1 GPS · 2 DGPS · **4 RTK-fixed** · **5 RTK-float**. + Corrections-healthy = quality **≥4** (fixed AND float are good). +- The **ZED-F9P is rated −40…+85 °C** — it's rarely the heat casualty. The weak + links are the **LiPo** (charge ≤45 °C), the **PowerBoost**, and the **4G modem**. +- Corrections come over the **4G modem**, not the GNSS — so a GNSS hot-reset does + **not** fix a dead NTRIP link. Recover by **power-cycling the stack**. +- UBX CFG-RATE 1 Hz/30 s commands and checksums are verified (see firmware-review). + +## Conventions +- Firmware: edit `04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino`; **bench-test before field + flash** (the sandbox can't compile for STM32). Keep v1 as reference. +- Hardware/BOM changes go in `03-Architecture-Hardware/hardware-respec-and-pcb-brief.md`. +- Dates absolute (e.g., 2026-06-19). Don't leak internal paths to the client. +- Client-facing material is **Spanish** and routes through Leo. + +## People +Helgen: Leo (founder/CEO), Adriana, Paola, Daniel, Gibran. +Client (ICAVE): **Agustín Alfonseca** (Supervisor de Investigación de Tecnología), +Leon David Piña (Hutchison Ports). + +## Codename +Helgen uses Irish place-names (Ennis, **Drogheda**). This project = "Drogheda." diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4953496 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Secrets — never commit +.mcp.json +**/.env +**/.env.* +!**/*.example +**/token.json +**/credentials.json +**/*.pem +**/*.key +**/id_rsa* +# OS / editor +.DS_Store +**/.DS_Store +*.swp +# Python / Node / build output +**/__pycache__/ +*.py[cod] +.venv/ +venv/ +**/node_modules/ +**/dist/ +**/build/ +*.log +# Claude local settings +**/.claude/settings.local.json diff --git a/00-Project-Management/Drive-Upload/README.md b/00-Project-Management/Drive-Upload/README.md new file mode 100644 index 0000000..3143e28 --- /dev/null +++ b/00-Project-Management/Drive-Upload/README.md @@ -0,0 +1,56 @@ +--- +tags: [project-drogheda, drive, upload, ops] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Subir artefactos a Drive (Project-Drogheda) + +## Por qué no es automático (la fricción real) + +- El **conector de Drive** (MCP) puede crear carpetas y Google Docs nativos, pero **no + sube archivos binarios** (PDF/.xlsx). +- **Claude in Chrome** no puede manejar el **diálogo nativo del sistema** que Drive abre + al pulsar "Subir archivo" (está fuera del navegador). + +Por eso los PDF/.xlsx de marca requieren uno de los caminos de abajo. Es una limitación +de la UI de Drive, no de la conexión. + +## Tres formas, de la más fácil a la más automatizable + +1. **Google Drive para escritorio (sin script)** — *la más fácil.* Instala Drive for + Desktop; la carpeta `Client Projects/Project-Drogheda` aparece como carpeta local; + arrastras los archivos ahí y se sincronizan solos. Cero scripts. +2. **rclone (una línea)** — tras `rclone config` con tu Drive: + ```bash + FID_01=1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95 # 01-Requirements + rclone copy 01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf gdrive: --drive-root-folder-id=$FID_01 + ``` +3. **Script de Python (Drive API)** — `drive_upload.py` (en esta carpeta) con el mapeo ya + incluido. Hace *upsert* (actualiza si ya existe). Ver el encabezado del archivo para el + setup (OAuth `credentials.json` una vez). Uso: + ```bash + cd ~/Documents/Claude/Projects/Drogheda + python3 00-Project-Management/Drive-Upload/drive_upload.py --dry-run # ver el plan + python3 00-Project-Management/Drive-Upload/drive_upload.py # subir + ``` + +## Mapa: qué documento va a qué carpeta + +| Archivo (en el repo) | Carpeta Drive | Folder ID | +|---|---|---| +| `01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf` | 01-Requirements | `1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95` | +| `01-Requirements/Drogheda-SRS-Requerimientos.pdf` | 01-Requirements | `1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95` | +| `01-Requirements/Drogheda-Requirements-Tracker.xlsx` | 01-Requirements | `1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95` | +| `03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf` | 03-Architecture-Hardware | `1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos` | +| `03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx` | 03-Architecture-Hardware | `1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos` | +| `06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf` | 06-Testing-Validation | `1CNLnjSa7ro3rkRGQ6w_DBUOIp5ukizi2` | +| `Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf` | Commercial (Cliente) | `1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ` | +| `Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf` | Commercial (Cliente) | `1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ` | + +Carpeta raíz: **Project-Drogheda** → `1sAwXEq7PU7LMfEq4-swWa1O0bDBCKZ13` +(dentro de "Client Projects"). Los `.tex`/fuentes editables se quedan en el repo; aquí +sólo van los PDF/.xlsx finalizados. **Regla:** el `.tex` del repo y su PDF en Drive son el +mismo artefacto en dos formatos. diff --git a/00-Project-Management/Drive-Upload/drive_upload.py b/00-Project-Management/Drive-Upload/drive_upload.py new file mode 100644 index 0000000..92bc951 --- /dev/null +++ b/00-Project-Management/Drive-Upload/drive_upload.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +drive_upload.py — push Drogheda branded artifacts to the Project-Drogheda Drive folders. + +WHY THIS EXISTS + The Drive MCP connector can create folders/native Docs but NOT upload binary files, + and the browser extension can't drive Drive's native file-picker dialog. This script + uses the Google Drive API directly, so it CAN upload the PDFs/xlsx. It "upserts": + if a file with the same name already exists in the target folder, it updates it; + otherwise it creates it. Run it again any time you rebuild a doc. + +ONE-TIME SETUP + 1) pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib + 2) In Google Cloud Console: create an OAuth client (Desktop app), enable the Drive API, + download the JSON as credentials.json next to this script. + 3) Run: python3 drive_upload.py → a browser opens once to authorize; a token.json + is cached so future runs are non-interactive. + +USAGE + cd ~/Documents/Claude/Projects/Drogheda + python3 00-Project-Management/Drive-Upload/drive_upload.py # upload all + python3 00-Project-Management/Drive-Upload/drive_upload.py --dry-run # show plan only +""" +import os, sys, argparse +REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) + +# file (relative to repo root) -> Drive folder ID (Project-Drogheda subfolders) +MAPPING = [ + ("01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements + ("01-Requirements/Drogheda-SRS-Requerimientos.pdf", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements + ("01-Requirements/Drogheda-Requirements-Tracker.xlsx", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements + ("03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf", "1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos"), # 03-Architecture-Hardware + ("03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx", "1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos"), # 03-Architecture-Hardware + ("06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf", "1CNLnjSa7ro3rkRGQ6w_DBUOIp5ukizi2"), # 06-Testing-Validation + ("Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf", "1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ"), # Commercial (Cliente) — ficha técnica + ("Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf", "1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ"), # Commercial (Cliente) — flyer intro +] +SCOPES = ["https://www.googleapis.com/auth/drive.file"] +HERE = os.path.dirname(os.path.abspath(__file__)) + + +def service(): + from google.oauth2.credentials import Credentials + from google_auth_oauthlib.flow import InstalledAppFlow + from google.auth.transport.requests import Request + from googleapiclient.discovery import build + tok, cred = os.path.join(HERE, "token.json"), os.path.join(HERE, "credentials.json") + creds = Credentials.from_authorized_user_file(tok, SCOPES) if os.path.exists(tok) else None + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + if not os.path.exists(cred): + sys.exit("Missing credentials.json — see ONE-TIME SETUP in this file's header.") + creds = InstalledAppFlow.from_client_secrets_file(cred, SCOPES).run_local_server(port=0) + open(tok, "w").write(creds.to_json()) + return build("drive", "v3", credentials=creds) + + +def upsert(svc, path, folder_id, dry): + from googleapiclient.http import MediaFileUpload + name = os.path.basename(path) + full = os.path.join(REPO, path) + if not os.path.exists(full): + print(f" SKIP {name} (not found at {path})"); return + if dry: + print(f" PLAN {name} -> {folder_id}"); return + q = f"name = '{name}' and '{folder_id}' in parents and trashed = false" + hits = svc.files().list(q=q, fields="files(id)").execute().get("files", []) + media = MediaFileUpload(full, resumable=True) + if hits: + svc.files().update(fileId=hits[0]["id"], media_body=media).execute() + print(f" UPDATED {name}") + else: + svc.files().create(body={"name": name, "parents": [folder_id]}, + media_body=media, fields="id").execute() + print(f" CREATED {name}") + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--dry-run", action="store_true") + args = ap.parse_args() + svc = None if args.dry_run else service() + print("Uploading Drogheda artifacts to Project-Drogheda (Drive):") + for path, fid in MAPPING: + upsert(svc, path, fid, args.dry_run) + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/00-Project-Management/Engineering-Handover.md b/00-Project-Management/Engineering-Handover.md new file mode 100644 index 0000000..aa516f8 --- /dev/null +++ b/00-Project-Management/Engineering-Handover.md @@ -0,0 +1,385 @@ +--- +tags: [project-drogheda, handover, onboarding, rca, architecture] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +audience: Helgen engineering (new + existing) +--- + +# Project Drogheda — Engineering Handover & Deep Context + +> **Read this if you're new to Drogheda, or returning after time away.** It is the +> human-readable brain-dump: what the product is, why it failed in the field, what we +> are fixing, how the repo is laid out, and what's still open. The terse machine index +> is [`CLAUDE.md`](../CLAUDE.md); current priorities are [`PLAN.md`](PLAN.md). When this +> doc and `CLAUDE.md` disagree, fix both — they should agree. + +--- + +## 1. TL;DR (60 seconds) + +**HBOX** is Helgen's GPS-**RTK** tracker for heavy port equipment. We deployed a first +batch on **Hutchison Ports ICAVE** machines in **Veracruz**. They worked "ish," then +failed. We are **mid-crisis and hardening the product for scale** — target **30 units +operational by August**, hundreds of cabinets planned across the Hutchison terminals +(**ICAVE, TIMSA, EIT, LCT**). + +The failure is **not one bug**. Three independent fronts, fixed in parallel: + +1. **Backend** (confirmed in code) — the production `retranslator.py` blocks the whole + fleet on any upstream slowness (head-of-line), and an anti-jump filter can + **permanently black-hole a healthy unit**. Some "dead" units are backend victims. +2. **Thermal** (component-level) — an **ASA** HBOX (UV-stable, softens ~95–100 °C — the + case is fine) sealed inside a sealed **ABS IP67** cabinet that *also* holds the + heat-generating **Mean Well** PSU, in full Veracruz sun → a nested heat trap for the + battery / boost / modem. +3. **Prototype hardware** — Dupont jumpers, hand-wiring, 18650 + PowerBoost. Fragile + under continuous machine vibration. The client (ICAVE) called this out themselves. + +**Current physical reality:** all units pulled from machines. **4 in hand (3 dead, 1 +working-ish)**, physically in **Veracruz**; the team is in **Querétaro** → we debug +**remotely**. Units run **firmware v1** (no thermal log), so we cannot yet *prove* the +thermal story from logs — hence the diagnostic protocol and v2 firmware. + +**Free triage you can do today:** a unit that is **ONLINE in htracker but OFFLINE in +Wialon** is a **backend victim**, not dead hardware. Check that before condemning a radio. + +--- + +## 2. What the product actually is + +Two-component RTK system: + +- **Rover (HBOX)** — rides on the machine (RTG crane, reach stacker, empty handler, + tractoplana, forklift). Computes cm-class position. +- **Base Station** — fixed reference (Ardusimple **AS-PRO-CORS**) that publishes **RTCM3** + corrections over **NTRIP**. One per terminal. Comes in **110 V and 230 V** flavors. + +The rover gets corrections **over the 4G modem (NTRIP), not over the GNSS**. This single +fact drives the whole recovery design: **if the link dies, you power-cycle the 4G stack — +a GNSS reset never fixes lost NTRIP.** + +**HBOX internals (per unit, 24 V production variant):** +simpleRTK2B **Pro** (u-blox **ZED-F9P**, L1/L2/L5/L6) · **4G NTRIP Master** (XBee LTE) · +**STM32F411 Blackpill** watchdog · **Mean Well SD-15B-05** (24→5 V, 3 A) · **PowerBoost +1000C** + **18650 1S4P** Li-ion backup · **DFR1015** (ignition conditioning) · 2N3904 + +IRF9540N + 2N7000 (watchdog power gate) · **GX16-5** connector (HBOX ↔ cabinet) · U-blox +multiband GNSS antenna + 2× LTE antennas · Telcel micro-SIM · ASA case. + +--- + +## 3. Corrected system architecture + +This is the **corrected** picture (the original BOM hid the cabinet/HBOX split). The +cabinet does the power conditioning and surge/ignition isolation; the HBOX receives +**clean 24 V** and makes its own rails. + +```text + Machine power+ignition (12/24/48/110/230 V, per equipment) + │ + ▼ ┌──────────────── Gabinete IP67 (ABS, Yetlebox P3901) ─────────────────┐ + breaker│ Phoenix SPD · fuse holder · DIN rail · SSR/opto (ignition isolation)│ + + SPD ►│ Mean Well DDR-60G-24 ──► regulated 24 Vdc ───┐ │ + │ │ GX16-5 connector │ + │ ┌────────── HBOX (3D-printed ASA) ─────────┼────┐ │ + │ │ Mean Well SD-15B-05 (18–36V→5V,3A) ──► 5V rail │ │ + │ │ ├► PowerBoost 1000C (+18650 Li-ion 1S4P) backup │ + │ │ ├► STM32F411 Blackpill WATCHDOG ──UART(PA2)──► simpleRTK2B Pro │ + │ │ │ └ switches RTK 5V via 2N3904→IRF9540N (PA1) │ + │ │ ├► simpleRTK2B Pro (ZED-F9P) + 4G NTRIP Master (XBee LTE) │ + │ │ └► ignition 24V ──► DFR1015 buck ──► STM32 PA5 (ign sense) │ + │ └──────────────────────────────────────────────────────────────┘ │ + └───────────────────────────────────────────────────────────────────┘ + 4G/LTE ▲ RTCM3 in (NTRIP) │ NMEA out (UDP) + Base station (Ardusimple AS-PRO-CORS) ────┘ ▼ + retranslator.py ─(anti-jump+speed filter)─► Wialon / Traccar ─► VERONICA +``` + +**ICAVE machine voltages:** RTG 24 V · Reach Stacker 24 V · Empty Handler 24 V · +Tractoplana 12 V · Montacargas 12 V. **24 V is the production variant**; the others are +theoretical until built. Base station is 110/230 V. + +--- + +## 4. Root-cause analysis (the three fronts, in depth) + +Full write-up: [`06-Testing-Validation/RCA-2026-06-field-failures.md`](../06-Testing-Validation/RCA-2026-06-field-failures.md). +Branded client version (es-MX): `06-Testing-Validation/RCA-Informe-Falla/` → PDF. + +### 4.1 Backend (confirmed in `htracker` code — not a hypothesis) + +The retranslator is the funnel every unit's NMEA passes through on its way to Wialon. +Three defects, all in [`02-Architecture-Software/backend-review.md`](../02-Architecture-Software/backend-review.md): + +- **Head-of-line blocking.** Single-threaded/synchronous forward: one slow upstream + (Wialon/Traccar) stalls **the entire fleet**, not just one unit. At 500 units this is + fatal. Fix → **async retranslator**, per-unit isolation; load-test with a deliberately + slow upstream. +- **Anti-jump filter black-holes healthy units.** On a rejected "jump," the reference + position is **not updated** and the lockout **persists across reconnect** → a healthy + unit can be silently excluded from Wialon forever. Fix → time/speed-based threshold, + **re-anchor** after N rejects, **clear `last_pos` on TTL**. +- **Identity by source IP breaks under CGNAT.** Telcel CGNAT means source IP is not a + stable unit identity → units collide/swap. Fix → identity by **`cell_id`** (provisioned + per unit), never source IP. +- **Live secret.** The MSSQL `sa` password is **committed in the repo** (`.env` / + `.htracker_env`). **Rotate it and purge from git history.** This is invariant #6. + +### 4.2 Thermal (component-level; not yet log-proven because v1 has no sensor) + +- **The ASA case is not the weak material.** ASA is UV-stable and softens at ~95–100 °C, + with margin over the cabinet interior. (Corrects an earlier PLA assumption — the case is fine.) +- **Nested heat trap.** The ASA HBOX is sealed inside a sealed ABS IP67 cabinet that also + holds the **Mean Well** (a heat source), under direct sun. Cabinet interior can run far + above ambient. **Design to measured cabinet-interior temperature, not spec-sheet ambient.** +- **The GNSS is not the casualty either.** ZED-F9P is rated **−40…+85 °C**. The heat victims + are the **18650/PowerBoost** (charge ≤45 °C; swells/degrades hot) and the **4G modem**. +- **A bare fan inside a sealed box does not cool it** — it just stirs hot air. **Conduct heat + out** (metal plate / cabinet wall), or use an **engineered waterproof duct + Blackpill- + controlled fan** (v3.0, preserves IP). Fixes: keep the ASA case (optional metal for extra + heatsinking), thermal path to a metal plate, resolve the battery (drop / LiFePO4 / supercap), + reduce solar load (reflective cabinet / sunshade / relocate). + +### 4.3 Prototype hardware + +Dupont + hand-wiring + 18650 + PowerBoost survive a bench, not a machine. Continuous +vibration walks connectors loose. Fix = **ruggedize now** (retained connectors, strain +relief, no Dupont) **+ a production PCB (Series 2)** that integrates the watchdog gate, +power, and ignition conditioning. Brief: [`03-Architecture-Hardware/hardware-respec-and-pcb-brief.md`](../03-Architecture-Hardware/hardware-respec-and-pcb-brief.md). + +--- + +## 5. The fixes (what "done" looks like) + +### 5.1 Firmware v2 — `04-Source-Code/firmware/` (own git repo `hbox-firmware`) + +> The firmware now lives in its **own git repo** (`ssh://git.helgen.dev:6611/hbox-firmware`) +> at `04-Source-Code/firmware/`, with README, `docs/PINOUT.md`, a redacted RTK-config +> template, and CHANGELOG. Canonical sketch: `blackpillrtkcode_v2/blackpillrtkcode_v2.ino`. + +The watchdog is an **STM32F411**. v2 encodes the recovery doctrine: + +- **Recover the link by power-cycling the 4G stack** (gate via 2N3904→IRF9540N on PA1), + **never** by resetting the GNSS. +- **Exponential backoff** between recovery attempts — never an infinite fast-reboot loop. +- **LIMP mode** — if corrections are lost, keep reporting **standalone** position rather + than going silent. *Never vanish, never loop.* +- **NMEA-silence watchdog** — detect a wedged receiver by absence of NMEA, not just power. +- **Thermal logging** — v1 has no temperature log; v2 adds it so the next field batch + *proves* the thermal story. +- **Power-off is the safe state** — gate pull-up keeps the RTK stack OFF until the MCU + asserts confidence. + +> **Hard rule: bench-test firmware before any field flash.** The sandbox cannot build +> STM32 — UBX checksums and logic were hand-verified, but a real build + bench cycle is +> mandatory before touching a field unit. Edit the **`_v2`** file; v1 is reference only. + +Firmware review + watchdog wiring: [`03-Architecture-Hardware/firmware-review.md`](../03-Architecture-Hardware/firmware-review.md), +[`03-Architecture-Hardware/watchdog-schematic.md`](../03-Architecture-Hardware/watchdog-schematic.md). + +### 5.2 Backend — `04-Source-Code/htracker/` (branch `feature/test500unidades`) + +Async retranslator (no HOL) · identity by `cell_id` · anti-jump rewrite (threshold + +re-anchor + TTL) · 500-unit load test against a slow upstream · **rotate + purge the MSSQL +secret**. Notifier (no secrets, reads `$MATTERMOST_WEBHOOK_URL`): +`04-Source-Code/notifications/notify_mattermost.py`. + +### 5.3 Hardware / thermal (Series 2) + +Keep ASA case (optional metal) · conduction path to metal · resolve battery · reduce solar +load · production PCB integrating watchdog/power/ignition · retained connectors. Locked +BOM + assembly doc required before the August lot. + +--- + +## 6. Canonical invariants (never violate; check every output against these) + +1. **Power-off is the safe state.** RTK stack defaults OFF until the MCU asserts; loss of + confidence → safe state, not best-guess. +2. **Never vanish, never loop.** Keep reporting (standalone if needed); never enter an + infinite fast-reboot loop. +3. **Corrections ride the 4G modem, not the GNSS** — recover the link by power-cycling the + stack; a GNSS reset is never a fix for lost NTRIP. +4. **No material that softens below ~80 °C (PLA-class) in the thermal path** — the ASA + enclosure complies. Design to measured cabinet-interior temperature. +5. **Backend reads/forwards without head-of-line blocking**, and the anti-jump filter must + never permanently lock out a healthy unit. +6. **No secrets in the repo — ever.** (The committed MSSQL password is a live violation to + remediate.) + +**Hard constraints:** HBOX input is the cabinet's regulated 24 V + isolated ignition +(don't re-implement the cabinet's surge/breaker/SSR on the HBOX) · **IP67 maintained** +(conduction cooling only; no IP-breaking vents/fans) · **bench-test before field flash** · +client-facing docs are **Spanish**, routed through Leo. + +--- + +## 7. Repo map (Helgen English `NN-` standard) + +| Folder | Holds | Key files | +|---|---|---| +| `00-Project-Management` | plan, ethos, hackathon, financials, **this handover**, client comms, Drive-upload tooling | `PLAN.md` · `ethos.md` · `hackathon-plan.md` · `Financials.md` · `Engineering-Handover.md` · `Drive-Upload/` · `client-comms/` | +| `01-Requirements` | what we build | `SRS.md` · `HBOX-product-spec.md` · branded SRS + ficha técnica (`.tex`→PDF) · requirements tracker (`.xlsx`) | +| `02-Architecture-Software` | backend/software analysis | `backend-review.md` | +| `03-Architecture-Hardware` | HBOX/cabinet, wiring, BOM, PCB | `hardware-respec-and-pcb-brief.md` · `cabinet-electrical-bom.md` · `watchdog-schematic.md` · `firmware-review.md` · `BOM-master.md` · BOM (`.xlsx`) · install/op manual (`.tex`→PDF) | +| `04-Source-Code` | firmware + backend + tooling | `firmware/` (**own repo `hbox-firmware`**: `blackpillrtkcode_v2/` deploy · `legacy/` v1) · `htracker/` · `notifications/` · `rtk-config/` | +| `05-Analytics-Reports` | SQL views, dashboards (VERONICA/PowerBI) | _(placeholder)_ | +| `06-Testing-Validation` | RCA, diagnostics, evidence | `RCA-2026-06-field-failures.md` · `remote-diagnostic-protocol.md` · branded RCA (`.tex`→PDF) | +| `Marketing` | product-facing collateral | `HBOX-Datasheet/` (industrial spec sheet) · `HBOX-Brochure-RTK/` (flyer) | +| `_brand` | shared LaTeX brand style | `estilo-helgen.tex` | +| `99-Archive` | superseded raw inputs (read-only) | `situation_ORIGINAL.md` · `blackpillrtkcode_ORIGINAL_loose.ino` | +| `.claude/skills` | vendored project skill | `project-drogheda/` | + +--- + +## 8. How to build / run + +**Firmware** — its own git repo (`hbox-firmware`) at `04-Source-Code/firmware/`; open +`blackpillrtkcode_v2/blackpillrtkcode_v2.ino` in the STM32duino toolchain on a real machine +(the sandbox cannot build STM32). Build/flash + push instructions are in that repo's +`README.md`. **Bench-test before any field flash.** + +**Backend** — `04-Source-Code/htracker/` (`ssh://git.helgen.dev:6611/htracker`, branch +`feature/test500unidades`). See `backend-review.md` before changing the retranslator. + +**Branded documents (LaTeX)** — each doc folder has `main.tex` + `cuerpo.tex`, sharing +`_brand/estilo-helgen.tex`. Build with **XeLaTeX** (fonts: **League Spartan** titles + +**MADE Evolve Sans** body — the latter is a *personal-use* license, **needs a commercial +license before client-facing embedded use**): + +```bash +cd 01-Requirements/HBOX-Ficha-Tecnica # or any doc folder +latexmk -xelatex main.tex # → main.pdf ; latexmk -c to clean aux +``` + +**Trackers / BOM (xlsx)** — built with openpyxl + Helgen brand colors; build scripts were +generated in the scratchpad. The committed `.xlsx` are the source of truth for the data. + +--- + +## 9. Deliverables produced this engagement (2026-06) + +**Branded LaTeX (customer-facing, es-MX), source in repo → PDF:** +- `01-Requirements/HBOX-Ficha-Tecnica/` — DRG-FT-001 (product spec sheet) +- `Marketing/HBOX-Datasheet/` — **industrial datasheet** (Mean Well-style, dense specs + + full **Ambiental/Térmica** section + thermal operating-window chart + power/signal block + diagram). Models the Canva "SPEC SHEET H-BOX". **This is the brochure-as-datasheet.** +- `01-Requirements/Especificacion-Requerimientos/` — DRG-REQ-001 (SRS) +- `03-Architecture-Hardware/Manual-Instalacion-Operacion/` — DRG-MAN-001 (field manual) +- `06-Testing-Validation/RCA-Informe-Falla/` — DRG-RCA-001 (failure report) + +**Branded xlsx (Helgen colors, openpyxl):** +- `01-Requirements/Drogheda-Requirements-Tracker.xlsx` — 34 reqs (FR/NFR/TR/BR), status + + category summaries, open-points sheet (modeled on Project Ennis's GateLogic tracker). +- `03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx` — HBOX common + 24 V production + + theoretical variants + cost summary + base station, one table per sheet. + +**Tooling:** `00-Project-Management/Drive-Upload/drive_upload.py` (+ README mapping) · +`04-Source-Code/notifications/notify_mattermost.py`. + +--- + +## 10. Where things live (storage model — one home per artifact) + +| Home | Role | Holds | +|---|---|---| +| **This repo** (`~/Documents/Claude/Projects/Drogheda`) | Working **source of truth** | Editable `.md`/`.tex`, code, `CLAUDE.md`, vendored skill | +| **Google Drive** `Drogheda (GPS RTK)` | Institutional / client-facing | Finalized exports: branded PDFs, native Docs/Sheets | +| **Second Brain vault** `03 Projects/Helgen/Project-Drogheda/` | Personal / thinking | Distilled decisions, lessons, contacts | + +**Flow:** author in repo → export finalized deliverable to the matching Drive folder → +distill the evergreen residue into the vault. A repo `.tex` and its Drive PDF are the same +artifact in two formats — the only allowed redundancy. + +**Drive upload reality (so the next engineer doesn't relearn it):** the Drive MCP +connector *can* upload binaries (base64), but the bytes pass through the model as text, so +it is **only reliable for small files** (≈≤15 KB; the Requirements Tracker and BOM `.xlsx` +were uploaded this way and verified by exact byte-size). For the 30–50 KB **PDFs**, use +**Google Drive for Desktop (drag)** or **`drive_upload.py`** (one-time OAuth, then it +upserts all artifacts to the right folders). Folder→file mapping: +`00-Project-Management/Drive-Upload/README.md`. **Never change Drive sharing/permissions — +that is Leo's action.** + +--- + +## 11. Status snapshot (2026-06-20) + +- **Units:** all pulled from machines. **4 in hand: 3 dead, 1 working-ish**, in **Veracruz**. +- **Team:** in **Querétaro** → **remote** debug. Diagnostic protocol: + `06-Testing-Validation/remote-diagnostic-protocol.md`. +- **Firmware in field:** **v1** (no thermal log). v2 ready for bench → field once tested. +- **Commercial:** productized across ICAVE/TIMSA/EIT/LCT; **30 units by August**. +- **Naming:** renamed **Galway → Drogheda** on 2026-06-20; repo migrated to Helgen English + `NN-` layout. Legacy Spanish dirs removed locally. +- **Drive:** Requirements Tracker + BOM `.xlsx` uploaded (verified); remaining branded PDFs + pending drag/script upload. + +--- + +## 12. Open items / next steps (priority order) + +**P0 (this week):** +1. **Backend anti-jump hotfix** — threshold + re-anchor + clear `last_pos` on TTL; stop + black-holing healthy units. (Owner: Gibran) +2. **`cell_id` identity** in the retranslator — kill the source-IP/CGNAT bug. (Gibran) +3. **Rotate + purge the MSSQL secret** from the repo and git history. (Gibran) +4. **Bench-test firmware v2**, then flash the working-ish unit; confirm 4G power-cycle + recovery + thermal logging. (Leo) + +**P1 (hardening for August):** +5. **Async retranslator** + 500-unit load test (slow upstream). (Gibran) +6. **Thermal redesign** — conduction path to metal + active waterproof-duct fan (v3.0); + resolve battery, reduce solar load. (ASA case is adequate — no swap.) (Daniel) +7. **Production PCB (Series 2)** — integrate watchdog/power/ignition; retained connectors. (Daniel) +8. **Lock BOM + assembly doc** for the August lot. (Adriana) + +**Housekeeping:** +9. Delete the legacy English folders + stray Google Docs in the **old** Drogheda Drive + (Leo to do). +10. Finish mirroring branded PDFs to Drive (drag / `drive_upload.py`). +11. Replace datasheet "validar en banco" power figures with measured values once available. + +--- + +## 13. People + +| Name | Email | Role | +|---|---|---| +| Leo Rodriguez | leo@helgen.tech / ceo@helgen.tech | Helgen — founder/CEO; routes client docs | +| Adriana Del Angel | adriana@helgen.tech | Helgen — implementation/plan | +| Paola Rodriguez | paola@helgen.tech | Helgen | +| Angel Daniel Martinez | daniel@helgen.tech | Helgen — hardware/thermal | +| Gibran Ortega | gibran@helgen.tech | Helgen — GPS/telemetry/backend | +| **Agustín Alfonseca** | alfonseca.agustin@icave.com.mx | **Client** — ICAVE Tech Research Supervisor | +| Leon David Piña | pina.david@hutchisonports.com.mx | Hutchison Ports | + +--- + +## 14. Glossary (don't relearn these) + +- **HBOX** — the rover unit (the product). +- **RTK** — Real-Time Kinematic; cm-class GNSS using carrier-phase + corrections. +- **NTRIP / RTCM3** — protocol / format for streaming RTK corrections over IP (here, 4G). +- **ZED-F9P** — u-blox multiband RTK receiver; rated **−40…+85 °C** (not the heat casualty). +- **NMEA GGA quality** — **≥4 = healthy** (4 = RTK-fixed, 5 = RTK-float); <4 = degraded. +- **LIMP mode** — keep reporting standalone position when corrections are lost. +- **HOL (head-of-line) blocking** — one slow consumer stalls everyone in a shared queue. +- **Anti-jump filter** — backend logic that rejects implausible position jumps (currently + over-aggressive; can lock out healthy units). +- **GX16-5** — 5-pin circular connector between HBOX and cabinet. +- **cell_id** — stable per-unit identity (use this, not source IP, under CGNAT). +- **Series 1 / Series 2** — prototype (ASA case, hand-wired) / production (PCB, ruggedized). +- **VERONICA** — downstream platform fed from Wialon/Traccar. + +--- + +## 15. Pointers + +- Machine index for Claude: [`CLAUDE.md`](../CLAUDE.md) +- Current priorities: [`PLAN.md`](PLAN.md) · Weekend plan: [`hackathon-plan.md`](hackathon-plan.md) +- RCA (full): [`06-Testing-Validation/RCA-2026-06-field-failures.md`](../06-Testing-Validation/RCA-2026-06-field-failures.md) +- Backend review: [`02-Architecture-Software/backend-review.md`](../02-Architecture-Software/backend-review.md) +- Remote diagnostics: [`06-Testing-Validation/remote-diagnostic-protocol.md`](../06-Testing-Validation/remote-diagnostic-protocol.md) +- Hardware re-spec + PCB: [`03-Architecture-Hardware/hardware-respec-and-pcb-brief.md`](../03-Architecture-Hardware/hardware-respec-and-pcb-brief.md) +- Drive upload mapping: [`Drive-Upload/README.md`](Drive-Upload/README.md) diff --git a/00-Project-Management/Financials.md b/00-Project-Management/Financials.md new file mode 100644 index 0000000..80fa12c --- /dev/null +++ b/00-Project-Management/Financials.md @@ -0,0 +1,48 @@ +--- +tags: [project-drogheda, finance, red-item, placeholder] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Draft +--- + +# Drogheda — Financials (🔴 RED ITEM — placeholder) + +> **Placeholder.** Finance is a tracked **red item** for Drogheda; the detailed +> work happens in a separate Claude conversation. This stub holds the structure and +> the open questions so the project doesn't lose the thread. Fill in as numbers land. +> Pricing/margins are **internal** — do not export to client-facing Drive folders. + +## Why it's red + +The project is scaling to **hundreds of cabinets** across four BUs with a hard +**30-units-by-August** target, while mid-crisis on reliability. Cost, cashflow, and +margin are as much a risk as the thermal/backend issues — and currently un-modeled +here. + +## What goes here (to fill in) + +- [ ] **Unit economics** — per-variant cost → price → **margin** (the BOM cost + rollup already exists: [[03-Architecture-Hardware/BOM-master|BOM-master]]; + HBOX ≈ 8,041 MXN + 24 V cabinet ≈ 6,365 MXN). Add the sell price + %. +- [ ] **Quote vs cost** — Helgen vs alternate-supplier ("Israel") deltas already in + the BOM sheet; decide sourcing and lock landed cost. +- [ ] **Per-BU rollup** — ICAVE / TIMSA / EIT / LCT unit counts × unit cost/price → + revenue + COGS per terminal (counts live in the component sheet). +- [ ] **Cashflow / billing** — milestone billing vs delivery; deposit terms; + currency (MXN/USD components — Ardusimple/Phoenix are USD-priced). +- [ ] **Burn / runway** — hackathon + rework + PCB NRE + pilot batch. +- [ ] **Base-station + infra** — AS-PRO-CORS base station, SIM/data plans (Telcel + per-unit), server/caster hosting. +- [ ] **Warranty / rework reserve** — the field-failure rework is a real cost line. + +## Open questions + +- [ ] Target gross margin per unit? Current implied margin from the sheet? +- [ ] Who owns the finance model (Adriana?) and where is the canonical sheet? +- [ ] MXN vs USD reporting; FX assumption. + +## Related + +- [[CLAUDE|Master Index]] · [[03-Architecture-Hardware/BOM-master|BOM-master]] · + [[00-Project-Management/PLAN|PLAN]] diff --git a/00-Project-Management/PLAN.md b/00-Project-Management/PLAN.md new file mode 100644 index 0000000..163a3fa --- /dev/null +++ b/00-Project-Management/PLAN.md @@ -0,0 +1,158 @@ +# PLAN.md — Project Drogheda URGENT Remediation + +**Updated:** 2026-06-19 · **Owner:** Leo Rodriguez +**Situation:** **all HBOX units have been removed from the machines.** We have +**4 units in hand — 3 not working, 1 working-ish.** They are **physically in +Veracruz**; the team is in **Querétaro → debugging is remote** until they ship or +someone on-site is our hands. Units run **v1 firmware** (no thermal log). +Strategy: **ruggedize now + PCB in parallel**; near-term work pivots to **remote +backend analysis (doable from QRO today)** + a **remote diagnostic protocol** +(`06-Testing-Validation/remote-diagnostic-protocol.md`). + +Legend: ⬜ todo · 🔄 in progress · ✅ done · 👤 owner TBD · ⛔ blocker + +--- + +## P0 — DO THIS WEEK (stop the bleeding, learn the truth) + +### Logistics — units are in Veracruz, we're in Querétaro (decide first) +- ⬜ 👤 **Ship the 4 units to Querétaro** *(recommended)* — small parcels; remote- + debugging dead electronics is slow and inconclusive vs 1–2 days for full bench + access. **Bring the working-ish unit too — it's the control.** Confirm with ICAVE + who **owns** the units / whether they may leave the terminal. +- ⬜ 👤 If they can't ship: stand up **remote-hands** in Veracruz using + `06-Testing-Validation/remote-diagnostic-protocol.md` (tiered: photos+current draw → USB dashboard + → remote desktop). Need a cooperative on-site tech with a bench PSU + laptop. + +### Diagnosis (cheapest first — could change everything) +- ⬜ 👤 **Backend triage — now the #1 lead** (backend code review done, see + `02-Architecture-Software/backend-review.md`; two confirmed software defects can take healthy units + off Wialon). Do these free checks first: + - **htracker-online vs Wialon-offline:** any unit ONLINE in Helgen's monitor but + OFFLINE in the client's Wialon = **backend victim, not dead hardware.** Check + ZPM 020 + RS-SAN-001 this way *before* the teardown. + - Confirm the **live process** (`systemctl status htracker` — service runs the + blocking `retranslator.py`; `run_retranslator.sh` runs a different file). + - Gateway log + **`netstat -su`** UDP drops in the death window; did all units + drop within seconds (→ HOL stall) or one-by-one (→ anti-jump lockout)? + - NTRIP caster (HelgenSRV) availability + **per-SIM data balance/suspension**. +- ⬜ 👤 **Pull the field logs** (battery V, fix quality, reset timestamps) and + correlate Wialon gaps with the ~5/10-min v1 reset cadence. +- ⬜ 👤 **Check the installed fuse** rating vs measured worst-case + inrush (H5). + +### Client (ICAVE) — relationship is on the line +- ✅ Units **removed** from the machines (all of them, not just ZPM 020 / RS-SAN-001). +- ⬜ 👤 **Reply to Agustín/ICAVE** (Spanish): acknowledge each review point, share + the plan, commit dates. *Draft not yet written — ask Claude to draft it; Leo sends.* +- ⬜ 👤 Agree **custody/shipping** of the 4 pulled units with ICAVE (see Logistics). + +### Bench (unblocks the hardware spec) +- ⬜ 👤 **Measure the power budget** — idle/1 Hz/**LTE-TX-burst** current + inrush + at 12 V and 24 V (`hardware brief §2`). Picks the DC-DC and fuse. +- ⬜ 👤 **Bench-test firmware v2** on a Blackpill + simpleRTK2B stack: power-cycle + recovery, NMEA-timeout, thermal read, LED patterns. *Do not field-flash + untested.* + +--- + +## P1 — SHORT TERM (≈1–3 weeks): ruggedize the fleet (Phase 1) + +- ⬜ 👤 **Quantify the heat:** thermocouple/logger inside a representative cabinet + for a full sunny shift → real interior-temp number to design against. +- ⬜ 👤 Build a **Phase-1 ruggedization kit** and rework **one reference unit**: + - **the ASA inner box stays** (UV-stable, ~95–100 °C — adequate); optionally + mount the boards to the cabinet plate for better conduction + - replace Dupont → locking JST/Molex connectors + - replace PowerBoost 1000C → **24 V→5 V** industrial buck ≥3 A (cabinet feeds 24 V) + - **verify the cabinet blade fuse** (Phoenix PT 6-FSI/C) vs measured worst-case + - resolve battery (relocate/replace/drop — see decision below) + - vibration mounting (isolated standoffs), retain boards, torque SMAs + - **thermal: conduct heat to the metal plate, sunshade/reflective — NO internal + fan in a sealed box (does nothing); keep a Gore vent for condensation** + - conformal coat; front-panel SIM access +- ⬜ 👤 **Flash firmware v2** on every unit (thermal logging + real 4G recovery). +- ⬜ 👤 **Triage the 4 pulled units** (3 dead + 1 working-ish as control) via + `06-Testing-Validation/remote-diagnostic-protocol.md`: bench power-on with **current-draw reading**, + LED + USB-dashboard capture, photos (swollen LiPo? discoloured boost? cracked + solder? loose SMA?). Classify each against the decision tree; **teardown the + genuinely-dead ones in QRO**. Update `06-Testing-Validation/RCA-2026-06-field-failures.md`. +- ⬜ 👤 **Instrument one unit** with an interior thermocouple for a full sunny + shift — quantify the thermal problem with a number. +- ⬜ 👤 Roll the validated Phase-1 build across the fleet. + +### Backend (parallel to the hardware work — see `02-Architecture-Software/backend-review.md`) +- ⬜ 👤 **Anti-jump hotfix** to the running `retranslator.py`: make the threshold + time/speed-aware, **re-anchor after N rejects (self-heal)**, and **clear + `device_last_pos` on TTL cleanup**. Small, safe, stops permanent black-holing. +- ⬜ 👤 Set `SQL_THROTTLE_S` back to its intended 300 s; bump UDP `SO_RCVBUF`. +- ⬜ 👤 **Rotate the MSSQL `sa` password** (committed in `README.md`) and purge + secrets (`.env`, `.htracker_env`) from git history. + +--- + +## P2 — PERMANENT FIX (≈3–8 weeks): the named incomplete tasks + +### ⭐ Incomplete task #1 — **Design the Watchdog PCB** +- ⬜ 👤 Schematic for the integrated carrier (protection front-end, ≥3 A buck, + load switch/eFuse, STM32 + sense + SWD/debug, hold-up footprint, locking I/O, + bulkhead SMA, front-panel/MFF2 SIM) — `hardware brief §4`. +- ⬜ 👤 Layout (4-layer, industrial temp, GNSS RF kept clean of switcher noise). +- ⬜ 👤 Fab + assemble **2–3 pilot boards**; bring up with v2 firmware. +- ⬜ 👤 Environmental test pilots (thermal soak + vibration) before fleet rollout. + +### ⭐ Backend — migrate to the async retranslator (the real concurrency fix) +- ⬜ 👤 Adopt `retranslator_async.py` (decoupled UDP→queue→workers, per-connection + async) **after** porting in: the corrected anti-jump filter, **cell_id-based + identity** (not source IP — CGNAT-safe), and realistic load testing. +- ⬜ 👤 Rewrite `load_test.py` to measure **end-to-end** (packets forwarded to + Wialon, ack latency, UDP drops) against a **slow upstream**, not just UDP sends. + +### ⭐ Incomplete task #2 — **Re-spec HBOX components for production** +- ⬜ 👤 Lock the BOM decisions (`hardware brief §6`): **battery strategy**, SIM + strategy, MCU choice, enclosure (plastic+sunshade vs metal heatsink), modem. +- ⬜ 👤 Produce a documented, repeatable production BOM + assembly doc ("apt for + production" — directly answers the client). + +--- + +## Key decisions Leo must make (block design — see hardware brief §6) + +0. **Logistics (now):** **ship the 4 units to QRO** (recommended) vs **remote-hands + in Veracruz** — gates how fast we can confirm the hardware root cause. +1. **Battery mode:** keep (→ LiFePO4/relocated), ride-through only (→ supercap), + or **drop** (vehicle-only)? *Recommended: drop if ICAVE doesn't need parked + tracking — removes the worst thermal/safety liability. Confirm with client.* +2. **Enclosure:** **ASA inner box is adequate** (UV-stable, ~95–100 °C) — no swap + needed; keep ABS IP67 cabinet + sunshade + **conduct heat to a metal plate**, or go + **metal cabinet as heatsink**. (A bare internal fan ≠ a fix; v3.0 uses a ducted fan.) +3. **SIM:** swappable front-panel vs **MFF2 eSIM**. +4. **MCU:** reuse F411 vs industrial-temp STM32. + +--- + +## 🔴 Cross-cutting red item — FINANCIALS +- ⬜ 👤 Model **unit economics** (BOM cost → price → margin), per-BU revenue/COGS, + cashflow/billing, burn (PCB NRE + pilot), warranty reserve. Placeholder + + open questions in [[00-Project-Management/Financials|Financials.md]]. *Detailed + work happens in a separate conversation; keep it in sync here.* + +## Deliverables in this repo (Helgen `NN-` layout, updated 2026-06-20) + +| File | What | +|---|---| +| `CLAUDE.md` | master index (inherits Helgen ethos) | +| `00-Project-Management/` | `PLAN.md` · `ethos.md` · `hackathon-plan.md` · `Financials.md` · `client-comms/` | +| `01-Requirements/` | `SRS.md` · `HBOX-product-spec.md` | +| `02-Architecture-Software/backend-review.md` | htracker review (HOL block + anti-jump lockout) | +| `03-Architecture-Hardware/` | `hardware-respec-and-pcb-brief.md` · `cabinet-electrical-bom.md` · `watchdog-schematic.md` · `firmware-review.md` · `BOM-master.md` | +| `04-Source-Code/` | `firmware/` (v2 deploy · v1 ref) · `htracker/` · `notifications/` (Mattermost) | +| `06-Testing-Validation/` | `RCA-2026-06-field-failures.md` · `remote-diagnostic-protocol.md` | + +## Still open / not done here +- ✅ `htracker` **code review done** → `02-Architecture-Software/backend-review.md`. +- ✅ Repo migrated to the **Helgen English `NN-` standard**; Mattermost notifier wired. +- ⬜ Apply the **anti-jump hotfix** + secret rotation (P1 backend, above). +- ⬜ **Financials** model (red item, above) — separate conversation. +- **Client reply (Spanish)** — not drafted yet (Leo opted to hold); ask when ready. +- Field logs not yet ingested — RCA stays DRAFT until they are. +- 🧹 Delete empty legacy Spanish dirs locally (the mount can't): `cd ~/Documents/Claude/Projects/Drogheda && rmdir 0?_* 99_Archivo docs hardware firmware repo emails backend skills 2>/dev/null` diff --git a/00-Project-Management/client-comms/2026-06-20_Actualizacion-Confiabilidad-Backend_ICAVE.md b/00-Project-Management/client-comms/2026-06-20_Actualizacion-Confiabilidad-Backend_ICAVE.md new file mode 100644 index 0000000..ef7fbb1 --- /dev/null +++ b/00-Project-Management/client-comms/2026-06-20_Actualizacion-Confiabilidad-Backend_ICAVE.md @@ -0,0 +1,84 @@ +# Actualización de Confiabilidad — Plataforma de Telemetría HBOX + +**Proyecto Drogheda (GPS-RTK) · Backend / Servidor** +Helgen Technologies · 20 de junio de 2026 · Preparado para ICAVE — Hutchison Ports + +--- + +## Resumen ejecutivo + +A raíz de las desconexiones reportadas, realizamos un **análisis de causa raíz del +backend** (el servidor que recibe la telemetría de las unidades y la reenvía a Wialon). +Identificamos y **corregimos tres causas de raíz en el software del servidor** que estaban +sacando unidades de Wialon — incluyendo casos en los que **la unidad transmitía +correctamente pero la plataforma la descartaba**. + +**Las tres correcciones ya están desplegadas en producción y verificadas.** La +ruggedización de la electrónica de campo, el PCB de producción y el manejo térmico del +gabinete avanzan **en paralelo**, como frentes independientes. + +## Lo que se observó en campo + +- La flota completa se desconectaba prácticamente **al mismo tiempo**. +- Unidades que "morían" y **no regresaban** a Wialon, aun estando encendidas y transmitiendo. + +## Causa raíz (en el backend, no en el radio) + +1. **Bloqueo en cadena.** El servidor enviaba a Wialon y esperaba la confirmación dentro del + mismo hilo en que recibía los datos de toda la flota. Un solo envío lento, o un enlace + superior intermitente, **congelaba la recepción de todas las unidades a la vez** → explica + la caída simultánea de la flota. +2. **Filtro "anti-salto" demasiado rígido.** Descartaba saltos de posición grandes, pero **sin + actualizar su punto de referencia**: una unidad que se movía mientras estaba desconectada + (o con un primer dato atípico) quedaba **bloqueada de forma permanente** — transmitía bien, + pero ya nunca volvía a aparecer. +3. **Identidad por dirección IP bajo CGNAT.** El operador celular agrupa muchas SIM detrás de + una misma IP pública; al identificar por IP, el servidor **confundía unas unidades con + otras**, generando saltos falsos y mezcla de datos. + +## Hallazgo clave + +Confirmamos que **varias unidades reportadas como "muertas" estaban en realidad EN LÍNEA y +transmitiendo correctamente**: la falla estaba en el backend, no en su hardware. Esto nos +permite separar con certeza las fallas de software de las de campo. + +## Correcciones desplegadas + +- **Procesamiento concurrente:** el reenvío a Wialon ya no bloquea la recepción. Una unidad + lenta o un enlace superior caído **ya no afecta al resto de la flota**. +- **Filtro anti-salto con auto-recuperación:** **nunca** bloquea de forma permanente a una + unidad sana — se re-ancla por sí solo — y sigue rechazando saltos físicamente imposibles. +- **Identidad por dispositivo (a prueba de CGNAT):** se elimina la mezcla de datos entre + unidades que comparten IP del operador. +- **Despliegue seguro:** con respaldo automático, verificación de estado y **reversión + automática** ante cualquier falla. + +## Mayor visibilidad + +- El tablero (**monitor.helgen.dev**) ahora distingue una **"unidad sin señal"** de una + **unidad en línea que el backend no estaba reenviando** — para no confundir un problema de + plataforma con una falla de equipo. +- Las bitácoras se envían a una plataforma en la nube (Grafana) para diagnóstico y + trazabilidad, **sin saturar el disco del servidor**. + +## Validación + +Pruebas de extremo a extremo que reproducen enlace superior lento, IPs compartidas (CGNAT) y +saltos de posición: **10 de 10 verificaciones aprobadas** antes de salir a producción. + +## Estado y próximos pasos + +| Frente | Estado | +|---|---| +| Backend / servidor (confiabilidad) | ✅ **Corregido y en producción** | +| Ruggedización de la electrónica de campo | 🔧 En proceso (paralelo) | +| PCB de producción | 🔧 En diseño (paralelo) | +| Manejo térmico del gabinete | 🔧 En proceso (paralelo) | + +El backend se **rediseñó para escalar a cientos de unidades**, en línea con la meta de **30 +unidades operativas para agosto**. + +--- + +*Borrador preparado por Helgen Technologies para revisión de Leo antes de compartirse con el +cliente.* diff --git a/00-Project-Management/client-comms/Correo de helgen - Solicitud de acceso a RTG ZPM 020 y RS-SAN-001 para retiro de H-Box RTK.pdf b/00-Project-Management/client-comms/Correo de helgen - Solicitud de acceso a RTG ZPM 020 y RS-SAN-001 para retiro de H-Box RTK.pdf new file mode 100644 index 0000000..c7b261b Binary files /dev/null and b/00-Project-Management/client-comms/Correo de helgen - Solicitud de acceso a RTG ZPM 020 y RS-SAN-001 para retiro de H-Box RTK.pdf differ diff --git a/00-Project-Management/ethos.md b/00-Project-Management/ethos.md new file mode 100644 index 0000000..7876049 --- /dev/null +++ b/00-Project-Management/ethos.md @@ -0,0 +1,59 @@ +--- +tags: [project-drogheda, ethos, principles] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Drogheda — Project & Product Ethos + +> Why we build HBOX the way we do. When a decision is unclear, it should be +> resolved in favor of these principles. Short on purpose. + +## The mission + +Give port operators **position they can trust** on equipment that runs in one of +the most hostile environments we ship into — heat, salt, vibration, dust, around +the clock. Centimetres, continuously, unattended. + +## Principles + +1. **Production-grade, not academic.** The ICAVE review was fair: prototype + wiring belongs on a bench, not on an RTG. Every unit we ship is repeatable, + documented, and assembled the same way. No Dupont in the field. No "it works + on mine." + +2. **Fail-safe and self-healing.** Power-off is the safe state. The watchdog + recovers the link by itself and, when it can't, **keeps reporting standalone + position rather than going dark**. A unit must never silently disappear, and + never thrash in a reboot loop. + +3. **Survive the environment — don't fight it.** We design for the cabinet's real + interior temperature, not the spec-sheet ambient. No materials that soften in + the sun (no PLA-class material; the current ASA case complies). Cooling by + conduction (or an engineered waterproof duct), sealing kept (IP67). Heat, vibration, + and humidity are requirements, not surprises. + +4. **Measure before you blame; rule out the cheap cause first.** A "dead" unit is + guilty until the logs say so. Backend, SIM, and caster get checked before a + truck rolls. Every unit carries enough telemetry to tell its own story. + +5. **Observable by design.** Temperature, battery, fix quality, resets — logged. + If we can't see it, we can't fix it, and we can't prove it to the client. + +6. **One source of truth.** One BOM, one folder structure, one master index + (`CLAUDE.md`). The Drive mirrors it. Versioned, dated, no "final_v3_real". + +7. **ICAVE is the stress lab.** We harden in the worst terminal first. What + survives Veracruz ships clean to TIMSA, EIT, and LCT. + +## What "done" means + +A unit is done when it **boots, fixes, transmits, recovers from a forced outage +without looping, survives a sunny-shift soak, and a stranger can build the next +one from the BOM and assembly doc.** Not before. + +## Related + +- [[CLAUDE|Master Index]] · [[01-Requirements/SRS|SRS]] · [[00-Project-Management/PLAN|PLAN]] diff --git a/00-Project-Management/hackathon-plan.md b/00-Project-Management/hackathon-plan.md new file mode 100644 index 0000000..2120f9b --- /dev/null +++ b/00-Project-Management/hackathon-plan.md @@ -0,0 +1,115 @@ +--- +tags: [project-drogheda, plan, hackathon] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Drogheda Hackathon — Run-of-Show, Backlog & Gantt + +> A weekend push to stabilize the fleet and de-risk the production build. Dates +> below assume a **Fri-evening → Sun** weekend; **adjust to your chosen weekend.** +> Goal: leave Monday with the backend fixed, firmware v2 bench-validated, the 4 +> pulled units triaged, the thermal number measured, and the PCB schematic started. + +## 1. Objectives (definition of success) + +1. **Backend stops killing healthy units** — anti-jump hotfix + cell_id identity + deployed; 500-unit slow-upstream load test passes; secrets rotated. +2. **Firmware v2 bench-validated** — recovery/backoff/LIMP/thermal verified on a + real Blackpill + simpleRTK2B; flash candidate signed off. +3. **The 4 pulled units classified** — hardware-dead vs backend-victim; RCA updated. +4. **Thermal quantified** — a real cabinet-interior temperature from a soak test; + battery + thermal-path decisions made. +5. **Production kicked off** — watchdog schematic → first PCB schematic draft; + BOM + SRS signed off; Notion board live. + +## 2. Tracks & suggested owners + +| Track | Focus | Suggested owner | +|---|---|---| +| A — Backend | anti-jump hotfix, cell_id identity, load test, deploy, secrets | Gibran + Leo | +| B — Firmware | bench-test v2, recovery/thermal validation, flash candidate | Leo | +| C — Hardware/Thermal | soak test, battery & thermal-path decisions, conduction mock-up, fuse check | Daniel | +| D — Diagnostics | triage 4 pulled units (remote-hands/shipped), classify, RCA update | Gibran | +| E — PCB/Docs | schematic from watchdog spec, BOM/SRS sign-off | Leo + Daniel | +| F — PM/Integrations | Notion board, Drive sync, client comms, run-of-show | Adriana | + +## 3. Run-of-show + +| When | Block | Output | +|---|---|---| +| **Fri PM** | Kickoff; assign tracks; **backend historical triage** (htracker-online vs Wialon-offline); bench setup | which "dead" units are backend victims; benches ready | +| **Sat AM** | A: anti-jump hotfix + cell_id · B: v2 bench bring-up · C: start thermocouple soak · D: unit power-on + current-draw | hotfix in test; v2 booting; soak logging; first unit classified | +| **Sat PM** | A: 500-unit slow-upstream load test · B: forced-outage recovery test · C: battery/thermal-path decision · E: PCB schematic start | load test result; recovery proven; material decisions; schematic skeleton | +| **Sun AM** | A: deploy fixed retranslator + rotate secrets · D: finish triage + RCA update · E: BOM/SRS sign-off | backend live; 4 units classified; docs frozen | +| **Sun PM** | Integrate; **demo**; definition-of-done checklist; retro; next-week plan | go/no-go; backlog for week +1 | + +## 4. Gantt + +```mermaid +gantt + dateFormat YYYY-MM-DD + title Drogheda Hackathon (adjust dates to your weekend) + axisFormat %a + + section A Backend + Historical triage :a1, 2026-06-19, 1d + Anti-jump hotfix + cell_id :a2, 2026-06-20, 1d + 500-unit load test :a3, after a2, 1d + Deploy + rotate secrets :a4, 2026-06-21, 1d + + section B Firmware + v2 bench bring-up :b1, 2026-06-20, 1d + Recovery/thermal validation :b2, after b1, 1d + Flash candidate sign-off :b3, 2026-06-21, 1d + + section C Hardware/Thermal + Thermocouple soak :c1, 2026-06-20, 2d + Battery + thermal decision :c2, 2026-06-20, 1d + Conduction mock-up :c3, 2026-06-21, 1d + + section D Diagnostics + Power-on + current draw :d1, 2026-06-20, 1d + Classify + RCA update :d2, 2026-06-21, 1d + + section E PCB/Docs + PCB schematic draft :e1, 2026-06-20, 2d + BOM + SRS sign-off :e2, 2026-06-21, 1d + + section F PM + Notion board + Drive sync :f1, 2026-06-19, 1d + Demo + retro :f2, 2026-06-21, 1d +``` + +## 5. Prioritized backlog (pull into Notion) + +| P | Item | Track | DoD | +|---|---|---|---| +| P0 | Anti-jump hotfix (time-aware + self-heal + clear on TTL) | A | healthy unit can't be permanently dropped; unit test | +| P0 | cell_id identity (drop source-IP keying) | A | CGNAT-safe pairing; verified with 2 sims behind 1 IP | +| P0 | v2 forced-outage recovery test | B | recovers via power-cycle within backoff, no loop | +| P0 | Thermocouple soak (measure cabinet interior) | C | a real °C number over a sunny shift | +| P0 | Classify the 4 pulled units | D | each tagged dead/backend-victim; RCA updated | +| P1 | 500-unit slow-upstream load test | A | no unit dropped; metrics captured | +| P1 | Confirm ASA case survives soak (expected OK) | C | sample part survives soak | +| P1 | Battery decision (drop/LiFePO4/supercap) | C | FR-7 closed | +| P1 | Rotate MSSQL secret + purge from git | A | secret out of repo + history | +| P1 | PCB schematic from watchdog spec | E | reviewable schematic | +| P2 | Front-panel SIM / MFF2 | E | maintainability path chosen | +| P2 | Verify cabinet blade fuse | C | rated vs worst-case + inrush | + +## 6. Logistics / pre-reqs + +- Benches: 24 V supply with **current limit + meter**, Blackpill + simpleRTK2B + + 4G master + antennas with sky view, USB-serial laptop. +- The **4 pulled units** in QRO (ship) or remote-hands in VER — + [[06-Testing-Validation/remote-diagnostic-protocol|protocol]]. +- htracker repo + a staging caster/Wialon endpoint for the load test. +- Notion workspace + Drive access for the team. + +## Related + +- [[CLAUDE|Master Index]] · [[00-Project-Management/PLAN|PLAN]] · [[01-Requirements/SRS|SRS]] · + [[02-Architecture-Software/backend-review|backend-review]] · [[06-Testing-Validation/RCA-2026-06-field-failures|RCA]] diff --git a/01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf b/01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf new file mode 100644 index 0000000..08ce23f Binary files /dev/null and b/01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf differ diff --git a/01-Requirements/Drogheda-Requirements-Tracker.xlsx b/01-Requirements/Drogheda-Requirements-Tracker.xlsx new file mode 100644 index 0000000..565db3f Binary files /dev/null and b/01-Requirements/Drogheda-Requirements-Tracker.xlsx differ diff --git a/01-Requirements/Drogheda-SRS-Requerimientos.pdf b/01-Requirements/Drogheda-SRS-Requerimientos.pdf new file mode 100644 index 0000000..60a0d6d Binary files /dev/null and b/01-Requirements/Drogheda-SRS-Requerimientos.pdf differ diff --git a/01-Requirements/Especificacion-Requerimientos/cuerpo.tex b/01-Requirements/Especificacion-Requerimientos/cuerpo.tex new file mode 100644 index 0000000..c82c45f --- /dev/null +++ b/01-Requirements/Especificacion-Requerimientos/cuerpo.tex @@ -0,0 +1,73 @@ +\section{Alcance} + +Un rastreador GPS-RTK montado en máquina y alojado en gabinete (\textbf{HBOX}) que +transmite posición corregida por RTK sobre 4G al backend (\textit{retranslator} +$\rightarrow$ Wialon/Traccar $\rightarrow$ VERONICA), sobrevive un entorno portuario +hostil (calor, vibración, humedad salina) y es manufacturable y mantenible a escala de +flota. Meta: \textbf{30 unidades operativas para agosto} en ICAVE, TIMSA, EIT y LCT. +Este documento es el complemento formal del tracker de requerimientos (.xlsx). + +\section{Requerimientos funcionales} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}l X >{\centering\arraybackslash}p{1.2cm}@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{ID} & \textcolor{white}{Requerimiento} & \textcolor{white}{Pri.}\\ +\midrule +FR-01 & Adquirir posición GNSS multibanda (simpleRTK2B Pro / ZED-F9P). & O\\ +FR-02 & Obtener correcciones RTCM3 vía NTRIP sobre 4G; lograr fix/float en cielo abierto. & O\\ +FR-03 & Emitir NMEA (GGA+RMC) por UDP al backend a la tasa configurada. & O\\ +FR-04 & Control de tasa: 1 Hz con ignición; 30 s en batería/reposo. & O\\ +FR-05 & Watchdog: recuperar el enlace reiniciando el stack con \textit{backoff}; nunca en bucle de reinicio. & O\\ +FR-06 & Reporte degradado (LIMP): seguir reportando posición estándar si no hay correcciones. & O\\ +FR-07 & Operar con 24 V del gabinete; proteger la celda (no cargar $>$45\,\textdegree C). & O\\ +FR-08 & LED de estado legible en campo. & R\\ +FR-09 & Identidad por unidad (cell\_id) y aprovisionamiento repetible. & O\\ +FR-10 & SIM/chip de datos serviciable sin desensamble total. & R\\ +FR-11 & Observabilidad: registrar temperatura, batería, calidad de fix y reinicios. & R\\ +FR-12 & Backend: ingesta concurrente sin bloqueo de cabecera; el filtro anti-salto nunca debe bloquear de forma permanente a una unidad sana. & O\\ +\bottomrule +\end{tabularx}} + +\section{Requerimientos no funcionales} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}l X L{4.2cm}@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{ID} & \textcolor{white}{Requerimiento} & \textcolor{white}{Objetivo}\\ +\midrule +NFR-01 Térmico & Operar dentro del gabinete sellado bajo sol de Veracruz. & Componentes $\geq$75\,\textdegree C; \textbf{sin PLA} en la ruta térmica; diseñar a la temperatura interior medida.\\ +NFR-02 Vibración & Resistir vibración continua de maquinaria. & Sin Dupont; conectores con seguro; placas retenidas; objetivo $\geq$2 G (validar).\\ +NFR-03 Ingreso & Mantener IP67; sin condensación. & Sellado; enfriamiento sólo por conducción (sin ventilación que rompa IP).\\ +NFR-04 Eléctrico & Tolerar el ambiente eléctrico de la máquina. & Surge/breaker/ignición en el gabinete; TVS local modesto en el HBOX.\\ +NFR-05 Disponibilidad & Minimizar desconexiones en Wialon. & Sin bucles (FR-05); sin bloqueo de cabecera en backend (FR-12); definir SLO.\\ +NFR-06 Mantenibilidad & Serviciable, documentado, repetible. & Una PCB; etiquetado; puntos de prueba; BOM publicado.\\ +NFR-07 Escalabilidad & Backend a escala de flota. & $\geq$500 unidades concurrentes sin pérdidas (prueba con \textit{upstream} lento).\\ +NFR-08 Seguridad & Sin secretos en el repo. & Rotar la contraseña MSSQL; secretos vía entorno/almacén; mínimo privilegio.\\ +NFR-09 Manufactura & Construible para el lote de agosto. & BOM bloqueado; documento de ensamble; pilotos validados antes de la flota.\\ +\bottomrule +\end{tabularx}} + +\section{Restricciones y supuestos} + +\begin{itemize} + \item La entrada del HBOX es el \textbf{24 V regulado} del gabinete + una señal de ignición \textbf{aislada}. + \item El \textbf{gabinete} (Yetlebox IP67 ABS + componentes DIN) permanece; el rediseño ataca los \textbf{internos del HBOX} + lo térmico/mecánico. + \item El ZED-F9P ($-40$ a $+85$\,\textdegree C) no es el eslabón térmico débil; lo son el \textbf{18650/PowerBoost}, el \textbf{módem 4G} y la \textbf{carcasa PLA}. + \item Las correcciones llegan por 4G, no por el GNSS (gobierna FR-05/FR-06). + \item El firmware se valida en banco antes del \textit{flasheo} en campo. +\end{itemize} + +\section{Criterios de aceptación (alto nivel)} + +\begin{itemize} + \item En banco: una unidad arranca, obtiene fix/float, transmite y \textbf{se recupera automáticamente} de un corte forzado de 4G/NTRIP dentro de la ventana de \textit{backoff}, sin entrar en bucle. + \item Backend: bajo una prueba de 500 unidades con \textit{upstream} lento, \textbf{ninguna unidad se descarta} ni queda filtrada de forma permanente. + \item Térmico: una unidad sobrevive un turno soleado a la temperatura interior medida del gabinete, sin deformación de PLA ni sobre-temperatura de batería. + \item Una unidad retirada permite distinguir, por sus registros, una muerte de hardware de una víctima del backend. +\end{itemize} + +\begin{helgennotice}[Trazabilidad] +Cada requerimiento se rastrea en el \textbf{Tablero Helgen Drogheda — Tracker de +Requerimientos} (.xlsx), con estado, verificación, responsable y puntos abiertos. +\end{helgennotice} diff --git a/01-Requirements/Especificacion-Requerimientos/main.pdf b/01-Requirements/Especificacion-Requerimientos/main.pdf new file mode 100644 index 0000000..60a0d6d Binary files /dev/null and b/01-Requirements/Especificacion-Requerimientos/main.pdf differ diff --git a/01-Requirements/Especificacion-Requerimientos/main.tex b/01-Requirements/Especificacion-Requerimientos/main.tex new file mode 100644 index 0000000..23b3608 --- /dev/null +++ b/01-Requirements/Especificacion-Requerimientos/main.tex @@ -0,0 +1,18 @@ +% Helgen Drogheda — Especificación de Requerimientos (SRS) · DRG-REQ-001 +\documentclass[11pt]{article} +\newcommand{\sistema}{Helgen Drogheda — HBOX RTK} +\newcommand{\sistemapdf}{Helgen Drogheda HBOX RTK} +\newcommand{\doctipo}{Especificación de Requerimientos} +\input{../../_brand/estilo-helgen.tex} +\begin{document} +\helgenportada{Especificación de Requerimientos}{Sistema de rastreo GPS-RTK HBOX para equipo portuario}{% + \begin{tabular}{@{}ll@{}} + \textbf{Documento} & DRG-REQ-001 — Especificación de Requerimientos\\ + \textbf{Versión} & 1.0 (Borrador)\\ + \textbf{Fecha} & 20 de junio de 2026\\ + \textbf{Autor} & Dr.\ Leopoldo Rodríguez Salazar — Helgen Technologies\\ + \textbf{Clasificación} & Confidencial — uso de Hutchison Ports y Helgen\\ + \textbf{Asistencia IA} & Elaborado con asistencia de Claude (Anthropic), revisado por el autor\\ + \end{tabular}} +\input{cuerpo.tex} +\end{document} diff --git a/01-Requirements/HBOX-Ficha-Tecnica/cuerpo.tex b/01-Requirements/HBOX-Ficha-Tecnica/cuerpo.tex new file mode 100644 index 0000000..15dce22 --- /dev/null +++ b/01-Requirements/HBOX-Ficha-Tecnica/cuerpo.tex @@ -0,0 +1,108 @@ +\section{Descripción del producto} + +El \textbf{HBOX} es una unidad de telemetría \textbf{GPS-RTK} de precisión, montada en +equipo portuario pesado, que entrega posición de \textbf{precisión centimétrica} sobre +\textbf{4G} al backend de Helgen (\textit{retranslator} $\rightarrow$ Wialon/Traccar +$\rightarrow$ VERONICA). Un \textbf{watchdog} embebido mantiene vivo el enlace de +correcciones de forma autónoma. Se entrega como un \textbf{ensamble de gabinete}: un +gabinete industrial IP67 (potencia, protección y aislamiento de ignición) que aloja el +HBOX interno (RTK + 4G + watchdog). + +\begin{helgennotice}[En una frase] +Centímetros de precisión, de forma continua y desatendida, en uno de los entornos más +hostiles en los que operamos: calor, salinidad, vibración y polvo, las 24 horas. +\end{helgennotice} + +\section{Variantes} + +La electrónica interna del HBOX es \textbf{idéntica en todas las variantes}; sólo cambia +la etapa de potencia del gabinete según el voltaje de la máquina. + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}l L{4.6cm} X@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Variante} & \textcolor{white}{Alimentación} & \textcolor{white}{Notas}\\ +\midrule +24 V & 24 Vcd $\rightarrow$ 24 V & \textbf{En producción} — RTG, Reach Stacker, Empty Handler\\ +12 V & 12 Vcd $\rightarrow$ 24 V & Teórico — Tractoplana, Montacargas\\ +48 V & 48 Vcd $\rightarrow$ 24 V & Teórico\\ +110 V & 110 Vca $\rightarrow$ 24 V & Teórico (+ SPD CA, termomagnético)\\ +230 V & 220 Vca $\rightarrow$ 24 V & Teórico (+ fusible Bussman)\\ +Base & 110 / 230 Vca & Estación base RTK/NTRIP (Ardusimple AS-PRO-CORS), 1 por terminal\\ +\bottomrule +\end{tabularx}} + +\section{Especificaciones técnicas (objetivo)} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}L{4.4cm} X@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Parámetro} & \textcolor{white}{Especificación}\\ +\midrule +GNSS & u-blox ZED-F9P, multibanda L1/L2, multiconstelación\\ +Precisión de posición & RTK fijo: $\sim$1–2 cm + 1 ppm CEP (cielo abierto, correcciones válidas)\\ +Tasa de actualización & 1 Hz (activo) / 30 s (reposo/batería)\\ +Correcciones & NTRIP/RTCM3 sobre 4G LTE (ArduSimple 4G NTRIP Master)\\ +Telemetría de salida & NMEA sobre UDP $\rightarrow$ retranslator $\rightarrow$ Wialon/Traccar $\rightarrow$ VERONICA\\ +Entrada del HBOX & 24 Vcd (desde el gabinete) + ignición aislada\\ +Riel interno de 5 V & Mean Well SD-15B-05 (18–36 V $\rightarrow$ 5 V, 3 A)\\ +Respaldo de energía & PowerBoost 1000C + 18650 Li-ion 1S4P\\ +MCU / watchdog & STM32F411 Blackpill (firmware \texttt{blackpillrtkcode\_v2})\\ +Conector HBOX$\leftrightarrow$gabinete & GX16-5 circular con seguro\\ +Antenas & 1$\times$ GNSS multibanda (SMA) + 2$\times$ LTE (AR1582)\\ +Gabinete & Yetlebox P3901, ABS, \textbf{IP67}, 39$\times$29$\times$16 cm, riel DIN\\ +Ambiental (objetivo) & $-10$ a $+75$\,\textdegree C interno; vibración $\geq$2 G (a validar); humedad costera\\ +\bottomrule +\end{tabularx}} + +\section{Arquitectura del sistema} + +\begin{center} +\begin{tikzpicture}[node distance=8mm and 12mm] + \node[cajaoscura, text width=2.6cm] (maq) {Máquina\\12/24/48/110/230 V}; + \node[caja, right=of maq, text width=3.1cm] (gab) {Gabinete IP67\\SPD · breaker · SSR\\Mean Well $\rightarrow$ 24 V}; + \node[caja, right=of gab, text width=3.2cm] (hbox) {HBOX\\simpleRTK2B Pro\\+ 4G NTRIP + watchdog}; + \node[caja, below=of hbox, text width=3.2cm] (be) {retranslator\\Wialon / Traccar}; + \draw[flecha] (maq) -- (gab); + \draw[flecha] (gab) -- node[etiqueta,above]{GX16-5 · 24 V} (hbox); + \draw[flechaacento] (hbox) -- node[etiqueta,right]{NMEA / UDP} (be); + \node[etiqueta, above=1mm of hbox] {RTCM3 / NTRIP (4G)}; +\end{tikzpicture} +\end{center} + +Las correcciones llegan por el \textbf{módem 4G} (no por el GNSS); ante una caída del +enlace, el watchdog recupera el sistema \textbf{reiniciando el stack}, nunca con un +\textit{reset} del receptor GNSS. + +\section{Interfaces y conectividad} + +\begin{itemize} + \item \textbf{GX16-5 (HBOX$\leftrightarrow$gabinete):} 24 V, GND, ignición (+ 2 reserva). + \item \textbf{Antenas:} SMA de mamparo (GNSS) + 2$\times$ LTE por glándula; con torque y fijador de rosca. + \item \textbf{SIM:} Telcel micro (Serie 1, interna) $\rightarrow$ panel frontal o eSIM MFF2 (Serie 2). + \item \textbf{Diagnóstico:} tablero ANSI por USB/UART (batería, fix, paquetes, temperatura, estado). +\end{itemize} + +\section{Indicación de estado (LED)} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}L{3.6cm} X@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Patrón} & \textcolor{white}{Significado}\\ +\midrule +Parpadeo rápido & Arranque — esperando 4G/GNSS\\ +Encendido fijo & Operando con energía de máquina, RTK fijo\\ +Latido (1 s) & Modo batería, 30 s\\ +Parpadeo lento & Buscando / sin correcciones\\ +Doble (tipo SOS) & LIMP — sin 4G, sigue reportando posición estándar\\ +Estrobo & Reiniciando el stack\\ +Apagado & Reposo / batería crítica\\ +\bottomrule +\end{tabularx}} + +\section{Hoja de ruta — Serie 2 (producción)} + +PLA $\rightarrow$ ASA/PC o metal\, ·\, cableado Dupont $\rightarrow$ PCB con conectores con +seguro\, ·\, watchdog discreto $\rightarrow$ \textit{load switch} integrado\, ·\, decisión de +batería\, ·\, SIM al panel frontal\, ·\, enfriamiento por conducción\, ·\, ensamble +documentado. La Serie 2 es el objetivo de PCB de producción para el lote de agosto. diff --git a/01-Requirements/HBOX-Ficha-Tecnica/main.pdf b/01-Requirements/HBOX-Ficha-Tecnica/main.pdf new file mode 100644 index 0000000..08ce23f Binary files /dev/null and b/01-Requirements/HBOX-Ficha-Tecnica/main.pdf differ diff --git a/01-Requirements/HBOX-Ficha-Tecnica/main.tex b/01-Requirements/HBOX-Ficha-Tecnica/main.tex new file mode 100644 index 0000000..b6cb413 --- /dev/null +++ b/01-Requirements/HBOX-Ficha-Tecnica/main.tex @@ -0,0 +1,23 @@ +% ===================================================================== +% Helgen Drogheda — HBOX · Ficha Técnica (cliente) +% Compilar: latexmk -xelatex main.tex (fuentes de marca vía ~/.fonts) +% Fuente en el repo; el PDF es artefacto. PDF final -> Drive. +% ===================================================================== +\documentclass[11pt]{article} +\newcommand{\sistema}{Helgen Drogheda — HBOX RTK} +\newcommand{\sistemapdf}{Helgen Drogheda HBOX RTK} +\newcommand{\doctipo}{Ficha Técnica} +\input{../../_brand/estilo-helgen.tex} + +\begin{document} +\helgenportada{Ficha Técnica del Producto}{HBOX — Rastreador GPS-RTK para equipo portuario pesado}{% + \begin{tabular}{@{}ll@{}} + \textbf{Documento} & DRG-FT-001 — Ficha Técnica HBOX\\ + \textbf{Versión} & 1.0\\ + \textbf{Fecha} & 20 de junio de 2026\\ + \textbf{Autor} & Dr.\ Leopoldo Rodríguez Salazar — Helgen Technologies\\ + \textbf{Clasificación} & Confidencial — uso de Hutchison Ports y Helgen\\ + \textbf{Asistencia IA} & Elaborado con asistencia de Claude (Anthropic), revisado por el autor\\ + \end{tabular}} +\input{cuerpo.tex} +\end{document} diff --git a/01-Requirements/HBOX-product-spec.md b/01-Requirements/HBOX-product-spec.md new file mode 100644 index 0000000..533f7c8 --- /dev/null +++ b/01-Requirements/HBOX-product-spec.md @@ -0,0 +1,91 @@ +--- +tags: [project-drogheda, product-spec, hbox, datasheet] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Draft +--- + +# HBOX — Product Specification (Series 1 → Series 2) + +> The HBOX as a product: what it is, its variants, interfaces, and target specs. +> Requirements live in [[01-Requirements/SRS|SRS]]; parts in +> [[03-Architecture-Hardware/BOM-master|BOM]]. **Series 1** = current +> module-based build (ASA case + Dupont wiring). **Series 2** = the production PCB target. + +## 1. Overview + +HBOX is a ruggedized, cabinet-housed **RTK GPS telemetry unit** for heavy port +equipment. It delivers **centimetre-class** position over 4G to the Helgen +backend, with an onboard **watchdog** that keeps the correction link alive +unattended. Sold as a **gabinete (cabinet) assembly**: an industrial IP67 cabinet +(power, protection, ignition isolation) + the internal HBOX (RTK + 4G + watchdog). + +## 2. Variants + +| Variant | Machine power | Cabinet supply → | Notes | +|---|---|---|---| +| 12 V | 12 Vdc | Mean Well DDR-60G-24 → 24 V | Tractoplana, Montacargas | +| **24 V** | 24 Vdc | DDR-60G-24 → 24 V | **dominant**: RTG, Reach Stacker, Empty Handler | +| 48 V | 48 Vdc | DDR-60L-24 → 24 V | — | +| 110 V | 110 Vac/dc | HDR-60-24 / Siemens → 24 V | + AC SPD + termomagnético | +| 230 V | 220 Vac | HDR-60-24 → 24 V | + Bussman fusing | +| **Base Station** | 110 Vac | — | Ardusimple AS-PRO-CORS (RTK source/NTRIP caster) | + +Internal HBOX electronics are **identical across variants**; only the cabinet +power/protection stage changes with machine voltage. + +## 3. Key specifications (targets) + +| Parameter | Spec | +|---|---| +| GNSS | u-blox ZED-F9P, multi-band L1/L2, multi-constellation | +| Position accuracy | RTK fixed: ~1–2 cm + 1 ppm CEP (open sky, valid corrections) | +| Update rate | 1 Hz (active) / 30 s (idle/battery) | +| Corrections | NTRIP/RTCM3 over 4G LTE (ArduSimple 4G NTRIP Master) | +| Telemetry out | NMEA over UDP → retranslator → Wialon/Traccar → VERONICA | +| HBOX input | 24 Vdc (from cabinet) + isolated ignition | +| Internal 5 V | Mean Well SD-15B-05 (18–36 V→5 V, 3 A) | +| Backup power | PowerBoost 1000C + 18650 Li-ion 1S4P *(under review — see SRS FR-7)* | +| MCU / watchdog | STM32F411 Blackpill, firmware `blackpillrtkcode_v2` | +| HBOX↔cabinet | GX16-5 locking circular connector | +| Antennas | 1× multiband GNSS (SMA) + 2× LTE (AR1582) | +| Status | external LED (multi-pattern) + onboard LED + debug UART dashboard | +| Cabinet | Yetlebox P3901, ABS, **IP67**, 39×29×16 cm, DIN rail | +| Environmental (target) | −10…+75 °C internal; ≥2 G vibration *(to validate)*; coastal humidity | +| Enclosure (HBOX) | **Series 1: ASA (adequate)** → Series 2: ASA or metal / plate-mount | + +## 4. Interfaces + +- **GX16-5 (HBOX↔cabinet):** 24 V, GND, ignition, (spare ×2) — finalize pinout on + the schematic before PCB. +- **Antennas:** bulkhead SMA (GNSS) + 2× LTE via glands; torque + thread-lock. +- **SIM:** Telcel micro (Series 1, internal) → front-panel or MFF2 eSIM (Series 2). +- **Debug:** USB/UART ANSI dashboard (battery, fix, packets, temp, state). + +## 5. Status indication (LED) + +Boot (fast blink) · Running+RTK (solid) · Battery mode (heartbeat) · Searching/ +degraded (slow blink) · LIMP/no-4G (SOS-ish double) · Power-cycling (strobe) · +Sleep/critical (off). See firmware v2. + +## 6. Model / SKU + +`H-BOX Series1` (current) → `H-BOX Series2` (PCB). Each unit carries a Helgen +data label (modelo, serie, datos técnicos) + warning + input-voltage label. + +## 7. Series 2 (production) deltas + +ASA case retained (optional metal) · Dupont/hand-wire → PCB + locking connectors · discrete +watchdog → integrated load switch · battery decision · front-panel SIM · +conduction cooling · documented assembly. See +[[03-Architecture-Hardware/hardware-respec-and-pcb-brief|hardware brief]]. + +## Open Questions + +- [ ] Finalize GX16-5 pinout · battery strategy · enclosure material. +- [ ] Validate the 2 G vibration and IP67-after-mods claims for the datasheet. + +## Related + +- [[CLAUDE|Master Index]] · [[01-Requirements/SRS|SRS]] · [[03-Architecture-Hardware/BOM-master|BOM]] diff --git a/01-Requirements/SRS.md b/01-Requirements/SRS.md new file mode 100644 index 0000000..e542256 --- /dev/null +++ b/01-Requirements/SRS.md @@ -0,0 +1,83 @@ +--- +tags: [project-drogheda, requirements, srs] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Draft +--- + +# Software/System Requirements — Drogheda HBOX RTK + +> What the Drogheda system must do and how well. Derived from the ICAVE field +> failures, the client review, and the productization goal (30 units by August +> across ICAVE/TIMSA/EIT/LCT). IDs are stable; trace fixes and tests back to them. + +## 1. Scope + +A vehicle-mounted, cabinet-housed **RTK GPS tracker (HBOX)** that streams +RTK-corrected position over 4G to the backend (`retranslator` → Wialon/Traccar → +VERONICA), survives a harsh, hot, high-vibration port environment, and is +manufacturable and serviceable at fleet scale. + +## 2. Functional requirements + +| ID | Requirement | Priority | +|---|---|---| +| FR-1 | Acquire GNSS position via simpleRTK2B Pro (ZED-F9P), multiband. | Must | +| FR-2 | Obtain RTCM3 corrections via NTRIP over 4G from the Helgen base station; achieve RTK fix/float in open sky. | Must | +| FR-3 | Emit NMEA (GGA+RMC) and deliver it via UDP to the backend at the configured rate. | Must | +| FR-4 | Rate control: 1 Hz when powered+ignition-on; 30 s on battery/idle (power saving). | Must | +| FR-5 | **Watchdog recovery:** detect loss of corrections / NMEA silence and recover by **power-cycling the RTK+4G stack** with exponential backoff; never enter an infinite fast-reboot loop. | Must | +| FR-6 | **Degraded reporting:** when corrections can't be restored, keep reporting standalone GPS (LIMP) instead of going offline. | Must | +| FR-7 | Power management: run on cabinet 24 V; ride-through / battery behavior per the chosen battery strategy; protect the cell (no charge >45 °C if a cell is retained). | Must | +| FR-8 | Field-readable status LED (boot / running / battery / searching / LIMP / power-cycle / sleep). | Should | +| FR-9 | Per-unit identity (cell_id) and provisioning (UART config, unique ID, NVM save) repeatable per the SOP. | Must | +| FR-10 | SIM/data-chip serviceable without full disassembly. | Should | +| FR-11 | Observability: log MCU/enclosure temperature, battery V, fix quality, reset events for field diagnosis. | Should | +| FR-12 | **Backend:** ingest from many units concurrently without head-of-line blocking; forward valid fixes to Wialon; anti-jump filter must never permanently lock out a healthy unit. | Must | + +## 3. Non-functional requirements + +| ID | Requirement | Target | +|---|---|---| +| NFR-1 Thermal | Operate continuously inside the sealed cabinet in Veracruz sun. | Internal components rated/derated for **≥75 °C**; enclosure is **ASA** (UV-stable, ~95–100 °C — no PLA-class material in the thermal path); measured cabinet-interior target set from a sunny-shift log. | +| NFR-2 Vibration | Survive continuous heavy-machinery vibration (RTG, reach stacker). | No Dupont/hand-wire; locking connectors; retained boards; conformal coat. Target ≥2 G per spec sheet — **validate**. | +| NFR-3 Ingress | Maintain **IP67**; no internal condensation. | Sealed; Gore vent for pressure/humidity; cooling via conduction only (no IP-breaking vents). | +| NFR-4 Electrical | Tolerate the machine electrical environment. | Surge/breaker/ignition isolation handled at the **cabinet** (Phoenix SPD + breaker + SSR); HBOX adds a modest local TVS + reverse-polarity. | +| NFR-5 Availability | Minimize Wialon disconnects per unit. | No reset loops (FR-5); backend HOL-block eliminated (FR-12); define a monthly availability SLO once stabilized. | +| NFR-6 Maintainability | Serviceable, documented, repeatable. | One PCB; labeled; test points; published BOM; SIM access (FR-10). | +| NFR-7 Scalability | Backend handles fleet scale. | ≥500 concurrent units without drop under realistic (slow-upstream) load tests. | +| NFR-8 Security | No secrets in the repo. | Rotate the committed MSSQL password; secrets via env/secret store; least privilege. | +| NFR-9 Manufacturability | Buildable for the August batch. | Production BOM locked; assembly doc; pilot units validated before fleet. | + +## 4. Constraints & assumptions + +- HBOX input is the cabinet's **regulated 24 V** + an **isolated ignition** signal. +- The **cabinet** (Yetlebox IP67 ABS + DIN industrial parts) stays; the redesign + targets the **HBOX internals** + thermal/mechanical. +- ZED-F9P (−40…+85 °C) is not the thermal weak link; the **18650/PowerBoost** and + **4G modem** are (the **ASA case** is adequate). +- Corrections flow over 4G, not the GNSS (drives FR-5/FR-6). +- Firmware must be bench-validated before field flash (no STM32 build in sandbox). + +## 5. Acceptance criteria (high level) + +- A unit on the bench: boots, gets RTK fix/float, transmits, and **recovers + automatically** from a forced 4G/NTRIP outage within the backoff window without + looping (FR-5/6). +- Backend: under a 500-unit slow-upstream load test, **no unit is dropped** and no + unit is permanently filtered out (FR-12, NFR-7). +- Thermal: a unit survives a full sunny-shift soak at the measured cabinet + interior temperature with no enclosure deformation and no battery over-temp (NFR-1). +- A pulled unit's logs distinguish hardware death from backend victimhood (FR-11). + +## Open Questions + +- [ ] Battery strategy (drop / LiFePO4 / supercap) → finalizes FR-7, NFR-1. +- [ ] Confirm the 2 G vibration claim and IP67 after the thermal mods (NFR-2/3). +- [ ] Define the numeric availability SLO (NFR-5) once the fleet is stable. + +## Related + +- [[CLAUDE|Master Index]] · [[01-Requirements/HBOX-product-spec|HBOX Product Spec]] · + [[06-Testing-Validation/RCA-2026-06-field-failures|RCA]] · [[00-Project-Management/PLAN|PLAN]] diff --git a/02-Architecture-Software/backend-review.md b/02-Architecture-Software/backend-review.md new file mode 100644 index 0000000..fc572d7 --- /dev/null +++ b/02-Architecture-Software/backend-review.md @@ -0,0 +1,230 @@ +# Backend Review — `htracker` retranslator + monitor + +**Date:** 2026-06-19 · **Reviewer:** Claude (for Leo) · branch `feature/test500unidades` +**Scope:** `retranslator.py`, `retranslator_async.py`, `gps_logger.py`, +`web_monitor.py`, `notifier.py`, `load_test.py`, service/unit files. +**Why:** the RCA flagged a **backend common-mode** (H2) as the cheapest, most +likely explanation for *all units disconnecting at once* — and for a *single unit +"dying" and never coming back*. **This review found concrete mechanisms for both, +in software, independent of the field hardware.** + +Severity: 🔴 can take units off Wialon · 🟠 reliability/scale · 🟡 hygiene. + +--- + +## ✅ RESOLUTION (2026-06-20) — fixed & deployed to production + +All three 🔴 mechanisms were fixed in `retranslator.py` and **deployed** via the new +`deploy.sh` (backup + health-check + auto-rollback; gateway/monitor/notifier all active). +Approach: **harden in place**, not the async rewrite. + +| # | Issue | Fix shipped | +|---|-------|-------------| +| P1 | Synchronous Wialon I/O in the receive loop (HOL blocking) | `WialonForwarder` worker pool decouples TCP I/O from UDP ingest; the receive loop never blocks. Verified: a healthy unit kept forwarding while another unit's upstream was dark. | +| P1 | Anti-jump filter permanently locks units out | Time-aware plausibility envelope (`MAX_SPEED·Δt`) + self-heal (re-anchor after N rejects) + clear anchor on disconnect. Verified: a teleporting unit recovers instead of being black-holed. | +| P2 | Identity keyed by source IP (CGNAT) | Transport = full `(ip, port)`; all per-device state + GGA/RMC pairing keyed by `cell_id`. Verified: two SIMs on one IP don't cross-mix. | +| 🟠 | `SQL_THROTTLE_S = 0`; thread-safety | Throttle set to 300 s; stats threads snapshot shared dicts with `list(...)`. | +| 🟠 | Load test measured the wrong thing | Superseded by an end-to-end smoke test (mock Wialon + slow/dark upstream + CGNAT + jump cases). | +| 🟡 | Secrets in repo | Parameterized out of README/compose into `.env` (gitignored) + `.env.example`. **Open:** rotate the password + purge git history. | + +Also shipped: the **backend-victim signal** in the monitor (ONLINE-on-UDP but not forwarding), +an **offline-unit detail panel**, a ~100× **log-volume trim** + off-box shipping to **Grafana +Loki** (via Alloy → journald, capped), and the unit-aware `deploy.sh`. Outstanding: secret +rotation + history purge; optional convergence of the dead retranslator variants; code review +(Leo to run). The original analysis is preserved below for the record. + +--- + +## TL;DR (read this if nothing else) + +1. **Production runs the *blocking* `retranslator.py`** (per `htracker.service`), + which sends each position to Wialon and **waits for the TCP ack — and even the + 20 s connect — inside the single UDP receive loop**. One slow/unreachable + upstream stalls the loop and **every device drops off Wialon at once**. This is + a textbook common-mode and will get dramatically worse at 500 units. 🔴 +2. **The anti-jump filter can permanently black-hole a healthy unit.** On a + rejected "jump" it returns **without updating the reference position**, and the + reference **survives disconnects**. A unit that moves >500 m while offline (or + whose first fix after reconnect is an outlier) has **100 % of its packets + dropped forever** — it transmits fine but never appears in Wialon. 🔴 +3. **Device identity is keyed by source IP**, which collides under carrier CGNAT + (500 SIMs, shared/rotating IPs) → **cross-device data mixing** → false jumps → + disconnects. 🔴 +4. There's a **correct async rewrite** (`retranslator_async.py`) that fixes the + blocking model — but it's **not deployed**, still **keys by IP**, and **dropped + the anti-jump filter** entirely. + +**Powerful triage signal (use today):** Helgen's own monitor marks a unit +**ONLINE on UDP receipt** (`_mark_seen`), *before* the anti-jump filter and the +Wialon send. So a unit hit by #2 or #1 shows **ONLINE in htracker but OFFLINE in +Wialon**. **Any unit with that split is a backend victim, not a dead radio** — +check this against ZPM 020 / RS-SAN-001 before blaming the hardware. + +--- + +## What is actually running (confirm this first) + +- `htracker.service` → `ExecStart=/usr/bin/python3 retranslator.py` ← **the + blocking gateway**, "Up to 500 Wialon TCP sockets," `LimitNOFILE=8192`. +- `htracker_ctl.sh` → also launches `retranslator.py`. +- `run_retranslator.sh` → launches **`rtk2mqtt_retranslator.py`** (a third + variant). ⚠️ **Ambiguity:** confirm which process is live in prod + (`systemctl status htracker`), because the review's #1/#2 are about + `retranslator.py`. + +--- + +## 🔴 P1 — Synchronous Wialon I/O in the single receive loop (HOL blocking) + +`main()` is one thread doing `recvfrom` → parse → `_process_device_telemetry` → +`send_to_wialon`, which **blocks**: + +- on connect: `s.connect((DEST_HOST, DEST_PORT))` with `TCP_TIMEOUT_S = 20`; +- on every packet: `current_sock.sendall(...)` then **`current_sock.recv(1024)`** + waiting for the Wialon ack. + +While that one device's socket is slow, **no UDP from any other device is read**; +the kernel UDP buffer fills and **datagrams from all 500 units are silently +dropped**. A brief upstream hiccup at `us.gpsgsm.org` therefore looks exactly like +"the whole fleet disconnected." Reconnect storms make it worse: cellular IP churn +closes/reopens sockets (each a 20 s-timeout `connect` in the loop) with +exponential backoff — all serialized in the same loop. + +**Symptom match:** simultaneous death (A) + recurrent disconnects (B). + +**Fix:** deploy the async model (decouple UDP ingest from Wialon I/O via a queue + +worker tasks; per-connection async with timeouts) — see `retranslator_async.py`, +which already does this. Until then, at minimum make `send_to_wialon` +non-blocking / move it off the receive loop, and bump `SO_RCVBUF`. + +--- + +## 🔴 P1 — Anti-jump filter permanently locks units out + +In `_process_device_telemetry`: + +```python +_prev = device_last_pos.get(cell_id) +if _prev: + _dist = haversine(_prev, (_lat,_lon)) + if _dist >= 0.5: # flat 500 m, ignores Δt + log.warning("Anti-Salto… descartado") + return # ← returns WITHOUT updating reference +device_last_pos[cell_id] = (_lat, _lon) # only runs if accepted +``` + +Three compounding defects: + +1. **Reference not updated on reject** → once "stuck," every later fix is measured + against a stale anchor. If the device keeps moving, every packet is >500 m from + the anchor → **permanent 100 % drop**. +2. **Anchor persists across disconnects** — `device_last_pos` is **not** cleared by + the TTL cleanup (unlike every other per-device dict). A unit that moves >500 m + while powered-off and reconnects is **black-holed on return**. +3. **Flat 500 m threshold ignores time.** After any packet-loss gap (see P1 + above), a normally-moving unit legitimately advances >500 m between *received* + fixes → rejected → then locked out by defect #1. **P1 packet loss directly + triggers P2 lockouts.** + +Also: a device's **first** fix after (re)appearing has no `_prev`, so it's accepted +unconditionally — if that first fix is an RTK outlier, it becomes the anchor and +locks out all subsequent *good* fixes. + +**Symptom match:** a unit that "died" and never came back, while its hardware and +UDP stream are fine (→ shows ONLINE in htracker, OFFLINE in Wialon). + +**Minimal safe hotfix** (time-aware + self-healing + clear on disconnect): + +```python +# store (lat, lon, ts); clear device_last_pos[cell_id] in the TTL cleanup too +_prev = device_last_pos.get(cell_id) +now = time.time() +if _prev: + dt = max(1.0, now - _prev[2]) + max_km = (MAX_SPEED_KMH / 3600.0) * dt * 1.5 # plausibility envelope + if _dist > max(0.5, max_km): + n = device_jump_rejects.get(cell_id, 0) + 1 + device_jump_rejects[cell_id] = n + if n < 3: # tolerate a few outliers… + return # …then re-anchor to self-heal +device_jump_rejects[cell_id] = 0 +device_last_pos[cell_id] = (_lat, _lon, now) +# …send to Wialon… +``` + +This keeps odometer protection (rejects true teleports) but **cannot lock a unit +out forever**. + +--- + +## 🔴 P2 — Identity keyed by source IP breaks under CGNAT + +`ip_to_device_cell_id[source_ip]`, `ip_to_last_gga[source_ip]`, +`ip_to_last_rmc[source_ip]` assume **one device ⇄ one IP**. On cellular at fleet +scale that's false: carrier **CGNAT** puts many SIMs behind shared public IPs and +rotates them. Consequences: + +- Two devices sharing a public IP **overwrite each other's** cell-id mapping and + **mix GGA/RMC** → the anti-jump filter sees a "jump" between two trucks' + positions → drops + can trigger the lockout above. +- The GGA↔RMC pairing (by IP, 2 s window) pairs the **wrong** device's sentences. + +**Fix:** key everything by **`cell_id`** (the `$GPTXT` identity), not IP. Buffer +last GGA/RMC per `cell_id`; treat `source_ip` only as transport. The async version +inherits this bug too and should be fixed the same way. + +--- + +## 🟠 Reliability / scale + +- **`load_test.py` measures the wrong thing.** It only counts **UDP sends from the + simulator** ("errores UDP"), never whether the retranslator kept up or whether + Wialon received anything. A green load test gives **false confidence** — it + can't see the P1 HOL stall (the actual bottleneck is the upstream TCP path). + Add end-to-end metrics: packets forwarded to Wialon, ack latency, UDP-buffer + drops (`netstat -su`), queue depth. +- **`SQL_THROTTLE_S = 0`** while the comment/log say "every 5 min." Every packet is + enqueued to the SQL writer (bounded queue 1000). The writer is correctly a + background thread (good — not blocking the loop), but at 500 pkt/s it can + saturate/drop. Set the throttle to the intended 300 s, or document the change. +- **GGA/RMC pairing is fragile**: requires *both* within 2 s, every cycle, or + nothing is sent. Under packet loss this drops position updates even with a valid + GGA fix. Consider sending on GGA alone with last-known speed/course. +- **Thread-safety:** worker threads mutate shared dicts the main loop also mutates + (`device_last_seen`, `wialon_sockets`, `device_first_connected`…). The GIL makes + single ops atomic, but `_save_device_state` iterating a dict the main loop grows + can raise `RuntimeError: dictionary changed size during iteration` (caught, so a + stats cycle is skipped). Snapshot with `list(...)` or add a lock. The async + model (single event loop) avoids this entirely. + +## 🟡 Hygiene / security + +- **Secrets in the repo.** `README.md` ships a **plaintext MSSQL `sa` password**; + `.env` and `.htracker_env` are committed. Rotate that password now, purge from + git history, and move to env/secret store. (`gps_logger.py` already does the + right thing — env var, "NUNCA hardcodear" — so just align the README/compose.) +- File literally named `import socket.py` in the repo root — will shadow the stdlib + `socket` if ever imported by name; delete/rename. +- Three live retranslator variants (`retranslator.py`, `_async`, `rtk2mqtt_…`, + plus `-original`) — pick one source of truth to avoid "which one is prod?" + +--- + +## Recommended sequence + +1. **Now (hours):** confirm the live process; pull the gateway log around the + death window; check **UDP buffer drops** (`netstat -su`) and whether all units + dropped within seconds of each other (→ P1) or one-by-one (→ P2 lockouts). + Cross-check **htracker-online-vs-Wialon-offline** for ZPM 020 / RS-SAN-001. +2. **This week (small, safe):** ship the **anti-jump hotfix** above to the running + `retranslator.py` and **clear `device_last_pos` on TTL cleanup**. This stops the + permanent black-holing with minimal risk. +3. **Migrate to `retranslator_async.py`** as the real concurrency fix, but first: + re-add the corrected anti-jump filter, **switch identity to `cell_id`**, and + re-run a **realistic** load test (slow-upstream + measured forwarding). +4. **Hygiene:** rotate/purge the DB secret; converge on one retranslator. + +> Backend fixes and the hardware/firmware fixes are complementary: the hardware +> can be perfect and units will still vanish from Wialon under P1/P2 — and vice +> versa. The RCA differential is updated to reflect that H2 now has **confirmed +> code-level mechanisms.** diff --git a/03-Architecture-Hardware/BOM-master.md b/03-Architecture-Hardware/BOM-master.md new file mode 100644 index 0000000..3b92f4c --- /dev/null +++ b/03-Architecture-Hardware/BOM-master.md @@ -0,0 +1,99 @@ +--- +tags: [project-drogheda, bom, sourcing, hardware] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# BOM Maestro — HBOX + Gabinete (24 V) + +> Updated master BOM for the **24 V** Drogheda unit (the dominant ICAVE variant). +> Costs in **MXN**, from `Selección de componentes para gabinete2.xlsx` and the +> H-BOX BOM sheet. This supersedes the older loose BOM. For 12/48/110/230 V +> variants only the cabinet power/protection parts change (see source sheet). + +> **Why this exists:** the earlier BOM drifted from reality. This pins the **as-built** +> parts (so diagnosis/PCB work targets the right hardware) and flags the +> **change-list** for the production fix. + +## 1. HBOX — internal unit (per unit, 24 V) + +| # | Item | Marca / Modelo | Qty | MXN | Notes | +|---|---|---|---:|---:|---| +| 1 | H-BOX Case | Helgen Series1 (**ASA print**) | 1 | 100 | UV-stable, ~95–100 °C — adequate | +| 2 | H-BOX Top | Helgen Series1 (**ASA**) | 1 | 50 | | +| 3 | DC PSU bracket | Helgen Series1 (ASA) | 1 | 20 | | +| 4 | Battery Case | Helgen Series1 (ASA) | 1 | 30 | houses 18650 | +| 5 | SIM | Telcel micro | 1 | 100 | per-unit data plan | +| 6 | **GNSS RTK** | Ardusimple simpleRTK2B **Pro** (AS-RTK2B-PROF9P-L1L2-NH-00) | 1 | 3,340 | ZED-F9P | +| 7 | **4G/NTRIP** | Ardusimple 4G NTRIP Master (AS-XBEE-4G-NA-UFL-00) | 1 | 2,440 | XBee LTE | +| 8 | **MCU watchdog** | DFRobot STM32F411 Blackpill | 1 | 104 | runs `blackpillrtkcode_v2` | +| 9 | GNSS antenna | Ardusimple AS-ANT2B-ANN-L1L2-50SMA | 1 | 700 | multiband | +| 10 | LTE antenna | Unit Electronics AR1582 (SMA→IPEX) | 2 | 42 | 4G diversity | +| 11 | Headers F 6/8/10-pin | 2.54 mm | 1ea | 10 | board interconnect | +| 12 | **5 V supply** | **Mean Well SD-15B-05** (18–36 V→5 V, 3 A) | 1 | 300 | ✅ industrial — keep | +| 13 | Boost / charger | Adafruit **PowerBoost 1000C** (2465) | 1 | 400 | battery path — review | +| 14 | **Ignition cond.** | DFRobot **DFR1015** buck | 1 | 106 | 24 V ign → Blackpill PA5 | +| 15 | **Battery** | Hyvel **18650 1S4P** (FG-0889, 3.6 V) | 1 | 204 | ⚠ charge ≤45 °C — decide | +| 16 | Status LED + holder | Steren 5 mm + bezel | 1 | 3 | PB0 path | +| 17 | Resistor | 1.5 kΩ (use TBC — see watchdog-schematic) | 1 | 1 | verify value | +| 18 | **HBOX↔cabinet** | **GX16-5** macho/hembra | 1 | 22 | ✅ locking — keep | +| 19 | Watchdog discretes | 2N3904, IRF9540N, 2N7000, R (1k/10k/220Ω) | — | — | → consolidate on PCB | +| 20 | Dupont terminals | red + black | 2 | 4 | ⚠ vibration — eliminate | +| 21 | Wire, glands, screws, label, switch | genérico / Steren | — | ~110 | NPT 3/8 gland | +| | **HBOX subtotal** | | | **≈ 8,041** | | + +## 2. Gabinete — cabinet (per unit, 24 V) + +| Item | Marca / Modelo | Qty | MXN | +|---|---|---:|---:| +| Enclosure IP67 (ABS) | Yetlebox **P3901** (39×29×16) | 1 | 1,435 | +| DIN rail | Weidmüller TS 35×7.5 | 1 | 50 | +| Neutral terminals | Weidmüller WDU 2.5 | 5 | 58 | +| Ground terminals | Weidmüller WPE4 | 4 | 154 | +| Fuse-holder terminal | Phoenix **PT 6-FSI/C** (3212166) | 3 | 900 | +| End stops | Wago 249-116 | 4 | 56 | +| Ignition SSR | Weidmüller **TOS 24VDC 2A** | 1 | 684 | +| Surge (SPD) | Phoenix **TTC-6-TVSD-C-24DC-PT-I** | 2 | 1,076 | +| Breaker | Phoenix **TMC 81C 05A** | 1 | 484 | +| **Supply → 24 V** | Mean Well **DDR-60G-24** | 1 | 748 | +| Fuse (blade) | automotriz **2 A** | 3 | 6 | +| Cable / ferrules / glands / labels | Indiana / Weidmüller / Helgen | — | ~110 | +| Shipping to port | — | 1 | 600 | +| **Cabinet subtotal** | | | **≈ 6,365** | + +**≈ Total per 24 V unit: ~14,400 MXN** (HBOX ~8,041 + cabinet ~6,365). An +alternate supplier ("Israel" column in the sheet) shows ~1,284 MXN/cabinet +savings at 24 V — sourcing decision lives here in `09_`. + +## 3. ✅ What's already good (don't re-buy / don't duplicate) + +- **Cabinet** is industrial-grade: SPD + breaker + isolated SSR ignition + Mean + Well 24 V. The HBOX needn't re-implement input surge/protection. +- **SD-15B-05** (24→5 V, 3 A) is a real industrial supply — keep. +- **GX16-5** locking HBOX↔cabinet connector — keep (good vibration practice). + +## 4. ⚠ Change-list for the production fix (feeds the PCB + hackathon) + +| Change | From → To | Why | Ref | +|---|---|---|---| +| Inner case | **ASA — keep** (adequate); optional metal / plate-mount | conduction, not material, is the lever | RCA, hardware-brief §2b | +| Internal wiring | **Dupont/hand-wire → PCB + locking connectors** | vibration intermittents | hardware-brief §4 | +| Battery path | **PowerBoost 1000C + 18650 → decide:** drop / LiFePO4 / supercap | heat + safety; do we need parked tracking? | hardware-brief §6.1 | +| Fuse | **verify the 2 A blade** vs measured worst-case + inrush | client's "under-rated fuse" | RCA H5 | +| Watchdog discretes | **2N3904/IRF9540N/pull-ups → integrated load switch on PCB** | fewer parts, defined turn-on | hardware-brief §4 | +| SIM | micro-SIM buried → **front-panel SIM or MFF2 eSIM** | maintainability | hardware-brief §6.2 | +| Thermal | **conduct heat to metal plate / sunshade; NO internal fan** | sealed-box physics | hardware-brief §2b | + +## Open Questions + +- [ ] Confirm the **installed blade-fuse value** (2 A may be under-rated for LTE inrush). +- [ ] Battery decision (drop vs LiFePO4 vs supercap) — blocks the PCB power section. +- [ ] Lock supplier per line (Helgen vs alternate quote) for the August batch. + +## Related + +- [[CLAUDE|Master Index]] · [[03-Architecture-Hardware/cabinet-electrical-bom|Cabinet BOM detail]] · + [[03-Architecture-Hardware/hardware-respec-and-pcb-brief|Hardware re-spec + PCB brief]] · + [[03-Architecture-Hardware/watchdog-schematic|Watchdog schematic]] diff --git a/03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx b/03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx new file mode 100644 index 0000000..251f4a9 Binary files /dev/null and b/03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx differ diff --git a/03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf b/03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf new file mode 100644 index 0000000..61843fc Binary files /dev/null and b/03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf differ diff --git a/03-Architecture-Hardware/Manual-Instalacion-Operacion/cuerpo.tex b/03-Architecture-Hardware/Manual-Instalacion-Operacion/cuerpo.tex new file mode 100644 index 0000000..188ff1b --- /dev/null +++ b/03-Architecture-Hardware/Manual-Instalacion-Operacion/cuerpo.tex @@ -0,0 +1,87 @@ +\section{Seguridad — leer primero} + +\begin{helgennotice}[Riesgo de batería de litio] +La unidad contiene una batería \textbf{18650 Li-ion}. Una celda \textbf{inflada, caliente +o deformada} es riesgo de incendio. \textbf{No} la cargue, no la perfore ni siga +energizando una unidad con la batería visiblemente dañada. Desconecte, aísle en bolsa o +recipiente seguro, ventile y repórtelo. +\end{helgennotice} + +\begin{itemize} + \item Trabaje con la máquina \textbf{apagada y bloqueada} (LOTO) durante la instalación. + \item Use EPP completo en todo momento en el patio. + \item Respete los voltajes: la entrada al HBOX es \textbf{24 V} desde el gabinete; nunca conecte el HBOX directo a la línea de la máquina. +\end{itemize} + +\section{Contenido del kit} + +\begin{itemize} + \item Gabinete IP67 (Yetlebox P3901) con platina, riel DIN y componentes de potencia/protección. + \item Unidad \textbf{HBOX} (simpleRTK2B Pro + 4G NTRIP Master + watchdog) con conector \textbf{GX16-5}. + \item Antena GNSS multibanda (SMA) y 2 antenas LTE. + \item SIM Telcel, glándulas, herrajes de montaje y etiquetas. +\end{itemize} + +\section{Instalación del gabinete} + +\begin{enumerate}[leftmargin=1.5em] + \item \textbf{Ubicación.} Monte el gabinete en un punto firme, con la menor exposición solar posible (preferir sombra) y buena vista al cielo para las antenas. + \item \textbf{Alimentación.} Conecte la alimentación de la máquina al breaker/SPD del gabinete según el voltaje (esta unidad: \textbf{24 V}). La fuente Mean Well entrega 24 V regulados al HBOX. + \item \textbf{Ignición.} Lleve la señal de ignición al relevador de estado sólido (SSR); éste entrega una señal aislada al HBOX. + \item \textbf{Conexión HBOX.} Acople el \textbf{GX16-5} entre el gabinete y el HBOX; apriete el anillo de seguro. + \item \textbf{Antenas.} Instale la antena GNSS y las 2 LTE por glándula; aplique torque y fijador de rosca a los SMA. La antena GNSS debe ver el cielo. + \item \textbf{SIM.} Verifique que la SIM Telcel esté insertada y activa. + \item \textbf{Sellado.} Cierre el gabinete asegurando el sello IP67; verifique glándulas apretadas. +\end{enumerate} + +\section{Encendido y verificación} + +Al energizar, observe el LED de estado. La secuencia normal es: \textbf{parpadeo rápido} +(arranque, esperando 4G/GNSS) $\rightarrow$ \textbf{parpadeo lento} (buscando) $\rightarrow$ +\textbf{encendido fijo} (operando con RTK fijo). Confirme en la plataforma que la unidad +reporta posición. + +\section{Estados del LED} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}L{3.8cm} X@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Patrón} & \textcolor{white}{Significado / acción}\\ +\midrule +Parpadeo rápido & Arranque; espere a que el 4G/GNSS estabilice (normal al inicio).\\ +Encendido fijo & Operando con energía de máquina y RTK fijo. Correcto.\\ +Latido (1 s) & Modo batería (máquina apagada), reporte cada 30 s. Normal.\\ +Parpadeo lento & Buscando / sin correcciones. Si persiste, revise antenas y señal 4G.\\ +Doble (tipo SOS) & LIMP: sin 4G, sigue reportando posición estándar. Reporte a Helgen.\\ +Estrobo & Reiniciando el stack (recuperación automática). Espere.\\ +Apagado & Reposo / batería crítica. Verifique alimentación de 24 V.\\ +\bottomrule +\end{tabularx}} + +\section{Mantenimiento} + +\begin{itemize} + \item Inspección trimestral: estado del sello, glándulas, torque de antenas y conectores; sin señales de calor (decoloración) ni batería inflada. + \item Mantenga el gabinete cerrado y sellado; \textbf{no} agregue ventiladores ni perforaciones (rompen el IP67). + \item Ante reemplazo de SIM o componentes, siga el procedimiento de Helgen y registre el cambio. +\end{itemize} + +\section{Solución de problemas} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}L{5.2cm} X@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Síntoma} & \textcolor{white}{Acción}\\ +\midrule +LED apagado con alimentación & Verifique 24 V en la salida de la fuente, breaker y fusible; revise el GX16-5.\\ +LED en parpadeo lento persistente & Revise vista al cielo de la antena GNSS, torque de SMA y cobertura/saldo de la SIM 4G.\\ +Desconexiones intermitentes en plataforma & Reporte a Helgen con el ID de la unidad; puede ser un punto de backend, no la unidad.\\ +Batería caliente o inflada & Detenga, desconecte y aísle la batería; \textbf{riesgo de incendio}. Reporte de inmediato.\\ +\bottomrule +\end{tabularx}} + +\begin{helgennotice}[Soporte] +Reporte fallas a Helgen con el \textbf{ID de la unidad (cell\_id)} y la máquina. Una unidad +que se ve en el monitor de Helgen pero no en la plataforma del cliente suele ser un punto +de backend, no una falla de campo. +\end{helgennotice} diff --git a/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.pdf b/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.pdf new file mode 100644 index 0000000..61843fc Binary files /dev/null and b/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.pdf differ diff --git a/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.tex b/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.tex new file mode 100644 index 0000000..fb27960 --- /dev/null +++ b/03-Architecture-Hardware/Manual-Instalacion-Operacion/main.tex @@ -0,0 +1,18 @@ +% Helgen Drogheda — Manual de Instalación y Operación · DRG-MAN-001 (es-MX) +\documentclass[11pt]{article} +\newcommand{\sistema}{Helgen Drogheda — HBOX RTK} +\newcommand{\sistemapdf}{Helgen Drogheda HBOX RTK} +\newcommand{\doctipo}{Manual de Instalación y Operación} +\input{../../_brand/estilo-helgen.tex} +\begin{document} +\helgenportada{Manual de Instalación y Operación}{HBOX + Gabinete — equipo portuario (24 V)}{% + \begin{tabular}{@{}ll@{}} + \textbf{Documento} & DRG-MAN-001 — Manual de Instalación y Operación\\ + \textbf{Versión} & 1.0\\ + \textbf{Fecha} & 20 de junio de 2026\\ + \textbf{Dirigido a} & Personal de instalación y mantenimiento en sitio\\ + \textbf{Clasificación} & Confidencial — uso de Hutchison Ports y Helgen\\ + \textbf{Asistencia IA} & Elaborado con asistencia de Claude (Anthropic), revisado por el autor\\ + \end{tabular}} +\input{cuerpo.tex} +\end{document} diff --git a/03-Architecture-Hardware/cabinet-electrical-bom.md b/03-Architecture-Hardware/cabinet-electrical-bom.md new file mode 100644 index 0000000..b4061fc --- /dev/null +++ b/03-Architecture-Hardware/cabinet-electrical-bom.md @@ -0,0 +1,77 @@ +# Cabinet Electrical BOM — the box the HBOX lives inside + +**Source:** `Selección de componentes para gabinete2.xlsx` (Helgen, 2026-06) +**Date captured:** 2026-06-19 +**Key point:** the HBOX is **not** mounted bare on a machine — it sits **inside an +industrial electrical cabinet (gabinete)** with DIN-rail power and protection +components. This BOM is that cabinet. It is **productized across voltages** (12 / +24 / 48 / 110 / 230 V machines) and **across multiple port terminals** (ICAVE, +Hutchison, TIMSA, EIT, LCT) — i.e. this is heading to **hundreds of cabinets**, so +the fixes must be production-grade and repeatable. *(Pricing/margins omitted here — +see the sheet.)* + +--- + +## Nested-enclosure architecture (important for thermal) + +``` + Machine power (12/24/48/110/230 V) + ignition + │ + ▼ ┌──────────────── Gabinete IP67 (ABS, Yetlebox P3901, 39×29×16 cm) ───────────┐ + breaker │ DIN rail · terminals · SPD · fuse holder · SSR/opto (ignition sense) │ + + SPD ──►│ Mean Well DDR-60G-24 ──► clean 24 Vdc ─────────────┐ (supply dissipates │ + │ │ heat *inside* box) │ + │ ┌────────── HBOX (3D-printed ASA, sealed) ────────┼───┐ │ + │ │ PowerBoost 1000C + LiPo · simpleRTK2B+4G · STM32 watchdog │ │ + │ └───────────────────────────────────────────────────┘ │ + └───────────────────────────────────────────────────────────────────────────┘ +``` + +Two sealed boxes, one inside the other, **with a 60 W-class power supply generating +heat in the outer one**, in full sun. See the thermal note in +`03-Architecture-Hardware/hardware-respec-and-pcb-brief.md` §thermal. + +--- + +## Cabinet catalog (Helgen final selection, by machine voltage) + +| Function | 12 V | 24 V | 48 V | 110–230 V | +|---|---|---|---|---| +| **Enclosure (IP67, ABS)** | Yetlebox P3901 | P3901 | P3901 | P3901 | +| DIN rail | Weidmüller TS 35×7.5 | ← | ← | ← | +| Neutral terminals ×5 | Weidmüller WDU 2.5 PA/VI | ← | ← | ← | +| Ground terminals | Weidmüller WPE4 | ← | ← | ← | +| Fuse-holder terminal ×3 | Phoenix Contact 3212166 **PT 6-FSI/C** (blade) | ← | ← | ← | +| Ignition isolation (SSR / opto) | Weidmüller **MOS 12-28VDC** | Weidmüller **TOS 24VDC 2A** | Wago **859-744** | IDEC **RV8S-L-D24-A120** | +| Surge protection (SPD) | Phoenix **TTC-6-TVSD-C-12DC-PT-I** | …**-24DC-** | …**-48DC-** | Phoenix DC + Total Ground **A2021** (AC) | +| Breaker | Phoenix **TMC 81C 05A** (5 A) | ← | ← | Schneider **A9F74202** (2 A, AC) | +| **Power supply → 24 Vdc** | Mean Well **DDR-60G-24** | Mean Well **DDR-60G-24** | Mean Well **DDR-60L-24** | Siemens **6EP1332-1LB00** (SITOP) | + +Output rail in all cases: **24 Vdc, ~2.4 A, 60 W**. + +--- + +## What this means for the fix (engineering read) + +1. **The cabinet's power & protection are already industrial-grade.** Surge + suppression (Phoenix TVS/SPD), a breaker, isolated ignition sensing (SSR/opto), + and a quality regulated supply (Mean Well / Siemens) are present at the cabinet + level. **The HBOX PCB does NOT need to re-implement input surge/breaker** — it + can assume a **clean 24 Vdc feed** and an **isolated ignition signal**. That + simplifies the HBOX redesign (see hardware brief). +2. **So the weak link is confirmed to be inside the HBOX**, exactly where the + client pointed: PowerBoost 1000C + LiPo + Dupont — not + the cabinet (the **ASA enclosure** is adequate). The HBOX-internal job is **24 V → 5 V (robust buck) + watchdog + + the RTK stack**, with the LiPo decision (drop/replace) still open. +3. **Verify the actual fuse value.** The PT 6-FSI/C is a *holder*; the fitted blade + fuse rating is what matters for the client's "under-rated fuse" concern — read + the installed fuse and compare to the measured worst-case + inrush. +4. **The Mean Well runs hot inside a sealed box.** At 60 W class it dissipates + several watts continuously; that heat has nowhere to go in a sealed ABS IP67 + cabinet → it raises the ambient the HBOX (and its 18650 + boost + modem) must + survive. This is now a **primary thermal driver**, not just solar load. + +> Open question for Leo to confirm: does the HBOX receive the cabinet's **24 V** +> (then step down internally), or the raw machine voltage? The BOM implies a clean +> 24 V feed + isolated ignition — confirm so the HBOX power redesign targets the +> right input. diff --git a/03-Architecture-Hardware/firmware-review.md b/03-Architecture-Hardware/firmware-review.md new file mode 100644 index 0000000..1ab633f --- /dev/null +++ b/03-Architecture-Hardware/firmware-review.md @@ -0,0 +1,120 @@ +# Firmware Review — `blackpillrtkcode.ino` (v1 → v2) + +Review of the as-deployed watchdog firmware, with the fixes shipped in +`04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino`. Severity: 🔴 likely field-impacting · +🟠 robustness · 🟡 cleanup. + +UBX command checksums and ADC divider math were **verified programmatically** +(see end of this doc) — those parts of v1 are correct. + +--- + +## Issues found + +### 🔴 1. Loss-of-corrections recovery targets the wrong subsystem +**v1:** after `SOFT_RESET_DELAY` (5 min) of `fixQuality < 4`, it sends +`UBX_SOFT_RESET` (a GNSS hot-reset) to the ZED-F9P. +**Problem:** corrections arrive over the **4G NTRIP modem**, not from the GNSS. +Hot-resetting the receiver does nothing for a hung modem — the dominant field +failure per the client. Five minutes are spent on a no-op before the only +effective action (a full power-cycle at 10 min). +**v2:** drops the GNSS hot-reset; recovery = **power-cycle the whole stack** +(which restarts the 4G module), gated by a grace window and backoff. + +### 🔴 2. Hard-reset has no backoff → reboot-loop / Wialon flapping +**v1:** after `HARD_RESET_DELAY` (10 min) it power-cycles, returns to BROWNOUT, +immediately powers back on, and — if the fault persists — does it **again every +~10 min indefinitely**. Each cycle is a multi-minute disconnect. +**Problem:** a genuinely dead 4G card, or a unit parked without sky view, makes +the unit **flap off Wialon forever**. This is a prime suspect for "desconexiones +recurrentes." +**v2:** **exponential backoff** (1→2→4→8→16 min, capped 20 min) then a **latched +LIMP mode** that keeps the unit **reporting standalone GPS** and only retries +recovery every 30 min. The unit never silently vanishes. + +### 🔴 3. No detection of a fully hung stack (NMEA silence) +**v1:** only reacts via `fixQuality`. If the GNSS/4G stack hangs and stops +emitting NMEA entirely, `fixQuality` simply freezes at its last value — the +watchdog may not act at all, or acts on stale data. +**v2:** tracks `lastNmeaMs`; **no NMEA for 60 s while powered ⇒ power-cycle now**. +Catches hung hardware far faster than the fix-quality ladder. + +### 🟠 4. No hysteresis / debounce on vehicle-power & ignition +**v1:** `pa4_on = (v5vF > 4.0)` is a single hard threshold; ignition is a raw +`digitalRead`. Around the threshold (alternator noise, ignition flicker), the +rate mode (1 Hz↔30 s) and state can chatter. +**v2:** hysteresis band (ON >4.2 V, OFF <3.8 V) + 750 ms debounce on both +vehicle-power and ignition. + +### 🟠 5. Brownout exit has no debounce (oscillation risk) +**v1:** entry to BROWNOUT is debounced 3 s, but the **exit** powers on the instant +`pa4_on || pa5_on || vBat>3.7`. A brief blip near the threshold can start a +power-on it can't sustain → drain → repeat. +**v2:** wake is driven by the same debounced/hysteretic signals; brownout entry +uses a 5 s confirmation. + +### 🟠 6. No thermal awareness (the actual field failure mode) +**v1:** nothing measures temperature, despite "died from heat." +**v2:** reads the STM32 internal temp sensor (F411 factory cal), logs session +**min/max**, **derates to 30 s** above 75 °C to cut self-heat/power, and shows it +on the dashboard. Firmware can't cool the box, but a pulled unit's `tMax` now +**proves or kills the thermal hypothesis**. (Disable with `ENABLE_MCU_THERMAL 0` +if your core lacks `ATEMP`/`AVREF`.) + +### 🟡 7. `String` heap churn on an ARM MCU +**v1:** comments claim `const char*` avoids fragmentation, but `nmeaBuffer` is a +`String`, and `drawDashboard` builds a `String paddedMsg` every refresh. +**v2:** fixed `char nmeaBuf[110]` parser (no heap) and `snprintf` padding. + +### 🟡 8. Doc/firmware mismatch: boot delay +The LED table says "waiting 25 s"; v1 `BOOT_STABILIZE_TIME` is **15 s**. +**v2:** set to **25 s** to match the documented SOP (gives the 4G modem more time +to attach before the supervisor starts judging the link). Tune on the bench. + +### 🟡 9. Minor: redundant `pinMode` before `GNSS.begin()` +Harmless (the serial peripheral reconfigures the pins); left as explicit intent +in v2. + +--- + +## What v1 got *right* (kept in v2) + +- **Float is not treated as failure.** `fixQuality >= 4` correctly counts both + RTK **fixed (4)** and **float (5)** as healthy. (Earlier verbal claim that + FLOAT triggered resets was wrong — corrected here.) +- Floating the TX pin on power-off to avoid back-powering the rail through a GPIO. +- Low-pass filtering the ADC readings. +- Hysteresis between `BAT_CRITICAL` (3.4 V) and `BAT_RECOVERY` (3.7 V). +- The UBX rate commands and their checksums (verified below). + +--- + +## v2 recovery state machine (summary) + +``` +SLEEP ──(vehicle pwr | ignition | Vbat>3.7)──► BOOTING ──(25 s)──► RUNNING + ▲ │ + │ Vbat<3.4 & no pwr & no ign (5 s) │ + └───────────────────────────────────────────────────────── RUNNING supervises: + • no NMEA 60 s ........................► POWER_CYCLE (gated by backoff) + • RTK lost > 120 s grace .............► POWER_CYCLE (gated by backoff) + • RTK healthy 5 min .................► clear backoff + LIMP latch + POWER_CYCLE (6 s rail off) ──► BOOTING ; after 5 fails ──► LIMP (keep reporting) +``` + +--- + +## Verification performed (2026-06-19) + +- **UBX checksums** recomputed (8-bit Fletcher over class…payload): `UBX_1HZ` + → `01 39` ✓, `UBX_30S` → `1E AD` ✓, `UBX_SOFT_RESET` → `0F 66` ✓. +- **CFG-RATE decode:** 1 Hz = measRate 1000 ms × navRate 1; 30 s = 1000 ms × 30 ✓. +- **ADC dividers (12-bit, 3.3 V, ÷2):** LiPo 3.4–4.2 V → 1.70–2.10 V at pin + (2110–2606 counts); 5 V rail → 2.50 V (3102 counts). No ADC clipping ✓. +- **Backoff ladder:** 1, 2, 4, 8, 16 min → LIMP @ 30 min, as designed ✓. +- **Compile:** v2 builds clean under `g++ -std=c++14 -Wall -Wextra` against a + hardware stub (logic/types/flow only). + +> ⚠️ The stub compile validates **logic**, not the STM32 target. **Bench-flash and +> test v2 on real hardware** (Arduino IDE / PlatformIO, STM32duino core) before +> any field deployment — especially the thermal read and the power-cycle timing. diff --git a/03-Architecture-Hardware/hardware-respec-and-pcb-brief.md b/03-Architecture-Hardware/hardware-respec-and-pcb-brief.md new file mode 100644 index 0000000..2b6a780 --- /dev/null +++ b/03-Architecture-Hardware/hardware-respec-and-pcb-brief.md @@ -0,0 +1,260 @@ +# Hardware Re-spec + Watchdog PCB Brief + +**Date:** 2026-06-19 · **Owner:** Leo Rodriguez (Helgen) +**Strategy (Leo's call):** **phased** — ship a ruggedized interim to the fleet +now (Phase 1), design the production PCB in parallel (Phase 2). +**Drivers:** the RCA (`06-Testing-Validation/RCA-2026-06-field-failures.md`) and the ICAVE client +review (prototype-grade, vibration, connectors, thermal, 4G failures). + +> This is an engineering brief, not a finished schematic. Specific part numbers +> are **starting points** to price/validate — confirm against the measured power +> budget (see §2) and your supplier availability. + +--- + +## 1. Design requirements (the bar the fix must clear) + +> **Context update (2026-06-19):** the HBOX sits **inside an industrial cabinet** +> that already provides surge protection, a breaker, isolated ignition sensing, and +> a regulated **24 Vdc** supply (Mean Well DDR-60G-24) — see +> `03-Architecture-Hardware/cabinet-electrical-bom.md`. So the HBOX can assume a **clean 24 V feed** +> and **need not re-implement input surge/reverse-polarity**. The redesign focus +> narrows to: **24 V→5 V**, the watchdog, the LiPo decision, and — now the top +> issue — **thermal management** (see §thermal). + +| Requirement | Target | Why | +|---|---|---| +| Operating temp | **−10…+75 °C** internal, survive +85 °C | sealed **ASA** box **inside** a sealed ABS IP67 cabinet that **also holds the heat-dissipating Mean Well supply**, in full sun | +| **Enclosure material** | **ASA (current) — keep**; UV-stable, ~95–100 °C | confirmed ASA (not PLA): margin over the cabinet interior; the case is not the liability | +| Input voltage | **clean ~24 V** from the cabinet (design the buck 9–36 V for margin) | cabinet's Mean Well already regulates; isolated ignition provided | +| Surge/transient | **handled at cabinet level** (Phoenix SPD + breaker); HBOX keeps a modest TVS | don't duplicate the cabinet's protection | +| Vibration | survive continuous machinery vibration | no Dupont, locking connectors, retained boards | +| Ingress | **IP67 maintained**, no internal condensation | coastal humidity; sealing constrains cooling (§thermal) | +| 5 V rail | **≥3 A continuous**, handle LTE TX bursts (~2 A) | modem peaks brown out a 1 A hobby boost | +| Maintainability | **SIM swap without disassembly**; labeled, keyed | client point #5 | +| Serviceability | one PCB, documented, test points, repeatable | "apt for production" | +| Observability | log temp + battery + link health | prove failures in the field | + +--- + +## 2. First, measure the power budget (blocks everything) + +Before choosing a DC-DC or fuse, **bench-measure** the stack on the actual +hardware: + +- Idle, 1 Hz, and **LTE-TX-burst** current at 5 V (modem bursts are the spike). +- Inrush at power-on (caps + modem). +- Repeat at 12 V and 24 V input. + +Expected ballpark (validate): avg ~0.3–0.6 A @5 V, **bursts 1.5–2 A** during LTE +transmit. That burst is almost certainly **beyond the PowerBoost 1000C's ~1 A +continuous rating** — a strong candidate for both the instability *and* the +"under-rated fuse" the client flagged. Everything below assumes ~2.5 A peak @5 V; +correct it once measured. + +--- + +## 2b. Thermal & enclosure — now the #1 physical fix + +**The problem is a nested thermal trap.** A sealed **ASA** HBOX sits inside a sealed +**ABS IP67** cabinet that **also contains the Mean Well supply** (60 W class, +dissipating several watts continuously), in full Veracruz sun. Two failure drivers +stack: + +- **The ASA shell is not the problem.** ASA is UV-stable and softens at ~95–100 °C, + with margin over the cabinet interior, so the HBOX shell will **not sag/warp** at + these temperatures. (Corrects an earlier PLA assumption.) +- **The LiPo is in that same hot space** (charge limit 45 °C) → degradation, + swelling, voltage sag → brownout. + +### About adding a fan — read this first +**A fan *inside* a sealed IP67 box does almost nothing.** It only stirs the same +trapped air; with no path to outside air, **no heat actually leaves the box** (it +even adds a little). Cooling requires either **moving heat out by conduction** or +**exchanging air with the outside** — and air exchange **breaks IP67**, which in a +salty, humid, dusty port invites corrosion. So: + +| Option | Removes heat? | Keeps IP67? | Verdict | +|---|---|---|---| +| Internal fan only (sealed) | ✗ (just stirs air) | ✓ | **Don't bother** | +| Filtered vents + extractor fan | ✓ | ✗ (→ ~IP54) | Only with filters + maintenance; risky in this environment | +| **Conduction: mount hot parts to a metal plate / metal enclosure** | ✓✓ | ✓ | **Best** — heatsink to the outside, stay sealed | +| **Reduce solar gain** (white/reflective, **sunshade**, relocate out of sun) | ✓ | ✓ | **Cheap, do it now** | +| **Reduce internal generation** (efficient DC-DC, **remove LiPo**) | ✓ | ✓ | **Do it** | +| Gore pressure-vent | ✗ (condensation only) | ✓ (membrane) | Keep for humidity, not for cooling | +| Active (Peltier / vortex / heat-exchanger) | ✓ | ✓ | Overkill/expensive — last resort | + +**Recommended thermal strategy (sealed, no IP compromise):** +1. **Keep the ASA enclosure** (UV-stable, ~95–100 °C — adequate). Optionally move to + **metal** for extra heatsinking, or **eliminate the inner box** and + mount the PCB directly to the cabinet plate. +2. **Conduct heat out:** bolt the DC-DC (and the new HBOX board's hot spots) to the + cabinet's **metal mounting plate / gland plate**, or move to a **metal (aluminium) + cabinet** that acts as a heatsink. Use thermal pads/gap-filler. +3. **Cut the solar load:** white/reflective cabinet, a **sunshade**, and mount out + of direct sun where the machine allows. +4. **Cut internal heat:** a higher-efficiency 24 V→5 V buck and **removing the + LiPo** both lower the steady-state interior temperature. +5. **Quantify it:** put a thermocouple/logger inside a representative cabinet for a + full sunny shift — design to the measured number, not a guess. + +--- + +## 3. Phase 1 — Interim ruggedization (do now, on existing modules) + +Goal: stop the bleeding on the deployed fleet without waiting for a PCB. All of +this is achievable with off-the-shelf parts and rework. + +1. **Kill the Dupont jumpers.** Replace every jumper with **locking, keyed + connectors** — JST-XH (signal) / Molex Micro-Fit 3.0 (power). Crimped, not + hand-twisted. This alone removes the #1 vibration intermittent. +2. **Replace the PowerBoost 1000C** with an **industrial DC-DC** stepping the + cabinet's **24 V → 5 V, ≥3 A** (e.g., a sealed **Mornsun/Recom** module or a + **TI LMR33630-class** buck for the LTE bursts). Design the input wide (9–36 V) + for margin, but the normal feed is the cabinet's clean 24 V. Sized from §2. +3. **Resolve the battery (decision — see §6).** At minimum **relocate the LiPo** + to the coolest wall, away from the modem/DC-DC, and add a temperature cutoff; + better, **replace it** with LiFePO4 or a supercap hold-up, or **drop battery + mode** if the client doesn't need parked tracking. +4. **Verify the cabinet fuse.** Read the **blade fuse fitted in the Phoenix PT + 6-FSI/C** holder and compare to the measured worst-case + inrush — this is the + "under-rated fuse" the client flagged. Surge/breaker are already handled at the + cabinet (Phoenix SPD + breaker); the HBOX keeps only a **modest local TVS**. +5. **Mechanical lockdown.** Mount each board on **standoffs with vibration + isolators/grommets**; retain the simpleRTK2B + 4G master in their sockets with + a bracket or retention clip; **thread-lock + torque** the SMA bulkheads. +6. **Conformal-coat** the watchdog/discrete wiring (acrylic or urethane) against + humidity and to stabilize solder joints under vibration. +7. **Thermal quick wins (see §2b — do NOT add an internal fan to a sealed box):** + keep the **ASA** inner box (adequate) or remount the boards on the metal plate; cut + solar gain (white/reflective cabinet + **sunshade**, relocate out of sun); + **conduct** the DC-DC/modem heat to the cabinet's metal plate; keep a + **Gore-style pressure-vent** for condensation only. +8. **SIM access:** bring the SIM to a **front-panel holder** so swaps don't need + teardown. +9. **Flash firmware v2** (thermal logging + proper 4G recovery) on every unit. +10. **Instrument** at least one unit with an interior **thermocouple log** for a + full sunny shift to quantify the thermal problem. + +Phase 1 is the "permanent-enough" stabilization while Phase 2 is fabricated. + +--- + +## 4. Phase 2 — Integrated Watchdog Carrier PCB (the real fix) + +A single **4-layer, industrial-temp** carrier board that replaces all the +hand-wiring and the hobby power boards, and onto which the simpleRTK2B and 4G +NTRIP Master mount via **locking board-to-board** (or retained XBee socket). + +### Block diagram + +``` + Cabinet 24V (already SPD/breaker-protected) ─► [keyed plug] + └► modest TVS + reverse-polarity FET ─► bulk cap + └► DC-DC buck 24V→5V (design 9–36V), ≥3A ─► 5V rail + ├► STM32 watchdog (3.3V LDO) ── UART ──► simpleRTK2B RX1 (UBX) + │ ├ sense: Vin, 5V, battery dividers (filtered) + │ ├ status LED + SWD header + debug UART (dashboard) + │ └ optional: 4G modem health/RESET line, RTC + ├► high-side LOAD SWITCH / eFuse ─► switched 5V ─► simpleRTK2B + 4G stack + └► hold-up: supercap bank OR LiFePO4 charger (see §6) + Antennas: bulkhead SMA pigtails (GNSS L1/L2 + LTE) · SIM: front-panel holder/MFF2 +``` + +### Key design choices vs the current discrete design + +| Current (discrete) | PCB replacement | Benefit | +|---|---|---| +| 2N3904 + IRF9540N + 10 k pull-up | integrated **high-side load switch / eFuse** (e.g., TPS25200 / TPS2596 / load-switch IC) | current-limited, fewer parts, defined turn-on, no gate-pull-up gotchas | +| PowerBoost 1000C | automotive **buck ≥3 A** + protection front-end | survives LTE bursts, wide Vin, transient-tolerant | +| LiPo in hot box | supercap hold-up **or** LiFePO4 (or none) | removes the thermal/safety liability | +| **ASA inner box (UV-stable, ~95–100 °C)** | keep (adequate); optional metal for extra heatsinking, or mount PCB to the cabinet plate | survives the cabinet's internal temperature | +| Dupont + screw terminals | **locking JST/Molex** + pluggable terminal block | vibration-proof | +| Bare boards loose in box | retained board-to-board + isolated standoffs | no movement, no solder flex | +| SIM buried in stack | **front-panel SIM** or **MFF2 eSIM** | client point #5 solved | +| No local protection | **modest TVS + RP FET** (cabinet has the SPD/breaker) | survives without duplicating the cabinet | +| No temp/observability | MCU temp + sense dividers + test points | provable diagnostics | + +### Board-level details to specify + +- **Protection front-end (light — the cabinet does the heavy lifting):** the + cabinet already provides surge (Phoenix SPD) + breaker + isolated ignition. The + HBOX board needs only a **reverse-polarity P-FET** and a **modest TVS** on its + 24 V input — don't duplicate the cabinet's SPD/eFuse. +- **DC-DC:** qualify input 9–36 V; ≥3 A @5 V; good thermal pad to a copper pour / + enclosure; spread-spectrum or proper filtering for GNSS RF cleanliness + (switcher noise can hurt the L1/L2 front-end — keep it away from the antenna + feed and filter it). +- **MCU:** keep STM32F411 to reuse v2 firmware, or step to an + **automotive/industrial-temp STM32 (G0/L4)**. Bring out **SWD** + a **debug + UART** for the dashboard. Add the internal-temp logging already in v2; consider + an external temp sensor near the LiPo/modem. +- **GNSS RF integrity:** keep the switcher and digital noise away from the GNSS + antenna trace; solid ground plane; the ZED-F9P front-end is sensitive — this + protects fix quality (and therefore the watchdog's view of "link health"). +- **Connectors:** all locking/keyed; bulkhead SMA with lock washers; a single + keyed vehicle-power plug. +- **Mechanical:** mounting holes with vibration isolators; design for conformal + coat or selective potting of the power section. +- **DFM/serviceability:** silkscreen labels, test points on every rail + UART, + a fab/assembly package, and a documented BOM (this is literally the "apt for + production" the client asked for). + +--- + +## 5. Indicative BOM (validate prices/availability/temp grade) + +| Function | Interim (Phase 1) | PCB (Phase 2) | +|---|---|---| +| DC-DC **24→5 V** ≥3 A | Recom/Mornsun/TI buck module | TI LMR33630-class on-board buck | +| Load switch | keep NPN+P-FET (retained) | TPS25200 / TPS2596 / load-switch IC | +| Input protection | modest TVS + RP diode (cabinet has SPD/breaker) | RP FET + modest TVS on-board | +| Hold-up / battery | relocate LiPo + temp cutoff | supercap bank **or** LiFePO4 charger | +| **Inner enclosure** | **ASA — keep** (adequate); or mount to plate | metal box / mount PCB to cabinet plate | +| MCU | existing Blackpill | STM32F411 (or G0/L4) on-board | +| Interconnect | JST-XH / Molex Micro-Fit | board-to-board + locking I/O | +| Antennas | re-torque bulkhead SMA | bulkhead SMA pigtails | +| SIM | front-panel holder | front-panel holder or MFF2 eSIM | +| Coating | acrylic conformal coat | conformal coat / pot power section | +| Thermal | sunshade + **conduct heat to metal plate** (no internal fan) | metal cabinet as heatsink; sealed (keep IP67) | + +--- + +## 6. Open decisions for Leo (these change the design) + +1. **Battery mode — keep, change, or drop?** + - *Keep tracking while the vehicle is off* (anti-theft / parked location)? + Then use **LiFePO4** (wider temp, safer) or a relocated/temperature-managed + cell — **not** a LiPo baking next to the modem. + - *Only need ride-through* across brief power dips? A **supercap** hold-up is + simpler, safer, and temperature-tolerant — but won't give long parked + tracking. + - *Vehicle-only* acceptable? **Drop the battery** entirely — removes the + single worst thermal/safety liability. **(Recommended if the client doesn't + require parked tracking — confirm with ICAVE.)** +2. **SIM strategy:** swappable **front-panel SIM** vs **MFF2 eSIM** (eliminates + the swap problem but commits the carrier). Depends on how often SIMs change. +3. **MCU:** reuse F411 (fast, keeps v2 firmware) vs move to an industrial-temp + STM32 (more margin, small port effort). +4. **Inner enclosure material:** **confirmed ASA** (UV-stable, ~95–100 °C) — adequate + for the nested-cabinet temperatures; no material change required. Optionally + eliminate the inner box and mount the PCB to the cabinet plate for better conduction. +5. **Cabinet thermal path:** keep ABS IP67 + sunshade + **conduct heat to a metal + plate**, or move to a **metal (aluminium) cabinet** as a heatsink (best, costs + more). **An internal fan in a sealed box is not a fix (see §2b)** — only + conduction or (IP-breaking) ventilation removes heat. +6. **Make vs buy the modem:** keep the ArduSimple 4G NTRIP Master, or move to a + qualified industrial LTE module with a known wide-temp rating. + +--- + +## 7. Suggested next actions (feeds `PLAN.md`) + +- Bench power-budget + inrush measurement (§2) → picks the 24→5 V buck. +- **Quantify cabinet temperature** (thermocouple in a representative cabinet, full + sunny shift) → design target for §2b. +- Decide §6.1 (battery), §6.4 (inner-box material), §6.5 (cabinet thermal path) — + the biggest levers. **The ASA enclosure is adequate — no material swap needed; focus on the conduction path + active thermal.** +- Phase-1 ruggedization kit BOM + rework one unit as the reference build. +- Phase-2 schematic → layout → fab/assembly; pilot 2–3 boards before fleet. +- Cabinet BOM is industrial-grade and stays — see `03-Architecture-Hardware/cabinet-electrical-bom.md`. diff --git a/03-Architecture-Hardware/watchdog-health-telemetry.md b/03-Architecture-Hardware/watchdog-health-telemetry.md new file mode 100644 index 0000000..5f0e144 --- /dev/null +++ b/03-Architecture-Hardware/watchdog-health-telemetry.md @@ -0,0 +1,192 @@ +--- +tags: [project-drogheda, hardware, firmware, watchdog, telemetry, nmea] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Analysis — review & bench-test before any wiring/firmware change +--- + +# Watchdog Health Telemetry — Feasibility & Improvement Analysis + +> **The question:** can the STM32F411 watchdog transmit its health (temp, reset +> cause, link state, recovery count…) over UART so it **lands in our server +> message**, e.g. as a custom `$GPTXT`? You've seen mixed information. This +> resolves it against the **as-built wiring** and proposes a clean design. + +--- + +## TL;DR + +- **As wired today: No.** The watchdog sits on the F9P's **UART1** (it reads NMEA + and sends UBX rate commands). The data that reaches the server flows out the + F9P's **UART2 → 4G NTRIP Master → server**. The watchdog is **not on that path**, + so nothing it prints can reach the backend. +- The `$GPTXT,NET,,TELCEL,,` you see at the server is the **4G + modem's** own network-status line (the `` is the modem's station ID set via + `AS+K`, which the backend treats as the `cell_id`). It is **not** produced by the + F9P or the watchdog. +- **It is achievable** — but it requires putting the watchdog **in the modem's + NMEA-uplink path** (a relay/injector) and emitting a **proprietary sentence** + (`$PHLG…`, *not* `$GPTXT` — see the collision note). This is a **wiring change**, + clean on the Phase-2 PCB, and it must be **bench-validated** against one unknown: + *does the ArduSimple 4G NTRIP Master forward arbitrary/proprietary NMEA, or only a + whitelist?* That single question is the source of the "mixed information." +- **Lower-risk interim (no rewiring):** the watchdog already has a full health + record — ship the **v2.1 parseable log** (`docs/LOGGING-SPEC.md`) over its console. + It's authoritative per-unit health, captured on pulled units; it just isn't *live + to the server*. In-band-to-server telemetry is the Phase-2 upgrade. + +--- + +## 1. The as-built data path (why the answer is "not yet") + +```text + ┌──────────────── simpleRTK2B Pro (ZED-F9P) ─────────────────┐ + │ │ + UART1 ◄──┤ TX1 → NMEA (GGA…) ──► STM32 PA3 (RX) [watchdog: READS] │ + │ RX1 ← UBX rate ◄── STM32 PA2 (TX) [watchdog: WRITES]│ + │ │ + UART2 ◄──┤ TX2 → NMEA/GGA @1Hz ──► 4G NTRIP Master (XBee) ──► SERVER │ ← server path + │ RX2 ← RTCM3 ◄── 4G NTRIP Master (NTRIP caster) │ + └────────────────────────────────────────────────────────────┘ + │ + 4G modem also emits its OWN $GPTXT,NET,,TELCEL,… + (network status + station id = the backend's cell_id) +``` + +Key facts (confirmed in firmware + the 20-Jun bench analysis): + +- Firmware: `HardwareSerial GNSS(PA3, PA2)` → **F9P UART1**. `parseNMEA()` only + *reads* GGA (for `fixQuality`/`satellites`/`rxCount`); `applyRate()` only *writes* + UBX. On power-off it **floats** PA2/PA3. The watchdog never transmits toward the + modem. +- The modem is on the Pro's **XBee socket = UART2** (the "XBEE↔UART2 switch"); GGA is + configured out **UART2 @ 1 Hz**. The server stream and the NTRIP GGA-uplink both + ride UART2. +- Therefore the watchdog (UART1) and the server path (UART2) are **electrically + separate**. No firmware change alone can bridge them. + +--- + +## 2. The three theoretical ways in (and which one to use) + +1. **Make the F9P emit it.** ✗ Not possible. The ZED-F9P has no command to inject an + arbitrary NMEA string into its UART2 output. It emits only its own sentences + UBX + replies. +2. **Tell the 4G modem to carry it.** ✗ No runtime path. The modem's `$GPTXT` is + network status; `AS+K` (station id) is *set-once* config, not a data channel the + MCU can stuff health into per-cycle. +3. **Put the watchdog in the uplink NMEA path (relay/injector).** ✓ **The viable + design.** Route the F9P→modem NMEA *through* the STM32: + + ```text + F9P TX2 (NMEA) ──► STM32 RX ──[relay verbatim]──► STM32 TX ──► modem RX + └─[inject $PHLG health @ ~1/30s in the idle gap] + modem ──► forwards EVERYTHING (GGA/RMC/GPTXT + $PHLG) ──► server + RTCM from the modem can still go straight to the F9P RX2 (corrections path intact). + ``` + + The backend already maps **(ip,port) → cell_id** (from the modem's `$GPTXT`), so a + `$PHLG` arriving on the same socket is **auto-associated to the right unit** — no + need to put the id in the health sentence. (This reuses the CGNAT-safe identity work + shipped in `retranslator.py`.) + +> **Retrofit variant (no full relay):** diode-OR / open-drain the STM32 TX onto the +> modem's RX and inject only during the F9P's idle gaps (at 115200 baud, 1 Hz NMEA +> leaves the line idle >90 % of the time). Cheaper to bolt on, but collision-prone and +> harder to guarantee — **prefer the clean relay on the PCB.** + +--- + +## 3. The proprietary sentence (don't reuse `$GPTXT`) + +**Collision warning:** the backend parses `$GPTXT` by taking **field 2 as the +`cell_id`**. A health `$GPTXT` would be mis-read as a device id. Use a **proprietary +talker** instead — `$PHLG` (`P` = proprietary, `HLG` = Helgen): + +```text +$PHLG,,,,,,,,,,,,,,*CS +e.g. $PHLG,2.1,RUN,LIMP,1,12,34192,4.01,5.02,63,71,2,1,420,IWDG*4F +``` + +Fields mirror the v2.1 `TLM` record (state, link, fix, sats, rx, battery, 5 V, +**MCU temp + session max**, recovery count, limp latch, NMEA age, **reset cause**). +`CS` = standard NMEA XOR checksum. + +**Backend side** is a ~15-line addition to the hardened `retranslator.py`: a branch +that recognises `$PHLG`, verifies the checksum, and stores the fields per `cell_id` +(keyed by the existing `(ip,port)` map). Then the monitor can surface, **per unit and +live**: MCU temp / `tMax` (the thermal-hypothesis evidence, without pulling the unit), +reset cause (reboot-loop forensics), recovery count, and LIMP state — and it ships to +Loki for free. + +--- + +## 4. What to bench-test before committing (resolves the "mixed info") + +1. **Does the 4G NTRIP Master forward a proprietary `$P…` sentence?** Inject one on + UART2-RX on the bench and watch the server `tcpdump`. *If it whitelists talkers:* + fall back to a `$GPTXT,HLT,…` subtype and special-case it in the backend by **field + 1** (`HLT` vs `NET`) so it never collides with the cell-id parse. **This is the one + experiment that settles the whole question.** +2. **GGA integrity for NTRIP.** The modem relays the F9P's GGA *up to the caster* for + network RTK. The relay must be **byte-exact and low-latency** and only inject in + idle gaps — never delay or mangle GGA, or corrections degrade. +3. **Rate / cost.** Health at **1 / 30–60 s** keeps cellular + backend load trivial. +4. **Checksum** must be correct or the backend (and possibly the modem) drops it. + +--- + +## 5. Other watchdog improvements (independent of the telemetry question) + +### 🔴 5a. The "no-RTK ≠ dead-modem" thrash risk — *act on this now* +v2 treats `fixQuality >= 4` (RTK) as the only healthy link, and after +`NTRIP_GRACE` (120 s) of `< 4` it **power-cycles the stack**. But the 20-Jun bench +reality is **every unit is SPS (quality 1), no RTK** (caster/FLASH/sky issue, *not* a +modem hang). With the watchdog connected as-is, a unit that is **GNSS-healthy and +forwarding fine at SPS** would be **power-cycled every ~2 min + backoff** — manufacturing +the very disconnects we're trying to kill. **Fix:** only power-cycle on evidence of a +*link/modem* hang (NMEA silence is already handled); treat *stable SPS with incrementing +`rx`* as a reportable **DEGRADED** state, **not** a recovery trigger — at least until the +corrections pipeline (move off rtk2go, save F9P config to FLASH, open-sky retest) is +fixed. This is the highest-value firmware change right now. + +### 🟠 5b. Ship the v2.1 parseable log (`docs/LOGGING-SPEC.md`) +The ANSI dashboard overwrites itself and has no timestamps/reset-cause → useless as a +field record. The v2.1 spec (BOOT/COLS/TLM/EVT, reset cause, ~60 additive lines) makes +the watchdog the **authoritative per-unit health record**. It's also the exact data the +`$PHLG` sentence would carry — **do this first**, so every later change is observable. + +### 🟠 5c. Independent watchdog (IWDG) — watch the watcher +Nothing currently recovers the Blackpill itself if *it* hangs. Enabling the STM32 IWDG +makes an MCU lockup self-reset and show `reset=IWDG` next boot — closing the last +"silent vanish" gap for a device whose whole job is fail-safe. + +### 🟠 5d. External temp sensor + persisted counters +v2 logs **MCU** temp, but the heat casualties are the **18650 / 4G modem**, not the die. +Add a small external sensor near the cell/modem (PCB brief already flags this), and +**persist** `tMax` / `recoveryCount` / reset history to RTC backup registers or flash so +a pulled unit's forensics survive the power-cycle. Directly serves the thermal RCA. + +--- + +## 6. Recommendation (phased) + +| When | Action | Risk | +|---|---|---| +| **Now** (firmware only) | 5a (stop SPS-driven power-cycling) + 5b (v2.1 log) + 5c (IWDG). Bench-test, bump v2.1, then flash. | Low — additive / behavioural guard. | +| **Bench experiment** | Test #1: does the modem forward `$P…`? Decide `$PHLG` vs `$GPTXT,HLT`. | None — measurement. | +| **Phase 2 (PCB)** | Watchdog as NMEA relay/injector on the UART2 uplink + external temp sensor; backend `$PHLG` parser + monitor/Loki surfacing. | Medium — relay must preserve GGA/NTRIP; validate before fleet. | + +**Bottom line for the deck:** *Yes, we can put unit health (incl. temperature) into the +live server feed — it needs the watchdog inserted into the modem's uplink path (a +Phase-2 PCB change) and one bench test of the modem's forwarding. Until then the same +health is captured on-unit via the v2.1 log. Separately, before we connect the watchdog +we must stop it from power-cycling healthy SPS units.* + +## Related +- [[03-Architecture-Hardware/watchdog-schematic|watchdog-schematic]] · + [[03-Architecture-Hardware/firmware-review|firmware-review]] · + [[03-Architecture-Hardware/hardware-respec-and-pcb-brief|PCB brief]] · + `04-Source-Code/firmware/docs/LOGGING-SPEC.md` · + `04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino` diff --git a/03-Architecture-Hardware/watchdog-pcb-v3-blockdiagram.svg b/03-Architecture-Hardware/watchdog-pcb-v3-blockdiagram.svg new file mode 100644 index 0000000..5390b02 --- /dev/null +++ b/03-Architecture-Hardware/watchdog-pcb-v3-blockdiagram.svg @@ -0,0 +1,83 @@ + +HBOX Watchdog Carrier PCB v3 — power and signal map +24 V cabinet feed passes reverse-polarity protection, then splits: to a new dedicated fan buck feeding two 5 V fan drivers and the extraction fans; and to the external Mean Well 5 V supply that powers the STM32F411 Blackpill, the RTK-stack load switch (to the external simpleRTK2B + 4G stack), and sense circuits. New I2C SHT40 + TMP102 temperature sensors and the fan drivers connect to the MCU. + + + + + +HBOX Watchdog Carrier PCB v3 — power & signal map + +new in v3 (fans + temp) + +existing + +external module +— power - - signal + +24 V feed +cabinet · GX16-5 + +Rev-pol + TVS +Q10 · D10 + +Mean Well 5 V +SD-15B-05 · ext + + +Fan buck U2 +24→5 V dedicated rail · JP1 + +Fan drv 1 +Q2 · flyback + +Fan drv 2 +Q3 · flyback + +2× 5 V fans +extraction · ext + +24 V + + +5V_FAN + + + +STM32F411 Blackpill +watchdog · on headers +reuses v2.1 firmware + +18650 / PowerBoost +battery · ext + +SHT40 + TMP102 +I²C temp + humidity + +Load switch +Q1 · Q5 · PPTC + +simpleRTK2B + 4G +SW_5V · ext + +Status LED +PB0 + +MAIN 5V + +5 V + +SW_5V + +PA1 EN + +UART PA2/3 + +PB0 + +PWM + tach PA6/7 · PB10/12 + +BAT sense PA0 + +I²C PB6/7 + diff --git a/03-Architecture-Hardware/watchdog-pcb-v3-bom.xlsx b/03-Architecture-Hardware/watchdog-pcb-v3-bom.xlsx new file mode 100644 index 0000000..062f759 Binary files /dev/null and b/03-Architecture-Hardware/watchdog-pcb-v3-bom.xlsx differ diff --git a/03-Architecture-Hardware/watchdog-pcb-v3-design.md b/03-Architecture-Hardware/watchdog-pcb-v3-design.md new file mode 100644 index 0000000..1e6ebe8 --- /dev/null +++ b/03-Architecture-Hardware/watchdog-pcb-v3-design.md @@ -0,0 +1,378 @@ +--- +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]] diff --git a/03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide.md b/03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide.md new file mode 100644 index 0000000..b10c447 --- /dev/null +++ b/03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide.md @@ -0,0 +1,234 @@ +--- +tags: [project-drogheda, hardware, pcb, kicad, jlcpcb, guide, how-to] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-21 +status: Active — working guide +--- + +# KiCad → JLCPCB: build & deploy the Watchdog Carrier PCB v3 + +> A from-zero, do-this-then-that guide for capturing the +> [[03-Architecture-Hardware/watchdog-pcb-v3-design|v3 design]] in **KiCad** and getting it +> **assembled by JLCPCB**. Written for someone who has done PCB work *a long time ago*. +> The schematic content (every net, part, value) lives in the design doc and the +> [[03-Architecture-Hardware/watchdog-pcb-v3-bom|BOM]] — this is the *tooling* path. + +--- + +## 0. Pick your path (read first) + +You have two realistic routes to a JLC-assembled board. Both end at the same place. + +| Path | Best when | Trade-off | +|---|---|---| +| **A — KiCad** (this guide) | you want a proper, version-controllable design you own outright | steeper, but the right long-term tool; offline; git-friendly | +| **B — EasyEDA (Std/Pro)** | you want the *fastest* first board with least friction | JLC-native: every part already carries its **LCSC number + footprint**, and "Order at JLCPCB" is one button. Less portable. | + +**My recommendation for *this* board, given you're newer to PCB and going JLC-assembly:** +do **Path A (KiCad)** because you'll iterate this board for the fleet and want to own it — +but if you feel stuck on footprints/part-matching, **Path B (EasyEDA) is a legitimate +shortcut** and uses the exact same LCSC parts in our BOM. Don't lose a week to tooling. + +The rest of this doc is Path A. + +--- + +## 1. Install KiCad + two plugins + +1. **KiCad 9 or 10** (current 2026) from kicad.org → install the full suite. +2. Open KiCad → **Plugin and Content Manager** (button on the home screen) and install: + - **Fabrication Toolkit** (by *bennymeg*) — one-click export of Gerbers + **BOM + CPL** + already in JLCPCB's format. This is the deploy button. + - **JLCPCB Tools** (*Bouni / kicad-jlcpcb-tools*) — lets you assign the **LCSC part + number** to each component from inside KiCad and query JLC stock. This is how the BOM + parts get matched. + +> Both are free, in the official plugin manager. With these two, you never hand-edit a +> JLC CSV. + +--- + +## 2. The mental model (the whole workflow in one picture) + +``` + Symbols + wires Footprints Place + route One-click export + (Schematic editor) ──► (per component) ──► (PCB editor) ──► (Fabrication Toolkit) + │ │ │ │ + ERC ✓ "what each part DRC ✓ Gerbers.zip + bom.csv + (no missing nets) physically is" (no shorts/ + positions.csv ──► JLCPCB + clearance) upload + assemble +``` + +You always go **left to right**. Don't start routing until the schematic passes ERC. + +--- + +## 3. Create the project + +1. **File → New Project** → name it `hbox-watchdog-v3`. Save it **inside the repo** at + `03-Architecture-Hardware/` (or a new `kicad/` subfolder) so it's version-controlled. +2. You get `.kicad_sch` (schematic) and `.kicad_pcb` (board). Open the **Schematic Editor**. + +--- + +## 4. Draw the schematic + +Work **one sub-circuit at a time**, matching design-doc §4. Use **net labels**, not long +wires — it keeps things readable. + +### 4.1 Place the power symbols first +Add power ports (the "place power symbol" tool): **+24V, +5V (main), +5V_FAN, +3V3, SW_5V, +GND**. Every supply/return connects to these by name — no need to draw rails across the page. + +### 4.2 The Blackpill (the trick that saves you) +You are **not** designing the STM32 chip — the Blackpill module plugs into **two 1×20 +headers**. So represent it as **two `Conn_01x20` connector symbols** (or grab a ready +"WeAct Blackpill" symbol from a community KiCad library — search GitHub for "Blackpill +KiCad library"). Label each pin with its **net name from design-doc §2** (PA0=BAT_SENSE, +PA1=PWR_CTRL, PA6=FAN1_PWM, PB6=I2C1_SCL, …). The header footprints become the socket the +module sits in. This avoids all MCU bring-up. + +### 4.3 Drop in each sub-circuit (design-doc §4) +For each, place the symbols, wire locally, and tie to power/net labels: + +- **Input + fan buck (§4.1–4.2):** Q10 (AO3401A), D10 TVS, C10/C11; then U2 **MP1584EN** with + L2, C22, C23, the FB divider R20/R21, and **JP1** (solder-jumper symbol). +- **Load switch (§4.3):** Q1 AO3401A + Q5 MMBT3904 + R1/R2 + **F1 PPTC** + C3. Net the drain to + **SW_5V**. (Optional U1 eFuse footprint — leave it **DNP**.) +- **Fan drivers ×2 (§4.4):** Q2/Q3 AO3400A, D2/D3 SS34 flyback, Rg/Rgs gate parts, tach + pull-up + series into **PB10 / PB12**. PWM gates from **PA6 / PA7**. +- **Sensors (§5):** U3 SHT40 + U4 TMP102 on **PB6/PB7**, 4.7k pull-ups, 100nF each. +- **Sense + LED (§4.5–4.6):** the three 10k/10k dividers (PA0/PA4/PA5), D5 clamp, Q4 + D1 + R3. +- **Connectors (§6):** P1–P8, H1, H3 (DNP), and the two 2×20 sockets. + +### 4.4 Annotate + ERC +- **Tools → Annotate** (gives refs R1, U2, …). +- **Inspect → Electrical Rules Check.** Fix every error (usually an unconnected pin or a + missing power flag). **Schematic is not done until ERC is clean.** + +--- + +## 5. Assign footprints + +Open **Tools → Assign Footprints** (or the Footprint Assignment tool). Map each part to a +JLC-friendly footprint: + +| Part type | Footprint | +|---|---| +| Resistors / caps | `Resistor_SMD:R_0603_1608Metric` / `Capacitor_SMD:C_0603_1608Metric` (C3/C22/C10 → 0805/1206/elec) | +| AO3400A / AO3401A / MMBT3904 | `Package_TO_SOT_SMD:SOT-23` | +| SS34 | `Diode_SMD:D_SMA` (DO-214AC) | +| MP1584EN | `Package_SO:SOIC-8-1EP_…` (the EP/thermal-pad variant) | +| SHT40 | `Sensor:Sensirion_DFN-4-1EP_…` (or import from LCSC — see below) | +| TMP102 | `Package_SON:SOT-563` (or import from LCSC) | +| JST-XH | `Connector_JST:JST_XH_B*` (B4B/B2B etc.) | +| Headers | `Connector_PinHeader_2.54mm:…` (1×4, 1×3, **2×20** for the sockets) | + +**For the odd parts (SHT40, TMP102, MP1584, JST):** the **JLCPCB Tools** plugin can pull the +**exact LCSC footprint** so it matches the assembler perfectly. Or use the classic trick: +on **easyeda2kicad** / the JLC part page, grab the EasyEDA footprint and import. Either way, +**verify pin-1 orientation** against the datasheet. + +--- + +## 6. Lay out the PCB + +1. Open the **PCB Editor** → **Update PCB from Schematic** (pulls in all parts + the + ratsnest of connections). +2. **Board outline:** draw a rectangle on `Edge.Cuts` (~100 × 80 mm is plenty). Put the + **mounting holes** so the board can bolt to the cabinet **metal plate** (design-doc §8 — + that's your conduction path). +3. **Place by functional block** (keep each §4 sub-circuit together): power-in + buck in one + corner, load switch near P3, fan drivers near P6/P7, sensors near an air edge / P5, the + Blackpill sockets central, connectors on the edges. +4. **Stackup:** 2-layer is fine (cheaper); **4-layer** is better for RF/buck noise. Add a + **ground pour** (filled zone = GND) on the bottom (and an inner layer if 4-layer). +5. **Route** (design-doc §8 rules): power/5V ≥0.5 mm, **SW_5V ≥0.8 mm** (modem bursts), + signals 0.25 mm. **Keep the MP1584 switch node + fan-PWM traces away from the GNSS + antenna pigtail.** Star-ground the pulsed (fan/modem) returns separately from the ADC/ + sensor ground. +6. **DRC** (**Inspect → Design Rules Checker**). Zero unrouted, zero clearance errors. +7. Add **silkscreen labels** on every connector (net names) + pin-1 arrows, and a board + title/rev. (Serviceability requirement.) + +--- + +## 7. Tell JLC which physical part goes where (LCSC numbers) + +Open the **JLCPCB Tools** plugin. For each component, set its **LCSC field** to the code in +the BOM (e.g., AO3400A → `C347222`, SHT40 → `C2909890`, TMP102 → `C99269`). For the +**yellow "verify" parts** in the BOM, search the plugin's parts DB live and pick an in-stock +equivalent. Mark **U1 (eFuse)** and **H3 (USART1 reserve)** as **DNP / "Do not place."** + +> This step is what makes assembly "just work" — the assembler places exactly these LCSC +> parts. The **Blackpill, the 2×20 sockets, and the GX16 wiring you hand-solder yourself** +> (mark them DNP for JLC, or "PCBA: no"). + +--- + +## 8. Export (the deploy button) + +Run **Fabrication Toolkit** (Tools/External-plugins menu in the PCB editor). One click +produces a `production/` folder with: + +- **Gerbers + drill**, zipped — the board. +- **bom.csv** — Designator, Footprint, Qty, Value, **LCSC** — already JLC-formatted. +- **positions.csv (CPL)** — Designator, Mid X/Y, Rotation, Layer — already JLC-formatted. + +No hand-editing. (If you ever do it manually: Plot Gerbers with JLC's layer settings, then +generate BOM + CPL — but the plugin removes that pain.) + +--- + +## 9. Order on JLCPCB + +1. **jlcpcb.com → Add gerber file →** upload the Gerber **.zip**. Confirm the preview looks + like your board. +2. Set basics: layers (2 or 4), 1.6 mm, HASL or ENIG, qty **5**. +3. Toggle **PCB Assembly = ON**. Choose **Economic** (cheaper, top-side only is fine here) + and **assembled qty 2–3**. +4. Upload **bom.csv** and **positions.csv** when prompted. JLC matches LCSC parts → fix any + "not found" by picking an in-stock alternative (this is why we kept to Basic parts). +5. **Review placements** carefully — **the #1 beginner bug is wrong part rotation** on + SOT-23 / SOIC / connectors. Check each against the datasheet pin-1; rotate in the review + tool as needed. +6. Confirm DNP parts (U1, H3) are excluded. Place the order. + +Lead time ~1–2 weeks to Mexico. Hand-solder the Blackpill, the 2×20 sockets, and the GX16 +pigtail when it arrives. + +--- + +## 10. First-board gotchas (the stuff that bites beginners) + +- **Rotations**: JLC's 0° reference often differs from KiCad's. Always eyeball the placement + preview; SOT-23 and electrolytics are the usual offenders. +- **Polarity**: double-check D10 TVS, C10 electrolytic, D2/D3 Schottky, the LED, and Q10/Q1 + source/drain before ordering. +- **The buck (U2)** is the only "real" analog block — if you're nervous, **populate JP1 + closed + upsize the 5 V supply** and leave U2 unpopulated for the first spin (design-doc + §4.2). One board, two options. +- **Don't let JLC place the Blackpill or the GX16** — those are hand-soldered. +- **Order 1–2 spare bare boards** for hand-rework experiments. +- **Conformal-coat** after you've tested it (mask the SHT40 slot + connectors). + +--- + +## 11. Pre-order checklist + +- [ ] Schematic **ERC clean**; every Blackpill pin labelled per design-doc §2. +- [ ] All footprints assigned; SHT40/TMP102/MP1584/JST verified against datasheet pin-1. +- [ ] PCB **DRC clean**; GND pour stitched; SW_5V wide; buck/fan noise away from GNSS feed. +- [ ] Mounting holes positioned to bolt to the cabinet metal plate. +- [ ] LCSC numbers set on all placed parts; **U1 + H3 = DNP**; Blackpill/GX16 = hand-solder. +- [ ] Fabrication Toolkit exported Gerbers + BOM + CPL. +- [ ] JLC placement preview rotations checked. +- [ ] Bench-test plan ready (design-doc §9) — **especially the §0a 5 V-rail sag test** before + you trust it in a cabinet. + +## Related +- [[03-Architecture-Hardware/watchdog-pcb-v3-design|v3 design (schematic spec)]] · + [[03-Architecture-Hardware/watchdog-pcb-v3-bom|v3 BOM (LCSC parts)]] · + [[03-Architecture-Hardware/watchdog-schematic|as-built watchdog]] · + [[CLAUDE|Master Index]] diff --git a/03-Architecture-Hardware/watchdog-schematic.md b/03-Architecture-Hardware/watchdog-schematic.md new file mode 100644 index 0000000..a881c38 --- /dev/null +++ b/03-Architecture-Hardware/watchdog-schematic.md @@ -0,0 +1,116 @@ +--- +tags: [project-drogheda, hardware, watchdog, schematic, pcb] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Watchdog — Wiring & Schematic Spec (STM32F411 Blackpill) + +> The as-built watchdog circuit, formalized for the production PCB. Pairs with +> the firmware [[04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2|blackpillrtkcode_v2.ino]] +> (pin map must match) and the [[03-Architecture-Hardware/BOM-master|BOM]]. + +## 1. Power tree (corrected) + +```text +Cabinet 24 V ─GX16-5─► HBOX + └► Mean Well SD-15B-05 (24→5 V, 3 A) ─► 5 V rail ─► PowerBoost 1000C (USB in) + PowerBoost (BAT=18650 1S4P) ─► PowerBoost 5V OUT ─► STM32 5V + IRF9540N source + 24 V ignition ─► DFR1015 (buck) ─► STM32 PA5 (ignition sense, ≤3.3 V) +``` + +## 2. STM32F411 pin map (matches firmware v2) + +| Pin | Net | Function | +|---|---|---| +| PA0 | BAT_SENSE | 18650 voltage ÷ divider → ADC | +| PA4 | 5V_SENSE | PowerBoost USB (vehicle-power present) ÷ divider → ADC | +| PA5 | IGN_SENSE | ignition via **DFR1015** (digital, INPUT_PULLDOWN) | +| PA1 | PWR_CTRL | drives the RTK power switch (NPN→P-MOS) | +| PA2 | GNSS TX2 | UBX commands → simpleRTK2B RX1 | +| PA3 | GNSS RX | NMEA from simpleRTK2B (read-back) | +| PB0 | EXT_LED | external status LED (via N-MOSFET) | +| PC13 | onboard LED | active-LOW | + +## 3. RTK power switch (high-side, P-channel) + +```text + 5V ───┬──────────────[10kΩ pull-up]──┐ + │ │ + IRF9540N Source Gate ── 2N3904 Collector + │ │ + Drain ──► simpleRTK2B 5V 2N3904 Emitter ── GND + │ + STM32 PA1 ──[1kΩ]── 2N3904 Base ──────────┘ +``` + +- PA1 **HIGH** → 2N3904 conducts → pulls IRF9540N gate **LOW** → P-MOS **ON** → + 5 V to the simpleRTK2B + 4G stack. PA1 LOW → gate held high by 10 kΩ → OFF. +- 10 kΩ gate pull-up = defined OFF at boot/reset (fail-safe: stack off until the + MCU asserts). + +> **PCB note:** replace the 2N3904 + IRF9540N + pull-up with an **integrated +> high-side load switch / eFuse** (e.g., TPS25200 / TPS2596) — current-limited, +> fewer parts, defined turn-on, no gate-pull-up gotchas. Keep the same fail-safe +> (load off until MCU enables). + +## 4. Status LED (low-side N-MOSFET, logic-level) + +```text + 3.3V ──[220–330Ω]──►|LED|──► 2N7000/BS170 Drain + Source ── GND + STM32 PB0 ── Gate ──┬── 10kΩ ── GND (pull-down: LED off while booting) +``` + +- 2N7000/BS170 are logic-level (switch fully at 3.3 V). 10 kΩ pull-down keeps the + LED off during MCU boot. PC13 onboard LED mirrors inverted (active-low). + +## 5. Sense dividers + +| Net | Source | Divider | At ADC (max) | Note | +|---|---|---|---|---| +| PA0 BAT | 18650 (3.0–4.2 V) | ÷2 (e.g. 10k/10k) | 1.5–2.1 V | firmware `DIVIDER_RATIO=2.0` | +| PA4 5V | PowerBoost USB ~5 V | ÷2 | ~2.5 V | "vehicle power present" | +| PA5 IGN | 24 V ignition | **DFR1015 buck** → ≤3.3 V | digital | isolated/scaled by DFR1015 | + +> ⚠ The BOM lists a lone **1.5 kΩ "????"** resistor — confirm whether it's a +> divider leg or the LED resistor and pin it down on the schematic. Keep ADC +> inputs ≤3.3 V with margin. + +## 6. Resistor / discrete BOM (watchdog) + +| Ref | Value | Purpose | +|---|---|---| +| R1 | 1 kΩ | PA1 → 2N3904 base | +| R2 | 10 kΩ | IRF9540N gate pull-up (to 5 V) | +| R3 | 220–330 Ω | LED current limit | +| R4 | 10 kΩ | PB0 LED-MOSFET gate pull-down | +| R5/R6 | 10 kΩ/10 kΩ | PA0 battery divider (÷2) | +| R7/R8 | 10 kΩ/10 kΩ | PA4 5 V divider (÷2) | +| Q1 | 2N3904 (NPN) | gate driver | +| Q2 | IRF9540N (P-MOS) | RTK 5 V high-side switch | +| Q3 | 2N7000 / BS170 (N-MOS) | LED switch | + +## 7. For the production PCB + +- Integrated load switch (above) instead of the discrete trio. +- All ADC dividers with RC filtering; clamp/Zener protection on PA5 ignition. +- Bring out **SWD** + a **debug UART** (the v2 ANSI dashboard) on a keyed header. +- Add an external temp sensor near the 18650 / modem (v2 logs MCU temp; an + external one near the battery is more representative). +- Keep switching-regulator noise away from the GNSS antenna feed (RF integrity). +- Single keyed **GX16-5** for the 24 V + ignition interface to the cabinet. + +## Open Questions + +- [ ] Pin down the 1.5 kΩ resistor's role; finalize all divider values. +- [ ] Confirm DFR1015 output level into PA5 (≤3.3 V) and whether it's analog or digital sense. +- [ ] Load-switch part choice + current limit (from the measured power budget). + +## Related + +- [[CLAUDE|Master Index]] · [[04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2|firmware v2]] · + [[03-Architecture-Hardware/hardware-respec-and-pcb-brief|Hardware re-spec + PCB brief]] · + [[03-Architecture-Hardware/BOM-master|BOM]] diff --git a/04-Source-Code/firmware b/04-Source-Code/firmware new file mode 160000 index 0000000..1eab7b9 --- /dev/null +++ b/04-Source-Code/firmware @@ -0,0 +1 @@ +Subproject commit 1eab7b92ff5a9421f0e358936ce038dbee8e39e1 diff --git a/04-Source-Code/htracker b/04-Source-Code/htracker new file mode 160000 index 0000000..f93a36d --- /dev/null +++ b/04-Source-Code/htracker @@ -0,0 +1 @@ +Subproject commit f93a36d3ae8d64ead1036f422a32024eb3cb9cbf diff --git a/04-Source-Code/notifications/README.md b/04-Source-Code/notifications/README.md new file mode 100644 index 0000000..6855505 --- /dev/null +++ b/04-Source-Code/notifications/README.md @@ -0,0 +1,49 @@ +--- +tags: [project-drogheda, notifications, mattermost] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Mattermost notifications + +There is **no Mattermost MCP connector** available (checked the connector registry), +so we post to Mattermost the simple, robust way: an **Incoming Webhook** + the +dependency-free `notify_mattermost.py`. No secrets in the repo — the webhook URL +lives in an environment variable. + +## One-time setup + +1. **Create the webhook** in Mattermost: *Main Menu → Integrations → Incoming + Webhooks → Add Incoming Webhook*. Choose the target channel (e.g. `#drogheda`) + and copy the URL: `https:///hooks/xxxxxxxxxxxxxxxxx`. + *(If "Integrations" is hidden, a System Admin must enable Incoming Webhooks in + System Console → Integrations → Integration Management.)* +2. **Export the URL** (add to your shell profile or a secrets manager — not git): + ```bash + export MATTERMOST_WEBHOOK_URL="https:///hooks/xxxxxxxxxxxxxxxxx" + ``` + +## Use + +```bash +python3 notify_mattermost.py "🛰️ Drogheda: anti-jump hotfix deployed ✅" +python3 notify_mattermost.py --channel drogheda --username "Drogheda Bot" "RTK fix restored on ZPM 020" +echo "line 1\nline 2" | python3 notify_mattermost.py - # read from stdin +python3 notify_mattermost.py --dry-run "test" # print payload, don't send +``` + +Markdown works in the message body (tables, bullets, `code`). The script exits `0` +on success, `2` on config/usage error, `1` on a send failure. + +## Wire it into workflows + +- **Hackathon / CI:** call it at the end of a deploy or test step. +- **Backend health:** the `htracker` notifier already does Discord+SMTP; a Mattermost + sink can be added the same way, or pipe its events through this script. +- **Scheduled status:** cron / launchd → a daily "fleet online/offline" digest. + +> If we later want richer two-way Mattermost integration (read channels, react to +> messages), build a small custom MCP with the `mcp-builder` skill — but for +> push notifications the webhook above is sufficient and lower-maintenance. diff --git a/04-Source-Code/notifications/notify_mattermost.py b/04-Source-Code/notifications/notify_mattermost.py new file mode 100644 index 0000000..e14ef13 --- /dev/null +++ b/04-Source-Code/notifications/notify_mattermost.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +""" +notify_mattermost.py — post a message to a Mattermost channel via Incoming Webhook. + +Dependency-free (stdlib urllib). The webhook URL is a secret and is read from the +environment — NEVER hardcode it (Helgen ethos: no secrets in the repo). + +Setup (one-time): + 1. In Mattermost: Main Menu → Integrations → Incoming Webhooks → Add. Pick the + channel, copy the URL (looks like https:///hooks/xxxxxxxxxxxxxxxxx). + 2. export MATTERMOST_WEBHOOK_URL="https:///hooks/xxxxxxxxxxxxxxxxx" + +Usage: + python3 notify_mattermost.py "Backend hotfix deployed ✅" + echo "multi-line\nstatus" | python3 notify_mattermost.py - + python3 notify_mattermost.py --channel ops --username "Drogheda Bot" "RTK fix restored" + python3 notify_mattermost.py --dry-run "test" # print payload, don't send + +Exit codes: 0 ok · 2 usage/config error · 1 send failure. +""" +import argparse +import json +import os +import sys +import urllib.request +import urllib.error + +DEFAULT_USERNAME = "Drogheda Bot" +DEFAULT_ICON = ":satellite:" + + +def build_payload(text, channel=None, username=DEFAULT_USERNAME, icon=DEFAULT_ICON): + payload = {"text": text, "username": username} + if icon: + payload["icon_emoji"] = icon + if channel: + payload["channel"] = channel # override the webhook's default channel + return payload + + +def send(url, payload, timeout=10): + data = json.dumps(payload).encode("utf-8") + req = urllib.request.Request( + url, data=data, headers={"Content-Type": "application/json"}, method="POST" + ) + with urllib.request.urlopen(req, timeout=timeout) as resp: + return resp.status, resp.read().decode("utf-8", "ignore") + + +def main(argv=None): + p = argparse.ArgumentParser(description="Post a message to Mattermost via webhook.") + p.add_argument("text", help="Message text (Markdown ok). Use '-' to read from stdin.") + p.add_argument("--channel", help="Override channel (name without #).") + p.add_argument("--username", default=DEFAULT_USERNAME) + p.add_argument("--icon", default=DEFAULT_ICON, help="Emoji, e.g. :satellite:") + p.add_argument("--url", default=os.environ.get("MATTERMOST_WEBHOOK_URL"), + help="Webhook URL (defaults to $MATTERMOST_WEBHOOK_URL).") + p.add_argument("--dry-run", action="store_true", help="Print payload; do not send.") + args = p.parse_args(argv) + + text = sys.stdin.read().rstrip("\n") if args.text == "-" else args.text + if not text.strip(): + print("error: empty message", file=sys.stderr) + return 2 + + payload = build_payload(text, args.channel, args.username, args.icon) + + if args.dry_run: + print(json.dumps(payload, indent=2, ensure_ascii=False)) + return 0 + + if not args.url: + print("error: no webhook URL. Set MATTERMOST_WEBHOOK_URL or pass --url.", + file=sys.stderr) + return 2 + + try: + status, body = send(args.url, payload) + if status == 200: + print("ok") + return 0 + print(f"send failed: HTTP {status}: {body}", file=sys.stderr) + return 1 + except urllib.error.URLError as e: + print(f"send failed: {e}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-Source-Code/rtk-config/4G_NTRIP_HelgenSRV-NMEA.txt b/04-Source-Code/rtk-config/4G_NTRIP_HelgenSRV-NMEA.txt new file mode 100644 index 0000000..3ca8a0c --- /dev/null +++ b/04-Source-Code/rtk-config/4G_NTRIP_HelgenSRV-NMEA.txt @@ -0,0 +1,41 @@ +$$$AS+I=1111 +$$$AS+A=internet.itelcel.com +$$$AS+Q=webgprs +$$$AS+q=webgprs2002 +$$$AS+O=0 +$$$AS+L=1 +$$$AS+D=1 +$$$AS+f=0 +$$$AS+B=115200 +$$$AS+N=1 +$$$AS+S=rtk2go.com +$$$AS+P=2101 +$$$AS+U=rtkh0021@helgen.tech:none +$$$AS+M=HelgenTestStation +$$$AS+n=0 +$$$AS+s= +$$$AS+p= +$$$AS+u= +$$$AS+m=0 +$$$AS+i= +$$$AS+a=0 +$$$AS+Z=0 +$$$AS+z=pp.services.u-blox.com +$$$AS+Y= +$$$AS+y=0 +$$$AS+X=0 +$$$AS+x=0 +$$$AS+W=0 +$$$AS+w= +$$$AS+V= +$$$AS+v= +$$$AS+t= +$$$AS+T=1 +$$$AS+r=199.85.208.94 +$$$AS+h=5010 +$$$AS+k=2 +$$$AS+K=H0021 +$$$AS+j=300 +$$$AS+C=1 +$$$AS+c=0 +$$$AS+F=1 \ No newline at end of file diff --git a/05-Analytics-Reports/README.md b/05-Analytics-Reports/README.md new file mode 100644 index 0000000..947ade9 --- /dev/null +++ b/05-Analytics-Reports/README.md @@ -0,0 +1,23 @@ +--- +tags: [project-drogheda, analytics, placeholder] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Draft +--- + +# Analytics & Reports (placeholder) + +> Reporting reads **curated stores / SQL views — never raw or transactional data** +> in the hot path (Helgen ethos #4). This folder will hold the SQL views, dashboard +> definitions, and report templates once the backend stabilizes. + +Planned: + +- **Fleet availability** views (per unit / per BU / per terminal) — feeds the + online/offline picture (see `htracker` web_monitor + notifier). +- **VERONICA / PowerBI** consumer mappings. +- Uptime + km-traveled + fix-quality reporting per unit. + +Source backend: [[02-Architecture-Software/backend-review|backend-review]] · +`04-Source-Code/htracker`. diff --git a/06-Testing-Validation/2026-06-20_Drogheda_Bench-Log-Analysis_v1.0.md b/06-Testing-Validation/2026-06-20_Drogheda_Bench-Log-Analysis_v1.0.md new file mode 100644 index 0000000..19acca2 --- /dev/null +++ b/06-Testing-Validation/2026-06-20_Drogheda_Bench-Log-Analysis_v1.0.md @@ -0,0 +1,233 @@ +--- +title: "Drogheda HBOX — Bench Log + Config Analysis (20 Jun 2026)" +created: 2026-06-20 +revised: 2026-06-20 (rev 2 — corrected swap labels + config files) +owner: Leo Rodriguez (Helgen) +status: draft-for-review +type: test-evidence +scope: 7 swap-matrix bench captures + NTRIP-caster & u-blox config files, v1 firmware +source_logs: "Drive: 06-Testing-Validation/Logs Prueba 20JUN26 (*_GPS.ubx, *_NTRIP.log, 4G_NTRIP_HelgenSRV-NMEA.txt, configuration_GNSS.txt)" +tooling: "06-Testing-Validation/log-analysis/analyze_logs.py + dashboard.html" +tags: [drogheda, hbox, rtk, ntrip, gnss, bench-test, rca, thermal, config] +--- + +# Drogheda HBOX — Bench Log + Config Analysis + +**One-line verdict (rev 3):** Of 7 bench captures, 4 pass the comms chain and +**only 1 is a confirmed fault** — `RTK21NTRIP21` (master 21: modem won't register, +511 AT errors). The two "no-forward" captures are **indeterminate**, not failed: the +NTRIP-master console can't prove forwarding either way (the healthy RTK14 baseline +logs the same sparse console). The confirmed fault reproduces cold → not heat. The +u-blox config is correct (GGA on UART2 @1 Hz); corrections come from the **free public +rtk2go caster**, and no unit reached RTK fix (SPS only — bench sky + corrections rarely +applied). + +> **Rev-3 correction (post-baseline, 2026-06-20).** A healthy RTK14 capture +> (`RTK14NTRIP14_440`) — **proven forwarding 1 Hz to the server by tcpdump and live in +> Wialon** — logs the *same* sparse NTRIP-master console (only `NET`; no `State main` / +> `UDP Bytes sent`) as the captures I had called "no-forward." **Absence of those console +> lines does not mean a unit failed.** So the earlier **"master-14 ingest fault" is +> withdrawn** (master 14 demonstrably forwards — it *is* the baseline). Only +> `RTK21NTRIP21` is a confirmed fault; `RTK14NTRIP14` and `RTK21NTRIP14` are +> **indeterminate**. §§3–4 below predate this and are superseded on the no-forward point. +> The structural fix: make the **Blackpill log fix + packets + state** so health is +> unambiguous — see `04-Source-Code/firmware/docs/LOGGING-SPEC.md`. + +> **Baseline (RTK14NTRIP14_440), 3 views:** receiver `.ubx` = 341 epochs, 100% valid, +> **12 sats, HDOP 0.75, SPS** (C/N0 ~20); server tcpdump = **984 pkts, steady 1 Hz, max +> gap 0.86 s, zero dropouts** to `…:5010`; master console = registered only. Healthy +> end-to-end **but SPS, not RTK** (corrections not applied). Use as the healthy-GNSS +> baseline; raise the bar to a fix≥4 capture once corrections work. + +> **Rev-2 note:** two captures were re-labelled by their swap pair — +> `RTK04NTRIP24 → RTK24NTRIP04` and `RTK14NTRIP21 → RTK21NTRIP14` (byte-identical, +> md5-verified). This **moved the no-forward fault from "board 14" to "master 14"** +> (the element common to both no-forward captures). Earlier drafts said board 14. + +--- + +## 1. Executive summary + +Two log streams were captured per unit on the Querétaro bench (20 Jun 2026): +`*_GPS.ubx` (the ZED-F9P over USB, via u-center) and `*_NTRIP.log` (the 4G NTRIP +Master console). Filenames `RTKNTRIP` = RTK board `bb` + NTRIP master `mm`, +forming a **swap matrix** that isolates board vs master faults. Two config files +were also analysed: the 4G master config and the F9P config dump. + +**What the bench can / cannot show:** it validates the **integration/comms chain** +(boot → GNSS position → cell registration → UDP forwarding). It does **not** validate +RTK accuracy (needs open sky + live corrections) and **cannot** measure heat (v1 has +no temperature channel; the bench runs cold). + +| Outcome | Captures | Why | +|---|---|---| +| **Healthy (bench)** | RTK17NTRIP17, RTK24NTRIP04, RTK24NTRIP24, RTK25NTRIP25 | boot → GNSS alive → TELCEL registered → UDP forwarding | +| **Failed — no forwarding** | RTK14NTRIP14, RTK21NTRIP14 | GNSS valid on USB, modem registered, but **0 UDP** and no state machine — **both use master 14** | +| **Failed — modem** | RTK21NTRIP21 | modem **never registers** (`0xFFFFF`, **511× AT-parser errors**), stuck in init | + +**Isolated faults:** + +1. **NTRIP master 14 — GGA ingest.** With *two different* GPS boards (14 and 21), + master 14 forwarded **nothing** while the F9P produced valid NMEA on USB. The + common element is the master → **master 14's serial input / NMEA ingest**, not the + board. (See config note: GGA *is* enabled on UART2, so it is not a message-rate + error.) +2. **NTRIP master 21 — LTE modem.** Will not register on Telcel; continuous + AT-parser errors. The GPS feeding it (board 21) is fine. + +Neither is the GNSS receiver; neither needs heat. + +--- + +## 2. The heat question (headline for the deck) + +**These logs do not prove the field failures are thermal — and that is useful.** +They are room-temperature bench captures with no thermal instrumentation, yet they +reproduce hard faults **cold** (master-14 ingest, master-21 modem). A fault you can +reproduce cold is **not** caused by heat → the fleet problem is **not a single +thermal cause**, matching the RCA's "don't collapse everything into heat." + +This neither confirms nor refutes the **field** thermal hypothesis (18650 / boost / modem +in a sealed cabinet in Veracruz sun, RCA H1/H7) — that needs a **thermocouple soak** +of a representative cabinet and/or **v2 MCU-temp logging**. + +--- + +## 3. Per-device findings + +Bench context is common: indoor/obstructed sky → **C/N0 ≈ 17–21 dB-Hz**, high HDOP, +**zero RTK fixes** anywhere. Read the GNSS columns as "is the receiver alive and +emitting valid NMEA," not as a positioning grade. + +| Capture | Board / Master | Status | GNSS (USB) | Cell | UDP out | dBm | AT err | Headline | +|---|---|---|---|---|---|---|---|---| +| **RTK24NTRIP04** | 24 / 04 | 🟢 Healthy | alive, SPS, 98% valid | TELCEL | **1555 dg** | −51 | 0 | full chain OK (master 04 good) | +| **RTK24NTRIP24** | 24 / 24 | 🟢 Healthy | alive, SPS, 85% valid | TELCEL | **1831 dg** | −51 | 0 | full chain OK | +| **RTK25NTRIP25** | 25 / 25 | 🟢 Healthy | alive, SPS, 100% valid | TELCEL | **2413 dg** | −51 | 0 | best forwarder | +| **RTK17NTRIP17** | 17 / 17 | 🟢 Healthy | alive, 12% valid (poor sky this run) | TELCEL | **2267 dg** | −63 | 0 | comms healthiest; GNSS had no sky | +| **RTK14NTRIP14** | 14 / 14 | 🔴 Failed | **alive, SPS, 100% valid** | TELCEL | **0** | −75 | 0 | registered but forwards nothing | +| **RTK21NTRIP14** | 21 / 14 | 🔴 Failed | **alive, SPS, 100% valid, 10 sats** | TELCEL | **0** | −83 | 0 | same master-14 symptom, other board | +| **RTK21NTRIP21** | 21 / 21 | 🔴 Failed | alive, SPS, 97% valid | **NOT reg** | **0** | −113 | **511** | modem never attaches | + +--- + +## 4. Swap-matrix analysis (isolation) + +**By NTRIP master:** + +| Master | Captures | Result | +|---|---|---| +| 04, 17, 24, 25 | one each | **healthy** — forwards position | +| **14** | RTK14NTRIP14 (board 14) **and** RTK21NTRIP14 (board 21) | **both fail to forward** despite valid USB NMEA | +| **21** | RTK21NTRIP21 (board 21) | modem **not registered**, AT errors | + +→ Master 14 fails with two different boards → **fault is master 14's NMEA ingest** +(serial input), confirmed common factor. Master 21 is a modem-registration fault. +Masters 04/17/24/25 are good. + +**By RTK board:** + +| Board | Captures | Read | +|---|---|---| +| 24 | NTRIP04 (✓), NTRIP24 (✓) | **healthy with two masters** → board 24 GNSS + UART2 output good | +| 17, 25 | one each (✓) | healthy | +| 21 | NTRIP14 (master-14 fault), NTRIP21 (master-21 fault) | GNSS alive on USB; its failures belong to the masters it was paired with | +| 14 | NTRIP14 only (master-14 fault) | GNSS alive on USB; **board-14 UART2 output unproven** — only ever paired with the bad master 14 | + +**Open item:** test board 14 (and board 21) with a *known-good* master to clear/convict +their UART2 output; test master 14 with a known-good board to confirm the ingest fault. + +--- + +## 5. Configuration findings (from the two config files) + +### 5.1 4G NTRIP Master (`4G_NTRIP_HelgenSRV-NMEA.txt`) + +| Item | Value | Note | +|---|---|---| +| NTRIP caster | **rtk2go.com : 2101**, mountpoint **HelgenTestStation** | **free public community caster — best-effort, no SLA** | +| APN | internet.itelcel.com (Telcel) | correct | +| UART baud (to F9P) | 115200 | matches F9P UART2 baud | +| NTRIP restart timer | **300 s** | matches "NTRIP DELAY n/300" in the logs | +| Telemetry out | UDP → Helgen server :5010, device **H0014** | position forwarding path | +| PointPerfect | configured but disabled | not in use | + +> **Flag:** corrections from a **free public caster (rtk2go)** is a reliability risk +> for a production fleet and a plausible reason the correction client rarely connected +> and no unit reached RTK. Move production to a **dedicated/own caster (HelgenSRV)**. + +### 5.2 ZED-F9P (`configuration_GNSS.txt`, UBX CFG-VALGET dump) + +| Item | Value | Note | +|---|---|---| +| Model / FW | ZED-F9P, HPG 1.32 | rover-grade | +| Mode | **Rover** (TMODE=0) | correct (not a base) | +| Update rate | **1 Hz** (1000 ms) | correct | +| **GGA on UART2** | **ENABLED (1/epoch)** | the feed the master needs — **correctly configured** | +| RMC on UART2 | ENABLED (1/epoch) | not required by NTRIP, harmless | +| UART2 | enabled · NMEA-out + RTCM3-in · 115200 | textbook 4G-NTRIP rover wiring | +| USB | full NMEA (GGA/GLL/GSA/GSV/RMC/VTG) | matches the `.ubx` captures | +| **Dump layer** | **RAM only** | **flash persistence NOT verified** | + +> **Two implications:** +> 1. **The message config is correct** — GGA *is* on UART2 @1 Hz. So master 14's dead +> feed is **not** a missing-GGA setting → it's the master's ingest (or a per-unit +> deviation), reinforcing §4. +> 2. **The dump is RAM-layer only.** A RAM-only config is **lost on every power cycle** +> (the classic "forgot Save Config to flash" trap). Confirm the **FLASH layer** on +> each unit — a unit that boots without GGA-on-UART2 would feed the master nothing. + +--- + +## 6. Recommendations / next steps + +1. **Master 14 — bench the ingest.** Feed master 14 a known-good GGA stream (or pair + with a healthy board) and watch for `State main` + `UDP Bytes sent`. If still zero, + the master's serial-in / firmware is the fault → swap/repair the master. +2. **Master 21 — modem triage.** Re-test registration with a known-good SIM + antenna; + 511 AT-parser errors + `0xFFFFF` point at the LTE modem / its AT link / power. Swap + the modem to confirm. +3. **Persist & verify the F9P config to FLASH** on every unit (u-center → Save Config), + then dump the **flash layer** to prove GGA-on-UART2 survives a power cycle. Check the + simpleRTK2B Pro **XBEE↔UART2 switch** too (UART2 is off by default). +4. **Move corrections off rtk2go** to a dedicated caster (HelgenSRV) with uptime, then + **re-run outdoors** with open sky to actually grade RTK (quality 4/5). +5. **Close the heat question separately** — thermocouple soak of a sealed cabinet in + full sun and/or v2 MCU-temp logging. +6. **Retranslator log (separate effort)** — pair this unit-side analysis with the + backend (HOL-stall / anti-jump) evidence for the full RCA picture. + +--- + +## 7. Method & reproducibility + +- **Tool:** `log-analysis/analyze_logs.py` (Python stdlib). Parses NMEA (GGA/RMC/GSV) + + UBX from `.ubx`, and the GPTXT diagnostic stream (`State main`, `NET`, `UDP Bytes + sent`, `NTRIP DELAY`, `SM_REG`, AT errors) from `.log`. Emits per-device metrics, + a swap-matrix with **common-factor isolation**, and per-device time-series. +- **Dashboard:** `log-analysis/dashboard.html` — dark (GitHub Dark-Dimmed), + self-contained: config summary, signal-flow diagram (marks the break + names the + common master), zoomable time-series, status board, swap matrix. +- **Config decode:** 4G master `$$$AS+` params; F9P UBX **CFG-VALGET** keys + (CFG-MSGOUT-NMEA_ID_GGA_UART2, CFG-UART2*, CFG-RATE*, CFG-TMODE-MODE). +- **Re-run:** `python3 analyze_logs.py --in raw_cur --out out && python3 build_dashboard.py` +- **Validation:** classification spot-checked vs raw logs; the two relabels were + md5-verified byte-identical to the originals. + +**Caveats:** ~10-min captures; indoor bench sky; v1 (no temperature); F9P config dump +is RAM-layer only; `ntr/tcp/pp` enum values inferred from behavior. None affect the +pass/fail conclusions, which rest on presence/absence of UDP forwarding, cellular +registration, and the swap-matrix common factor. + +--- + +## 8. Glossary + +- **GGA fix quality:** 0 no-fix · 1 SPS · 2 DGPS · **4 RTK-fixed** · **5 RTK-float**. +- **C/N0 (dB-Hz):** satellite carrier-to-noise; ~35–50 open sky, ~20 indoors. +- **NTRIP master state `main,ntr,tcp,pp,ntr_srv,udp`:** main=4 operational / 1 init; + udp=1 forwarding position out; ntr = correction-client state (0 idle … 2 active). +- **`NET,,,`:** TELCEL = registered; **`0xFFFFF` = not registered**. +- **NTRIP DELAY n/300:** seconds since last correction vs the 300 s restart watchdog. +- **GGA→UART2:** whether the F9P's GGA is reaching the 4G master over UART2 (XBee + socket). Inferred from the master running its loop + forwarding UDP. diff --git a/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings-Summary.md b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings-Summary.md new file mode 100644 index 0000000..41565e8 --- /dev/null +++ b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings-Summary.md @@ -0,0 +1,85 @@ +--- +title: "Project Drogheda — GNSS Findings Summary (Beamer source)" +created: 2026-06-20 +owner: Leo Rodriguez (Helgen) +type: findings-summary +purpose: source material for a later LaTeX Beamer deck +scope: 20 Jun 2026 bench captures + RTK14_440 healthy baseline + config files +tags: [drogheda, hbox, gnss, rtk, ntrip, findings, presentation] +--- + +# GNSS Findings Summary — Project Drogheda (20 Jun 2026) + +> Presentation-ready. Each `##` is a candidate slide; bullets are speaker points. +> Full detail: `2026-06-20_Drogheda_Bench-Log-Analysis_v1.0.md`. Tooling: `log-analysis/`. + +## 1 · What we did + +- Analyzed three independent log views per unit from the 20 Jun bench: u-blox receiver + (`.ubx`), 4G-NTRIP-Master console (`.log`), and a server-side retranslator capture + (tcpdump). Decoded the NTRIP-caster and ZED-F9P config files. +- Built local tooling (in-browser **Log Explorer**, batch analyzer, dark dashboard) and + established a **healthy GNSS baseline**. + +## 2 · Headline: the GNSS receivers are fine — RTK is not being achieved + +- **Every board produces valid NMEA** — good satellite counts (6–12) and HDOP (≈0.75–1.5). + The **ZED-F9P is not the failure.** +- **No unit reached RTK fix.** Every epoch is **SPS (quality 1)**; zero quality 4/5. + Centimetre positioning was never delivered. +- **Why:** corrections are not being applied — a combination of (a) corrections sourced + from a **free public caster (rtk2go.com, no SLA)**, (b) the NTRIP correction client + rarely reaching "connected," and (c) bench/indoor sky. The F9P config is actually + **correct** (GGA on UART2 @1 Hz) — but the dump is **RAM-only; flash persistence is + unverified** (a config lost on power-cycle would silently break the feed). + +## 3 · The healthy baseline (RTK14_440) — what "good" looks like today + +- Proven **end-to-end**: **984 packets, steady 1 Hz, zero dropouts**, valid Querétaro + position delivered to the server — live in Wialon and the monitor. +- **12 sats, HDOP 0.75** — excellent geometry. **But SPS, not RTK.** +- So **"healthy delivery" ≠ "RTK."** The baseline is *healthy SPS + reliable delivery*; + raise the bar to a fix-≥4 capture once corrections work. + +## 4 · The diagnostic trap (and a retraction) + +- The **4G-NTRIP-Master console is an unreliable health signal.** The provably-healthy + baseline logs the **same sparse console** (only cellular `NET` lines, no forwarding + lines) as units that *looked* failed. +- **Retraction:** the earlier "master-14 ingest fault" is **withdrawn** — master 14 is the + baseline and demonstrably forwards. **Only master 21 is a confirmed fault** (modem never + registers: `0xFFFFF` + 511 AT-parser errors). +- **Lesson:** per-unit health must come from a component that *parses the fix itself* — the + Blackpill — logged in a trustworthy form. + +## 5 · Heat is not implicated by these logs + +- Room-temperature bench captures, no thermal channel (v1). The faults reproduce **cold**, + so heat is **not** the cause of what we see here. The **field** thermal hypothesis stays + open — needs a thermocouple cabinet soak and/or v2 MCU-temp logging. + +## 6 · Changes made this activity + +- **Blackpill v2.1 logging spec** — replace the self-overwriting ANSI dashboard with a + **structured, timestamped, parseable** stream (state, fix, packets, battery, MCU temp, + recovery, **reset cause**). Makes per-unit health unambiguous from the unit's own log. +- **Log tooling** — `hbox-log-tools` repo on `git.helgen.dev`: in-browser Log Explorer + (parse + auto-plot + compare to baseline + PNG/SVG export), batch analyzer, dark dashboard. +- **Corrected analysis + findings doc** (the master-14 retraction above). + +## 7 · Next steps + +1. **Persist the F9P config to FLASH and verify** (RAM-only is a power-cycle risk); check + the simpleRTK2B Pro `XBEE↔UART2` switch. +2. **Move corrections off rtk2go** to a dedicated caster (HelgenSRV) with uptime. +3. **Open-sky RTK retest** with a verified caster → confirm fix ≥4/5. +4. **Connect the Blackpill** with v2.1 logging → capture a true per-unit health log + (drops into the Log Explorer as a fifth view). +5. **Modem triage master 21** (known-good SIM/antenna; swap to confirm). +6. **Thermal soak test** to settle the field heat hypothesis. + +## One-line takeaway + +> The radios work; we are delivering a **standalone-GPS** position reliably, **not RTK**. +> The blockers are the **correction source/persistence** and **observability** — both now +> addressed by config/caster changes and the new Blackpill logging + tooling. diff --git a/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.pdf b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.pdf new file mode 100644 index 0000000..1b961c3 Binary files /dev/null and b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.pdf differ diff --git a/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.tex b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.tex new file mode 100644 index 0000000..3e5d922 --- /dev/null +++ b/06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.tex @@ -0,0 +1,263 @@ +% Project Drogheda — GNSS / Log Analysis findings (Helgen Beamer / Metropolis) +% Built from the helgen-beamer template. Compile: latexmk -xelatex .tex +\documentclass[10pt,aspectratio=169]{beamer} + +\usetheme[progressbar=frametitle, numbering=fraction, block=fill]{metropolis} + +% ---------- Helgen official palette (Guia de Identidad v1.5, 2026) ---------- +\definecolor{HelgenIndigo}{HTML}{2C3C5B} +\definecolor{HelgenCyan}{HTML}{51F3DA} +\definecolor{HelgenTeal}{HTML}{17A08C} +\definecolor{HelgenMist}{HTML}{EFF1F5} + +\setbeamercolor{normal text}{fg=HelgenIndigo, bg=white} +\setbeamercolor{alerted text}{fg=HelgenTeal} +\setbeamercolor{frametitle}{bg=HelgenIndigo, fg=white} +\setbeamercolor{progress bar}{fg=HelgenCyan, bg=HelgenMist} +\setbeamercolor{title separator}{fg=HelgenCyan} +\setbeamercolor{block title}{bg=HelgenMist, fg=HelgenIndigo} + +% ---------- Fonts: XeLaTeX + bundled brand fonts, clean fallback otherwise ---------- +\usepackage{iftex} +\ifXeTeX + \usepackage{fontspec} + \IfFontExistsTF{League Spartan}{% + \newfontfamily\helgentitle{League Spartan}[BoldFont={* Bold}] + \setbeamerfont{title}{family=\helgentitle, series=\bfseries} + \setbeamerfont{frametitle}{family=\helgentitle, series=\bfseries} + \setbeamerfont{section title}{family=\helgentitle, series=\bfseries} + \setbeamerfont{standout}{family=\helgentitle, series=\bfseries} + }{} + \IfFontExistsTF{MADE Evolve Sans}{\setsansfont{MADE Evolve Sans}}{} +\else + \usepackage[T1]{fontenc} +\fi +\usepackage{booktabs} +\usepackage{tikz} +\usetikzlibrary{arrows.meta, positioning, shapes.misc, fit} +\usepackage{listings} +\lstset{ + basicstyle=\ttfamily\scriptsize, + keywordstyle=\color{HelgenIndigo}\bfseries, + commentstyle=\color{gray}, + stringstyle=\color{HelgenTeal}, + showstringspaces=false, breaklines=true, frame=single, + framerule=0.2pt, rulecolor=\color{HelgenMist} +} + +% Reusable block / arrow styles (Helgen palette) +\tikzset{ + blk/.style={rounded rectangle, draw=HelgenIndigo, fill=HelgenMist, font=\scriptsize, + minimum height=8mm, inner xsep=2.4mm, align=center}, + acc/.style={rounded rectangle, draw=HelgenTeal, fill=white, font=\scriptsize, + minimum height=8mm, inner xsep=2.4mm, align=center}, + arr/.style={-{Stealth}, thick, HelgenTeal}, + arrx/.style={-{Stealth}, thick, red!70!black, dashed}, +} + +% ---------- Footer ---------- +\setbeamertemplate{frame footer}{% + \scriptsize\color{gray}\insertshortauthor\ · \insertshorttitle\ · Confidencial — Helgen Servicios Especializados} + +% ---------- Metadata ---------- +\title[Drogheda · GNSS/Logs]{Project Drogheda — GNSS \& Log Analysis} +\subtitle{HBOX RTK fleet: bench triage, healthy baseline, and observability} +\author[Helgen]{Helgen Technologies — CTO/CSO} +\date{20 June 2026} +\titlegraphic{} + +\begin{document} + +\maketitle + +\begin{frame}{Agenda} + \tableofcontents +\end{frame} + +% ===================================================================== +\section{Context} + +\begin{frame}{Where we are} + \begin{columns}[T] + \column{0.58\textwidth} + \begin{itemize} + \item HBOX RTK trackers on Hutchison Ports heavy equipment (ICAVE, Veracruz) — worked, then failed. + \item Three independent failure fronts, fixed in parallel: + \begin{itemize}\footnotesize + \item \textbf{Backend} (retranslator) — could drop the fleet / black-hole a unit + \item \textbf{Thermal} — PLA box in a sealed cabinet (heat trap) + \item \textbf{Prototype HW + observability} — hand-wired; logs untrustworthy + \end{itemize} + \item Field units pulled $\rightarrow$ bench triage in Querétaro (firmware v1, no thermal log). + \end{itemize} + \column{0.4\textwidth} + \begin{alertblock}{Invariant} + Corrections ride the \textbf{4G modem}, not the GNSS — recover the link by power-cycling the stack. + \end{alertblock} + \end{columns} +\end{frame} + +\begin{frame}{The signal chain} + \centering + \begin{tikzpicture}[node distance=7mm and 13mm] + \node[blk] (f9p) {ZED-F9P\\(GNSS)}; + \node[blk, right=of f9p] (mas) {4G NTRIP\\Master}; + \node[blk, right=of mas] (ret) {retranslator\\(htracker)}; + \node[blk, right=of ret] (wial) {Wialon}; + \node[acc, above=10mm of mas] (cas) {NTRIP caster\\(rtk2go)}; + \draw[arr] (f9p) -- node[above, font=\tiny]{UART2: GGA} (mas); + \draw[arr] (mas) -- node[above, font=\tiny]{UDP} (ret); + \draw[arr] (ret) -- (wial); + \draw[arr] (cas) -- node[right, font=\tiny]{RTCM in} (mas); + \end{tikzpicture} + \vfill + \footnotesize Corrections come \emph{down} from the caster over 4G; position goes \emph{out} by UDP to the retranslator. The GNSS receiver never talks to the network directly. +\end{frame} + +% ===================================================================== +\section{Backend (retranslator)} + +\begin{frame}{Backend — hardened \& deployed to prod} + \begin{itemize} + \item \textbf{Head-of-line blocking removed:} a \texttt{WialonForwarder} thread pool does all Wialon TCP I/O \emph{off} the UDP receive loop — one slow upstream no longer drops the fleet. + \item \textbf{Anti-jump now time-aware + self-healing:} re-anchors after rejects, clears on disconnect — a unit is never permanently black-holed. + \item \textbf{Identity keyed by \texttt{cell\_id}} (CGNAT-safe), not \texttt{(ip,port)}. + \item Monitor gained a \textbf{backend-victim} KPI; logs ship to Grafana Loki. \alert{Live in production.} + \end{itemize} + \begin{alertblock}{Free triage} + ONLINE in htracker + OFFLINE in Wialon = \textbf{backend victim}, not a dead radio. + \end{alertblock} +\end{frame} + +% ===================================================================== +\section{GNSS log findings} + +\begin{frame}{What we analyzed} + \begin{itemize} + \item \textbf{7 swap-matrix bench captures} (RTK board $\times$ NTRIP master) + one healthy baseline. + \item \textbf{Three views per unit:} u-blox \texttt{.ubx} (receiver), 4G-NTRIP console \texttt{.log}, server retranslator \texttt{tcpdump}. + \item Decoded the NTRIP-caster and ZED-F9P configuration files. + \item Built local tooling so this is repeatable (explorer + analyzer + dashboard). + \end{itemize} +\end{frame} + +\begin{frame}{Headline: receivers are fine — RTK is not being achieved} + \begin{itemize} + \item Every board emits \textbf{valid NMEA} (6–12 sats, good HDOP). The ZED-F9P is \emph{not} the failure. + \item But \textbf{every epoch is SPS (quality 1)} — zero RTK (4/5). Centimetre position never delivered. + \item \textbf{Why:} free public \textbf{rtk2go} caster (no SLA) + F9P config saved to \textbf{RAM only} + indoor/bench sky. + \item The F9P message config itself is \textbf{correct} — GGA on UART2 @ 1\,Hz. + \end{itemize} +\end{frame} + +\begin{frame}{Every unit is SPS — none reached RTK} + \centering + \includegraphics[width=0.88\textwidth]{fig/fig_fix_compare.pdf}\\[2mm] + \footnotesize Max GGA fix quality per capture (7 bench + baseline) — all flat at 1 (SPS); the RTK line (4) is never reached. +\end{frame} + +\begin{frame}{Why: the corrections never settle} + \centering + \includegraphics[width=0.82\textwidth]{fig/fig_ntrip_delay.pdf}\\[2mm] + \footnotesize Even on a healthy unit the NTRIP correction delay sawtooths up to the 300\,s restart watchdog — the free rtk2go feed is intermittent. +\end{frame} + +\begin{frame}{Healthy baseline — \texttt{RTK14\_440} (three views agree)} + \begin{columns}[T] + \column{0.56\textwidth} + \begin{itemize}\small + \item \textbf{Receiver:} 341 epochs, 100\% valid, 12 sats, HDOP 0.75 — SPS. + \item \textbf{Server:} 984 packets, steady 1\,Hz, \textbf{zero dropouts}, live in Wialon. + \item \textbf{Console:} ``registered'' only — no forwarding lines. + \end{itemize} + \column{0.4\textwidth} + \begin{alertblock}{The trap} + Healthy \textbf{delivery} $\neq$ \textbf{RTK}. Baseline = healthy SPS + reliable delivery. + \end{alertblock} + \end{columns} +\end{frame} + +\begin{frame}{Baseline receiver — healthy geometry, stuck at SPS} + \centering + \includegraphics[width=0.93\textwidth]{fig/fig_baseline_gnss.pdf}\\[1mm] + \footnotesize \texttt{RTK14\_440} u-blox capture: 12 sats, HDOP 0.75, C/N0 $\sim$20 dB-Hz — but the fix never leaves 1 (SPS). +\end{frame} + +\begin{frame}{Baseline delivery — healthy end-to-end} + \centering + \includegraphics[width=0.93\textwidth]{fig/fig_delivery.pdf}\\[1mm] + \footnotesize Server-side tcpdump: 984 datagrams, steady $\sim$1\,Hz, max gap 0.86\,s — the unit is genuinely reporting (live in Wialon). +\end{frame} + +\begin{frame}{The console blind spot — and a retraction} + \begin{itemize} + \item The \emph{provably-healthy} unit logs the \textbf{same sparse console} as units that looked ``failed''. + \item $\Rightarrow$ the 4G-NTRIP console \textbf{cannot prove forwarding}. Absence of lines $\neq$ failure. + \item \alert{Retraction:} the earlier ``master-14 ingest fault'' is \textbf{withdrawn}. + \item Only \textbf{master 21} is a confirmed fault: modem won't register (\texttt{0xFFFFF} + 511 AT-parser errors). + \end{itemize} +\end{frame} + +\begin{frame}{Per-master verdict} + \centering + \begin{tabular}{ll} + \toprule + \textbf{NTRIP master} & \textbf{Result} \\ + \midrule + \#04 · \#17 · \#24 · \#25 & Healthy — registered + forwarding \\ + \#14 & \alert{Indeterminate} — console can't confirm; baseline proves it forwards \\ + \#21 & \alert{Failed} — modem won't register (\texttt{0xFFFFF} + 511 AT errors) \\ + \bottomrule + \end{tabular} + \vfill + \footnotesize Confirm a unit only from a \texttt{.ubx} (fix), the Blackpill, or a server delivery capture — never the console alone. +\end{frame} + +\begin{frame}{Heat — not implicated (by these logs)} + \begin{itemize} + \item Room-temperature bench captures, \textbf{no thermal channel} (firmware v1). + \item The hard faults reproduce \textbf{cold} $\Rightarrow$ heat is \emph{not} the cause of what we see here. + \item The \textbf{field} thermal hypothesis stays open — needs a thermocouple cabinet soak and/or v2 MCU-temp logging. + \end{itemize} +\end{frame} + +% ===================================================================== +\section{Changes \& tooling} + +\begin{frame}{Observability — the real fix} + \begin{itemize} + \item The Blackpill watchdog already computes fix, packets, state, battery, MCU temp. + \item \textbf{v2.1 logging spec:} replace the self-overwriting ANSI dashboard with a \textbf{structured, timestamped, parseable} stream (+ decoded reset cause). + \item \textbf{\texttt{hbox-log-tools}} repo: in-browser Log Explorer (parse + plot + compare to baseline, offline), batch analyzer, dashboard, manual. + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{The new per-unit log line (v2.1)} +\begin{lstlisting} +BOOT,32,fw=2.1,reset=POR +COLS,ms,state,link,fix,sat,rx,rate,vbat,v5v,veh,ign,tc,tmax,der,rec,limp,nmea_age +TLM,190000,RUN,OK,4,12,340,1HZ,5.01,1,1,48,49,0,0,0,310 +EVT,490000,RUN,DEGR,RTK lost -> recycle 4G/NTRIP +\end{lstlisting} + \footnotesize Fix + packets + state + reset cause $\Rightarrow$ per-unit health is unambiguous from the unit's own log — no server tcpdump needed. +\end{frame} + +% ===================================================================== +\section{Next steps} + +\begin{frame}{Next steps} + \begin{enumerate} + \item Save the F9P config to \textbf{FLASH} + verify (RAM-only is a power-cycle risk); check the simpleRTK2B Pro \texttt{XBEE$\leftrightarrow$UART2} switch. + \item Move corrections off rtk2go $\rightarrow$ a \textbf{dedicated caster} (HelgenSRV) with uptime. + \item \textbf{Open-sky RTK retest} with a verified caster $\rightarrow$ confirm fix $\geq 4$. + \item Connect the \textbf{Blackpill} with v2.1 logging; modem-triage master 21; thermal soak. + \end{enumerate} +\end{frame} + +\begin{frame}[standout] + The radios work.\\ + We deliver \textbf{SPS} reliably — \textbf{not RTK}.\\[2mm] + {\normalsize The blockers are \textbf{corrections} and \textbf{observability} — both now addressed.} +\end{frame} + +\end{document} diff --git a/06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf b/06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf new file mode 100644 index 0000000..c3e1e69 Binary files /dev/null and b/06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf differ diff --git a/06-Testing-Validation/RCA-2026-06-field-failures.md b/06-Testing-Validation/RCA-2026-06-field-failures.md new file mode 100644 index 0000000..9b5f5f7 --- /dev/null +++ b/06-Testing-Validation/RCA-2026-06-field-failures.md @@ -0,0 +1,200 @@ +# RCA — HBOX RTK Field Failures (Veracruz / ICAVE) + +**Status:** DRAFT — open hypotheses, awaiting field logs +**Date:** 2026-06-19 · **Owner:** Leo Rodriguez (Helgen) +**Scope:** 5 HBOX units at Hutchison Ports ICAVE (RTG crane, forklift, electric +truck, 2 reachstackers). "Working-ish," then a near-simultaneous death; chronic +recurrent disconnects in Wialon. Client escalation 2026-06-17. + +> ⚠️ This RCA is **hypothesis-ranked**, not concluded. The single most useful +> next input is the field log set (battery voltage, MCU temp, fix quality, +> reconnect timestamps) plus the backend/SIM/caster logs. Update §6 when they +> arrive. + +--- + +## 1. The two failures are probably *different* failures + +Do not collapse these into one cause: + +- **(A) Sudden, near-simultaneous death of the fleet.** Multiple independent + units failing at ~the same time points to a **common-mode** stressor, not five + coincidental hardware faults. Candidates: a heat spell, a backend/caster/SIM + outage, or a server/retranslator problem. +- **(B) Chronic "recurrent disconnections" in Wialon over time.** This is the + drip-drip the client has been logging for weeks. Candidates: vibration-induced + intermittents (Dupont/SMA/solder), 4G NTRIP card hangs, and a watchdog that + recovers slowly and noisily. + +Treating (A) as "heat" and stopping there risks missing a cheap backend fix — +and treating (B) as "heat" risks shipping a thermal redesign that doesn't stop +the vibration intermittents. Both need addressing; the permanent fix does. + +--- + +## 2. Differential diagnosis (check cheap/common before exotic) + +| # | Hypothesis | Explains A? | Explains B? | How to confirm (fast) | Prior | +|---|---|:--:|:--:|---|:--:| +| H1 | **Thermal (component-level).** A sealed **ASA** HBOX sits **inside a sealed ABS IP67 cabinet that also holds the Mean Well supply** (internal heat source), in full sun → cooks the **18650 / modem / boost**. The **ASA shell itself is fine** (UV-stable, softens ~95–100 °C). | ✓✓ | ✓ | Inspect pulled units for swollen **18650**, discoloured boost; thermocouple inside a representative cabinet over a sunny shift | **High** | +| H2 | **Backend common-mode — CONFIRMED mechanism.** The production `retranslator.py` sends to Wialon and waits for the TCP ack (and 20 s connect) **inside the single UDP loop** → one slow upstream stalls the loop and **all units drop at once**. Plus caster/SIM-plan exhaustion. | ✓✓ | ✓ | Gateway log + `netstat -su` UDP drops in the death window; did all units drop within seconds (→ this) or one-by-one (→ H2b)? See `02-Architecture-Software/backend-review.md` | **High (and cheapest)** | +| H2b | **Anti-jump filter lock-out — CONFIRMED mechanism.** On a rejected "jump" the filter never updates its reference and the reference survives reconnects → a healthy unit's packets are dropped **forever**; it transmits but never reaches Wialon. | ✗ | ✓✓ | The unit shows **ONLINE in htracker but OFFLINE in Wialon** (monitor marks online on UDP receipt, before the filter). Check this per unit. | **High** | +| H3 | **Vibration intermittents** — Dupont/SMA/terminal back-out, cracked solder | ✗ | ✓✓ | Pulled-unit inspection: wiggle test, continuity under vibration, visual on joints/SMA torque | **High** | +| H4 | **4G NTRIP card hang/failure** (client's #1 point) | ~ | ✓✓ | Pulled-unit bench: does modem re-enumerate after power cycle? thermal-soak test | **High** | +| H5 | **Under-rated fuse** (client's one remaining power suspicion) | ✓ (if blown by inrush/load) | ~ | Check fuse rating vs measured worst-case + inrush current; look for blown/derated fuses | **Medium** | +| H6 | **Watchdog logic** turning brief RTK loss into long outages / reboot loops | ✗ | ✓✓ | Code review (done — §4) + correlate Wialon gaps with ~5/10-min cadence | **High for B** | +| H7 | **LiPo brownout** — hot/aged cell sags under load, watchdog cuts power at 3.4 V | ✓ | ✓ | Battery-voltage log; capacity test on pulled cells | **Medium-High** | +| H8 | **Power-rail/EMI on machinery** — alternator/inverter transients, ground bounce on a crane | ~ | ✓ | Scope the 12–24 V input under operation; check for resets correlated with motion | **Medium** | + +The two **cheapest** checks (H2 backend, H5 fuse) should be done **first** — they +can be confirmed or killed in an hour without pulling a single unit, and H2 in +particular could explain the simultaneous death entirely. + +> **Backend code review is done (`02-Architecture-Software/backend-review.md`) and it materially +> raised the priors on H2/H2b.** Two software defects can take healthy units off +> Wialon: (1) synchronous Wialon send-and-ack in the single receive loop → +> head-of-line stall → *simultaneous* fleet dropout; (2) an anti-jump filter that +> can *permanently* black-hole one unit. The single best triage step is now free: +> **compare Helgen's htracker monitor (online on UDP receipt) against the client's +> Wialon (online on positions received).** Units that are online in one and +> offline in the other are **backend victims, not dead hardware** — check +> ZPM 020 / RS-SAN-001 this way before the teardown. + +--- + +## 3. Leading physical hypothesis: thermal (H1/H7) + +Why heat is the prime suspect for the sudden death — and **the new component data +makes it worse than first assumed**: + +- **It's a nested thermal trap, not one box.** The HBOX is a **3D-printed ASA** + shell sealed **inside** a second sealed enclosure — the **ABS IP67 cabinet** — + which **also contains the Mean Well 24 V supply** (60 W class, several watts of + continuous self-heat). So the HBOX bakes in an enclosure that has **both** solar + gain **and** an internal heat source, with **no airflow** (IP67). See + `03-Architecture-Hardware/cabinet-electrical-bom.md`. +- **The ASA enclosure is adequate — not the liability.** ASA is UV-stable and + **softens at ~95–100 °C**, with margin over the cabinet's interior temperature, so + the shell will **not sag/warp** at these temperatures. (This corrects an earlier + assumption that the inner shell was PLA; both the ABS outer cabinet, Tg ~105 °C, + and the inner **ASA** are fine — the thermal risk is to the **components**.) +- **Environment:** Veracruz in June — coastal, hot, humid, full sun. Sealed + enclosure interiors commonly run **20–35 °C above ambient**; with an internal + supply added, **60–80 °C is realistic**. +- **The weak components sit below the GNSS:** + - **LiPo** — safe **charge only 0–45 °C**; above ~45 °C you get lithium + plating, swelling, capacity loss, and (worst case) thermal runaway. Optimal + is 20–25 °C. A hot LiPo also **sags under load** → the watchdog reads + <3.4 V → cuts power → unit goes dark ("Solid OFF / hibernate"). This is a + clean mechanism for *simultaneous* death during a hot spell. + - **PowerBoost 1000C** — a hobby-grade boost/charger; efficiency and thermal + headroom fall as it heats; possible thermal shutdown. + - **4G NTRIP Master modem** — LTE modems self-heat and thermally throttle/drop; + matches the client's "recurrent 4G NTRIP card failures." +- **The GNSS itself is *not* the weak link:** the **ZED-F9P is rated −40…+85 °C**. + So "the RTK died of heat" most likely means the **battery, charger, or modem** + around it died — not the receiver. + +**Implication for the fix:** you cannot firmware your way out of heat. The +permanent fix is thermal-mechanical: the **ASA inner box stays** (it's adequate; +optionally move to metal for extra heatsinking, or mount the PCB straight to the +cabinet plate), **remove/relocate the battery**, +replace the boost with an efficient 24 V→5 V buck, and **conduct heat out to a +metal plate/cabinet** — **not** a bare internal fan, which does nothing in a sealed box +(see hardware brief §2b). Firmware's job is to **measure** temperature (v2 logs MCU +max temp) so the pulled units prove or kill this hypothesis. + +--- + +## 4. Firmware's contribution to the *chronic* disconnects (H6) + +Full bug list in `03-Architecture-Hardware/firmware-review.md`. The two that most plausibly create +Wialon "desconexiones recurrentes": + +1. **The 5-minute soft-reset is aimed at the wrong subsystem.** v1 reacts to lost + corrections by sending a **GNSS hot-reset** (`UBX-CFG-RST`). That restarts the + ZED-F9P only — it **cannot** revive a hung 4G/NTRIP modem, which is the + client's #1 reported failure. Five minutes are wasted on a no-op. +2. **The 10-minute hard reset has no backoff → reboot-loop flapping.** If the 4G + card is genuinely stuck (or the unit is parked where it can't see sky), v1 + power-cycles the whole stack **every ~10 minutes, forever**. Each cycle is a + multi-minute Wialon disconnect. A bank of units doing this looks exactly like + "recurrent disconnections." + +Both are fixed in **v2**: recover by power-cycling the **4G stack** (the thing +that actually restarts the modem), detect NMEA silence in ~60 s, apply +**exponential backoff**, and after repeated failures drop into **LIMP mode that +keeps reporting standalone GPS** instead of vanishing. A unit with a dead modem +now still shows on Wialon (degraded) rather than disconnecting. + +> Note: v1 does **not** mistreat RTK **float** (quality 5) as a failure — it +> correctly treats fixed(4) and float(5) as healthy. The chronic-disconnect +> driver is the recovery *strategy*, not the fix-quality threshold. + +--- + +## 5. The client's hardware indictment is correct (H3/H4) + +ICAVE's review is, frankly, a fair description of a lab prototype shipped to a +brutal industrial environment. Each point maps to a real failure mechanism: + +| Client point | Mechanism | Permanent fix | +|---|---|---| +| Dupont connectors | vibration backs them out → intermittent open | locking connectors (JST-XH/Molex), no jumpers | +| Solder-joint stress | flex + heat-cycling cracks joints | PCB with strain relief; no dead-bug wiring | +| No vibration design | resonance loosens everything | PCB + standoffs + grommets + conformal coat | +| Loose terminals/antennas | SMA + screw terminals work loose | torqued bulkhead SMA, retained connectors | +| 4G NTRIP card failures | thermal + mechanical + no recovery | thermal fix + v2 recovery + qualified modem | +| Hard SIM swaps | SIM buried in the stack | front-panel SIM access / eSIM | +| "Not apt for production" | hand-wired one-offs | designed PCB, documented, repeatable | + +None of this is solved by firmware. It's the core of the **PCB + ruggedization** +work in `03-Architecture-Hardware/hardware-respec-and-pcb-brief.md`. + +--- + +## 6. Evidence still needed (fill this in) + +> **Constraint (2026-06-19):** all units are pulled and sitting in **Veracruz**; +> team is in **Querétaro**. Hands-on items below are gated on **shipping to QRO** +> or **remote-hands** — see `06-Testing-Validation/remote-diagnostic-protocol.md`. These units are +> **v1 (no MCU thermal log)**, so thermal evidence = **physical inspection + bench +> power test**, not firmware logs. + +- [ ] **Historical logs** (do from QRO now): battery V, fix quality, reset + timestamps; and the **htracker-online vs Wialon-offline** split per unit +- [ ] **Backend**: anti-jump warnings spamming a `cell_id` + `netstat -su` UDP + drops around the death window (`02-Architecture-Software/backend-review.md`) +- [ ] **Caster**: HelgenSRV NTRIP availability log +- [ ] **Carrier**: per-SIM data balance / suspension status (rules H2 in/out fast) +- [ ] **Fuse**: installed rating vs measured worst-case + inrush (H5) +- [ ] **4 pulled units (3 dead + 1 working-ish control)**: bench **current-draw** + on power-up, LED + USB-dashboard capture, photos of LiPo (swelling?), boost + (discolour?), solder, SMA. Genuinely-dead units → **teardown in QRO** + (modem power-cycle test, LiPo capacity, thermal-soak) +- [ ] **Thermal quantification**: thermocouple in a sealed box in full sun (can be + done with a spare enclosure in VER or QRO) to get a real interior-temp number + +--- + +## 7. Provisional conclusion + +The evidence now points to **at least two independent causes, not one**: + +1. **Backend (H2/H2b) — confirmed in code.** The blocking retranslator can drop + the *whole fleet* at once on any upstream hiccup, and the anti-jump filter can + *permanently* black-hole an individual unit. Some — possibly much — of the + "deaths" and "recurrent disconnects" may be **server-side, with healthy radios + in the field.** This is testable for free (htracker-online vs Wialon-offline) + and must be done before/alongside the teardown. +2. **Hardware/thermal (H1/H7/H3/H4).** A heat event on the LiPo/modem/boost, on + top of a vibration-fragile prototype, remains the leading *physical* cause for + units that are genuinely dead — confirmed once units are pulled (swelling, + discolour, cracked joints) and from the v2 temperature log. +3. **Firmware (H6)** amplifies real blips into long outages — fixed in v2. + +The permanent fix is deliberately robust to all of them: **backend hardening +(async + cell_id identity + fixed anti-jump)** + thermal-mechanical redesign + +ruggedized interconnect (PCB) + smarter firmware (v2) + verified fusing. Do not +spend the whole budget on a thermal redesign before confirming how much of the +problem is the server. diff --git a/06-Testing-Validation/RCA-Informe-Falla/cuerpo.tex b/06-Testing-Validation/RCA-Informe-Falla/cuerpo.tex new file mode 100644 index 0000000..ed039bb --- /dev/null +++ b/06-Testing-Validation/RCA-Informe-Falla/cuerpo.tex @@ -0,0 +1,89 @@ +\section{Resumen ejecutivo} + +Cinco unidades HBOX en Hutchison Ports ICAVE (Veracruz) operaron de forma intermitente y +luego fallaron de manera casi simultánea, con desconexiones recurrentes en la plataforma. +La evidencia apunta a \textbf{al menos dos causas independientes}, no una sola: +(1) defectos de \textbf{backend} confirmados en el código que pueden sacar de la plataforma +a unidades sanas, y (2) un \textbf{evento térmico} sobre un prototipo frágil. El watchdog +(firmware) amplificaba cada interrupción. Este informe es preliminar hasta ingerir los +registros de campo. + +\section{Las dos fallas son, probablemente, fallas distintas} + +\begin{itemize} + \item \textbf{(A) Muerte súbita y casi simultánea de la flota.} Apunta a un factor de + \textbf{modo común} (no a cinco fallas coincidentes): un golpe de calor, o un corte de + backend/SIM/caster. + \item \textbf{(B) Desconexiones recurrentes a lo largo del tiempo.} Intermitentes por + vibración (Dupont/SMA/soldadura), bloqueos de la tarjeta 4G y un watchdog que se recupera + lento y ruidoso. +\end{itemize} + +\section{Diagnóstico diferencial (lo barato primero)} + +\hwtab{% +\begin{tabularx}{\textwidth}{@{}>{\bfseries}l X >{\centering\arraybackslash}p{2.2cm}@{}} +\toprule +\rowcolor{HelgenIndigo}\textcolor{white}{Hipótesis} & \textcolor{white}{Mecanismo} & \textcolor{white}{Prioridad}\\ +\midrule +Backend (modo común) & El \textit{retranslator} espera el ACK TCP dentro del único bucle UDP $\rightarrow$ una lentitud aguas arriba tira a toda la flota. & Muy alta\\ +Térmico & Carcasa PLA dentro de gabinete ABS IP67 sellado con la fuente Mean Well dentro: trampa de calor anidada. & Muy alta\\ +Filtro anti-salto & Bloquea de forma permanente a una unidad sana (no actualiza referencia; sobrevive reconexiones). & Alta\\ +Vibración & Dupont/SMA/soldadura se aflojan $\rightarrow$ intermitencias. & Alta\\ +SIM/caster & Saldo de datos agotado o caster NTRIP caído. & Media\\ +Fusible sub-dimensionado & Posible disparo bajo inrush/carga real (sospecha del cliente). & Media\\ +\bottomrule +\end{tabularx}} + +\begin{helgennotice}[Prueba gratuita primero] +Una unidad que aparece \textbf{en línea en el monitor de Helgen pero fuera de línea en la +plataforma del cliente} es \textbf{víctima del backend, no hardware muerto}. Verificar esto +en los registros históricos no cuesta nada y puede exonerar al hardware. +\end{helgennotice} + +\section{Hipótesis térmica (reforzada por el BOM)} + +Es una \textbf{trampa de calor anidada}: una carcasa \textbf{PLA} (ablanda $\sim$50–60\,\textdegree C) +sellada dentro de un gabinete \textbf{ABS IP67} que además aloja la fuente Mean Well (fuente +interna de calor), bajo sol pleno. El PLA puede \textbf{deformarse} antes de que ningún chip +esté sobre-temperatura. El ZED-F9P está calificado a $-40$\,a\,$+85$\,\textdegree C; los +afectados por calor son el \textbf{18650/PowerBoost}, el \textbf{módem 4G} y la \textbf{carcasa +PLA}, no el receptor. Un \textbf{ventilador dentro de una caja sellada no la enfría}; el calor +debe conducirse afuera (o se rompe el IP67). + +\section{Backend — defectos confirmados en el código} + +\begin{itemize} + \item \textbf{Recuperación mal dirigida (5 min).} El v1 envía un \textit{reset} del GNSS, + que no revive un módem 4G colgado (la falla dominante reportada). + \item \textbf{Reinicio duro sin \textit{backoff} (10 min).} Si la tarjeta 4G está colgada, la + unidad se reinicia cada $\sim$10 min indefinidamente $\rightarrow$ desconexiones recurrentes. + \item \textbf{Filtro anti-salto.} Puede dejar a una unidad fuera de la plataforma de forma + permanente. (Ambos corregidos en el firmware v2 y en el plan de backend.) +\end{itemize} + +\section{La observación del cliente es correcta} + +ICAVE describe correctamente un prototipo de laboratorio en un entorno industrial brutal: +conectores Dupont, estrés en soldaduras, sin diseño para vibración, conectores flojos, +fallas recurrentes de la tarjeta 4G, baja mantenibilidad de SIM y electrónica «no apta para +producción». Nada de esto se resuelve por software: es el núcleo del trabajo de +\textbf{ruggedización + PCB} (Serie 2). + +\section{Conclusión provisional} + +Lo más probable es un \textbf{evento térmico} sobre un \textbf{prototipo frágil}, con el +\textbf{backend} amplificando cada interrupción —— a menos que la muerte simultánea resulte +ser un \textbf{modo común de backend/SIM/caster}, que debe descartarse primero por ser lo más +barato. El plan de corrección es robusto a todo lo anterior: rediseño térmico-mecánico +(sin PLA, sin batería en el punto caliente, conducción de calor), interconexión ruggedizada +(PCB), firmware más inteligente (v2), fusible verificado y \textbf{endurecimiento del backend}. + +\section{Evidencia pendiente} + +\begin{itemize} + \item Registros de campo (V de batería, calidad de fix, marcas de tiempo de reinicio) y la + comparación monitor-Helgen vs.\ plataforma del cliente. + \item Backend: \textit{logs} del \textit{retranslator} + caídas de buffer UDP en la ventana de la falla. + \item Unidades retiradas: fotos de PLA deformado, batería inflada, soldaduras; prueba de banco del módem; soak térmico. +\end{itemize} diff --git a/06-Testing-Validation/RCA-Informe-Falla/main.pdf b/06-Testing-Validation/RCA-Informe-Falla/main.pdf new file mode 100644 index 0000000..c3e1e69 Binary files /dev/null and b/06-Testing-Validation/RCA-Informe-Falla/main.pdf differ diff --git a/06-Testing-Validation/RCA-Informe-Falla/main.tex b/06-Testing-Validation/RCA-Informe-Falla/main.tex new file mode 100644 index 0000000..84236fd --- /dev/null +++ b/06-Testing-Validation/RCA-Informe-Falla/main.tex @@ -0,0 +1,18 @@ +% Helgen Drogheda — RCA / Informe de Falla de Campo · DRG-RCA-001 +\documentclass[11pt]{article} +\newcommand{\sistema}{Helgen Drogheda — HBOX RTK} +\newcommand{\sistemapdf}{Helgen Drogheda HBOX RTK} +\newcommand{\doctipo}{RCA — Informe de Falla de Campo} +\input{../../_brand/estilo-helgen.tex} +\begin{document} +\helgenportada{Análisis de Causa Raíz (RCA)}{Fallas de campo de la flota HBOX — ICAVE, Veracruz}{% + \begin{tabular}{@{}ll@{}} + \textbf{Documento} & DRG-RCA-001 — RCA / Informe de Falla\\ + \textbf{Versión} & 1.0 (Borrador — pendiente de registros)\\ + \textbf{Fecha} & 20 de junio de 2026\\ + \textbf{Autor} & Dr.\ Leopoldo Rodríguez Salazar — Helgen Technologies\\ + \textbf{Clasificación} & Confidencial — uso de Hutchison Ports y Helgen\\ + \textbf{Asistencia IA} & Elaborado con asistencia de Claude (Anthropic), revisado por el autor\\ + \end{tabular}} +\input{cuerpo.tex} +\end{document} diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.pdf b/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.pdf new file mode 100644 index 0000000..6d42b81 Binary files /dev/null and b/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.pdf differ diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.tex b/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.tex new file mode 100644 index 0000000..2593b7e --- /dev/null +++ b/06-Testing-Validation/RCA-Presentacion-2026-06-22/2026-06-22_Drogheda_RCA-Presentacion_v1.0.tex @@ -0,0 +1,486 @@ +% Helgen Technologies — HBOX RTK · RCA + Plan de Robustez (Project Drogheda) +% Audiencia: ICAVE (cliente, supervisor tecnico). Tono: responsable, tecnico, honesto. +% Compilar con XeLaTeX: latexmk -xelatex -interaction=nonstopmode .tex +\documentclass[10pt,aspectratio=169]{beamer} + +\usetheme[progressbar=frametitle, numbering=fraction, block=fill]{metropolis} + +% ---------- Paleta oficial Helgen (Guia de Identidad v1.5, 2026) ---------- +\definecolor{HelgenIndigo}{HTML}{2C3C5B} % Indigo — color primario +\definecolor{HelgenCyan}{HTML}{51F3DA} % Verde cian — acento +\definecolor{HelgenTeal}{HTML}{17A08C} % derivado: cian oscurecido para TEXTO sobre blanco +\definecolor{HelgenMist}{HTML}{EFF1F5} % derivado: tinte indigo 8% para rellenos +\definecolor{HelgenAmber}{HTML}{C0392B} % rojo de alerta (umbrales/peligro) + +\setbeamercolor{normal text}{fg=HelgenIndigo, bg=white} +\setbeamercolor{alerted text}{fg=HelgenTeal} +\setbeamercolor{frametitle}{bg=HelgenIndigo, fg=white} +\setbeamercolor{progress bar}{fg=HelgenCyan, bg=HelgenMist} +\setbeamercolor{title separator}{fg=HelgenCyan} +\setbeamercolor{block title}{bg=HelgenMist, fg=HelgenIndigo} +\setbeamercolor{block title alerted}{bg=HelgenIndigo, fg=white} + +% ---------- Fuentes de marca + idioma ---------- +\usepackage{iftex} +\ifXeTeX + \usepackage{fontspec} + \IfFontExistsTF{League Spartan}{% + \newfontfamily\helgentitle{League Spartan}[BoldFont={* Bold}] + \setbeamerfont{title}{family=\helgentitle, series=\bfseries} + \setbeamerfont{frametitle}{family=\helgentitle, series=\bfseries} + \setbeamerfont{section title}{family=\helgentitle, series=\bfseries} + \setbeamerfont{standout}{family=\helgentitle, series=\bfseries} + }{} + \IfFontExistsTF{MADE Evolve Sans}{\setsansfont{MADE Evolve Sans}}{} +\else + \usepackage[T1]{fontenc} +\fi +% babel-spanish no esta en este TeX Live; XeLaTeX maneja acentos UTF-8 nativamente. +\usepackage{booktabs} +\usepackage{graphicx} +\usepackage{array} +\newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1}} +\usepackage{tikz} +\usetikzlibrary{arrows.meta, positioning, shapes.misc, fit, backgrounds} +\usepackage{listings} +\lstset{ + basicstyle=\ttfamily\scriptsize, + keywordstyle=\color{HelgenIndigo}\bfseries, + commentstyle=\color{gray}, + stringstyle=\color{HelgenTeal}, + showstringspaces=false, breaklines=true, frame=single, + framerule=0.2pt, rulecolor=\color{HelgenMist} +} + +% ---------- Estilos TikZ ---------- +\tikzset{ + box/.style={rounded rectangle, draw=HelgenIndigo, fill=HelgenMist, + font=\scriptsize, minimum height=7mm, inner xsep=2mm, align=center}, + st/.style={rounded rectangle, draw=HelgenIndigo, fill=HelgenMist, + font=\scriptsize, minimum height=7mm, inner xsep=2mm, align=center}, + hot/.style={rounded rectangle, draw=HelgenAmber, fill=HelgenMist, very thick, + font=\scriptsize, minimum height=7mm, inner xsep=2mm, align=center}, + arr/.style={-{Stealth}, thick, HelgenTeal}, + arrb/.style={-{Stealth}, thick, HelgenIndigo} +} + +% ---------- Vinetas + separador (la fuente de marca no trae bullet ni middot) ---------- +\setbeamertemplate{itemize item}{\textcolor{HelgenTeal}{\rule[0.25ex]{0.85ex}{0.85ex}}} +\setbeamertemplate{itemize subitem}{\textcolor{HelgenTeal}{\rule[0.25ex]{0.6ex}{0.6ex}}} +\setbeamertemplate{itemize subsubitem}{\textcolor{HelgenTeal}{\rule[0.25ex]{0.5ex}{0.5ex}}} +\newcommand{\sep}{\raisebox{0.35ex}{\rule{0.45ex}{0.45ex}}} +% Pie de figura con ancho acotado (evita desbordes al centrar texto largo) +\newcommand{\figcap}[1]{\par\vspace{1.2mm}\centerline{\begin{minipage}{0.92\textwidth}\footnotesize\centering #1\end{minipage}}} + +% ---------- Pie de pagina ---------- +\setbeamertemplate{frame footer}{% + \scriptsize\color{gray}\insertshortauthor\ \sep\ \insertshorttitle\ \sep\ Confidencial — Helgen Servicios Especializados} + +% ---------- Metadatos ---------- +\title[RCA HBOX RTK \sep\ ICAVE]{HBOX RTK — Análisis de Causa Raíz y Plan de Robustez} +\subtitle{Fallas de campo en ICAVE (Veracruz) \sep\ Camino a HBOX v3.0} +\author[Helgen Technologies]{Helgen Technologies — Ingeniería de Producto} +\date{22 de junio de 2026} + +\begin{document} + +\maketitle + +\begin{frame}{Agenda} + \setbeamertemplate{section in toc}[sections numbered] + \tableofcontents[hideallsubsections] +\end{frame} + +% ===================================================================== +\section{Contexto y hallazgo principal} + +\begin{frame}{¿Qué pasó?} + \begin{itemize} + \item 5 HBOX RTK instalados en equipo pesado de ICAVE (RTG, reach stackers, montacargas, tractoplana). + \item Operación intermitente desde el arranque y, después, una \alert{falla casi simultánea} en toda la flota. + \item En paralelo, durante semanas: \alert{desconexiones recurrentes} en Wialon. + \item Escalamiento del cliente (17 jun). Unidades retiradas: 4 en mano (3 fuera de servicio, 1 funcional) en Veracruz; equipo en Querétaro. + \end{itemize} + \vfill + \begin{alertblock}{Lo primero: transparencia} + Nuestra primera hipótesis fue el calor. El análisis con datos revela un cuadro más complejo, y este informe \textbf{corrige el diagnóstico con evidencia}. + \end{alertblock} +\end{frame} + +\begin{frame}{Hallazgo principal} + \begin{itemize} + \item \textbf{No hay una causa única.} La evidencia apunta a \alert{al menos dos causas independientes}, amplificadas por una tercera. + \item \textbf{(1) Backend} — \alert{confirmado en el código}: puede retirar de Wialon a unidades que en realidad están \emph{sanas}. + \item \textbf{(2) Hardware / térmico} — causa física probable de las unidades genuinamente dañadas (a confirmar con desmontaje + termopar). + \item \textbf{(3) Firmware} — convierte interrupciones breves en cortes prolongados (ya corregido en v2). + \end{itemize} + \vfill + \begin{block}{Una consecuencia favorable} + Varias unidades reportadas como «caídas» pueden tener el \textbf{radio sano} y ser \textbf{víctimas del servidor}: esa parte es de corrección inmediata y de bajo costo. + \end{block} +\end{frame} + +\begin{frame}{En una línea} + \begin{block}{Síntesis} + \large Los radios \textbf{funcionan}: entregamos posición GPS estándar (SPS) de forma confiable, \alert{todavía no RTK}. Los frentes por cerrar son la \textbf{fuente de correcciones}, la \textbf{observabilidad}, el \textbf{térmico} y la \textbf{vibración} — los cuatro, en corrección. + \end{block} +\end{frame} + +% ===================================================================== +\section{Metodología de diagnóstico} + +\begin{frame}{Cómo diagnosticamos} + \begin{itemize} + \item \textbf{Principio:} validar primero las causas \alert{más probables y de comprobación inmediata} —sin desmontar unidades— antes que las hipótesis exóticas. + \item \textbf{3 vistas de log independientes por unidad:} receptor u-blox (\texttt{.ubx}), consola del 4G-NTRIP-Master (\texttt{.log}) y captura del retranslator en el servidor (\texttt{tcpdump}). + \item \textbf{+} archivos de configuración (caster NTRIP y ZED-F9P) \textbf{+} un \alert{baseline sano} de referencia (\texttt{RTK14\_440}). + \item \textbf{Herramientas propias} (\texttt{hbox-log-tools}): Log Explorer en navegador, analizador por lotes y dashboard. + \end{itemize} +\end{frame} + +\begin{frame}{Diagnóstico diferencial (H1–H8)} + \centering\scriptsize + \setlength{\tabcolsep}{4pt}\renewcommand{\arraystretch}{1.15} + \begin{tabular}{@{}c L{0.42\textwidth} c c L{0.18\textwidth}@{}} + \toprule + \# & Hipótesis & A: simult. & B: descon. & Estado / Prior \\ + \midrule + H1 & Térmico (batería / booster / módem en gabinete sellado + Mean Well) & Sí & Sí & Alto \\ + H2 & Backend HOL (retranslator bloqueante) & Sí & Sí & \textbf{Confirmado (código)} \\ + H2b & Filtro anti-salto (bloqueo permanente) & No & Sí & \textbf{Confirmado (código)} \\ + H3 & Vibración (Dupont / SMA / soldadura) & No & Sí & Alto \\ + H4 & Módulo 4G NTRIP (cuelgue / falla) & Parc. & Sí & Alto \\ + H5 & Fusible sub-dimensionado & Sí* & Parc. & Medio \\ + H6 & Lógica de watchdog (reset / loop) & No & Sí & Alto (para B) \\ + H7 & Brownout de batería (caída de tensión por calor) & Sí & Sí & Medio-alto \\ + H8 & Riel de potencia / EMI de la máquina & Parc. & Sí & Medio \\ + \bottomrule + \end{tabular} + \vfill + \footnotesize Las dos comprobaciones \alert{más rápidas} (backend H2, fusible H5) se confirman en una hora, \textbf{sin desmontar una sola unidad}. +\end{frame} + +% ===================================================================== +\section{Evidencia: los datos} + +\begin{frame}{Dos fallas, probablemente \emph{dos} causas} + \begin{columns}[T] + \column{0.5\textwidth} + \textbf{(A) Falla casi simultánea} + \begin{itemize}\small + \item Apunta a un estresor de \alert{modo común}. + \item Candidatos: calor, backend / caster / SIM, servidor. + \end{itemize} + \column{0.5\textwidth} + \textbf{(B) Desconexión crónica} + \begin{itemize}\small + \item Apunta a \alert{intermitencias}. + \item Candidatos: vibración, cuelgue del 4G, watchdog lento. + \end{itemize} + \end{columns} + \vfill + \begin{alertblock}{Por qué no reducirlo todo a «calor»} + Atribuir todo al calor pierde una corrección barata de \textbf{backend}, y un rediseño térmico \textbf{no} detendría las intermitencias por \textbf{vibración}. Se atienden ambas. + \end{alertblock} +\end{frame} + +\begin{frame}{Ninguna unidad alcanzó RTK} + \centering + \includegraphics[width=\linewidth,height=0.76\textheight,keepaspectratio]{figs/fig_fix_compare.pdf} + \vfill + \figcap{Todas las capturas en \textbf{SPS (calidad 1)}; \alert{cero en RTK (4/5)}. El posicionamiento centimétrico nunca se entregó.} +\end{frame} + +\begin{frame}{El receptor está sano — el problema no es el ZED-F9P} + \centering + \includegraphics[width=\linewidth,height=0.76\textheight,keepaspectratio]{figs/fig_baseline_gnss.pdf} + \vfill + \figcap{Baseline \texttt{RTK14\_440}: \textbf{12 satélites, HDOP 0.75}, C/N0 sano — pero atrapado en SPS. El receptor funciona.} +\end{frame} + +\begin{frame}{La entrega extremo-a-extremo es sana} + \centering + \includegraphics[width=\linewidth,height=0.74\textheight,keepaspectratio]{figs/fig_delivery.pdf} + \vfill + \figcap{\texttt{RTK14\_440}: \textbf{984 paquetes, 1 Hz estable, cero caídas} al servidor. Lección clave: \alert{«entrega sana» $\neq$ «RTK»}.} +\end{frame} + +\begin{frame}{Las correcciones son marginales} + \centering + \includegraphics[width=\linewidth,height=0.70\textheight,keepaspectratio]{figs/fig_ntrip_delay.pdf} + \vfill + \figcap{\textbf{Causa:} caster público gratuito \texttt{rtk2go} (sin SLA) + configuración del F9P \alert{solo en RAM} + cielo interior (banco). El cliente NTRIP rara vez alcanza el estado «conectado».} +\end{frame} + +\begin{frame}{La trampa diagnóstica (y una rectificación)} + \begin{itemize} + \item La \textbf{consola del 4G-NTRIP-Master no es un indicador de salud confiable}: el baseline \emph{sano} registra la misma consola escasa que las unidades que «parecían» falladas. + \item \alert{Rectificación:} la supuesta «falla de ingest en master-14» queda \textbf{retirada} — master-14 \emph{es} el baseline y demuestra reenvío. + \item \textbf{Solo master-21} es una falla confirmada (el módem nunca registra: \texttt{0xFFFFF} + 511 errores del parser AT). + \end{itemize} + \vfill + \begin{block}{Lección} + La salud por unidad debe provenir de un componente que \textbf{interpreta el fix} —el Blackpill— registrado en un formato confiable. + \end{block} +\end{frame} + +% ===================================================================== +\section{Causas raíz} + +\begin{frame}{Térmico (H1 / H7) — qué es real y qué falta probar} + \begin{itemize} + \item \textbf{Trampa térmica anidada:} el HBOX va sellado dentro de un gabinete ABS IP67 que \emph{además} aloja la fuente Mean Well, a pleno sol en Veracruz (interior estimado 60–80\,°C). + \item \textbf{La carcasa ASA fue una elección correcta:} UV-estable y reblandece a $\sim$95–100\,°C, con margen frente a esa temperatura. \alert{El gabinete no es el eslabón débil.} + \item \textbf{El eslabón débil son los componentes de potencia de grado prototipo} en ese espacio caliente: batería 18650 (carga $\leq$45\,°C $\rightarrow$ caída de tensión / hinchamiento), PowerBoost 1000C (booster \emph{hobby}) y el módem 4G (throttling térmico). + \item El \textbf{ZED-F9P} ($-40\ldots+85$\,°C) \alert{no} es la víctima. + \end{itemize} + \vfill + \begin{alertblock}{Honesto} + En banco las fallas se reproducen \textbf{en frío} $\rightarrow$ el calor \emph{no} explica lo observado en estos logs. La hipótesis térmica de \textbf{campo} sigue \alert{abierta} (falta soak con termopar). + \end{alertblock} +\end{frame} + +\begin{frame}[fragile]{Backend (H2 / H2b) — confirmado en el código} + \begin{itemize}\small + \item \textbf{H2:} el envío a Wialon (con \texttt{connect} de 20\,s y espera de ACK) ocurre \alert{dentro} del único bucle UDP $\rightarrow$ un upstream lento detiene el bucle y \textbf{toda la flota cae a la vez}. + \item \textbf{H2b:} el filtro anti-salto no actualiza su referencia tras un «salto» rechazado $\rightarrow$ una unidad sana queda \alert{fuera de Wialon de forma permanente}. + \end{itemize} +\begin{lstlisting} +# retranslator.py (defecto): envio sincrono en el bucle de recepcion +for pkt in udp_recv_loop(): # un solo hilo, toda la flota + wialon.connect(timeout=20) # bloquea a TODOS si el upstream tarda + wialon.send(pkt); wialon.wait_ack() # head-of-line stall +\end{lstlisting} + \footnotesize Triage \alert{sin costo}: ONLINE en htracker pero OFFLINE en Wialon $\Rightarrow$ víctima del backend, no hardware dañado. +\end{frame} + +\begin{frame}{Firmware (H6) — convierte una interrupción breve en un corte largo} + \begin{itemize} + \item v1 reacciona a la pérdida de correcciones con un \texttt{hot-reset} del \textbf{GNSS} (\texttt{UBX-CFG-RST}) $\rightarrow$ reinicia el ZED-F9P, \alert{no} el módem 4G colgado (la falla \#1 reportada). 5 min en una acción sin efecto. + \item v1: reset duro cada $\sim$10 min \alert{sin backoff} $\rightarrow$ \textbf{ciclo de reinicios}; cada ciclo equivale a varios minutos de desconexión. Una flota así produce «desconexiones recurrentes». + \end{itemize} + \vfill + \begin{block}{v2 (ya disponible)} + Recupera \textbf{ciclando el stack 4G}, detecta silencio de NMEA en $\sim$60\,s, aplica \textbf{backoff exponencial} y cae a \textbf{modo LIMP} (sigue reportando GPS estándar) en lugar de desaparecer. + \end{block} +\end{frame} + +\begin{frame}{La revisión de ICAVE es correcta (H3 / H4)} + \centering\scriptsize + \setlength{\tabcolsep}{4pt}\renewcommand{\arraystretch}{1.18} + \begin{tabular}{@{}L{0.27\textwidth}L{0.33\textwidth}L{0.32\textwidth}@{}} + \toprule + Punto del cliente & Mecanismo & Corrección permanente \\ + \midrule + Conectores Dupont & la vibración los afloja $\rightarrow$ apertura intermitente & conectores con seguro (JST / Molex) \\ + Esfuerzo en soldaduras & flexión + ciclos térmicos los agrietan & PCB con alivio de esfuerzos \\ + Sin diseño anti-vibración & la resonancia afloja todo & PCB + standoffs + grommets + coat \\ + SMA / terminales flojos & se aflojan con la vibración & SMA con torque, conectores retenidos \\ + Fallas de la tarjeta 4G & térmico + mecánico + sin recuperación & fix térmico + v2 + módem calificado \\ + Cambio de SIM difícil & SIM enterrada en el stack & SIM en panel frontal / eSIM \\ + «No apto para producción» & cableado a mano, piezas únicas & PCB diseñada, documentada, repetible \\ + \bottomrule + \end{tabular} + \vfill + \footnotesize Nada de esto se resuelve con firmware: es el núcleo del trabajo de \textbf{PCB + ruggedización}. +\end{frame} + +% ===================================================================== +\section{Acciones ya tomadas} + +\begin{frame}{Backend — reescrito} + \begin{itemize} + \item Retranslator + monitor: \alert{envío asíncrono}, sin bloqueo de cabeza de línea (HOL). + \item \textbf{Identidad por \texttt{cell\_id}} (no por orden de llegada) $\rightarrow$ la trazabilidad por unidad deja de depender del orden de los paquetes. + \item Filtro anti-salto \textbf{corregido}: ya no deja fuera de forma permanente a una unidad sana. + \end{itemize} + \vfill + \begin{block}{Resultado esperado} + Una unidad sana \textbf{ya no puede quedar fuera por el servidor}. Esto descarta una de las dos causas de modo común antes de la próxima prueba de campo. + \end{block} +\end{frame} + +\begin{frame}{Observabilidad — Blackpill v2.1 + herramientas} + \begin{itemize} + \item \textbf{Spec de logging v2.1:} reemplaza el dashboard ANSI auto-sobrescrito por un flujo \alert{estructurado, con marca de tiempo y parseable}: estado, fix, paquetes, batería, \textbf{temperatura del MCU}, recuperación y \textbf{causa del reset}. + \item \textbf{\texttt{hbox-log-tools}:} Log Explorer (parseo + graficado automático + comparación contra baseline + export PNG/SVG), analizador por lotes y dashboard. + \end{itemize} + \vfill + \begin{block}{Efecto} + La salud por unidad deja de ser ambigua: proviene de la \textbf{propia unidad}, no de una consola que no es confiable. + \end{block} +\end{frame} + +\begin{frame}[fragile]{GNSS / correcciones — persistencia y caster} + \begin{itemize}\small + \item Persistir la configuración del F9P en \alert{FLASH} (hoy es \textbf{solo RAM} $\rightarrow$ se pierde en un ciclo de energía y rompe el feed de forma silenciosa). + \item Migrar las correcciones de \texttt{rtk2go} (gratuito, sin SLA) a un \textbf{caster dedicado (HelgenSRV)} con disponibilidad garantizada. + \item Reverificar el switch \texttt{XBEE$\leftrightarrow$UART2} del simpleRTK2B Pro. + \end{itemize} +\begin{lstlisting} +; Persistir configuracion en BBR + FLASH (no solo RAM) -- ilustrativo +UBX-CFG-CFG saveMask = ioPort | msgConf | navConf + devices = devBBR | devFlash +; GGA en UART2 @1 Hz -> verificado correcto +\end{lstlisting} +\end{frame} + +% ===================================================================== +\section{Camino a HBOX v3.0} + +\begin{frame}{Estrategia fásica} + \begin{columns}[T] + \column{0.5\textwidth} + \begin{block}{Fase 1 — Ruggedizar ahora} + \small Sobre los módulos actuales, sin esperar la PCB: eliminar Dupont, buck industrial 24$\rightarrow$5\,V $\geq$3\,A (retira el PowerBoost), fijación mecánica + conformal coat, SIM frontal, conducción térmica al plato + parasol, flashear v2. + \end{block} + \column{0.5\textwidth} + \begin{block}{Fase 2 — PCB de producción} + \small Una placa \emph{carrier} industrial que reemplaza todo el cableado a mano y las placas \emph{hobby}. La corrección \textbf{de fondo}, en paralelo. + \end{block} + \end{columns} + \vfill + \footnotesize Estabilización «permanente-suficiente» de la flota \textbf{ahora}; la PCB se fabrica en paralelo. +\end{frame} + +\begin{frame}{Térmico v3.0 — gestión activa con ducto impermeable} + \begin{itemize} + \item \textbf{Hecho de ingeniería:} un ventilador dentro de una caja \emph{sellada} no enfría — solo agita el aire atrapado. + \item \textbf{v3.0:} un \alert{ducto impermeable} (membrana hidrófoba tipo Gore / laberinto sellado) crea una trayectoria de aire controlada que \emph{sí} extrae calor \textbf{sin comprometer el ingreso} (agua / polvo / salitre). + \item \textbf{Control inteligente del ventilador} gobernado por el Blackpill: sensa la temperatura interior $\rightarrow$ modula el flujo. + \item \textbf{La carcasa ASA se mantiene} (es adecuada); el foco es \textbf{conducir el calor} al plato metálico, reducir la ganancia solar (parasol) y bajar el calor interno (buck eficiente, retirar / reubicar la batería). + \end{itemize} + \vfill + \footnotesize \alert{Abierto:} definir la membrana / ducto y validar con soak de termopar. Opcional: gabinete metálico como disipador. +\end{frame} + +\begin{frame}{PCB de producción (Fase 2) — diagrama de bloques} + \centering + \begin{tikzpicture}[node distance=4.5mm and 6mm] + \node[box] (vin) {24\,V gabinete\\(ya protegido)}; + \node[box, right=of vin] (prot) {TVS +\\FET anti-inv.}; + \node[box, right=of prot] (buck) {Buck 24$\rightarrow$5\,V\\$\geq$3\,A}; + \node[box, right=of buck] (rail) {Riel 5\,V}; + \node[box, above=of rail] (mcu) {STM32\\watchdog}; + \node[box, right=of rail] (sw) {Load switch\\/ eFuse}; + \node[box, right=of sw] (stack){Stack RTK\\+ 4G}; + \node[box, below=of buck] (sense){Sense: Vin / 5\,V / batt / \alert{temp}}; + \draw[arrb] (vin)--(prot); \draw[arrb] (prot)--(buck); \draw[arrb] (buck)--(rail); + \draw[arrb] (rail)--(sw); \draw[arrb] (sw)--(stack); + \draw[arr] (rail)--(mcu); \draw[arr] (mcu) -| (sw) node[midway,above,font=\tiny,text=HelgenIndigo]{enable}; + \draw[arr] (sense)-- (mcu.south west); + \draw[arr] (mcu.east) -- ++(0.6,0) |- (stack.north) node[near start,above,font=\tiny,text=HelgenIndigo]{UART/UBX}; + \end{tikzpicture} + \vfill + \footnotesize Conectores con seguro \sep\ SMA bulkhead con torque \sep\ SIM en panel frontal / MFF2 \sep\ test points en cada riel. +\end{frame} + +\begin{frame}{Vibración — mitigación del módulo 4G NTRIP} + \begin{itemize} + \item Standoffs con \textbf{aisladores / grommets}; retención del módulo XBee 4G en su socket con bracket o clip. + \item Conformal coat del cableado discreto; conectores con seguro; SMA con thread-lock + torque. + \end{itemize} + \vfill + \begin{alertblock}{Abierto — en definición} + Definir el \textbf{esquema de amortiguación específico del módulo 4G} (montaje aislado vs. retención rígida), a confirmar con una \textbf{prueba de vibración} controlada. Este es el punto que todavía estamos cerrando. + \end{alertblock} +\end{frame} + +\begin{frame}{Watchdog — FSM de recuperación (v2 $\rightarrow$ v3)} + \centering + \begin{tikzpicture}[node distance=8mm and 14mm] + \node[st] (normal) {NORMAL\\(reporta RTK/SPS)}; + \node[hot, right=of normal] (cycle) {CICLAR STACK 4G\\(backoff exp.)}; + \node[st, right=of cycle] (limp) {LIMP\\(GPS estándar,\\sigue reportando)}; + \node[st, below=of cycle] (temp) {SENSAR TEMP\\$\rightarrow$ CTRL VENTILADOR}; + \draw[arr] (normal) -- node[above,font=\tiny,text=HelgenIndigo]{silencio NMEA 60\,s} (cycle); + \draw[arr] (cycle) -- node[above,font=\tiny,text=HelgenIndigo]{persiste} (limp); + \draw[arr] (limp.north) to[bend right=35] node[above,font=\tiny,text=HelgenIndigo]{enlace OK} (normal.north); + \draw[arr] (cycle.south west) to[bend right=20] node[left,font=\tiny,text=HelgenIndigo]{recupera} (normal.south); + \draw[arrb, dashed] (normal) -- (temp); + \draw[arrb, dashed] (temp) -- (limp); + \end{tikzpicture} + \vfill + \footnotesize Invariantes: \textbf{nunca desaparecer, nunca ciclo infinito}; el estado seguro por defecto es \alert{OFF}. v3 añade el lazo térmico (líneas punteadas). +\end{frame} + +% ===================================================================== +\section{Estado honesto y plan a una semana} + +\begin{frame}{Dónde estamos — sin adornos} + \begin{columns}[T] + \column{0.34\textwidth} + \begin{block}{Confirmado} + \scriptsize + \begin{itemize} + \item Backend HOL + anti-salto (en código) + \item Receptores GNSS sanos; SPS, no RTK + \item Consola 4G no es indicador de salud + \item master-21: módem caído + \end{itemize} + \end{block} + \column{0.32\textwidth} + \begin{block}{Hipótesis} + \scriptsize + \begin{itemize} + \item Térmico de \textbf{campo} (falta termopar / soak) + \item Fusible sub-dimensionado + \item Brownout de batería + \end{itemize} + \end{block} + \column{0.34\textwidth} + \begin{alertblock}{Abierto} + \scriptsize + \begin{itemize} + \item ¿Resuelto? — pendiente reflash v2 + análisis en entorno controlado (QRO) + \item Anti-vibración del 4G + \item Fix RTK $\geq$4 con caster dedicado en cielo abierto + \end{itemize} + \end{alertblock} + \end{columns} +\end{frame} + +\begin{frame}{Plan a una semana $\rightarrow$ viernes 26 de junio} + \centering\footnotesize + \setlength{\tabcolsep}{6pt}\renewcommand{\arraystretch}{1.25} + \begin{tabular}{@{}l L{0.78\textwidth}@{}} + \toprule + Ventana & Actividad \\ + \midrule + Lun–Mar & Desplegar \textbf{watchdog v2} y analizar en \textbf{entorno controlado (QRO)}; persistir F9P en FLASH + caster dedicado \\ + Mar–Mié & \textbf{Retest RTK en cielo abierto} (confirmar fix $\geq$4/5); kit de ruggedización Fase 1 sobre 1 unidad de referencia \\ + Mié–Jue & Definir \textbf{ducto + ventilador inteligente} + esquema anti-vibración del 4G; soak térmico con termopar \\ + \textbf{Vie 26} & \alert{Enviar la versión más robusta del HBOX a Veracruz} para continuar las pruebas \\ + \bottomrule + \end{tabular} + \vfill + \footnotesize El objetivo de esta semana no es «cerrado»: es un \textbf{HBOX medible y más robusto} de vuelta en el campo. +\end{frame} + +\begin{frame}{Compromisos — responsable y fecha} + \centering\footnotesize + \setlength{\tabcolsep}{6pt}\renewcommand{\arraystretch}{1.25} + \begin{tabular}{@{}lll@{}} + \toprule + Acción & Responsable & Fecha \\ + \midrule + Watchdog v2 + análisis controlado (QRO) & Leo / Daniel & 23–24 jun \\ + Persistir F9P en FLASH + caster dedicado & Gibran & 24 jun \\ + Retest RTK en cielo abierto & Gibran & 25 jun \\ + Kit de ruggedización Fase 1 (unidad ref.) & Adriana & 25 jun \\ + Diseño ducto + fan + anti-vibración 4G & Leo & 25–26 jun \\ + Envío del HBOX robusto a Veracruz & Adriana & 26 jun \\ + \bottomrule + \end{tabular} +\end{frame} + +% ===================================================================== +\begin{frame}[standout] + Diagnóstico con datos.\\ + Corrección en ejecución.\\[2mm] + {\normalsize HBOX v3.0 — más robusto — a prueba el \textbf{26 de junio}.} + \vfill + {\footnotesize Gracias \sep\ Preguntas} +\end{frame} + +\end{document} diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_baseline_gnss.pdf b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_baseline_gnss.pdf new file mode 100644 index 0000000..070de2b Binary files /dev/null and b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_baseline_gnss.pdf differ diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_delivery.pdf b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_delivery.pdf new file mode 100644 index 0000000..359658c Binary files /dev/null and b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_delivery.pdf differ diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_fix_compare.pdf b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_fix_compare.pdf new file mode 100644 index 0000000..10913a2 Binary files /dev/null and b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_fix_compare.pdf differ diff --git a/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_ntrip_delay.pdf b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_ntrip_delay.pdf new file mode 100644 index 0000000..b39ffe4 Binary files /dev/null and b/06-Testing-Validation/RCA-Presentacion-2026-06-22/figs/fig_ntrip_delay.pdf differ diff --git a/06-Testing-Validation/fig/fig_baseline_gnss.pdf b/06-Testing-Validation/fig/fig_baseline_gnss.pdf new file mode 100644 index 0000000..070de2b Binary files /dev/null and b/06-Testing-Validation/fig/fig_baseline_gnss.pdf differ diff --git a/06-Testing-Validation/fig/fig_delivery.pdf b/06-Testing-Validation/fig/fig_delivery.pdf new file mode 100644 index 0000000..359658c Binary files /dev/null and b/06-Testing-Validation/fig/fig_delivery.pdf differ diff --git a/06-Testing-Validation/fig/fig_fix_compare.pdf b/06-Testing-Validation/fig/fig_fix_compare.pdf new file mode 100644 index 0000000..10913a2 Binary files /dev/null and b/06-Testing-Validation/fig/fig_fix_compare.pdf differ diff --git a/06-Testing-Validation/fig/fig_ntrip_delay.pdf b/06-Testing-Validation/fig/fig_ntrip_delay.pdf new file mode 100644 index 0000000..b39ffe4 Binary files /dev/null and b/06-Testing-Validation/fig/fig_ntrip_delay.pdf differ diff --git a/06-Testing-Validation/log-analysis b/06-Testing-Validation/log-analysis new file mode 160000 index 0000000..cda046b --- /dev/null +++ b/06-Testing-Validation/log-analysis @@ -0,0 +1 @@ +Subproject commit cda046b9d4bef72f35e8fd8af7da846ae0200082 diff --git a/06-Testing-Validation/remote-diagnostic-protocol.md b/06-Testing-Validation/remote-diagnostic-protocol.md new file mode 100644 index 0000000..b4522b6 --- /dev/null +++ b/06-Testing-Validation/remote-diagnostic-protocol.md @@ -0,0 +1,180 @@ +# Remote Diagnostic Protocol — Pulled HBOX Units + +**Date:** 2026-06-19 · **Owner:** Leo Rodriguez +**Situation:** all HBOX units were **removed from the machines**. We have **4 units +in hand: 3 not working, 1 working-ish.** They are **physically in Veracruz**; the +team is in **Querétaro** → debugging is **remote** until they're shipped or someone +on-site acts as our hands. +**Firmware on these units = v1** (no MCU thermal log) → thermal evidence must come +from **physical inspection + a bench power test**, not from the firmware. + +> The single working-ish unit is the **control**. Every measurement below should be +> taken on it too, so "bad" readings on the dead units have a baseline to compare +> against. + +--- + +## 0. Decision gate (make this call first) + +**Recommendation: ship all 4 units to Querétaro.** They are small, and remote- +debugging *dead* electronics is slow and rarely conclusive — a 1–2 day courier buys +full bench access (PSU with current limit, scope, u-center, reflash, thermal soak, +teardown). Bring the working-ish unit too; it's the reference. + +Choose remote-hands **only if** shipping is blocked (client chain-of-custody, the +units must stay on-site, or courier delay is unacceptable). Confirm with ICAVE who +**owns** the units and whether they may leave the terminal. + +| Path | When | Cost | +|---|---|---| +| **Ship to QRO** *(recommended)* | units may leave Veracruz | courier + 1–2 days | +| **Remote-hands in Veracruz** | units must stay; cooperative on-site tech exists | needs the protocol below + their time | + +Either way, **start §2 (backend analysis) now from Querétaro** — it needs no units. + +--- + +## 1. What we can / cannot learn remotely + +| Question | Remote-hands? | How | +|---|---|---| +| Is the LiPo swollen / burnt / leaking? | ✅ | photo checklist (§3 Tier 0) | +| Connectors/SMA loose, solder cracked? | ✅ | photos + wiggle test | +| Does it draw current / boot on power? | ✅ | bench PSU **current reading** (§3) | +| LED state on power-up? | ✅ | film the LED, decode (§4) | +| Battery V, fix quality, state, packets | ✅ (Tier 1) | USB dashboard capture | +| Does the 4G modem attach / get a fix? | ✅ (Tier 1–2) | dashboard / u-center / remote desktop | +| Does it reach our gateway when powered? | ✅ | watch htracker for that unit's `cell_id` | +| Exact failed component / thermal soak | ❌ (needs bench) | **ship to QRO** | + +So ~80% of triage is remotely achievable; only component-level confirmation and +thermal reproduction strictly require the units in QRO. + +--- + +## 2. Do this NOW from Querétaro (no units needed) + +The backend is on our own server — fully debuggable from QRO, and per +`02-Architecture-Software/backend-review.md` it can take healthy units off Wialon by itself. + +1. **Historical online-vs-offline split.** For the days before removal, compare each + unit's **htracker** record (online on UDP receipt) against **Wialon** (online on + positions received). A unit that was **online in htracker but offline in Wialon** + was a **backend victim** (anti-jump lock-out / HOL stall), *not* dead hardware — + that reframes which "dead" units are actually hardware failures. +2. **Gateway logs around the death window:** look for the anti-jump warning + (`¡Filtro Anti-Salto Activado!`) spamming one `cell_id` (→ lock-out), and for + `netstat -su` UDP receive-buffer drops (→ HOL stall). +3. **Bench-reproduce with the working-ish unit** once it's powered (in VER via + remote-hands, or in QRO): point it at the gateway and watch whether the + retranslator forwards it cleanly or trips the filter. +4. Caster (HelgenSRV) uptime + **per-SIM data balance/suspension** for the window. + +This is the highest-leverage work we can do today and it needs nobody in Veracruz. + +--- + +## 3. Remote-hands protocol (tiered by what the on-site person has) + +Give the on-site tech the matching tier. A ready-to-send Spanish version is in §6. + +### Tier 0 — power + a phone camera (minimum) +For **each** unit, before powering: +1. **Photograph**: the whole inside, then close-ups of the **LiPo** (any swelling, + bulging, brown/black marks, leaks?), the **PowerBoost**, every **connector/Dupont**, + the **SMA antenna** joints, and any **discoloured/burnt** components. +2. **Wiggle test** (gently): do any Dupont/terminal/SMA connectors move or fall off? +3. **Bench power-on with a current-limited PSU** at the unit's normal voltage + (12 V or 24 V), current limit ~2 A: + - Read **inrush** then **steady current**. Record the number. + - **0 mA / trips at 0:** open circuit / dead input stage / blown fuse. + - **Hits the current limit / very hot:** short → **power off immediately**. + - **~50–150 mA steady, nothing else:** MCU only, stack not powering (load switch + / boost / modem). + - **Normal-ish draw + LED activity:** boots — go to Tier 1. + - Compare every reading to the **working-ish unit**. +4. **Film the LED** for ~60 s from power-up (decode in §4). + +### Tier 1 — Tier 0 + USB cable + a laptop +5. Plug the Blackpill **USB** into the laptop; open **PuTTY/TeraTerm at 115200**. + The v1 watchdog prints a live dashboard. **Capture 3–5 min** (log to file) and + send it. Read off: **BATTERY V**, **SYSTEM STATE**, **RTK FIX**, **PACKETS TX'd**, + PWR/IGN inputs. (No MCU-temp line — these are v1.) +6. Note whether **PACKETS TX'd increments** (GNSS+serial alive) and whether **RTK + FIX** ever reaches 4/5 (corrections arriving = 4G/NTRIP working). + +### Tier 2 — Tier 1 + remote-desktop (AnyDesk/TeamViewer) into the laptop +7. We drive from QRO: read the live dashboard, run **u-center** on the ZED-F9P + (fix, sat levels, config), issue **AT commands** to the 4G modem (attach, signal), + and if useful **reflash v2** to gain thermal logging + better recovery. + +--- + +## 4. LED quick-decode (v1) + +| LED | State | Read as | +|---|---|---| +| Fast blink | BOOTING | powered, waiting for 4G/GNSS | +| Solid ON | RUNNING, vehicle power, RTK fixed | healthy | +| Heartbeat (1 s) | RUNNING on battery | on LiPo, 30 s rate | +| Slow blink | searching / no corrections | 4G/NTRIP not delivering | +| Off (with power applied) | SLEEP / dead | battery-critical logic **or** not booting | + +"Off while powered from a good bench supply" on a unit that should boot = a strong +**no-boot / power-stage** signal — correlate with the current reading. + +--- + +## 5. Per-unit triage decision tree + +``` +Power on bench PSU (current-limited) +├─ 0 mA ............................. dead input / blown fuse / open → ship to QRO +├─ hits limit / hot ................ SHORT (power off!) ............... → ship to QRO +└─ draws current + ├─ no LED, no USB dashboard ...... MCU/boot dead .................... → ship to QRO + └─ boots (LED + dashboard) + ├─ PACKETS not incrementing ... GNSS/serial dead ................ Tier 2 / ship + ├─ FIX stuck 0/1 (never 4/5) .. 4G/NTRIP not delivering ......... check modem/SIM/antenna + └─ FIX 4/5 + packets OK ........ HARDWARE IS FINE ............... → backend victim (§2) +``` + +The last branch is the important one: a pulled unit that **boots, fixes, and +transmits on the bench is not the failure** — its disappearance from Wialon is then +backend (§2), and re-installing it unchanged would just repeat the problem. + +--- + +## 6. Safety — heat-damaged LiPo (tell the on-site tech) + +A swollen/puffy or hot LiPo is a **fire risk**. Do **not** charge it, puncture it, or +keep powering a unit whose battery is visibly swollen. Disconnect it, place it in a +**LiPo-safe / fireproof bag or a metal container**, keep it away from anything +flammable, and ventilate. Photograph first, then isolate. If it's hot, vented, or +smells: stop and isolate immediately. + +--- + +## 6b. Spanish — instrucciones para el técnico en sitio (Tier 0/1) + +> Para **cada** unidad: (1) **Fotos** del interior y de cerca de la **batería LiPo** +> (¿inflada, manchas cafés/negras, fuga?), del **PowerBoost**, de **cada conector +> Dupont**, de las **antenas SMA**, y de cualquier componente **quemado/decolorado**. +> (2) Prueba de **movimiento** suave: ¿algún conector se mueve o se cae? (3) Enciéndela +> con una **fuente de banco** a su voltaje normal (12 V/24 V), límite de corriente ~2 A, +> y anota la **corriente** (arranque y estable). Si llega al límite o se calienta, +> **apágala de inmediato** (corto). (4) **Graba el LED** ~60 s desde el encendido. +> (5) Si hay laptop: conecta el **USB** del Blackpill, abre **PuTTY/TeraTerm a 115200** +> y **graba 3–5 min** del tablero (BATERÍA V, ESTADO, RTK FIX, PAQUETES). **Seguridad:** +> una batería **inflada o caliente** es riesgo de incendio — no la cargues ni la +> perfores; aíslala en bolsa/recipiente seguro y ventila. + +--- + +## 7. What to send back to Querétaro (capture checklist) + +Per unit, labeled with its `cell_id`/machine: the **photos**, the **current reading** +(inrush + steady), the **LED video**, the **3–5 min USB dashboard capture**, and a +one-line note (boots? fix? packets?). With that, we can classify all 4 units against +the tree in §5 and the backend evidence in §2 — and decide which truly need teardown +in QRO. diff --git a/99-Archive/blackpillrtkcode_ORIGINAL_loose.ino b/99-Archive/blackpillrtkcode_ORIGINAL_loose.ino new file mode 100644 index 0000000..97dd478 --- /dev/null +++ b/99-Archive/blackpillrtkcode_ORIGINAL_loose.ino @@ -0,0 +1,338 @@ +#include + +// --- Pin Map --- +#define PIN_BAT_SENSE PA0 // Battery (3.6V nominal) +#define PIN_5V_SENSE PA4 // Powerboost 5V Rail +#define PIN_IGN_SENSE PA5 // Extra Signal: 12V Ignition +#define PIN_PWR_CTRL PA1 // MOSFET to Power SimpleRTK2B +#define PIN_LED PC13 // Onboard LED (Active LOW) +#define PIN_EXT_LED PB0 // External LED (via MOSFET) + +// --- Constants & Thresholds --- +const float ADC_REF = 3.3; +const float DIVIDER_RATIO = 2.0; +const float BAT_CRITICAL = 3.4; +const float BAT_RECOVERY = 3.7; + +// --- Timing (ms) --- +const unsigned long SOFT_RESET_DELAY = 300000; // 5 mins +const unsigned long HARD_RESET_DELAY = 600000; // 10 mins total +const unsigned long BOOT_STABILIZE_TIME = 15000; // Time for 4G/GNSS to boot +const unsigned long POWER_CYCLE_TIME = 5000; // 5 seconds to drain caps on Hard Reset + +// --- UBX Commands --- +// UBX-CFG-RATE: measRate = 1000ms, navRate = 1 (1Hz Output) +const byte UBX_1HZ[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xE8, 0x03, 0x01, 0x00, 0x01, 0x00, 0x01, 0x39}; +// UBX-CFG-RATE: measRate = 1000ms, navRate = 30 (30s Output) +const byte UBX_30S[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xE8, 0x03, 0x1E, 0x00, 0x01, 0x00, 0x1E, 0xAD}; +// UBX-CFG-RST: Hot Start / Software Reset +const byte UBX_SOFT_RESET[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x66}; + +// --- State Variables --- +enum SystemState { BROWNOUT, BOOTING, RUNNING, HARD_RESETTING }; +SystemState currentState = BROWNOUT; + +int fixQuality = 0; +int satellites = 0; +unsigned long rxCount = 0; +float vBatF = 3.7, v5vF = 5.0; +unsigned long lastDashUpdate = 0; +unsigned long bootTimer = 0; +unsigned long resetCooldownTimer = 0; +unsigned long brownoutTimer = 0; + +bool isSlowTxRate = false; +bool ntripWasWorking = false; +unsigned long failureTimer = 0; +bool softResetDone = false; +bool newDataReceived = false; + +// Optimized: Using const char* prevents memory fragmentation on ARM MCUs +const char* sysMsg = "System Initialized."; +String nmeaBuffer = ""; + +HardwareSerial GNSS(PA3, PA2); + +// --- UTILITIES --- + +// Formats uptime safely into a character buffer +void getUptimeString(char* buffer, size_t maxLen) { + unsigned long seconds = millis() / 1000; + int d = seconds / 86400; + int h = (seconds % 86400) / 3600; + int m = (seconds % 3600) / 60; + snprintf(buffer, maxLen, "%dd%dh%dm", d, h, m); +} + +void updateSensors() { + float rawBat = (analogRead(PIN_BAT_SENSE) / 4095.0) * ADC_REF * DIVIDER_RATIO; + float raw5v = (analogRead(PIN_5V_SENSE) / 4095.0) * ADC_REF * DIVIDER_RATIO; + vBatF = (vBatF * 0.9) + (rawBat * 0.1); // Low-pass filter for stability + v5vF = (v5vF * 0.9) + (raw5v * 0.1); +} + +void setPower(bool ON) { + if (ON) { + pinMode(PA2, OUTPUT); + pinMode(PA3, INPUT); + digitalWrite(PIN_PWR_CTRL, HIGH); + GNSS.begin(115200); + bootTimer = millis(); + currentState = BOOTING; + fixQuality = 0; + satellites = 0; + sysMsg = "POWER ON: Booting..."; + } else { + digitalWrite(PIN_PWR_CTRL, LOW); + GNSS.end(); + pinMode(PA2, INPUT); + pinMode(PA3, INPUT); + ntripWasWorking = false; + sysMsg = "POWER OFF: Standby"; + } +} + +void parseNMEA() { + while (GNSS.available()) { + char c = GNSS.read(); + if (c == '$') { nmeaBuffer = "$"; continue; } + if (c == '\n') { + if (nmeaBuffer.startsWith("$GNGGA") || nmeaBuffer.startsWith("$GPGGA")) { + int commaIndex = 0; + int satStart = -1; + for (unsigned int i = 0; i < nmeaBuffer.length(); i++) { + if (nmeaBuffer[i] == ',') { + commaIndex++; + if (commaIndex == 6 && (i + 1) < nmeaBuffer.length()) { + fixQuality = nmeaBuffer[i + 1] - '0'; + } + else if (commaIndex == 7 && (i + 1) < nmeaBuffer.length()) { + satStart = i + 1; + } + else if (commaIndex == 8 && satStart != -1) { + satellites = nmeaBuffer.substring(satStart, i).toInt(); + break; + } + } + } + rxCount++; + newDataReceived = true; + } + nmeaBuffer = ""; + } else if (c != '\r' && nmeaBuffer.length() < 100) { + nmeaBuffer += c; + } + } +} + +// Local Dashboard (ANSI Colors for PuTTY/TeraTerm) +void drawDashboard(bool pa4_on, bool pa5_on) { + char uptimeBuf[16]; + getUptimeString(uptimeBuf, sizeof(uptimeBuf)); + + Serial.print("\033[H"); // Reset cursor to top-left + Serial.println("==========================================="); + Serial.println(" RTK BLACKPILL TELEMETRY SYSTEM "); + Serial.println("==========================================="); + + // 1. System State + Serial.print("SYSTEM STATE: "); + if (currentState == RUNNING) Serial.println("\033[1;32mRUNNING \033[0m"); + else if (currentState == BOOTING) Serial.println("\033[1;33mBOOTING \033[0m"); + else if (currentState == HARD_RESETTING) Serial.println("\033[1;31mPWR CYCLING \033[0m"); + else Serial.println("\033[1;35mBROWNOUT \033[0m"); + + // 2. Hardware Monitors + Serial.print("BATTERY: "); Serial.print(vBatF, 2); Serial.println(" V "); + Serial.print("UPTIME: "); Serial.println(uptimeBuf); + + Serial.print("PWR IN (PA4): "); + if (pa4_on) Serial.println("\033[1;32mACTIVE (>4.0V)\033[0m "); + else Serial.println("\033[1;37mOFF (BATTERY)\033[0m "); + + Serial.print("IGN IN (PA5): "); + if (pa5_on) Serial.println("\033[1;32mACTIVE (HIGH)\033[0m "); + else Serial.println("\033[1;37mOFF (LOW)\033[0m "); + + // 3. Update Rate (Restored) + Serial.print("GNSS TX RATE: "); + if (currentState == BOOTING || currentState == BROWNOUT) { + Serial.println("\033[1;37mOFFLINE \033[0m"); + } else if (isSlowTxRate) { + Serial.println("\033[1;33mEVERY 30 SECONDS\033[0m"); + } else { + Serial.println("\033[1;36m1Hz (1 SECOND) \033[0m"); + } + + // 4. GNSS Status + Serial.print("RTK FIX: "); + if (fixQuality == 4) Serial.println("\033[1;32m[4] FIXED \033[0m "); + else if (fixQuality == 5) Serial.println("\033[1;36m[5] FLOAT \033[0m "); + else if (fixQuality > 0) Serial.println("\033[1;33m[1] 3D FIX\033[0m "); + else Serial.println("\033[1;31m[0] NO FIX\033[0m "); + + Serial.print("PACKETS TX'd: "); Serial.print(rxCount); Serial.println(" "); + + // 5. System Logs + Serial.println("-------------------------------------------"); + Serial.print("LAST EVENT: "); + + // Padding the const char* message to cleanly overwrite previous longer messages + String paddedMsg = String(sysMsg); + while(paddedMsg.length() < 30) paddedMsg += " "; + Serial.println(paddedMsg); + Serial.println("==========================================="); +} + +// Visual State Indicator +void updateLEDs() { + unsigned long m = millis(); + bool ext = false; + switch (currentState) { + case BROWNOUT: + ext = (m % 2000 < 50); // Heartbeat pulse (Sleep) + break; + + case BOOTING: + ext = (m % 500 < 250); // Steady Medium Blink (Booting GNSS) + break; + + case HARD_RESETTING: + ext = (m % 100 < 50); // Aggressive Strobe (Fatal Error / Cycle) + break; + + case RUNNING: + if (fixQuality == 4) { + ext = true; // Solid ON (RTK Fixed - Perfect) + } + else if (fixQuality > 0) { + unsigned long cycle = m % 1000; + ext = (cycle < 100) || (cycle > 200 && cycle < 300); // Double blink (Float/3D) + } + else { + ext = (m % 1000 < 500); // Steady Slow Blink (Searching) + } + break; + } + + digitalWrite(PIN_EXT_LED, ext); + digitalWrite(PIN_LED, !ext); // Inverted for onboard Active-Low LED +} + +// --- SETUP --- +void setup() { + pinMode(PIN_PWR_CTRL, OUTPUT); + pinMode(PIN_LED, OUTPUT); + pinMode(PIN_EXT_LED, OUTPUT); + pinMode(PIN_IGN_SENSE, INPUT_PULLDOWN); + + Serial.begin(115200); + analogReadResolution(12); + + // Reserving memory prevents heap fragmentation + nmeaBuffer.reserve(100); + + Serial.print("\033[2J\033[H"); // Clear entire screen in PuTTY on boot + setPower(false); + currentState = BROWNOUT; +} + +// --- MAIN LOOP --- +void loop() { + updateSensors(); + parseNMEA(); + updateLEDs(); + + bool pa4_on = (v5vF > 4.0); + bool pa5_on = (digitalRead(PIN_IGN_SENSE) == HIGH); + + // Rate Truth Table: 1Hz only when both 5V and Ignition are active + bool fastTxMode = (pa4_on && pa5_on); + + // Dashboard Update + if (newDataReceived || millis() - lastDashUpdate > 2000) { + drawDashboard(pa4_on, pa5_on); + lastDashUpdate = millis(); + newDataReceived = false; + } + + // Brownout Check Failsafe + if (vBatF < BAT_CRITICAL && !pa4_on && !pa5_on) { + if (brownoutTimer == 0) brownoutTimer = millis(); + if (millis() - brownoutTimer > 3000) { + if (currentState != BROWNOUT && currentState != HARD_RESETTING) { + sysMsg = "ERR: Bat Critical. Sleeping."; + setPower(false); + currentState = BROWNOUT; + } + } + } else { brownoutTimer = 0; } + + // State Machine + switch (currentState) { + case BROWNOUT: + if (pa4_on || pa5_on || vBatF > BAT_RECOVERY) setPower(true); + break; + + case BOOTING: + if (millis() - bootTimer > BOOT_STABILIZE_TIME) { + sysMsg = "Applying initial UBX."; + if (fastTxMode) { + GNSS.write(UBX_1HZ, sizeof(UBX_1HZ)); + isSlowTxRate = false; + } else { + GNSS.write(UBX_30S, sizeof(UBX_30S)); + isSlowTxRate = true; + } + currentState = RUNNING; + failureTimer = 0; + } + break; + + case HARD_RESETTING: + if (millis() - resetCooldownTimer > POWER_CYCLE_TIME) currentState = BROWNOUT; + break; + + case RUNNING: + // A. Dynamic Rate Switching + if (fastTxMode && isSlowTxRate) { + sysMsg = "Ignition ON: 1Hz Mode"; + GNSS.write(UBX_1HZ, sizeof(UBX_1HZ)); + isSlowTxRate = false; + } else if (!fastTxMode && !isSlowTxRate) { + sysMsg = "Ignition OFF: 30s Mode"; + GNSS.write(UBX_30S, sizeof(UBX_30S)); + isSlowTxRate = true; + } + + // B. Watchdog Logic (Reset triggers if RTK drops) + if (fixQuality >= 4) { + if (!ntripWasWorking) { + sysMsg = "Fix OK. Watchdog Armed."; + ntripWasWorking = true; + } + failureTimer = 0; + softResetDone = false; + } + else if (ntripWasWorking && fixQuality < 4) { + if (failureTimer == 0) failureTimer = millis(); + unsigned long elapsed = millis() - failureTimer; + + // Soft Reset Trigger + if (elapsed > SOFT_RESET_DELAY && !softResetDone) { + sysMsg = "WARN: Fix Lost. Soft Reset..."; + GNSS.write(UBX_SOFT_RESET, sizeof(UBX_SOFT_RESET)); + softResetDone = true; + } + + // Hard Reset Trigger + if (elapsed > HARD_RESET_DELAY) { + sysMsg = "FATAL: Hard Cycle..."; + setPower(false); + currentState = HARD_RESETTING; + resetCooldownTimer = millis(); + } + } + break; + } +} \ No newline at end of file diff --git a/99-Archive/situation_ORIGINAL.md b/99-Archive/situation_ORIGINAL.md new file mode 100644 index 0000000..116c23c --- /dev/null +++ b/99-Archive/situation_ORIGINAL.md @@ -0,0 +1,22 @@ +# Contexto del Sistema de Rastreo GPS RTK + +## 1. Hardware y Ensamblaje +* **Dispositivo GPS:** Unidades Ardusimple modelo RTK2GO con módulos 4G integrados. +* **Alimentación:** Rango operativo de 12V a 24V, adaptable según la unidad en la que se instale. +* **Carcasa Interna:** Cajas "HBOX" impresas en 3D. +* **Protección Externa:** Gabinete con certificación IP65 para resistir las inclemencias del clima. +* **Control de Fallos (Watchdog):** Circuito interno basado en un microcontrolador Blackpill. Ejecuta un firmware en C++ (`blackpillrtkcode.ino`) encargado de monitorear la energía (batería e ignición) y reiniciar el sistema GPS en caso de pérdida de conexión NTRIP. + +## 2. Proceso de Configuración del GPS +La configuración de cada módulo se realiza mediante las herramientas **4G NTRIP Master** y **u-center** siguiendo este procedimiento: +* Desactivar temporalmente los puertos UART 1 y UART 2 (baudrate a 115200). +* Cargar el archivo de parámetros base (`4G_NTRIP_HelgenSRV-NMEA.txt`), modificando el ID de manera individual para cada unidad. +* Reactivar los puertos UART 1 y UART 2 con la siguiente configuración: + * **Protocol In:** RTCM3 + * **Protocol Out:** NMEA +* Guardar la configuración en la memoria no volátil del dispositivo. + +## 3. Recepción de Datos y Backend +* **Gateway/Servicio:** Un script en Python (`retranslator.py`) recibe vía UDP la telemetría NMEA que envían los módulos 4G. +* **Filtros de Seguridad:** El script cuenta con validaciones anti-saltos y un filtro de velocidad. Si el vehículo reporta un avance o brinco superior al límite establecido (más de 50 km/h o brincos de coordenadas irreales), el sistema descarta esa transmisión para proteger la integridad del odómetro hasta que la métrica regrese a la normalidad. +* **Plataforma de Cliente:** Una vez procesada y filtrada, la información legítima se retransmite hacia **Wialon**, que sirve como el panel final de visualización y reporte para el cliente. \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ea9afc6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,235 @@ +--- +tags: [project-drogheda, master-index, gps-rtk, hbox] +project: Project Drogheda +author: Helgen CTO +date: 2026-06-20 +status: Active +--- + +# Project Drogheda — Master Index (CLAUDE.md) + +> **Source of truth for this workspace.** Read this first, then +> [[00-Project-Management/PLAN|PLAN]] for current priorities. **Human engineers +> onboarding** should start with [[00-Project-Management/Engineering-Handover|Engineering-Handover]] +> (deep context: product, RCA, fixes, open items, glossary) and the repo `README.md`. +> You are acting as the +> **CTO/CSO of Helgen Technologies** — principal hardware/software architect for +> the Drogheda GPS-RTK fleet. Tone: precise, fail-safe first. Keep this index and +> its directory table current as files change. + +> **Codename:** the team / Google Drive / official manual call this project +> **Drogheda (GPS RTK)**. This local folder was renamed from "Galway" → "Drogheda" +> on 2026-06-20; all docs now use **Drogheda**. + +--- + +## 0. Where we are (2026-06-20) + +Helgen deployed **HBOX** RTK trackers on heavy port equipment at **Hutchison Ports +ICAVE (Veracruz)**. They worked "ish," then failed. We are mid-crisis and +hardening the product for scale. Three independent failure fronts, all being +fixed in parallel: + +1. **Backend (confirmed in code).** The production `retranslator.py` blocks the + whole fleet on any upstream slowness, and an anti-jump filter can permanently + black-hole a healthy unit → some "dead" units may be backend victims, not dead + radios. See [[02-Architecture-Software/backend-review|backend-review]]. +2. **Thermal (component-level).** The **ASA** HBOX (UV-stable, softens ~95–100 °C — + the case is fine) sits sealed inside a sealed **ABS IP67** cabinet that also holds + the **Mean Well** supply → nested heat trap that cooks the **battery / boost / + modem** in full sun. See [[06-Testing-Validation/RCA-2026-06-field-failures|RCA]]. +3. **Prototype hardware.** Dupont, hand-wiring, 18650 + PowerBoost — the client's + (ICAVE) own indictment. Fix = ruggedize now + production PCB. + +**Status of units:** all pulled from machines. **4 in hand (3 dead, 1 +working-ish)**, physically in **Veracruz**; team in **Querétaro** → remote debug. +Units run **firmware v1** (no thermal log). Triage: +[[06-Testing-Validation/remote-diagnostic-protocol|remote-diagnostic-protocol]]. + +**Commercial reality:** productized across **ICAVE, TIMSA, EIT, LCT** (Hutchison +terminals); target **30 units operational by August**; hundreds of cabinets +planned. The fix must be production-grade and repeatable. + +**Logs & observability (2026-06-20).** Analyzed the first real bench + healthy-baseline +log set (u-blox `.ubx`, 4G-NTRIP console `.log`, server retranslator tcpdump) + the +caster/F9P config. **GNSS receivers are healthy but no unit reached RTK — all SPS** +(corrections not applied: free **rtk2go** caster + **RAM-only** F9P config + indoor sky). +The **4G-NTRIP console is an unreliable health signal** (see §6 — "master-14 fault" +retracted; only **master 21** confirmed). Built **`hbox-log-tools`** (Log Explorer + +analyzer + dashboard + manual) and a **Blackpill v2.1 logging spec** to fix observability. +Canonical: `06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings-Summary.md`. + +--- + +## 1. System architecture (corrected from the BOM, 2026-06-20) + +```text + Machine power+ignition (12/24/48/110/230 V, per equipment) + │ + ▼ ┌──────────────── Gabinete IP67 (ABS, Yetlebox P3901) ─────────────────┐ + breaker│ Phoenix SPD · fuse holder · DIN rail · SSR/opto (ignition isolation)│ + + SPD ►│ Mean Well DDR-60G-24 ──► regulated 24 Vdc ───┐ │ + │ │ GX16-5 connector │ + │ ┌────────── HBOX (3D-printed ASA) ─────────┼────┐ │ + │ │ Mean Well SD-15B-05 (18–36V→5V,3A) ──► 5V rail │ │ + │ │ ├► PowerBoost 1000C (+18650 Li-ion 1S4P) backup │ + │ │ ├► STM32F411 Blackpill WATCHDOG ──UART(PA2)──► simpleRTK2B Pro │ + │ │ │ └ switches RTK 5V via 2N3904→IRF9540N (PA1) │ + │ │ ├► simpleRTK2B Pro (ZED-F9P) + 4G NTRIP Master (XBee LTE) │ + │ │ └► ignition 24V ──► DFR1015 buck ──► STM32 PA5 (ign sense) │ + │ └──────────────────────────────────────────────────────────────┘ │ + └───────────────────────────────────────────────────────────────────┘ + 4G/LTE ▲ RTCM3 in (NTRIP) │ NMEA out (UDP) + Base station (Ardusimple AS-PRO-CORS) ────┘ ▼ + retranslator.py ─(anti-jump+speed filter)─► Wialon / Traccar ─► VERONICA +``` + +- **Power into HBOX = clean 24 V** from the cabinet (via **GX16-5**). Internal + **Mean Well SD-15B-05** makes 5 V; **PowerBoost 1000C + 18650** is battery + backup; **DFR1015** conditions ignition to the Blackpill. +- **Corrections arrive over the 4G modem** (RTCM3 via NTRIP), not the GNSS — so a + GNSS reset can't fix a dead link; recover by power-cycling the stack (v2 does). +- **Watchdog** = STM32F411; see [[03-Architecture-Hardware/watchdog-schematic|watchdog-schematic]]. + +--- + +## 1b. Canonical invariants (inherits `Helgen Project Ethos`) + +The rules this system must never violate. Check every design/code output against +these before presenting; if it breaks one, say so and propose the compliant fix. + +1. **Power-off is the safe state.** The RTK stack defaults OFF (gate pull-up) until + the MCU asserts; loss of confidence → safe state, not best-guess. +2. **Never vanish, never loop.** A unit must keep reporting (standalone if needed) + rather than go silent, and must never enter an infinite fast-reboot loop. +3. **Corrections ride the 4G modem, not the GNSS** — recover the link by + power-cycling the stack; a GNSS reset is never a fix for lost NTRIP. +4. **No material that softens below ~80 °C (PLA-class) in the thermal path** — the + current **ASA** enclosure complies. Design to the measured cabinet-interior + temperature, not spec-sheet ambient. +5. **Backend reads/forwards without head-of-line blocking**, and the anti-jump + filter must never permanently lock out a healthy unit. +6. **No secrets in the repo — ever.** (The committed MSSQL password is a live + violation to remediate.) + +## 1c. Hard constraints (non-negotiables) + +- HBOX input is the cabinet's **regulated 24 V** + isolated ignition; don't + re-implement the cabinet's surge/breaker/SSR on the HBOX. +- **IP67 maintained** — cooling by conduction only; no IP-breaking vents/fans. +- **Bench-test firmware before any field flash** (no STM32 build in the sandbox). +- Client-facing docs are **Spanish**, routed through Leo. +- Use the **file tools** for file changes; verify the shell mount before bulk moves. + +--- + +## 2. Directory (Helgen English `NN-` standard — keep this table current) + +| Folder | Holds | Key files | +|---|---|---| +| `00-Project-Management` | plan, ethos, hackathon, **financials**, **handover**, client comms, Drive tooling | [[00-Project-Management/PLAN\|PLAN]] · [[00-Project-Management/Engineering-Handover\|Engineering-Handover]] · `ethos.md` · `hackathon-plan.md` · `Financials.md` · `Drive-Upload/` · `client-comms/` | +| `01-Requirements` | what we build | `SRS.md` · `HBOX-product-spec.md` | +| `02-Architecture-Software` | backend/software analysis, ADRs | `backend-review.md` | +| `03-Architecture-Hardware` | HBOX/cabinet, wiring, BOM, **watchdog PCB v3** | `hardware-respec-and-pcb-brief.md` · `cabinet-electrical-bom.md` · `watchdog-schematic.md` · `watchdog-health-telemetry.md` · `firmware-review.md` · `BOM-master.md` · **`watchdog-pcb-v3-design.md`** (schematic spec: +2 fans, +I²C temp/RH) · `watchdog-pcb-v3-bom.xlsx` (proto+production, LCSC) · `watchdog-pcb-v3-kicad-guide.md` (KiCad→JLCPCB) · `watchdog-pcb-v3-blockdiagram.svg` | +| `04-Source-Code` | firmware + backend repos | `firmware/` (**own git repo `hbox-firmware`**: `blackpillrtkcode_v2/` deploy · `legacy/` v1 · `docs/LOGGING-SPEC.md` v2.1) · `htracker/` · `notifications/` · `rtk-config/` | +| `05-Analytics-Reports` | SQL views, dashboards (VERONICA/PowerBI) | _(placeholder)_ | +| `06-Testing-Validation` | RCA, diagnostics, test evidence, **log analysis & tooling** | `RCA-2026-06-field-failures.md` · `remote-diagnostic-protocol.md` · `2026-06-20_Drogheda_Bench-Log-Analysis_v1.0.md` (rev-3) · `2026-06-20_Drogheda_GNSS-Findings-Summary.md` · `log-analysis/` (repo **`hbox-log-tools`**: `log-explorer.html` · `analyze_logs.py` · `build_dashboard.py` · `docs/manual.pdf`) | +| `Marketing` | product-facing collateral (LaTeX→PDF) | `HBOX-Datasheet/` (industrial spec sheet) · `HBOX-Brochure-RTK/` (flyer) | +| `99-Archive` | superseded raw inputs (read-only) | `situation_ORIGINAL.md` | +| `.claude/skills/` | vendored project skill | `project-drogheda/` | + +> Structure follows the **[[Helgen Project Template]]** (English `NN-`). The Google +> Drive mirror keeps the legacy Spanish `00_…99_` scheme (Drive manual); finalized +> deliverables export there. Naming: `AAAA-MM-DD_Drogheda_Descripción_vX.X` — no +> loose files in root except this index. **Empty legacy Spanish dirs remain (the +> mount can't delete them) — remove locally:** `rmdir 0?_* 99_Archivo docs hardware …` + +--- + +## 3. HBOX BOM (per unit, 24 V variant — see `03-Architecture-Hardware/BOM-master`) + +simpleRTK2B **Pro** (AS-RTK2B-PROF9P-L1L2-NH-00) · 4G NTRIP Master +(AS-XBEE-4G-NA-UFL-00) · Blackpill STM32F411 · U-blox multiband antenna +(AS-ANT2B-ANN-L1L2-50SMA) + 2× LTE antennas (AR1582) · **Mean Well SD-15B-05** +(24→5 V) · **PowerBoost 1000C** + **18650 1S4P** · **DFR1015** (ignition) · +2N3904 + IRF9540N + 2N7000 watchdog · **GX16-5** HBOX↔cabinet · ASA case/top/ +brackets · Telcel micro-SIM. + +--- + +## 4. Deployment & people + +**BUs / terminals:** ICAVE (stress lab, live), TIMSA, EIT, LCT. **ICAVE machine +voltages:** RTG 24 V, Reach Stacker 24 V, Empty Handler 24 V, Tractoplana 12 V, +Montacargas 12 V. **Target:** 30 units by August. + +| Name | Email | Role | +|---|---|---| +| Leo Rodriguez | leo@helgen.tech / ceo@helgen.tech | Helgen — founder/CEO | +| Adriana Del Angel | adriana@helgen.tech | Helgen — implementation/plan | +| Paola Rodriguez | paola@helgen.tech | Helgen | +| Angel Daniel Martinez | daniel@helgen.tech | Helgen | +| Gibran Ortega | gibran@helgen.tech | Helgen — GPS/telemetry | +| **Agustín Alfonseca** | alfonseca.agustin@icave.com.mx | **Client** — ICAVE Tech Research Supervisor | +| Leon David Piña | pina.david@hutchisonports.com.mx | Hutchison Ports | + +--- + +## 5. Where things live (off this workspace) + +- **Google Drive (team source of truth):** `Drogheda (GPS RTK)` — official + numbered structure 00–99 + manual `LEEME_Drogheda_Estructura_y_Uso`. Mirror this + workspace's structure to it for finalized deliverables. +- **Second Brain vault** (`~/Documents/Second Brain/03 Projects/Helgen/`): only + polished, evergreen notes (decisions, lessons, contacts) — see + `Project-Drogheda/` once synced. +- **Backend repo:** `ssh://git.helgen.dev:6611/htracker` (retranslator + monitor), + cloned at `04-Source-Code/htracker` (branch `feature/test500unidades`). +- **Firmware repo:** `ssh://git.helgen.dev:6611/hbox-firmware` (STM32F411 watchdog), + lives at `04-Source-Code/firmware` (branch `main`) — its own git repo with README, + `docs/PINOUT.md`, redacted RTK-config template, CHANGELOG. Push instructions in its README. + **v2.1 logging spec** at `docs/LOGGING-SPEC.md` (structured/parseable Blackpill log; not yet flashed). +- **Log-tools repo:** `ssh://git.helgen.dev:6611/hbox-log-tools` (HBOX log analysis — + in-browser **Log Explorer** + batch **analyzer** + dashboard + Spanish LaTeX **manual**), + lives at `06-Testing-Validation/log-analysis/`. Raw logs + config files git-ignored + (credentials). Built 2026-06-20. +- **Project tracker:** Notion — **`HBOX Unit Status — Bench Triage`** board under + *Project Drogheda — GPS RTK* (corrected to rev-3, 2026-06-20) + Mattermost (team chat). + +--- + +## 6. Hard facts — don't relearn these + +- NMEA GGA quality ≥4 = healthy (4 = RTK-fixed, 5 = RTK-float). +- ZED-F9P rated −40…+85 °C — heat casualties are the **18650/PowerBoost** and the + **4G modem**, not the GNSS or the case. +- **The case is ASA** (UV-stable, softens ~95–100 °C) — adequate for the nested ABS + IP67 cabinet interior; the enclosure is **not** the liability. **A bare fan inside a + sealed box does not cool it** — conduct heat out, or use a ducted fan (v3.0). See hardware brief §2b. +- Corrections come over 4G → recover by power-cycling the stack (v2), not a GNSS + reset. Backend can take healthy units off Wialon (HOL block + anti-jump lockout). +- HBOX input = **24 V** from the cabinet; design the redesign around that. +- Firmware: edit `04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino` + (own git repo `hbox-firmware`); **bench-test before field flash** (sandbox can't build STM32). +- **20 Jun logs — receivers are fine; RTK is not being achieved.** Every board emits valid + NMEA (good sats/HDOP) but **every epoch is SPS (1), zero RTK (4/5)**. Corrections aren't + applied: **free public rtk2go caster** (no SLA), F9P config dumped from **RAM only** (verify + saved to FLASH; check the Pro's XBEE↔UART2 switch), plus indoor sky. F9P message config is + correct (GGA on UART2 @1 Hz). +- **The 4G-NTRIP-Master console does NOT prove forwarding** — a healthy unit logs the same + sparse console ("registered", no `State main`/`UDP Bytes sent`) as a failed-looking one. + Confirm health only from a `.ubx` (fix), the Blackpill, or a server delivery capture. + → **"master-14 ingest fault" RETRACTED**; only **master 21** (modem `0xFFFFF` + 511 AT errors) + is a confirmed fault. +- **Healthy GNSS baseline = `RTK14NTRIP14_440`** (server-verified 1 Hz delivery, 12 sats, + HDOP 0.75, **but SPS not RTK**). Raise the bar to an open-sky fix-≥4 capture. +- **Heat is NOT implicated** by the bench logs (faults reproduce cold; v1 has no thermal + channel). Field thermal hypothesis still needs a thermocouple cabinet soak. +- **`dashboard.html` predates the rev-3 retraction** (still marks the no-forward captures + "Failed"); the canonical corrected record is the GNSS summary + the rev-3 findings doc. + +## 7. Working notes +- Use the **file tools** (Read/Write/Edit) for file changes; the shell mount can + go stale — verify before bulk shell moves. Six empty legacy dirs (`docs`, + `firmware`, …) remain from the restructure; delete them manually. +- Client-facing docs are **Spanish** and route through Leo. diff --git a/Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf b/Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf new file mode 100644 index 0000000..49f6c92 Binary files /dev/null and b/Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf differ diff --git a/Marketing/HBOX-Brochure-RTK/main.pdf b/Marketing/HBOX-Brochure-RTK/main.pdf new file mode 100644 index 0000000..49f6c92 Binary files /dev/null and b/Marketing/HBOX-Brochure-RTK/main.pdf differ diff --git a/Marketing/HBOX-Brochure-RTK/main.tex b/Marketing/HBOX-Brochure-RTK/main.tex new file mode 100644 index 0000000..a8fd72c --- /dev/null +++ b/Marketing/HBOX-Brochure-RTK/main.tex @@ -0,0 +1,84 @@ +% Helgen Drogheda — HBOX · Ficha comercial de producto (flyer 1 pág.) +% Compilar: latexmk -xelatex main.tex +\documentclass[10pt]{article} +\newcommand{\sistema}{HBOX} +\newcommand{\sistemapdf}{HBOX RTK — Localización de Alta Precisión} +\newcommand{\doctipo}{Ficha de Producto} +\input{../../_brand/estilo-helgen.tex} +\geometry{margin=1.3cm} +\pagestyle{empty} +\setlength{\parindent}{0pt} + +\begin{document} + +% ---------- Cabecera de marca ---------- +\begin{tcolorbox}[enhanced, colback=HelgenIndigo, colframe=HelgenIndigo, arc=3pt, + left=14pt,right=14pt,top=12pt,bottom=12pt, boxrule=0pt] + {\helgentitle\fontsize{26}{28}\selectfont\color{white}Helgen}\hfill + {\helgentitle\small\color{HelgenCyan}Ingeniería que produce, mantenimiento que protege}\\[8pt] + {\color{HelgenCyan}\rule{\linewidth}{1.4pt}}\\[8pt] + {\helgentitle\fontsize{24}{27}\selectfont\color{white}HBOX — Localización de Alta Precisión}\\[3pt] + {\helgentitle\fontsize{13}{16}\selectfont\color{HelgenCyan}GPS RTK · precisión centimétrica para equipo portuario e industrial} +\end{tcolorbox} + +\vspace{6pt} +{\helgentitle\large\color{HelgenIndigo}\textbf{Precisión centimétrica. Conectividad robusta. Ingeniería Helgen.}}\\[4pt] +La \textbf{HBOX} es una solución integral de posicionamiento \textbf{RTK} para entornos +donde la exactitud y la confiabilidad son esenciales. Arquitectura de dos componentes — +\textbf{Rover} (en el equipo) + \textbf{Estación Base} (correcciones diferenciales) — que +entrega posición de \textbf{nivel centimétrico}, estable y continua, sobre red 4G. + +\vspace{8pt} +\begin{minipage}[t]{0.56\textwidth} + {\helgentitle\large\color{HelgenIndigo}\textbf{Beneficios clave}}\\[5pt] + \begin{itemize}[leftmargin=1.3em, itemsep=4pt] + \item Precisión \textbf{$<$2 cm} con NTRIP hasta \textbf{35 km}, actualización desde 1 s. + \item Arquitectura \textbf{Rover + Base Station} para máxima precisión y continuidad. + \item Tecnología \textbf{RTK} confiable en patios, grúas y maquinaria pesada. + \item Carcasa \textbf{IP67}: resistente a polvo, agua y vibración. + \item Diseño \textbf{compacto y robusto}, de fácil integración en cualquier plataforma. + \item \textbf{Watchdog} embebido: recupera el enlace de forma autónoma y nunca queda en silencio. + \end{itemize} + + \vspace{4pt} + {\helgentitle\large\color{HelgenIndigo}\textbf{Aplicaciones}}\\[4pt] + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Portuaria \quad + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Logística \quad + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Industrial \quad + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Agrícola \quad + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Marítima \quad + \textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}~Urbana +\end{minipage}\hfill +\begin{minipage}[t]{0.40\textwidth} + \begin{tcolorbox}[enhanced, colback=HelgenMist, colframe=HelgenIndigo, arc=2pt, boxrule=0.6pt, + left=8pt,right=8pt,top=7pt,bottom=7pt, coltitle=white, colbacktitle=HelgenIndigo, + fonttitle=\helgentitle\bfseries\small, title={Especificaciones}] + \footnotesize + \begin{tabularx}{\linewidth}{@{}>{\bfseries\color{HelgenIndigo}}l X@{}} + GNSS & u-blox ZED-F9P, multibanda\\ + Bandas & L1 · L2 · L5 · L6\\ + Precisión & $<$2 cm (NTRIP, $\leq$35 km)\\ + Tasa & desde 1 Hz\\ + Correcciones & NTRIP/RTCM3 sobre 4G\\ + Red & 2G · 3G · 4G (MicroSIM)\\ + Entrada & 24 V (gabinete) + ignición\\ + Conector & GX16-5 con seguro\\ + Carcasa & IP67 (polvo/agua/vibración)\\ + \end{tabularx} + \end{tcolorbox} + + \vspace{6pt} + \begin{center} + \begin{tikzpicture}[node distance=5mm and 7mm, font=\scriptsize] + \node[cajaoscura, text width=2.0cm] (base) {Estación\\Base RTK}; + \node[caja, below=of base, text width=2.0cm] (rover) {Rover\\HBOX}; + \node[etiqueta, right=10mm of base] (sat) {NTRIP / 4G}; + \draw[flechaacento] (base) -- node[etiqueta,right]{RTCM3} (rover); + \end{tikzpicture} + \end{center} +\end{minipage} + +\vfill +{\color{HelgenCyan}\rule{\linewidth}{1.2pt}}\\[3pt] +{\footnotesize\helgentitle\color{HelgenIndigo}\textbf{Helgen Technologies}\quad\textcolor{gray}{·\ \ Helgen Servicios Especializados S.A. de C.V.\ \ ·\ \ ventas@helgen.tech\ \ ·\ \ Confidencial}} +\end{document} diff --git a/Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf b/Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf new file mode 100644 index 0000000..37e0b77 Binary files /dev/null and b/Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf differ diff --git a/Marketing/HBOX-Datasheet/main.pdf b/Marketing/HBOX-Datasheet/main.pdf new file mode 100644 index 0000000..37e0b77 Binary files /dev/null and b/Marketing/HBOX-Datasheet/main.pdf differ diff --git a/Marketing/HBOX-Datasheet/main.tex b/Marketing/HBOX-Datasheet/main.tex new file mode 100644 index 0000000..2d6ff7f --- /dev/null +++ b/Marketing/HBOX-Datasheet/main.tex @@ -0,0 +1,175 @@ +% Helgen Drogheda — HBOX · Ficha Técnica / Datasheet (estilo industrial, 2 pág.) +% Modelo de referencia: SPEC SHEET H-BOX (Canva). Compilar: latexmk -xelatex main.tex +\documentclass[9pt]{extarticle} +\newcommand{\sistema}{HBOX} +\newcommand{\sistemapdf}{HBOX Datasheet} +\newcommand{\doctipo}{Ficha Técnica} +\input{../../_brand/estilo-helgen.tex} +\usepackage{amssymb} +\geometry{margin=1.0cm} +\pagestyle{empty} +\setlength{\parindent}{0pt} +\setlength{\parskip}{0pt} +\renewcommand{\arraystretch}{1.04} + +% Banda de sección compacta +\newcommand{\dssec}[1]{\vspace{3pt}{\setlength{\fboxsep}{2.2pt}\noindent\colorbox{HelgenIndigo}% + {\makebox[\dimexpr\linewidth-2\fboxsep][l]{\helgentitle\footnotesize\color{white}\,#1}}}\vspace{1.5pt}\\[0.5pt]} +% Fila de spec: \spec{Parámetro}{Valor} +\newcommand{\dstab}[1]{\begin{tabularx}{\linewidth}{@{}>{\bfseries\color{HelgenIndigo}}p{4.6cm} X@{}}#1\end{tabularx}} + +\begin{document} + +% ---------- Cabecera ---------- +\noindent +\begin{minipage}[c]{0.30\linewidth}{\helgentitle\fontsize{22}{24}\selectfont\color{HelgenIndigo}Helgen}\end{minipage}% +\begin{minipage}[c]{0.42\linewidth}\centering{\helgentitle\large\color{HelgenIndigo}HBOX\,–\,24V\,–\,LTE}\\[1pt]{\scriptsize\color{HelgenTeal}Ficha Técnica · Rastreador GPS-RTK}\end{minipage}% +\begin{minipage}[c]{0.28\linewidth}\raggedleft{\helgentitle\large\color{HelgenIndigo}HBOX}\,{\footnotesize\color{HelgenTeal}series}\end{minipage}\\[3pt] +{\color{HelgenCyan}\rule{\linewidth}{1.4pt}}\\[4pt] + +{\footnotesize La \textbf{HBOX} entrega posición \textbf{RTK de precisión centimétrica} sobre 4G en equipo +portuario e industrial. Arquitectura \textbf{Rover + Estación Base}, gabinete \textbf{IP67}, watchdog +embebido. La electrónica interna es idéntica en todas las variantes de gabinete (12/24/48/110/230 V).}\\[2pt] + +% ================= GNSS / RTK ================= +\dssec{GNSS / RTK — Desempeño} +\dstab{% +GNSS & u-blox ZED-F9P, multibanda, multiconstelación (GPS, GLONASS, Galileo, BeiDou)\\ +Bandas & L1 · L2 · L5 · L6\\ +Precisión (RTK fijo) & $<$2 cm + 1 ppm CEP, con NTRIP hasta 35 km\\ +Tasa de salida & 1 Hz (activo) / 30 s (reposo, ahorro de energía)\\ +Salida de posición & NMEA (GGA, RMC) por UDP $\rightarrow$ retranslator $\rightarrow$ Wialon/Traccar/VERONICA\\ +} + +% ================= Comunicaciones ================= +\dssec{Comunicaciones — 4G / NTRIP} +\dstab{% +Correcciones & NTRIP / RTCM3 sobre LTE (ArduSimple 4G NTRIP Master)\\ +Redes & 2G · 3G · 4G — MicroSIM (Telcel)\\ +Antenas & 1$\times$ GNSS multibanda (SMA) + 2$\times$ LTE (AR1582)\\ +Recuperación de enlace & Watchdog reinicia el stack con \textit{backoff}; modo LIMP sigue reportando posición estándar\\ +} + +% ================= Entrada ================= +\dssec{Entrada (Input)} +\dstab{% +Voltaje de entrada al HBOX & 24 Vcd (desde el gabinete; diseño 18–36 Vcd)\\ +Voltaje de máquina (gabinete) & 12 / 24 / 48 Vcd · 110 / 230 Vca, según equipo\\ +Ignición & Señal aislada vía relevador de estado sólido (SSR) del gabinete\\ +Consumo típico / pico & $\sim$0.3–0.6 A @ 5 V; picos de TX LTE $\sim$1.5–2 A (validar en banco)\\ +} + +% ================= Salida / rieles internos ================= +\dssec{Rieles internos (Output)} +\dstab{% +Fuente 5 V & Mean Well SD-15B-05 (18–36 V $\rightarrow$ 5 V, 3 A, 15 W)\\ +Fuente del gabinete & Mean Well DDR-60G-24 / HDR-60-24 (entrada por voltaje $\rightarrow$ 24 V, 60 W)\\ +Respaldo de energía & PowerBoost 1000C + batería 18650 Li-ion 1S4P\\ +MCU / watchdog & STM32F411 Blackpill (firmware \texttt{blackpillrtkcode\_v2})\\ +} + +% ================= Protección ================= +\dssec{Protección y seguridad} +\dstab{% +Sobrecorriente (fuente) & Modo \textit{Hiccup}: recuperación automática al retirar la falla\\ +Sobre-temperatura (fuente) & Corte de salida con \textbf{recuperación automática} al disminuir la temperatura\\ +Protección de entrada & Fusible + supresor de picos (SPD Phoenix) + interruptor (breaker), a nivel gabinete\\ +Aislamiento & Ignición aislada (SSR/opto); ZED-F9P alimentado por riel conmutado\\ +} + +% ================= Diagrama de potencia y señal ================= +\dssec{Diagrama de potencia y señal} +\begin{center} +\begin{tikzpicture}[node distance=6mm and 8mm, font=\scriptsize, every node/.style={align=center}] + \node[cajaoscura, text width=1.9cm] (m) {Máquina\\12–230 V}; + \node[caja, right=of m, text width=3.1cm] (gab) {Gabinete IP67\\{\scriptsize SPD · Breaker · SSR}\\Mean Well DDR-60G-24\\$\to$ 24 Vcd}; + \node[caja, right=of gab, text width=3.5cm] (hb) {HBOX\\{\scriptsize SD-15B-05 $\to$ 5 V · PowerBoost+18650}\\STM32 watchdog · ZED-F9P · 4G}; + \node[cajaoscura, above=7mm of hb, text width=2.3cm] (base) {Estación Base\\RTK / NTRIP}; + \node[caja, right=of hb, text width=1.8cm] (cloud) {Wialon /\\VERONICA}; + \draw[flecha] (m) -- node[etiqueta,above]{pot.+ign.} (gab); + \draw[flecha] (gab) -- node[etiqueta,above]{GX16-5} (hb); + \draw[flechaacento] (base) -- node[etiqueta,right]{RTCM3 · 4G} (hb); + \draw[flecha] (hb) -- node[etiqueta,above]{NMEA} (cloud); +\end{tikzpicture} +\end{center} + +\newpage + +% ================= AMBIENTAL / TÉRMICA (destacado) ================= +\dssec{Ambiental / Térmica} +\dstab{% +Temp.\ de operación (interno) & \textbf{$-10$ a $+75$\,\textdegree C} objetivo — componentes calificados $\geq$75\,\textdegree C\\ +Temp.\ del receptor GNSS & $-40$ a $+85$\,\textdegree C (ZED-F9P)\\ +Temp.\ de almacenamiento & $-40$ a $+85$\,\textdegree C\\ +Carga de batería (18650) & \textbf{$\leq$45\,\textdegree C} — no cargar por encima (riesgo de degradación/inflado)\\ +Material de carcasa & \textbf{Serie 1: PLA (ablanda $\sim$50–60\,\textdegree C — a reemplazar)} $\rightarrow$ Serie 2: ASA/PC o metal\\ +Protección térmica & Corte de salida con auto-recuperación (fuente Mean Well)\\ +Humedad & Hasta 95\% RH, sin condensación\\ +Ingreso & \textbf{IP67} (polvo, agua, vibración)\\ +Enfriamiento & Por \textbf{conducción} (caja sellada; sin ventilación que rompa IP67)\\ +} + +\begin{helgennotice}[Nota térmica de diseño (crítica)] +El HBOX opera dentro de un gabinete sellado bajo sol directo: la temperatura interior real +puede superar ampliamente el ambiente. \textbf{Diseñar a la temperatura medida del gabinete}, no +a la ambiente de placa de datos. \textbf{Sin PLA} en la ruta térmica; \textbf{un ventilador dentro +de una caja sellada no la enfría} — el calor debe conducirse al exterior. +\end{helgennotice} + +% ================= Ventana térmica (derating) ================= +\dssec{Ventana térmica de operación} +\begin{center} +\begin{tikzpicture}[xscale=0.082, font=\scriptsize] + \fill[red!14] (50,-0.15) rectangle (60,2.55); + \fill[HelgenTeal!20] (-40,0.0) rectangle (85,0.55); + \fill[HelgenCyan!50] (-10,0.7) rectangle (75,1.25); + \fill[HelgenIndigo!20] (-10,1.4) rectangle (45,1.95); + \node[right] at (-40,0.275) {\,ZED-F9P: $-40$ a $+85$\,\textdegree C}; + \node[right] at (-10,0.975) {\,Objetivo interno: $-10$ a $+75$\,\textdegree C}; + \node[right] at (-10,1.675) {\,Carga 18650: $\leq 45$\,\textdegree C}; + \node[red!65!black, align=center, font=\scriptsize\bfseries] at (55,2.78) {PLA\\50\textendash60\,\textdegree C}; + \draw[->,thick,HelgenIndigo] (-42,-0.28) -- (90,-0.28); + \foreach \t in {-40,-10,0,25,45,60,75,85} + \draw[HelgenIndigo] (\t,-0.20) -- (\t,-0.36) node[below,font=\tiny]{\t\textdegree}; + \node[below,font=\tiny,HelgenIndigo] at (90,-0.46) {Temp.\ ambiente}; +\end{tikzpicture} +\end{center} + +% ================= Mecánico ================= +\dssec{Mecánico} +\dstab{% +Gabinete & Yetlebox P3901, ABS, IP67, 39$\times$29$\times$16 cm, riel DIN, platina\\ +Conector HBOX$\leftrightarrow$gabinete & GX16-5 circular con seguro\\ +Antenas & SMA de mamparo (GNSS) + 2$\times$ LTE por glándula (NPT 3/8)\\ +Montaje & Sobre estructura firme; preferir sombra y buena vista al cielo\\ +} + +% ================= Variantes ================= +\dssec{Variantes de gabinete} +\hwtab{% +\begin{tabularx}{\linewidth}{@{}>{\bfseries}l X >{\centering\arraybackslash}p{2.8cm}@{}} +\rowcolor{HelgenIndigo}\textcolor{white}{Voltaje} & \textcolor{white}{Etapa de potencia (cambia con el voltaje)} & \textcolor{white}{Estado}\\ +24 V & Mean Well DDR-60G-24 · SSR TOS 24VDC · SPD 24DC & \cellcolor{HelgenCyan}\textbf{En producción}\\ +12 V & DDR-60G-24 · SSR MOS 12-28VDC · SPD 12DC & Teórico\\ +48 V & DDR-60L-24 · opto Wago 859-744 · SPD 48DC & Teórico\\ +110 V & HDR-60-24 / Siemens · SSR IDEC · SPD CA & Teórico\\ +230 V & HDR-60-24 · SSR IDEC · SPD CA + fusible Bussman & Teórico\\ +Base & Estación base RTK/NTRIP (Ardusimple AS-PRO-CORS), 110/230 Vca & 1 por terminal\\ +\end{tabularx}} + +% ================= Nomenclatura / pedido ================= +\vspace{4pt} +\noindent\begin{minipage}[t]{0.52\linewidth} +\dssec{Nomenclatura de modelo} +{\footnotesize \texttt{HBOX – \underline{\ \ V\ \ } – LTE} \quad donde \texttt{V} = voltaje del gabinete\\[2pt] +Ej.: \texttt{HBOX–24V–LTE} (unidad en producción).} +\end{minipage}\hfill +\begin{minipage}[t]{0.44\linewidth} +\dssec{Uso previsto} +{\footnotesize $\square$ Flotilla \quad $\square$ Particular \quad $\square$ Pruebas \quad $\square$ Otro: \rule{2.2cm}{0.4pt}} +\end{minipage} + +\vfill +{\color{HelgenCyan}\rule{\linewidth}{1pt}}\\[2pt] +{\scriptsize\helgentitle\color{HelgenIndigo}\textbf{Helgen Technologies}\ ·\ Helgen Servicios Especializados S.A.\ de C.V.\ ·\ ventas@helgen.tech \hfill File: HBOX DS · 2026-06-20 · Confidencial} +\end{document} diff --git a/Marketing/HBOX-Datasheet/main.xdv b/Marketing/HBOX-Datasheet/main.xdv new file mode 100644 index 0000000..2b3267a Binary files /dev/null and b/Marketing/HBOX-Datasheet/main.xdv differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0dc29bb --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# Project Drogheda (GPS RTK) + +Helgen Technologies' **HBOX** RTK GPS tracker fleet for **Hutchison Ports** heavy +equipment (ICAVE / TIMSA / EIT / LCT) — centimetre-class position over 4G, streamed to the +Helgen backend (`retranslator` → Wialon/Traccar → VERONICA), built to survive a hot, salty, +high-vibration port environment. + +> **New here? Read [`00-Project-Management/Engineering-Handover.md`](00-Project-Management/Engineering-Handover.md) first.** +> It is the deep context: product, failure analysis, fixes, repo map, open items, glossary. +> This README is the 2-minute orientation; [`CLAUDE.md`](CLAUDE.md) is the terse machine index. + +--- + +## Where we are (2026-06-20) + +We deployed a first batch of HBOX trackers on ICAVE machines in **Veracruz**. They worked +"ish," then failed. We are **mid-crisis, hardening for scale** — target **30 units by +August**. The failure is **three independent fronts**, fixed in parallel: + +1. **Backend** (confirmed in code) — `retranslator.py` head-of-line blocks the whole fleet + on slow upstream, and an anti-jump filter can **permanently black-hole a healthy unit**. +2. **Thermal** — an **ASA** case (UV-stable, ~95–100 °C — adequate) sealed in a sealed **IP67** + cabinet that also holds the heat-generating **Mean Well** PSU, in full sun → nested heat trap + for the **battery / boost / modem** (not the case). +3. **Prototype hardware** — Dupont + hand-wiring + 18650/PowerBoost; fragile under vibration. + +**Units:** all pulled — **4 in hand (3 dead, 1 working-ish)** in Veracruz; team in Querétaro +→ **remote debug**. Field firmware is **v1** (no thermal log). + +> **Free triage:** a unit **ONLINE in htracker but OFFLINE in Wialon** is a **backend +> victim**, not dead hardware. Check that before condemning a radio. + +--- + +## The product in one breath + +**Rover (HBOX)** on the machine + **Base Station** (Ardusimple AS-PRO-CORS, 110/230 V) +publishing **RTCM3** corrections over **NTRIP**. The rover gets corrections **over the 4G +modem, not the GNSS** → recover a dead link by **power-cycling the 4G stack**, never a GNSS +reset. HBOX core: simpleRTK2B **Pro** (ZED-F9P) · 4G NTRIP Master · STM32F411 watchdog · +Mean Well SD-15B-05 (24→5 V) · PowerBoost + 18650 · GX16-5 to the cabinet. **24 V is the +production variant.** + +## Invariants (never violate) + +1. Power-off is the safe state. 2. Never vanish, never loop. 3. Corrections ride the 4G +modem, not the GNSS. 4. No material that softens below ~80 °C (PLA-class) in the thermal path — +the ASA enclosure complies; design to *measured* cabinet temperature. 5. Backend forwards without head-of-line blocking; anti-jump never +locks out a healthy unit. 6. **No secrets in the repo** (the committed MSSQL password is a +live violation to rotate). + +## Repo layout + +`00-Project-Management` (plan, ethos, financials, **handover**, Drive tooling) · +`01-Requirements` (SRS, product spec, tracker) · `02-Architecture-Software` (backend review) · +`03-Architecture-Hardware` (cabinet/HBOX, BOM, PCB brief, watchdog, manual) · +`04-Source-Code` (`firmware/` · `htracker/` · `notifications/`) · +`05-Analytics-Reports` (SQL views — placeholder) · `06-Testing-Validation` (RCA, diagnostics) · +`Marketing` (datasheet, brochure) · `_brand` (LaTeX style) · `99-Archive` (raw inputs). + +## Quick start + +```bash +# Branded docs (XeLaTeX; League Spartan + MADE Evolve Sans): +cd 01-Requirements/HBOX-Ficha-Tecnica && latexmk -xelatex main.tex + +# Firmware (own repo 'hbox-firmware', at 04-Source-Code/firmware): +# open blackpillrtkcode_v2/blackpillrtkcode_v2.ino in the STM32duino toolchain. +# ⚠ Bench-test before any field flash — the sandbox cannot build STM32. + +# Backend: 04-Source-Code/htracker (ssh://git.helgen.dev:6611/htracker, feature/test500unidades) +``` + +## Storage model (one home per artifact) + +**This repo** = working source of truth (editable `.md`/`.tex`, code). **Google Drive** +`Drogheda (GPS RTK)` = finalized exports (branded PDFs, native Docs/Sheets). **Second Brain +vault** = distilled decisions/lessons. Author in repo → export final to Drive → distill to +vault. Drive upload how-to + mapping: `00-Project-Management/Drive-Upload/README.md`. Never +change Drive sharing — that's Leo's call. + +## Go deeper + +- **Deep context / onboarding** → [`00-Project-Management/Engineering-Handover.md`](00-Project-Management/Engineering-Handover.md) +- Current priorities → [`00-Project-Management/PLAN.md`](00-Project-Management/PLAN.md) +- Machine index → [`CLAUDE.md`](CLAUDE.md) +- Failure analysis → [`06-Testing-Validation/RCA-2026-06-field-failures.md`](06-Testing-Validation/RCA-2026-06-field-failures.md) diff --git a/_brand/estilo-helgen.tex b/_brand/estilo-helgen.tex new file mode 100644 index 0000000..328e489 --- /dev/null +++ b/_brand/estilo-helgen.tex @@ -0,0 +1,117 @@ +% ---------- Estilo de marca Helgen (compartido, Project Drogheda) ---------- +% Reutiliza la identidad de Project Ennis (_estilo-helgen.tex). Cada documento define +% \sistema, \sistemapdf y \doctipo antes de \input{...}. Compilar con XeLaTeX. +% ---------- Geometría y base ---------- +\usepackage[letterpaper, margin=2.4cm, headheight=14pt]{geometry} +\usepackage{polyglossia} +\setmainlanguage[variant=mexican]{spanish} +\usepackage{xcolor} +\usepackage{colortbl} +\usepackage{graphicx} +\usepackage{booktabs} +\usepackage{array} +\usepackage{tabularx} +\usepackage{enumitem} +\usepackage{titlesec} +\usepackage{float} +\usepackage{fancyhdr} +\usepackage{tikz} +\usetikzlibrary{arrows.meta, positioning, shapes.misc, calc, fit, automata} +\usepackage{caption} +\captionsetup{font=small, labelfont={bf,color=HelgenIndigo}} +\usepackage{microtype} +\setlength{\emergencystretch}{3em}\tolerance=2000\hbadness=2000 +\usepackage{hyperref} +\usepackage{textcomp} +\usepackage{ragged2e} +\usepackage{xltabular} +\usepackage[export]{adjustbox} +\usepackage[most]{tcolorbox} + +\providecommand{\sistema}{Helgen Drogheda} +\providecommand{\sistemapdf}{Helgen Drogheda} +\providecommand{\doctipo}{Documento} + +% ---------- Paleta oficial Helgen ---------- +\definecolor{HelgenIndigo}{HTML}{2C3C5B} +\definecolor{HelgenCyan}{HTML}{51F3DA} +\definecolor{HelgenTeal}{HTML}{17A08C} +\definecolor{HelgenMist}{HTML}{EFF1F5} +\definecolor{HelgenLightCyan}{HTML}{E7FCF8} +\definecolor{HelgenLightIndigo}{HTML}{EDEFF4} + +\newtcolorbox{helgennotice}[1][Aviso]{enhanced,breakable,colback=HelgenLightCyan,colframe=HelgenIndigo,boxrule=0.6pt,arc=2pt,left=9pt,right=9pt,top=7pt,bottom=7pt,coltitle=white,colbacktitle=HelgenIndigo,fonttitle=\helgentitle\bfseries\small,title={#1}} +\newcommand{\hwtab}[1]{\begin{center}\footnotesize\renewcommand{\arraystretch}{1.25}#1\end{center}} + +\hypersetup{colorlinks=true, linkcolor=HelgenTeal, urlcolor=HelgenTeal, pdftitle={\sistemapdf{} - \doctipo}, pdfauthor={Dr. Leopoldo Rodríguez Salazar (Helgen Technologies)}} + +% ---------- Tipografía de marca (XeLaTeX) ---------- +\usepackage{iftex} +\ifXeTeX + \usepackage{fontspec} + \IfFontExistsTF{MADE Evolve Sans}{\setmainfont{MADE Evolve Sans}[AutoFakeSlant=0.18]}{\setmainfont{Latin Modern Sans}[AutoFakeSlant=0.18]} + \IfFontExistsTF{League Spartan}{\newfontfamily\helgentitle{League Spartan}[BoldFont={League Spartan SemiBold}]}{\newcommand{\helgentitle}{\sffamily\bfseries}} +\else + \usepackage[T1]{fontenc} + \renewcommand{\familydefault}{\sfdefault} + \newcommand{\helgentitle}{\sffamily\bfseries} +\fi + +% ---------- Color de texto global ---------- +\color{HelgenIndigo} + +% ---------- Títulos de sección ---------- +\titleformat{\section}{\helgentitle\Large\bfseries\color{HelgenIndigo}}{\thesection}{0.8em}{}[{\color{HelgenCyan}\titlerule[1.6pt]}] +\titleformat{\subsection}{\helgentitle\large\bfseries\color{HelgenIndigo}}{\thesubsection}{0.8em}{} +\titleformat{\subsubsection}{\helgentitle\normalsize\bfseries\color{HelgenIndigo}}{\thesubsubsection}{0.8em}{} +\titlespacing{\section}{0pt}{18pt}{10pt} + +% ---------- Encabezado / pie ---------- +\pagestyle{fancy} +\fancyhf{} +\fancyhead[L]{\helgentitle\small\color{HelgenIndigo}\textbf{\sistema} — \doctipo} +\fancyhead[R]{\helgentitle\small\color{HelgenIndigo}Hutchison Ports · ICAVE} +\fancyfoot[L]{\helgentitle\scriptsize\color{gray}Confidencial — Helgen Servicios Especializados S.A. de C.V.} +\fancyfoot[R]{\helgentitle\scriptsize\color{HelgenIndigo}\thepage} +\renewcommand{\headrulewidth}{0.4pt} +\renewcommand{\headrule}{\color{HelgenCyan}\hrule width\headwidth height 1.2pt} + +% ---------- Estilos TikZ Helgen ---------- +\tikzset{ + caja/.style={draw=HelgenIndigo, fill=HelgenMist, rounded corners=2pt, text=HelgenIndigo, + align=center, font=\small, inner sep=5pt, minimum height=9mm}, + cajaoscura/.style={draw=HelgenIndigo, fill=HelgenIndigo, rounded corners=2pt, text=white, + align=center, font=\small\bfseries, inner sep=5pt, minimum height=9mm}, + zona/.style={draw=HelgenIndigo!50, dashed, rounded corners=4pt, inner sep=8pt}, + etiqueta/.style={font=\scriptsize\bfseries, text=HelgenTeal}, + flecha/.style={-{Stealth[length=2.6mm]}, thick, draw=HelgenIndigo}, + flechaacento/.style={-{Stealth[length=2.6mm]}, very thick, draw=HelgenTeal}, +} + +\newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1}} +\newcolumntype{C}[1]{>{\centering\arraybackslash}p{#1}} + +% ---------- Listas con viñeta de marca ---------- +\setlist[itemize]{label={\textcolor{HelgenTeal}{\rule[0.18ex]{0.5em}{0.5em}}}, leftmargin=1.4em, itemsep=2pt} + +% ---------- Portada de marca (comando reutilizable) ---------- +% Uso: \helgenportada{Título}{Subtítulo cian}{Bloque de metadatos (tabular)} +\newcommand{\helgenportada}[3]{% +\begin{titlepage} +\begin{tikzpicture}[remember picture, overlay] + \fill[HelgenIndigo] (current page.south west) rectangle (current page.north east); + \draw[HelgenCyan, line width=2.5pt] ([xshift=2.4cm, yshift=-8.1cm]current page.north west) -- ([xshift=-2.4cm, yshift=-8.1cm]current page.north east); + \node[anchor=north west, text=white] at ([xshift=2.4cm, yshift=-3.2cm]current page.north west) + {\helgentitle\fontsize{30}{34}\selectfont Helgen}; + \node[anchor=north west, text=HelgenCyan] at ([xshift=2.4cm, yshift=-4.4cm]current page.north west) + {\helgentitle\small Ingeniería que produce, mantenimiento que protege}; + \node[anchor=north west, text=white, text width=16.5cm] at ([xshift=2.4cm, yshift=-9.0cm]current page.north west) + {\helgentitle\fontsize{24}{30}\selectfont #1\\[6pt] + \helgentitle\fontsize{14}{19}\selectfont\color{HelgenCyan} #2}; + \node[anchor=north west, text=white, text width=16.5cm] at ([xshift=2.4cm, yshift=-12.6cm]current page.north west) + {\helgentitle\fontsize{18}{22}\selectfont \sistema\\[5pt] + \normalsize Fleet RTK GPS — Hutchison Ports (ICAVE · TIMSA · EIT · LCT)}; + \node[anchor=south west, text=white] at ([xshift=2.4cm, yshift=2.2cm]current page.south west) + {\footnotesize\helgentitle #3}; +\end{tikzpicture} +\end{titlepage}}