Environment
- Adapter version: 8.0.3
- Node.js: v22.22.2
- js-controller: 7.0.7
Description
Cyclic Write (CW) on Holding Registers does not work when the "Poll" checkbox is disabled for the register. The CW write never fires, even though cw: true is set in the config. No error or warning is logged — the register is silently excluded.
Root Cause (Source Code Analysis)
Bug 1: Config filter excludes non-polled registers
In src/main.ts (index.ts compiled), the holdingRegs config is filtered before iterateAddresses() processes CW flags:
// ~line 1218
device.holdingRegs.config = (this.config.holdingRegs as Modbus.RegisterInternal[]).filter(
e => e.poll && e.deviceId === deviceId,
);
Registers without poll: true are excluded from the config array entirely. When iterateAddresses() later processes the config to populate the cyclicWrite array, the CW register is simply not there.
Bug 2: ackObjects dependency prevents write even if filter is bypassed
In src/lib/Master.ts, the CW write method reads from ackObjects:
// ~line 643
if (this.ackObjects[id]) {
await this.#writeValue(id, this.ackObjects[id].val);
}
ackObjects is only populated during the READ (poll) phase. If a register is never polled, ackObjects[id] is undefined, and the write is silently skipped.
Expected Behavior
A register with cw: true and poll: false should still be written cyclically. The adapter should:
- Not filter out CW registers from the config just because poll is disabled
- For CW-only registers (no poll), use the last
setState value (from ioBroker state DB) instead of requiring a polled ackObjects entry
Steps to Reproduce
- Add a Holding Register with CW enabled and Poll disabled
- Set the ioBroker state value to any number (e.g., 1) with ack: true
- Enable debug logging
- Observe: no write appears in the debug log for that register
Use Case
This is critical for devices that require a periodic heartbeat write (e.g., Vestel EVC04 EV charger, which expects a write to register 6000 every few seconds to keep the TCP connection alive). CW would be the perfect built-in mechanism for this, but currently requires a workaround script.
Workaround
Use an ioBroker JavaScript script to periodically write the value:
setInterval(() => {
setState('modbus.0.holdingRegisters.46001_Heartbeat', 1, false);
}, 5000);
Environment
Description
Cyclic Write (CW) on Holding Registers does not work when the "Poll" checkbox is disabled for the register. The CW write never fires, even though
cw: trueis set in the config. No error or warning is logged — the register is silently excluded.Root Cause (Source Code Analysis)
Bug 1: Config filter excludes non-polled registers
In
src/main.ts(index.ts compiled), the holdingRegs config is filtered beforeiterateAddresses()processes CW flags:Registers without
poll: trueare excluded from the config array entirely. WheniterateAddresses()later processes the config to populate thecyclicWritearray, the CW register is simply not there.Bug 2: ackObjects dependency prevents write even if filter is bypassed
In
src/lib/Master.ts, the CW write method reads fromackObjects:ackObjectsis only populated during the READ (poll) phase. If a register is never polled,ackObjects[id]is undefined, and the write is silently skipped.Expected Behavior
A register with
cw: trueandpoll: falseshould still be written cyclically. The adapter should:setStatevalue (from ioBroker state DB) instead of requiring a polledackObjectsentrySteps to Reproduce
Use Case
This is critical for devices that require a periodic heartbeat write (e.g., Vestel EVC04 EV charger, which expects a write to register 6000 every few seconds to keep the TCP connection alive). CW would be the perfect built-in mechanism for this, but currently requires a workaround script.
Workaround
Use an ioBroker JavaScript script to periodically write the value: