Files
ems/docs/04-modules/modbus-registers-teltocharge.md
Dusan Vojacek 54288ee2fd fix(modbus): reg 15 re-asert kazdy tick + per-charger failsafe (BUG1)
Zivy incident home-01 (TeltoCharge .16): od ~22:45 UTC 12.6. nevznikl zadny
telto journal radek (ani failed), auto jelo failsafe 8 A misto planovanych 0 A.

Root cause: reg 15 (amps) byl write-on-change proti journalu
(fn_modbus_device_state_map). Jakmile mel reg 15 radek "0 verified" a plan
dal chtel 0, NIKDY nevznikl novy prikaz -- a TeltoCharge si po vypadku
komunikace sam prepsal reg 15 na failsafe (reg 20) BEZ journal radku. Verify
cte zpet jen 'written' radky, takze tichy drift 0 -> 8 A nikdo nevidel ani
neopravil.

- reg 15 (amps to use) se zapisuje VZDY (re-asert) -- volatilni ridici
  registr, ne EEPROM; drzi verify jobu cerstvy written radek -> drift se
  zachyti a hned opravi. _split_amps_and_watchdog odděluje 15 od 19/20.
- reg 19/20 (watchdog config, EEPROM) zustavaji write-on-change.
- per-charger failsafe/timeout: asset_ev_charger.watchdog_failsafe_a /
  watchdog_comm_timeout_s (V106; default 8 A / 300 s). "Zakaz nabijeni" =
  reg 15 = 0 (protokol rev 0.5 nema samostatny enable registr).
- testy test_ev_write_on_change.py; docs teltocharge + journal + data-model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:03:11 +02:00

103 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Teltonika TeltoCharge — Modbus registry (EMS)
Zdroj: oficiální „TeltoCharge Modbus RTU Communication protocol" rev 0.5
(2024-07-23). Připojení: sdílená RS485 sběrnice → Waveshare RS485 TO POE ETH (B) **172.16.1.16:502** (V097; 9600 8N1, Modbus TCP↔RTU; WB1 = unit 1, WB2 = unit 2, plánovaný Chint = unit 3) → Modbus TCP (endpoint
`site_endpoint`, FC 3 čtení, FC 6/16 zápis). Wallbox musí být v aplikaci
nastaven jako *secondary (server)*.
## Čtení (telemetry_collector, blok 040 jedním FC 3)
| Reg | Význam | Formát |
|-----|--------|--------|
| 02 | Napětí L1L3 | int16, V |
| 35 | Proud L1L3 | int16, ×10 A |
| 6 | **EVSE status (DLM)** | 0=C nabíjí · 13=B1B3 připojeno · 4=D1 stop od EV · 56=D2D3 zákaz · 7=A bez EV · 8=F chyba · 9=E |
| 27 | Charge point state | 09 (informativní) |
| 33 | IEC61851 stav | 08 |
| 34/35 | Warning / Error bity | bitfield |
| 38 | Okamžitý výkon | uint16, W |
| 39 | Energie session | uint16, kWh×100 |
| 40 | Trvání session | uint16, s |
| 4144 | Celková energie (FW ≥1.13) | uint64, kWh×100 |
Mapování stavů v EMS (`TELTO_STATUS_MAP` v `telemetry_collector.py`):
7→`available`, 0→`charging`, 13→`preparing`, 4→`suspended_ev`,
56→`suspended_evse`, 8→`faulted`, 9→`unknown`. Detekce příjezdu/odjezdu
(`fn_ev_session_transition`) stojí na přechodu `available` ↔ ≠`available`.
**Při selhání čtení se vzorek NEzapisuje** — fabrikovaný `available` by falešně
ukončil session a EV výkon 0 by špinil bazál (pravidlo 15).
## Zápis (control exporter — `write_ev_setpoints`, `write_ev_arrival_hold`)
| Reg | R/W | Význam | Hodnoty | EMS zapisuje |
|-----|-----|--------|---------|--------------|
| 15 | R/W | **Amps to use** (limit proudu) | 0 = stop, 632 A | hodnota z plánu (`ev{1,2}_current_a`); příjezd EV → hold 0 A. **Zapisuje se KAŽDÝ tick** (re-asert, ne write-on-change — viz níže) |
| 16 | R/W | Start/Stop session | 0 nic · 1 stop · 2 start | ne (tvrdé zastavení řešíme reg 15 = 0) |
| 19 | R/W | Communication timeout (watchdog) | 0600 s (0 = vypnuto), default 30 | per charger `asset_ev_charger.watchdog_comm_timeout_s` (default **300**) |
| 20 | R/W | Failsafe current | 0, 632 A, default 6 | per charger `asset_ev_charger.watchdog_failsafe_a` (default **8**) |
Všechny čtyři registry jsou dle oficiálního protokolu (wiki *External control
RS485* / protokol rev 0.5) **R/W** — verify job je čte zpět standardní FC 3
větví (žádný write-only registr v této sadě).
**„Zákaz nabíjení" = reg 15 = 0.** Protokol rev 0.5 v této sadě **nemá**
samostatný boolean „charging enable/disable" registr — řízení je proudovým
limitem (reg 15: 0 = stop) plus volitelně reg 16 (1 = stop session). EMS
používá **reg 15 = 0** jako řízené zastavení (arrival-hold i běžný plán);
reg 16 se nezapisuje. Failsafe (reg 20) je hodnota PŘI výpadku komunikace,
ne při běžném provozu — běžně auto stojí na 0 A, dokud plán neřekne jinak.
### Reg 15 (amps) — VŽDY re-asert; reg 19/20 — write-on-change (EEPROM)
Export tick běží ~8×/hod (control_export `:14,:29,:44,:59` + rolling replan
`*/15` s exportem).
- **reg 15 (amps to use) se zapisuje při KAŽDÉM ticku** (`write_ev_setpoints`
i `write_ev_arrival_hold`). **Důvod (incident 2026-06-13):** TeltoCharge si
po výpadku komunikace sám přepíše reg 15 na failsafe (reg 20) — bez journal
řádku. Kdyby byl reg 15 write-on-change proti journalu (poslední
„0 verified"), EMS by tichý drift **0 → 8 A** na zařízení **NIKDY
nezahlédlo** (verify čte zpět jen `written` řádky) a nikdy ho neopravilo:
auto po každém krátkém výpadku spojení tiše jelo 8 A místo plánovaných 0 A.
Reg 15 je volatilní řídicí registr (ne EEPROM), opakovaný zápis je v pořádku;
re-asert každý tick zároveň drží verify jobu čerstvý `written` reg-15 řádek
→ případný drift se zachytí a hned opraví.
- **reg 19/20 (watchdog config) zůstávají write-on-change** přes
`_drop_registers_matching_last_verified` proti **`ems.fn_modbus_device_state_map`**
(nejnovější řádek journalu per registr, stav `written` **nebo** `verified`):
zapíší se jednou po nasazení / po výpadku zařízení (nejnovější řádek
`failed`/`mismatch` ⇒ registr v mapě chybí ⇒ znovu se zapíše) a pak už ne,
dokud se hodnota nezmění — šetří EEPROM. Čekání na verify skip neblokuje,
`written` (TCP ack) stačí.
Implementace: `_telto_setpoint_registers` (per-charger failsafe/timeout),
`_split_amps_and_watchdog` (reg 15 vs 19/20) v `services/control/outputs.py`.
### Watchdog — sytí ho i čtení; failsafe konfigurovatelný
Protokol definuje timeout jako *„if no **valid communication** is present
after a configurable time interval…"* — timer resetuje **jakákoli** validní
Modbus komunikace s unit ID wallboxu, **včetně FC 3 čtení**. Telemetrie čte
blok 040 každých **60 s**, takže watchdog 300 s je trvale sycen čtením a
**periodické zápisy k udržení spojení nejsou potřeba**. Failsafe (reg 20
„max allowed current on comm timeout") nastane až po `watchdog_comm_timeout_s`
bez jakéhokoli pollingu = skutečný výpadek EMS.
**Failsafe je per charger** (`asset_ev_charger.watchdog_failsafe_a`, default
8 A; `watchdog_comm_timeout_s`, default 300 s; migrace V106):
- default **8 A** = po skutečném výpadku EMS se auto přes noc pomalu dobije
místo stání na 0 A;
- snížit lze na **6 A** (IEC 61851 minimum) nebo **0** (po výpadku nenabíjet),
dle dotačních / komfortních požadavků;
- **běžný provoz po zapojení řídí reg 15 z plánu** (0 A drží arrival-hold +
sycení watchdogu čtením telemetrie), failsafe se uplatní jen při výpadku —
rozpor „chci řízený default 0 A, ale po výpadku malý proud" je tím vyřešen.
## WB2 mimo EMS (V105, 2026-06-13)
ev-charger-2 deaktivován (endpoint disabled + schedulable=false) — dle
dotačních podmínek ho řídí elektroměr mimo EMS. Telemetrie ani zápisy na
unit 2 neběží (RS485 brána 172.16.1.16 slouží jen WB1 + budoucí Chint).
Reaktivace: dva UPDATE v komentáři V105__wb2_deactivate.sql.