Files
eddys_snippets/modbus_params
..
2025-07-17 17:06:13 +03:00
2026-05-28 13:57:35 +03:00
2025-07-11 11:13:40 +03:00
2025-07-11 11:13:40 +03:00
2025-07-11 11:13:40 +03:00
2025-07-11 11:13:40 +03:00
2025-07-11 11:13:40 +03:00
2026-05-28 13:57:35 +03:00
2025-07-11 11:13:40 +03:00
2025-07-11 11:13:40 +03:00

modbus_params

modbus_params is a command-line tool and network server for interacting with Modbus RTU devices. It uses a humanreadable dictionary that maps register addresses to mnemonic codes, supports periodic dumping of selected parameters to a TSV file, and provides a TCP/UNIX socket interface for remote access.

The project is hosted at:
https://github.com/eddyem/eddys_snippets/tree/master/modbus_params

Features

  • Read and write Modbus holding registers by address or mnemonic code.
  • Define a dictionary (code → register) with optional comments and readonly flags.
  • Dump selected registers periodically to a TSV file.
  • Start a TCP or UNIX socket server that accepts commands to:
    • Read/write registers (by code or address).
    • List dictionary entries or aliases.
    • Start/stop and rename the dump file.
  • Define aliases macros that expand to other commands or expressions.
  • Verbose logging with configurable level.

Dependencies

  • libmodbus (Modbus RTU backend)
  • usefull_macros (argument parsing, socket helpers, logging macros)
  • POSIX threads (pthread)
  • Standard C library

Building

Clone the repository together with the required submodule:

git clone --depth=1 https://github.com/eddyem/eddys_snippets.git
cd eddys_snippets/modbus_params

Then compile with make:

make

The resulting executable is named modbus_params. You can copy this file wherever you want, e.g.

su -c "cp modbus_params /usr/local/bin

Commandline usage

modbus_params [options]

General options

Option Argument Description
-h, --help Show help and exit
-v, --verbose Increase verbosity (can be used multiple times)
-D, --dictionary file Dictionary file (format: code register value readonly). Required for most operations.
-a, --alias file Aliases file (format: name : command). Optional.
-s, --slave ID Modbus slave ID (default: 1)
-d, --device path Serial device (default: /dev/ttyUSB0)
-b, --baudrate rate Baud rate (default: 9600)

Read/write operations

Option Description
-r, --readr addr Read register by address (multiply: -r 100 -r 200)
-R, --readc code Read register by dictionary code (multiply)
-w, --writer addr=val Write value to register by address (multiply)
-W, --writec code=val Write value to register by dictionary code (multiply)
-O, --outdic file Read all registers listed in the dictionary and save them (with current values) into a new dictionary file

Dumping (periodic TSV output)

Option Description
-k, --dumpkey code Add a dictionary key to the dump list (multiply)
-o, --outfile file TSV file for the dump
-t, --dumptime seconds Dump interval (default: 0.1 s)

Server mode

Option Description
-N, --node spec Start a server. For TCP: IP address or hostname with port after ':' (e.g. :1212 or localhost:1212). For UNIX socket: a path (e.g. /tmp/mb_sock).
-U, --unixsock Use UNIX domain socket instead of TCP (requires -N with a path)

If -N is given, the program runs as a server after executing any immediate read/write/dump operations. The server stays alive until interrupted (Ctrl+C).

File formats

Dictionary file

Each line defines one register. Empty lines and text after # are ignored.
Format:

<code> <register> <value> <readonly>
  • code mnemonic string (no spaces).
  • register 16bit Modbus address (decimal).
  • value initial/default value (16bit, not used by the tool except for documentation).
  • readonly 0 (writable) or 1 (readonly).

Example (from dictionary.dic):

F00.00  61440  1  0   # Motor management (V/F)
F00.01  61441  0  0   # Selecting the START command task
...
A02.01  41473  0  1   # Output frequency (read-only)

Aliases file

Aliases provide a way to define shortcuts or composite commands.
Format:

name : expression   # optional comment

The expression is a string that will be interpreted as a command (register read/write, another alias, or a builtin server command). Aliases are resolved recursively.

Example:

stop  : F00.00=0            # stop motor
start : F00.00=1            # start motor
freq  : F00.11              # read current frequency

When the server receives stop, it writes 0 to register mapped to F00.00.

Dump file (TSV)

When dumping is active, the tool writes a tab-separated file with a header line:

#   time,s  <code1> <code2> ...

Each subsequent line contains a timestamp (seconds since dump start) followed by the current value of each registered key. Unreadable registers are shown as ----.

Example:

#   time,s F00.00 F00.11
     0.000 1      5000
     0.102 1      5000

Server protocol

The server listens for plain text commands, terminated by newline (\n).
Each command is a keyvalue pair: key[=value].

  • Getter just the key → returns current value.
  • Setter key=value → writes the value (if the register is writable).

Builtin keys (handlers)

Key Description
list As a getter: prints the whole dictionary (one line per entry). As a setter (e.g. list=F00.01): prints details of the given code or register address.
alias Getter: prints all aliases. Setter (e.g. alias=myalias): prints that specific alias.
newdump Getter: returns current dump file name. Setter (e.g. newdump=/path/file.dump): closes current dump file (if any), opens a new one and starts dumping.
clodump Stops the dump thread and closes the dump file. No value expected.

Default handler (register access)

If the key is not a builtin handler, the program tries to interpret it as:

  • A register address (decimal integer) → find the corresponding dictionary entry.
  • A dictionary code → find the entry.
  • An alias → recursively expand and execute.

Then:

  • Getter (only key) → reads the register and replies key=value.
  • Setter (key=value) → writes the value to the register (if not readonly) and replies OK.

Examples (using netcat, telnet or socat)

TCP server (assuming -N :5020):

$ nc localhost 5020
F00.00
F00.00=1
F00.00=0
OK
list=F00.01
F00.01 61441 0 0 # Selecting the START command task
newdump=/tmp/motor.dump
OK
clodump
OK

UNIX socket:

$ socat - UNIX-CONNECT:/tmp/mb_sock
F00.11
F00.11=5000

Examples

1. Simple read by address

modbus_params -D dictionary.txt -s 1 -d /dev/ttyUSB0 -b 9600 -r 61440

2. Write a value using a code

modbus_params -D dictionary.txt -W "F00.00=1"

3. Dump two registers every 0.5 seconds to data.tsv

modbus_params -D dictionary.txt -k F00.00 -k F00.11 -o data.tsv -t 0.5

(No server started the program will dump until interrupted.)

4. Start a local server with dumping already active

modbus_params -D dictionary.txt -k F00.00 -o /tmp/dump.csv -t 1 -N localhost:5020

Now you can connect to port 5020 and use newdump to change the output file, or clodump to stop.

5. Read all dictionary registers and save current values

modbus_params -D dictionary.dic -O current_state.dic

Signals

  • SIGINT (Ctrl+C), SIGTERM, SIGQUIT gracefully close Modbus, dump files, and exit.
  • SIGHUP, SIGTSTP ignored (no action).

Notes

  • The dictionary is shared between the main program and the server thread. Do not modify the dictionary file while the program is running.
  • The dump thread uses a separate timer; if a read operation hangs, the dump may stall.
  • When using UNIX sockets, the path can be prefixed with \0 or @ (for abstract sockets). The program accepts plain paths for filesystem sockets.
  • The usefull_macros library provides the sl_dtime() highresolution timer and threadsafe logging macros.

License

Copyright 2022 Edward V. Emelianov edward.emelianoff@gmail.com. GNU General Public License v3 or later.

Full text of the GPLv3 is available at https://www.gnu.org/licenses/gpl-3.0.html.