nx_modbus_shim.nx
buildroot/runtime/shims/nx_modbus_shim.nx
about
nx_modbus_shim.nx -- Modbus RTU + TCP protocol shim.
WHEELER-IMPLEMENTATION-OF-CANONICAL-SPEC.
Wire-spec sources (re-implemented clean-room from published specs;
no external code imported per sovereignty audit):
- Modbus Application Protocol Specification V1.1b3 (Modbus Org, 2012)
- Modbus over Serial Line Specification and Implementation Guide V1.02
- MODBUS Messaging on TCP/IP Implementation Guide V1.0b
Second protocol family per INTEROPERABILITY_CHARTER.md §5.2.
Proves the 5-file shim template generalises from automotive
(OBD-II in §5.1) to industrial (Modbus in §5.2).
Status: SEED v0.1.0. 2026-05-27.
WINNER-TIER: BASELINE-C provisional
INCUMBENTS: libmodbus v3.1.x (LGPL C library; most-deployed),
pyModbus v3.x (Python), Modbus.NET (commercial .NET),
ModScan / ModSlave (commercial Windows tools)
NUMBERS: V1 ships parsing + framing for the 4 dominant
function codes (03/04/06/16); paired throughput +
latency bench vs libmodbus pending real serial-port
hardware
GAP: unmeasured today; comparable parse-cost expected
(Modbus framing is trivial vs OBD's ISO-TP);
Nishi adds value-add via the same substrate
consumer pattern as OBD pred-maint
PLAN: M-next: paired bench on a real Modbus RTU serial bus
+ Modbus TCP server vs libmodbus client; re-rate
EXEMPTION REASON: n/a; provisional pending measurement
V1 SCOPE:
- Modbus RTU framing parse + build (serial)
- Modbus TCP framing parse + build (MBAP header)
- Function code 03 (read holding registers)
- Function code 04 (read input registers)
- Function code 06 (write single register)
- Function code 16 (write multiple registers)
- CRC-16-Modbus (bit-by-bit; no precomputed table for
sovereignty; ~30 cycles per byte on x86_64)
dependencies 1 imports · 1 importers
imports: nx_syscalls.nx
imported by: nx_modbus_pred_maint_pipeline.nx
structs
| 118 | struct NxModbusShim |
consts
| 57 | const NX_SHIM_OK: i64 = 0 |
| 58 | const NX_SHIM_BAD_FRAME: i64 = 1 |
| 59 | const NX_SHIM_PROTOCOL_VIOLATION: i64 = 2 |
| 60 | const NX_SHIM_TIMEOUT: i64 = 3 |
| 61 | const NX_SHIM_BACKPRESSURE: i64 = 4 |
| 62 | const NX_SHIM_UNSUPPORTED_PID: i64 = 5 // here: unsupported function code |
| 63 | const NX_SHIM_NO_WIRE: i64 = 6 |
| 66 | const NX_MODBUS_VERDICT_BASE: i64 = 200 |
| 67 | const NX_MODBUS_BAD_CRC: i64 = 200 // RTU CRC mismatch |
| 68 | const NX_MODBUS_BAD_MBAP_LEN: i64 = 201 // TCP MBAP length field disagrees with payload |
| 69 | const NX_MODBUS_BAD_PROTO_ID: i64 = 202 // TCP MBAP protocol_id != 0 |
| 70 | const NX_MODBUS_EXCEPTION_RESP: i64 = 203 // function | 0x80 received; exception_code follows |
| 71 | const NX_MODBUS_BAD_FUNCTION: i64 = 204 // function code unsupported in V1 |
| 72 | const NX_MODBUS_TRUNCATED: i64 = 205 // frame ends mid-field |
| 73 | const NX_MODBUS_BAD_QUANTITY: i64 = 206 // read/write count outside spec bounds |
| 76 | const NX_MODBUS_FC_READ_COILS: i64 = 0x01 // V2+ TODO |
| 77 | const NX_MODBUS_FC_READ_DISCRETE_INPUTS: i64 = 0x02 // V2+ |
| 78 | const NX_MODBUS_FC_READ_HOLDING_REGS: i64 = 0x03 // V1 |
| 79 | const NX_MODBUS_FC_READ_INPUT_REGS: i64 = 0x04 // V1 |
| 80 | const NX_MODBUS_FC_WRITE_SINGLE_COIL: i64 = 0x05 // V2+ |
| 81 | const NX_MODBUS_FC_WRITE_SINGLE_REG: i64 = 0x06 // V1 |
| 82 | const NX_MODBUS_FC_WRITE_MULTI_COILS: i64 = 0x0F // V2+ |
| 83 | const NX_MODBUS_FC_WRITE_MULTI_REGS: i64 = 0x10 // V1 |
| 84 | const NX_MODBUS_FC_READ_WRITE_MULTI: i64 = 0x17 // V2+ |
| 85 | const NX_MODBUS_FC_EXCEPTION_OFFSET: i64 = 0x80 // function | 0x80 = exception response |
| 88 | const NX_MODBUS_EXC_ILLEGAL_FUNCTION: i64 = 0x01 |
| 89 | const NX_MODBUS_EXC_ILLEGAL_DATA_ADDR: i64 = 0x02 |
| 90 | const NX_MODBUS_EXC_ILLEGAL_DATA_VALUE: i64 = 0x03 |
| 91 | const NX_MODBUS_EXC_SLAVE_DEVICE_FAILURE: i64 = 0x04 |
| 92 | const NX_MODBUS_EXC_ACKNOWLEDGE: i64 = 0x05 |
| 93 | const NX_MODBUS_EXC_SLAVE_BUSY: i64 = 0x06 |
| 94 | const NX_MODBUS_EXC_GATEWAY_PATH_UNAVAIL: i64 = 0x0A |
| 95 | const NX_MODBUS_EXC_GATEWAY_TARGET_NO_RESP: i64 = 0x0B |
| 98 | const NX_MBAP_OFF_TXN_ID: i64 = 0 // u16 BE; client-chosen; echoed by server |
| 99 | const NX_MBAP_OFF_PROTO_ID: i64 = 2 // u16 BE; MUST be 0x0000 |
| 100 | const NX_MBAP_OFF_LENGTH: i64 = 4 // u16 BE; bytes after length field (unit_id + PDU) |
| 101 | const NX_MBAP_OFF_UNIT_ID: i64 = 6 // u8; slave address |
| 102 | const NX_MBAP_HEADER_SIZE: i64 = 7 |
| 105 | const NX_MODBUS_RTU_MIN_LEN: i64 = 4 // addr + func + CRC(2) |
| 106 | const NX_MODBUS_RTU_MAX_LEN: i64 = 256 |
| 107 | const NX_MODBUS_TCP_MIN_LEN: i64 = 8 // MBAP(7) + function |
| 108 | const NX_MODBUS_TCP_MAX_LEN: i64 = 260 |
| 111 | const NX_MODBUS_EVT_HOLDING_REG_READ: i64 = 300 // v0=reg_addr, v1=value, v2=unit_id |
| 112 | const NX_MODBUS_EVT_INPUT_REG_READ: i64 = 301 |
| 113 | const NX_MODBUS_EVT_WRITE_ACK: i64 = 302 // v0=reg_addr, v1=quantity_or_value, v2=unit_id |
| 114 | const NX_MODBUS_EVT_EXCEPTION: i64 = 303 // v0=function_code, v1=exception_code, v2=unit_id |
functions
| 128 | func nx_modbus_shim_init(s: *NxModbusShim) -> i64 |
| 147 | func nx_modbus_crc16(buf: *u8, len: i64) -> i64 |
| 173 | func nx_modbus_read_u16_be(buf: *u8, off: i64) -> i64 |
| 179 | func nx_modbus_write_u16_be(buf: *u8, off: i64, value: i64) -> i64 |
| 196 | func nx_modbus_rtu_parse(s: *NxModbusShim, frame: *u8, frame_len: i64, |
| 235 | func nx_modbus_tcp_parse(s: *NxModbusShim, frame: *u8, frame_len: i64, |
| 274 | func nx_modbus_dispatch_pdu(s: *NxModbusShim, func_code: i64, unit_id: i64, called by 2: nx_modbus_rtu_parsenx_modbus_tcp_parse calls 2: nx_modbus_parse_read_regsnx_modbus_read_u16_be |
| 336 | func nx_modbus_parse_read_regs(s: *NxModbusShim, evt_kind: i64, unit_id: i64, |
| 371 | func nx_modbus_rtu_build_read_holding(s: *NxModbusShim, unit_id: i64, |
| 394 | func nx_modbus_tcp_build_read_holding(s: *NxModbusShim, txn_id: i64, unit_id: i64, |
| 418 | func nx_modbus_diag_fn(s: *NxModbusShim, out_vec: *i64) -> i64 |