Trois polls échoués d'affilée marquent la borne injoignable. Sur le lien
dégradé du 2026-08-28 (~70 % de perte), cela donne 12 bascules « unreachable »
en vingt minutes. Une borne qui disparaît toutes les deux minutes ne se pilote
pas, quel que soit le code : LM-1204-b, le plancher réel et EV_GRID_START
porteraient tous ce bruit.
Rien n'est changé dans le comportement — seulement documenté, parce que le
seuil ne PEUT PAS être tranché sur un lien malade. Le protocole tient en trois
étapes, à dérouler une fois le point d'accès rapproché : mesurer le taux de
polls avortés, compter les bascules sur la même fenêtre, puis conclure. Si les
bascules disparaissent, le seuil est bon et détecte vite une borne réellement
absente ; si elles persistent malgré un bon signal, c'est lui qui est trop
serré.
Le relever maintenant masquerait le vrai défaut au lieu de le corriger, et
retarderait d'autant la détection d'une borne vraiment partie.
Commentaire seul côté code : le .so n'est pas modifié, pas de redéploiement.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A41CTzMvir1Y5ABPXW8xpB
The Trydan ESP32 WiFi stack saturates under nymea's periodic ICMP pings
(observed: latency > 14 s, Modbus timeouts, charger drops off).
Changes:
- NetworkDeviceMonitor removed entirely from setupThing() and thingRemoved().
The charger is expected to have a DHCP-reserved IP (documented in code).
Address comes from trydanThingAddressParamTypeId param — already the correct
path after the previous setup fix.
- Connectivity now derives from Modbus poll success/failure only (reachable()
from TrydanModbusTcpMaster), NOT from network ping. A ping response does
not imply Modbus usability on this hardware.
- postSetupThing() timer (30 s): if not reachable, calls connectDevice() for
reconnect; if reachable, calls update(). The 30 s period IS the backoff —
no aggressive retry loop.
- Modbus timeout: 2 s → 5 s (tolerates residual WiFi latency spikes).
- Modbus retries per read: 1 → 0 (abort fast on first timeout; full poll
sequence already aborts at first error via doNextRead).
- k_errorLimit: 5 → 3 (3 × 5 s = 15 s before marking unreachable).
- New state "statusMessage" (QString): set on disconnect with timestamp +
cause ("timeout Modbus — vérifier signal WiFi de la borne"); cleared on
successful poll. Visible in nymea-app; helps SAV without SSH access.
- "networkdevice" removed from JSON interfaces (contract broken without monitor).
Invariants preserved: Big/Big float32 decode, no block read, PauseState+Lock
mirror, PauseDynamic conflict management, writeCompleted address filtering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Architecture choice: separate the register map / business logic from the
physical transport. TrydanModbusMaster (abstract QObject) holds:
- the complete ReadRegister / WriteRegister enums with addresses
- float32 Big/Big decode helpers (memcpy pattern, matches pymodbus
BinaryPayloadDecoder byteorder=Endian.Big wordorder=Endian.Big)
- all last-polled cached values accessible via simple getters
TrydanModbusTcpMaster (concrete, Étape 1) wraps libnymea-modbus
ModbusTcpMaster. Each register read is a distinct FC3(addr, 2)
transaction via a recursive runReadSequence/doNextRead pattern —
never a block read, because the V2C firmware's register windows
overlap (0x0BC2 and 0x0BC3 each span 2 registers, so a range read
starting at 0x0BC2 for ≥3 registers returns garbage for the second
value). Writes use FC6 (single uint16, not float).
writeCompleted(quint16 address, bool success) carries the originating
register address so that concurrent background writes (e.g. PauseDynamic
from the conflict manager firing during an action) do not interfere with
action handlers waiting on a specific register's acknowledgement.
0x177E (Dynamic) is intentionally absent from WriteRegister: writing it
to 0 silences ChargePower telemetry even though the charger keeps running.
cf. evcc charger/trydan.go and github.com/evcc-io/evcc/issues/28047.
Étape 2 (RTU) will add TrydanModbusRtuMaster on the same interface;
no changes to plugin or register logic will be required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>