Initial commit (Hub backfill)
This commit is contained in:
commit
fd7b26b357
49
.claude/skills/project-drogheda/SKILL.md
Normal file
49
.claude/skills/project-drogheda/SKILL.md
Normal file
@ -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."
|
||||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -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
|
||||||
56
00-Project-Management/Drive-Upload/README.md
Normal file
56
00-Project-Management/Drive-Upload/README.md
Normal file
@ -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.
|
||||||
92
00-Project-Management/Drive-Upload/drive_upload.py
Normal file
92
00-Project-Management/Drive-Upload/drive_upload.py
Normal file
@ -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()
|
||||||
385
00-Project-Management/Engineering-Handover.md
Normal file
385
00-Project-Management/Engineering-Handover.md
Normal file
@ -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)
|
||||||
48
00-Project-Management/Financials.md
Normal file
48
00-Project-Management/Financials.md
Normal file
@ -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]]
|
||||||
158
00-Project-Management/PLAN.md
Normal file
158
00-Project-Management/PLAN.md
Normal file
@ -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`
|
||||||
@ -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.*
|
||||||
Binary file not shown.
59
00-Project-Management/ethos.md
Normal file
59
00-Project-Management/ethos.md
Normal file
@ -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]]
|
||||||
115
00-Project-Management/hackathon-plan.md
Normal file
115
00-Project-Management/hackathon-plan.md
Normal file
@ -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]]
|
||||||
BIN
01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf
Normal file
BIN
01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf
Normal file
Binary file not shown.
BIN
01-Requirements/Drogheda-Requirements-Tracker.xlsx
Normal file
BIN
01-Requirements/Drogheda-Requirements-Tracker.xlsx
Normal file
Binary file not shown.
BIN
01-Requirements/Drogheda-SRS-Requerimientos.pdf
Normal file
BIN
01-Requirements/Drogheda-SRS-Requerimientos.pdf
Normal file
Binary file not shown.
73
01-Requirements/Especificacion-Requerimientos/cuerpo.tex
Normal file
73
01-Requirements/Especificacion-Requerimientos/cuerpo.tex
Normal file
@ -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}
|
||||||
BIN
01-Requirements/Especificacion-Requerimientos/main.pdf
Normal file
BIN
01-Requirements/Especificacion-Requerimientos/main.pdf
Normal file
Binary file not shown.
18
01-Requirements/Especificacion-Requerimientos/main.tex
Normal file
18
01-Requirements/Especificacion-Requerimientos/main.tex
Normal file
@ -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}
|
||||||
108
01-Requirements/HBOX-Ficha-Tecnica/cuerpo.tex
Normal file
108
01-Requirements/HBOX-Ficha-Tecnica/cuerpo.tex
Normal file
@ -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.
|
||||||
BIN
01-Requirements/HBOX-Ficha-Tecnica/main.pdf
Normal file
BIN
01-Requirements/HBOX-Ficha-Tecnica/main.pdf
Normal file
Binary file not shown.
23
01-Requirements/HBOX-Ficha-Tecnica/main.tex
Normal file
23
01-Requirements/HBOX-Ficha-Tecnica/main.tex
Normal file
@ -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}
|
||||||
91
01-Requirements/HBOX-product-spec.md
Normal file
91
01-Requirements/HBOX-product-spec.md
Normal file
@ -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]]
|
||||||
83
01-Requirements/SRS.md
Normal file
83
01-Requirements/SRS.md
Normal file
@ -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]]
|
||||||
230
02-Architecture-Software/backend-review.md
Normal file
230
02-Architecture-Software/backend-review.md
Normal file
@ -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.**
|
||||||
99
03-Architecture-Hardware/BOM-master.md
Normal file
99
03-Architecture-Hardware/BOM-master.md
Normal file
@ -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]]
|
||||||
BIN
03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx
Normal file
BIN
03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx
Normal file
Binary file not shown.
Binary file not shown.
@ -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}
|
||||||
BIN
03-Architecture-Hardware/Manual-Instalacion-Operacion/main.pdf
Normal file
BIN
03-Architecture-Hardware/Manual-Instalacion-Operacion/main.pdf
Normal file
Binary file not shown.
@ -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}
|
||||||
77
03-Architecture-Hardware/cabinet-electrical-bom.md
Normal file
77
03-Architecture-Hardware/cabinet-electrical-bom.md
Normal file
@ -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.
|
||||||
120
03-Architecture-Hardware/firmware-review.md
Normal file
120
03-Architecture-Hardware/firmware-review.md
Normal file
@ -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.
|
||||||
260
03-Architecture-Hardware/hardware-respec-and-pcb-brief.md
Normal file
260
03-Architecture-Hardware/hardware-respec-and-pcb-brief.md
Normal file
@ -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`.
|
||||||
192
03-Architecture-Hardware/watchdog-health-telemetry.md
Normal file
192
03-Architecture-Hardware/watchdog-health-telemetry.md
Normal file
@ -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,<id>,TELCEL,<rssi>,<cell>` you see at the server is the **4G
|
||||||
|
modem's** own network-status line (the `<id>` 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,<AS+K id>,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,<fw>,<state>,<link>,<fix>,<sat>,<rx>,<vbat>,<v5v>,<tc>,<tmax>,<rec>,<limp>,<nmea_age_ms>,<reset>*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`
|
||||||
83
03-Architecture-Hardware/watchdog-pcb-v3-blockdiagram.svg
Normal file
83
03-Architecture-Hardware/watchdog-pcb-v3-blockdiagram.svg
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<svg viewBox="0 0 680 460" xmlns="http://www.w3.org/2000/svg" font-family="Helvetica, Arial, sans-serif">
|
||||||
|
<title>HBOX Watchdog Carrier PCB v3 — power and signal map</title>
|
||||||
|
<desc>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.</desc>
|
||||||
|
<rect x="0" y="0" width="680" height="460" fill="#ffffff"/>
|
||||||
|
<defs>
|
||||||
|
<marker id="ap" markerWidth="9" markerHeight="9" refX="6.5" refY="3" orient="auto"><path d="M0,0 L6.5,3 L0,6 z" fill="#555555"/></marker>
|
||||||
|
<marker id="as" markerWidth="9" markerHeight="9" refX="6.5" refY="3" orient="auto"><path d="M0,0 L6.5,3 L0,6 z" fill="#999999"/></marker>
|
||||||
|
</defs>
|
||||||
|
<text x="16" y="20" font-size="14" font-weight="700" fill="#1a2233">HBOX Watchdog Carrier PCB v3 — power & signal map</text>
|
||||||
|
<line x1="16" y1="36" x2="38" y2="36" stroke="#E07A2F" stroke-width="2"/>
|
||||||
|
<text x="43" y="39" font-size="10.5" fill="#555555">new in v3 (fans + temp)</text>
|
||||||
|
<rect x="196" y="31" width="15" height="9" rx="2" fill="none" stroke="#888888" stroke-width="1.3"/>
|
||||||
|
<text x="216" y="39" font-size="10.5" fill="#555555">existing</text>
|
||||||
|
<rect x="276" y="31" width="15" height="9" rx="2" fill="none" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="296" y="39" font-size="10.5" fill="#555555">external module</text>
|
||||||
|
<text x="487" y="39" font-size="9.5" fill="#999999">— power - - signal</text>
|
||||||
|
<rect x="16" y="54" width="118" height="38" rx="7" fill="#ffffff" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="27" y="71" font-size="12.5" font-weight="600" fill="#1a2233">24 V feed</text>
|
||||||
|
<text x="27" y="85" font-size="10.5" fill="#555555">cabinet · GX16-5</text>
|
||||||
|
<rect x="150" y="54" width="120" height="38" rx="7" fill="#ffffff" stroke="#888888" stroke-width="1.3"/>
|
||||||
|
<text x="161" y="71" font-size="12.5" font-weight="600" fill="#1a2233">Rev-pol + TVS</text>
|
||||||
|
<text x="161" y="85" font-size="10.5" fill="#555555">Q10 · D10</text>
|
||||||
|
<rect x="470" y="54" width="150" height="38" rx="7" fill="#ffffff" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="481" y="71" font-size="12.5" font-weight="600" fill="#1a2233">Mean Well 5 V</text>
|
||||||
|
<text x="481" y="85" font-size="10.5" fill="#555555">SD-15B-05 · ext</text>
|
||||||
|
<line x1="134" y1="73" x2="148" y2="73" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<rect x="16" y="150" width="208" height="40" rx="7" fill="#FCF1E8" stroke="#E07A2F" stroke-width="1.8"/>
|
||||||
|
<text x="27" y="167" font-size="12.5" font-weight="600" fill="#1a2233">Fan buck U2</text>
|
||||||
|
<text x="27" y="181" font-size="10.5" fill="#555555">24→5 V dedicated rail · JP1</text>
|
||||||
|
<rect x="16" y="214" width="100" height="44" rx="7" fill="#FCF1E8" stroke="#E07A2F" stroke-width="1.8"/>
|
||||||
|
<text x="26" y="232" font-size="12" font-weight="600" fill="#1a2233">Fan drv 1</text>
|
||||||
|
<text x="26" y="246" font-size="10.5" fill="#555555">Q2 · flyback</text>
|
||||||
|
<rect x="124" y="214" width="100" height="44" rx="7" fill="#FCF1E8" stroke="#E07A2F" stroke-width="1.8"/>
|
||||||
|
<text x="134" y="232" font-size="12" font-weight="600" fill="#1a2233">Fan drv 2</text>
|
||||||
|
<text x="134" y="246" font-size="10.5" fill="#555555">Q3 · flyback</text>
|
||||||
|
<rect x="40" y="300" width="160" height="40" rx="7" fill="#ffffff" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="51" y="317" font-size="12.5" font-weight="600" fill="#1a2233">2× 5 V fans</text>
|
||||||
|
<text x="51" y="331" font-size="10.5" fill="#555555">extraction · ext</text>
|
||||||
|
<path d="M205,92 L205,120 L120,120 L120,148" fill="none" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<text x="124" y="116" font-size="9.5" fill="#555555">24 V</text>
|
||||||
|
<line x1="66" y1="190" x2="66" y2="212" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<line x1="174" y1="190" x2="174" y2="212" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<text x="70" y="206" font-size="9.5" fill="#555555">5V_FAN</text>
|
||||||
|
<path d="M66,258 L66,300" fill="none" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<path d="M174,258 L174,300" fill="none" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<rect x="250" y="150" width="172" height="72" rx="7" fill="#ffffff" stroke="#1a2233" stroke-width="1.5"/>
|
||||||
|
<text x="262" y="180" font-size="13" font-weight="700" fill="#1a2233">STM32F411 Blackpill</text>
|
||||||
|
<text x="262" y="198" font-size="10.5" fill="#555555">watchdog · on headers</text>
|
||||||
|
<text x="262" y="212" font-size="10.5" fill="#555555">reuses v2.1 firmware</text>
|
||||||
|
<rect x="250" y="300" width="172" height="40" rx="7" fill="#ffffff" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="261" y="317" font-size="12.5" font-weight="600" fill="#1a2233">18650 / PowerBoost</text>
|
||||||
|
<text x="261" y="331" font-size="10.5" fill="#555555">battery · ext</text>
|
||||||
|
<rect x="250" y="392" width="172" height="42" rx="7" fill="#FCF1E8" stroke="#E07A2F" stroke-width="1.8"/>
|
||||||
|
<text x="261" y="410" font-size="12.5" font-weight="600" fill="#1a2233">SHT40 + TMP102</text>
|
||||||
|
<text x="261" y="424" font-size="10.5" fill="#555555">I²C temp + humidity</text>
|
||||||
|
<rect x="470" y="150" width="150" height="40" rx="7" fill="#ffffff" stroke="#888888" stroke-width="1.3"/>
|
||||||
|
<text x="481" y="167" font-size="12.5" font-weight="600" fill="#1a2233">Load switch</text>
|
||||||
|
<text x="481" y="181" font-size="10.5" fill="#555555">Q1 · Q5 · PPTC</text>
|
||||||
|
<rect x="470" y="212" width="150" height="42" rx="7" fill="#ffffff" stroke="#aaaaaa" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||||
|
<text x="481" y="230" font-size="12.5" font-weight="600" fill="#1a2233">simpleRTK2B + 4G</text>
|
||||||
|
<text x="481" y="244" font-size="10.5" fill="#555555">SW_5V · ext</text>
|
||||||
|
<rect x="470" y="300" width="150" height="38" rx="7" fill="#ffffff" stroke="#888888" stroke-width="1.3"/>
|
||||||
|
<text x="481" y="317" font-size="12.5" font-weight="600" fill="#1a2233">Status LED</text>
|
||||||
|
<text x="481" y="331" font-size="10.5" fill="#555555">PB0</text>
|
||||||
|
<path d="M545,92 L545,150" fill="none" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<text x="550" y="118" font-size="9.5" fill="#555555">MAIN 5V</text>
|
||||||
|
<path d="M486,92 L486,120 L424,120 L424,150" fill="none" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<text x="392" y="116" font-size="9.5" fill="#555555">5 V</text>
|
||||||
|
<line x1="545" y1="190" x2="545" y2="210" stroke="#555555" stroke-width="1.6" marker-end="url(#ap)"/>
|
||||||
|
<text x="550" y="205" font-size="9.5" fill="#555555">SW_5V</text>
|
||||||
|
<line x1="422" y1="166" x2="468" y2="166" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="430" y="161" font-size="9.5" fill="#999999">PA1 EN</text>
|
||||||
|
<path d="M422,205 L450,205 L450,232 L468,232" fill="none" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="426" y="201" font-size="9.5" fill="#999999">UART PA2/3</text>
|
||||||
|
<path d="M414,222 L440,260 L440,316 L468,316" fill="none" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="444" y="288" font-size="9.5" fill="#999999">PB0</text>
|
||||||
|
<path d="M250,196 L235,210 L228,228" fill="none" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="150" y="200" font-size="9.5" fill="#999999">PWM + tach PA6/7 · PB10/12</text>
|
||||||
|
<line x1="300" y1="300" x2="300" y2="224" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="304" y="262" font-size="9.5" fill="#999999">BAT sense PA0</text>
|
||||||
|
<path d="M414,222 L436,250 L436,410 L424,410" fill="none" stroke="#999999" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#as)"/>
|
||||||
|
<text x="440" y="380" font-size="9.5" fill="#999999">I²C PB6/7</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.8 KiB |
BIN
03-Architecture-Hardware/watchdog-pcb-v3-bom.xlsx
Normal file
BIN
03-Architecture-Hardware/watchdog-pcb-v3-bom.xlsx
Normal file
Binary file not shown.
378
03-Architecture-Hardware/watchdog-pcb-v3-design.md
Normal file
378
03-Architecture-Hardware/watchdog-pcb-v3-design.md
Normal file
@ -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 <few-hundred mA; for margin use a bigger P-FET like **SI2305** —
|
||||||
|
buck input current at 24 V is small, ~0.2 A, so AO3401A is fine).
|
||||||
|
- **D10** TVS **SMBJ33A** (33 V standoff) 24 V→GND, plus **C10** 100 µF/50 V
|
||||||
|
electrolytic + **C11** 100 nF bulk/decoupling.
|
||||||
|
|
||||||
|
### 4.2 Dedicated fan 5 V buck (U2) — see caveat §0a
|
||||||
|
- **U2 = MP1584EN** (SOP-8, 4.5–28 V in, up to 3 A) set to **5.0 V** out.
|
||||||
|
- FB divider for 5 V: **R20 = 100 kΩ top**, **R21 = 19.1 kΩ bottom** (Vout =
|
||||||
|
0.8 V·(1+R20/R21) ≈ 5.0 V — verify against the MP1584 datasheet FB = 0.8 V).
|
||||||
|
- **L2 = 10 µH** (≥1 A sat), **C22 = 22 µF** in, **C23 = 22 µF** out, freewheel
|
||||||
|
Schottky per datasheet (SS34), EN tied to 24V-rail-derived enable.
|
||||||
|
- **JP1**: 0 Ω / solder-jumper. **Open** = fans on U2 (recommended). **Closed** =
|
||||||
|
fans bridged to MAIN 5 V (only if SD-15B-05 is upsized; never both).
|
||||||
|
- Keep U2's switching node small; **keep this buck away from the GNSS antenna feed**
|
||||||
|
(RF cleanliness — see §8).
|
||||||
|
|
||||||
|
> Simpler alternative if you'd rather not lay out a buck on the first spin: drop U2,
|
||||||
|
> close **JP1**, and **upsize the external 5 V supply to a 5 V/5 A** unit so the rail
|
||||||
|
> has true headroom. The board supports it; it's a BOM/jumper choice, not a respin.
|
||||||
|
|
||||||
|
### 4.3 RTK-stack high-side load switch (Q1 trio — proven, SMD)
|
||||||
|
Keeps the as-built topology (so **firmware PA1 logic is unchanged**) but in
|
||||||
|
solid, sourceable SMD, plus a resettable fuse for the missing current limit:
|
||||||
|
|
||||||
|
```
|
||||||
|
MAIN 5V ──[F1 PPTC 3A]──┬───────────────[R2 10k]──┐
|
||||||
|
│ │
|
||||||
|
Q1 AO3401A Source Gate ── Q5 MMBT3904 Collector
|
||||||
|
(P-MOS) │ │
|
||||||
|
Drain ──► SW_5V ─► P3 stack Q5 Emitter ── GND
|
||||||
|
│
|
||||||
|
PA1 ──[R1 1k]── Q5 Base ───────────────┘
|
||||||
|
```
|
||||||
|
- PA1 **HIGH** → MMBT3904 on → P-FET gate low → **SW_5V ON**. PA1 LOW → R2 holds
|
||||||
|
gate high → **OFF** (fail-safe at boot). Identical to as-built.
|
||||||
|
- **AO3401A** is logic-level and switches fully from the 5 V gate swing (the old
|
||||||
|
**IRF9540N was not** — a real latent bug we're removing).
|
||||||
|
- **F1 PPTC** (3 A hold / ~6 A trip, e.g., **MF-MSMF300**) gives overcurrent
|
||||||
|
protection without a hard-to-source eFuse, and **cannot permanently latch a healthy
|
||||||
|
unit out** (it self-resets) — satisfies the "never black-hole" invariant.
|
||||||
|
- **C3 = 47 µF** bulk on SW_5V (modem-burst reservoir).
|
||||||
|
- **Optional upgrade footprint U1**: pin-compatible-ish current-limit load switch /
|
||||||
|
eFuse (e.g., **AP2553W6-7** SOT-23-6, EN active-high, FLAGn). Lay the footprint
|
||||||
|
*and* the discrete; populate one. Verify LCSC stock at order; **default = discrete**.
|
||||||
|
|
||||||
|
### 4.4 Fan 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]]
|
||||||
234
03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide.md
Normal file
234
03-Architecture-Hardware/watchdog-pcb-v3-kicad-guide.md
Normal file
@ -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]]
|
||||||
116
03-Architecture-Hardware/watchdog-schematic.md
Normal file
116
03-Architecture-Hardware/watchdog-schematic.md
Normal file
@ -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]]
|
||||||
1
04-Source-Code/firmware
Submodule
1
04-Source-Code/firmware
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 1eab7b92ff5a9421f0e358936ce038dbee8e39e1
|
||||||
1
04-Source-Code/htracker
Submodule
1
04-Source-Code/htracker
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit f93a36d3ae8d64ead1036f422a32024eb3cb9cbf
|
||||||
49
04-Source-Code/notifications/README.md
Normal file
49
04-Source-Code/notifications/README.md
Normal file
@ -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://<your-mattermost>/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://<your-mattermost>/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.
|
||||||
90
04-Source-Code/notifications/notify_mattermost.py
Normal file
90
04-Source-Code/notifications/notify_mattermost.py
Normal file
@ -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://<host>/hooks/xxxxxxxxxxxxxxxxx).
|
||||||
|
2. export MATTERMOST_WEBHOOK_URL="https://<host>/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())
|
||||||
41
04-Source-Code/rtk-config/4G_NTRIP_HelgenSRV-NMEA.txt
Normal file
41
04-Source-Code/rtk-config/4G_NTRIP_HelgenSRV-NMEA.txt
Normal file
@ -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
|
||||||
23
05-Analytics-Reports/README.md
Normal file
23
05-Analytics-Reports/README.md
Normal file
@ -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`.
|
||||||
@ -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 `RTK<bb>NTRIP<mm>` = 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,<op>,<dBm>,<MCCMNC>`:** 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.
|
||||||
@ -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.
|
||||||
BIN
06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.pdf
Normal file
BIN
06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.pdf
Normal file
Binary file not shown.
263
06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.tex
Normal file
263
06-Testing-Validation/2026-06-20_Drogheda_GNSS-Findings_v1.0.tex
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
% Project Drogheda — GNSS / Log Analysis findings (Helgen Beamer / Metropolis)
|
||||||
|
% Built from the helgen-beamer template. Compile: latexmk -xelatex <file>.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}
|
||||||
BIN
06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf
Normal file
BIN
06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf
Normal file
Binary file not shown.
200
06-Testing-Validation/RCA-2026-06-field-failures.md
Normal file
200
06-Testing-Validation/RCA-2026-06-field-failures.md
Normal file
@ -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.
|
||||||
89
06-Testing-Validation/RCA-Informe-Falla/cuerpo.tex
Normal file
89
06-Testing-Validation/RCA-Informe-Falla/cuerpo.tex
Normal file
@ -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}
|
||||||
BIN
06-Testing-Validation/RCA-Informe-Falla/main.pdf
Normal file
BIN
06-Testing-Validation/RCA-Informe-Falla/main.pdf
Normal file
Binary file not shown.
18
06-Testing-Validation/RCA-Informe-Falla/main.tex
Normal file
18
06-Testing-Validation/RCA-Informe-Falla/main.tex
Normal file
@ -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}
|
||||||
Binary file not shown.
@ -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 <file>.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}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
06-Testing-Validation/fig/fig_baseline_gnss.pdf
Normal file
BIN
06-Testing-Validation/fig/fig_baseline_gnss.pdf
Normal file
Binary file not shown.
BIN
06-Testing-Validation/fig/fig_delivery.pdf
Normal file
BIN
06-Testing-Validation/fig/fig_delivery.pdf
Normal file
Binary file not shown.
BIN
06-Testing-Validation/fig/fig_fix_compare.pdf
Normal file
BIN
06-Testing-Validation/fig/fig_fix_compare.pdf
Normal file
Binary file not shown.
BIN
06-Testing-Validation/fig/fig_ntrip_delay.pdf
Normal file
BIN
06-Testing-Validation/fig/fig_ntrip_delay.pdf
Normal file
Binary file not shown.
1
06-Testing-Validation/log-analysis
Submodule
1
06-Testing-Validation/log-analysis
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit cda046b9d4bef72f35e8fd8af7da846ae0200082
|
||||||
180
06-Testing-Validation/remote-diagnostic-protocol.md
Normal file
180
06-Testing-Validation/remote-diagnostic-protocol.md
Normal file
@ -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.
|
||||||
338
99-Archive/blackpillrtkcode_ORIGINAL_loose.ino
Normal file
338
99-Archive/blackpillrtkcode_ORIGINAL_loose.ino
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// --- 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
99-Archive/situation_ORIGINAL.md
Normal file
22
99-Archive/situation_ORIGINAL.md
Normal file
@ -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.
|
||||||
235
CLAUDE.md
Normal file
235
CLAUDE.md
Normal file
@ -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.
|
||||||
BIN
Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf
Normal file
BIN
Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf
Normal file
Binary file not shown.
BIN
Marketing/HBOX-Brochure-RTK/main.pdf
Normal file
BIN
Marketing/HBOX-Brochure-RTK/main.pdf
Normal file
Binary file not shown.
84
Marketing/HBOX-Brochure-RTK/main.tex
Normal file
84
Marketing/HBOX-Brochure-RTK/main.tex
Normal file
@ -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}
|
||||||
BIN
Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf
Normal file
BIN
Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf
Normal file
Binary file not shown.
BIN
Marketing/HBOX-Datasheet/main.pdf
Normal file
BIN
Marketing/HBOX-Datasheet/main.pdf
Normal file
Binary file not shown.
175
Marketing/HBOX-Datasheet/main.tex
Normal file
175
Marketing/HBOX-Datasheet/main.tex
Normal file
@ -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}
|
||||||
BIN
Marketing/HBOX-Datasheet/main.xdv
Normal file
BIN
Marketing/HBOX-Datasheet/main.xdv
Normal file
Binary file not shown.
87
README.md
Normal file
87
README.md
Normal file
@ -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)
|
||||||
117
_brand/estilo-helgen.tex
Normal file
117
_brand/estilo-helgen.tex
Normal file
@ -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}}
|
||||||
Loading…
Reference in New Issue
Block a user