2 Commits

Author SHA1 Message Date
Patrick Schurig
076a0dcae9 feat(v2c): remove NetworkDeviceMonitor, add poll-based connectivity + statusMessage
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>
2026-06-12 18:15:30 +02:00
Patrick Schurig
14f07cc638 feat(v2c): add Modbus transport abstraction + TCP implementation
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>
2026-06-12 13:49:11 +02:00