Skip to content

industry/nxmodbus: Add FC20 and FC21 file record support. - #3910

Merged
acassis merged 2 commits into
apache:masterfrom
daniel-p-carvalho:feat/nxmodbus-file-record
Oct 24, 2026
Merged

acassis merged 2 commits into
apache:masterfrom
daniel-p-carvalho:feat/nxmodbus-file-record

Conversation

@daniel-p-carvalho

Copy link
Copy Markdown
Contributor

Summary

Add the server side of FC20 (Read File Record) and FC21 (Write File Record) to nxmodbus, sections 6.14 and 6.15 of the Modbus Application Protocol Specification V1.1b3. They move whole files, such as a configuration or a firmware image, to a device that only has a Modbus link.

  • New options NXMODBUS_FUNC_READ_FILE and NXMODBUS_FUNC_WRITE_FILE, both default n so existing configurations do not grow.
  • The data stays with the application: a new file_cb in struct nxmb_callbacks_s gets one group of records (file, record, nregs) and reads or writes the buffer in wire order. -ENOENT becomes exception 2.
  • Every sub-request is validated before the first callback. A write is not atomic, as the specification does not make it so.
  • examples/nxmbserver gets two RAM files to exercise both functions.

Impact

Only users who enable the new options are affected. On an Arm Cortex-M4 the handlers take 612 and 362 bytes of code. The new file_cb member sits before priv in struct nxmb_callbacks_s, so a structure initialized by position would break; nothing in the tree does that. The user documentation (function code list and options) will follow in a PR to nuttx.

Testing

Host: Linux x86_64, arm-none-eabi-gcc 15.2.1. Target: NUCLEO-L432KC, nxmbserver as the init task on USART2 (ST-LINK VCP), RTU 115200 8E1, nuttx master (846e525657) plus this PR.

Client: pymodbus 3.16.1, 11 cases: both examples of the specification, 100-record reads and writes, two groups in one request, and the exceptions (file or record out of range, response over 0xf5 bytes). The handlers were also run on the host against stub headers under AddressSanitizer and UBSan.

Client output
ok:   read: two groups (file 1 records 0-9, file 2 records 5-6)  [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [4101, 4102]]
ok:   read: all 100 records of file 2
ok:   write: 5 records to file 1 at record 20
ok:   read back (neighbours untouched)
ok:   write: two groups in one request
ok:   write and read back 100 records (200 bytes)
ok:   read: file 3 does not exist -> exception 2  got 2
ok:   read: record 99 + 2 registers (past the end) -> exception 2  got 2
ok:   read: record 10000 -> exception 2  got 2
ok:   write: file 9 does not exist -> exception 2  got 2
ok:   read: 123 registers (response over 0xf5 bytes) -> exception 3  got 3

0 failures

Add the Read File Record (FC20, 0x14) and Write File Record (FC21, 0x15)
server functions of the Modbus Application Protocol Specification V1.1b3
(sections 6.14 and 6.15).  They move whole files, such as a
configuration or a firmware image, to a device that has only a Modbus
link.

Each function has its own option, NXMODBUS_FUNC_READ_FILE and
NXMODBUS_FUNC_WRITE_FILE.  Both are off by default so that the existing
configurations do not change in size: on an Arm Cortex-M4 the handlers
take 612 and 362 bytes of code.

The data model stays with the application, as for the other function
codes.  A new file_cb callback in struct nxmb_callbacks_s gets one group
of records (file 1 to 0xffff, record 0 to 9999, nregs 16-bit registers)
and reads or writes the buffer in wire order.  It returns -ENOENT for a
file or record that does not exist, which becomes exception 2.  The
member sits before priv, so a structure initialized by position would
break; nothing in the tree does that.

The sub-requests are validated in full before the first callback: the
reference type must be 6, the group must stay inside the 10000 records
of the file, and the read response must fit in 0xf5 bytes and in the
ADU.  The response of a read overwrites the request, so its groups are
parsed first, up to 35 (the most a request can carry), in an array of 6
bytes each on the stack, 210 bytes in all.  A write is not atomic, as
the specification does not make it so: a callback failure leaves the
earlier groups written.

Tested on the host, with the real handlers built against stub headers:
the two examples of the specification byte for byte, the limits of the
fields, the exceptions, 35 sub-requests in place and the largest group,
also under AddressSanitizer and UBSan.  Built for an Arm Cortex-M4.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Add two files of 100 records each, kept in RAM and served through the
file_cb callback, so that the FC20 and FC21 functions of nxmodbus can be
exercised.  They are filled with a known pattern: record n of file 1 is
n, and record n of file 2 is 0x1000 + n.  A file or a record out of
range returns -ENOENT, which the stack turns into exception 2.

The functions are enabled with NXMODBUS_FUNC_READ_FILE and
NXMODBUS_FUNC_WRITE_FILE.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>

@cederom cederom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff thank you @daniel-p-carvalho :-)

Please also provide documentation with examples in https://nuttx.apache.org/docs/latest/applications/industry/nxmodbus/index.html :-)

@daniel-p-carvalho

Copy link
Copy Markdown
Contributor Author

Thanks @cederom! The documentation is in apache/nuttx#20966 and has already been approved by @xiaoxiang781216. It covers the options, the file_cb contract and error mapping, and the file records served by nxmbserver. The example server can be used to try both functions. I'd rather keep that PR as it is now; worked examples can follow in a separate documentation PR if you think they're needed.

@acassis
acassis merged commit 6f4e0d5 into apache:master Oct 24, 2026
20 of 40 checks passed
acassis pushed a commit to apache/nuttx that referenced this pull request Oct 24, 2026
Describe the Read File Record (FC20) and Write File Record (FC21)
server functions of nxmodbus, added by apache/nuttx-apps#3910: the
NXMODBUS_FUNC_READ_FILE and NXMODBUS_FUNC_WRITE_FILE options, the
file_cb callback, how its errors map to Modbus exceptions, the
validation done before the first callback and the fact that a write is
not atomic.  Also describe the file records served by the nxmbserver
example.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants