Existing atMb interface backed by the C TCP engine
Import AtomModbus-Compatible.export into a BACKUP COPY of your existing atomLibrary, accepting replacement of the matching ModbusMaster objects. This preserves the other Atom helpers referenced by the original code. Do not import it into your small CmpAtomModbus external-function library.
In the copied atomLibrary's Library Manager add your CmpAtomModbus 0.2.0.0
library. Ensure its namespace/alias is exactly CmpAtomModbus, as the wrapper
calls CmpAtomModbus.atMbCall_cext. Keep existing dependencies including
ModbusFB, SysTime, the standard timer library and your other Atom helpers.
ModbusFB remains for legacy enum types and RTU; TCP no longer creates or calls
its client/request objects.
The native export preserves all 78 original object IDs and adds 10 methods. Review source is in All_Source_Review.st. Original_HomeCodesys.export is the unchanged supplied original. The export's XML/schema structure and public declaration preservation have been checked, but the revised IEC code has NOT been compiled in CODESYS here. Compile the copied atomLibrary and test before replacing the installed production version.
Same application calls
Your export's exact request names include Req:
VAR
mbClient : atMbClientTCP;
mbRead : atMbReqReadHolding;
mbWrite : atMbReqWriteHolding;
holding : ARRAY[0..9] OF UINT;
setpoints : ARRAY[0..1] OF UINT;
END_VAR
mbClient(
in_ipAddress := To_IPAddress(192,168,1,50),
in_port := 502,
in_timeout := T#1S
);
mbRead(
client := mbClient,
in_unitId := 1,
in_startAddr := 0,
in_quantity := 10,
in_dataOffset := 0,
in_minDelay := T#200MS,
data := holding
);
mbWrite(
client := mbClient,
in_unitId := 1,
in_startAddr := 100,
in_quantity := 2,
in_dataOffset := 0,
in_minDelay := T#1S,
data := setpoints
);
// mbWrite.writeNow(); // Existing method boosts priority, respecting min delay.
Only use mapped register ranges for your device; the write example is an API example, not part of the read-only bench test. Existing names, input defaults, BOOL/UINT open arrays, client reference, stats properties and writeNow are kept. Use your existing library namespace prefix if required by Library Manager.
in_dataOffset remains an ACTUAL ARRAY INDEX as in the old implementation,
not a relative offset from LOWER_BOUND. Native capacity clipping now considers
that starting index. Bad indices set out_parameterError and disable scheduling
until corrected. Quantity zero disables a request. Protocol-excessive quantities
are rejected with a request parameter error. Don't change the client reference
of a registered request while running; request registrations are persistent.
Added diagnostics/options (old call sites need no new arguments)
Client: out_connected, out_connectionState, out_lastNativeError, out_socketError, out_reconnectCount, out_registrationFull. State: 0 disabled, 1 retry, 2 connecting, 3 ready, 4 sending, 5 receiving. Request: out_nativeError, out_socketError, out_writeUncertain, out_validData, out_dataAgeMs, out_stale, out_parameterError. Existing stats remain available.
Extra TCP inputs: in_enable (TRUE), in_connectTimeout (2s), in_reconnectDelay (1s), in_holdUncertainWrites (FALSE). Extra request input: in_staleAfter (2s).
Priorities/minimum polling delays and cyclic writes are preserved. The C engine never automatically replays a failed packet, but the IEC scheduler submits a fresh snapshot on the next eligible write cycle, as the original code does. For command writes where that is inappropriate, set in_holdUncertainWrites=TRUE: an uncertain write holds that request until its existing writeNow() is called. A valid Modbus exception counts as responsive communication, but does not refresh read-data freshness. A client endpoint change closes the old context, cancels its transaction and invalidates its old read data.
The client processes its registered requests, as before. Request FB calls bind their arrays/register the requests; they must still run every scan. After an online change registrations are rebuilt by those calls. The native base's FB_reinit resets in-flight state and TCP FB_exit closes the C context when disposed. These lifecycle paths require target testing. An application STOP alone does not guarantee FB_exit; set in_enable=FALSE and run one scan (or call release()) before STOP/download. Re-enable afterwards.
All native clients/requests must be owned by ONE IEC task because the C pool is not thread-safe. Up to 16 native clients and 100 registered requests/client. Do not retain/persist client handles. Arrays/instances must live for the client lifetime. No IEC data pointer is held in the C engine between calls.
Rebuild the Pi component once
This wrapper uses new command 11 for per-request timeouts. Rebuild the included component; do not use the previous .so. No external function declaration, checksum or library interface regeneration is needed. Version stays 0.2.0.0.
cd ~/modbus-build/atom-modbus
make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" clean
make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" all
sudo /etc/init.d/codesyscontrol stop
sudo cp component/out/libCmpAtomModbus.so /usr/lib/
sudo /etc/init.d/codesyscontrol start
The stop/start requires the same safe bench maintenance conditions as before. The existing ComponentManager entry stays valid. Add the copied/compiled Atom library to a test application, use only the read block initially and verify disconnect/reconnect, per-request timeouts, priorities and lifecycle handling.
Validation
Public input/output/in-out declarations from the original export are retained. The C engine and bridge regression tests cover TCP recovery, framing and exceptions. The new per-request timeout command is exercised by the TCP bridge driver. This does not substitute for CODESYS compile, target scheduler tests or production commissioning.