Use virtual ports with Modbus RTU, ASCII, and SECS-I
You can run the serial exercises of the Modbus and SECS/GEM tutorials on virtual serial ports instead of real equipment and cables. Your application only changes the port name. This page lists the line settings each protocol needs and how to connect.
General rules
Section titled “General rules”- Match the lab’s line settings to the protocol. The lab starts each port with the node’s
line. Linux pseudo terminals can’t carry the data bits and parity an application requests, so the lab’s values apply there. - Use
realisticpacing. Character gaps and response times must match a real line for timeouts and frame boundaries to be tested properly. - Change the port name. Use the lab’s
linkpath (such as/tmp/pdcomm-plc) orpathfor pseudo terminals, a driver port such asCOM201on Windows, and a name such as/dev/ttyPDV0in Linux driver mode.
Modbus RTU
Section titled “Modbus RTU”The Modbus serial line specification (Modbus over Serial Line Specification and Implementation Guide V1.02, §2.5.1) defines an RTU character as 1 start, 8 data, 1 parity, and 1 stop bit, with even parity as the default. Without parity, two stop bits are required, and the PDComm Modbus SDK rejects no parity with one stop bit when you configure it.
| Item | Lab node | Modbus configuration |
|---|---|---|
| Format | {"baud": 19200, "data_bits": 8, "parity": "even", "stop_bits": "1"} |
"transport": "rtu", "baud": 19200, "parity": "even", "stop_bits": 1 |
| Port | A pseudo terminal link or a driver port |
"port": "/tmp/pdcomm-drive1" or "COM201" |
An RTU frame ends with 3.5 character times of silence (same specification, §2.5.1.1). With several devices on one bus and instant pacing, there is no silence between a reply and the next request, so always use realistic when you test RTU on an RS-485 bus.
For a step-by-step exercise, see Test Modbus RTU on a virtual RS-485 bus. The Modbus tutorials explain the protocol itself.
Modbus ASCII
Section titled “Modbus ASCII”ASCII mode sends 7 data bits per character, most often with even parity and one stop bit (7E1). Give the lab node the same format:
{"name": "plc", "port": {"line": {"baud": 9600, "data_bits": 7, "parity": "even", "stop_bits": "1"}}}The Modbus configuration is "transport": "ascii", "baud": 9600, "parity": "even", and "stop_bits": 1. An ASCII frame starts with a colon (:) and ends with CR LF, so it’s less sensitive to character gaps, and you can also use instant pacing to test logic quickly.
SECS-I
Section titled “SECS-I”SECS-I (SEMI E4) carries SECS-II messages over a point-to-point RS-232 line with ENQ/EOT line control, blocks, checksums, and retries. Characters have 8 data bits, no parity, and one stop bit; 9600 bit/s is a common speed. A null-modem lab between a host and equipment looks like this:
{ "engine": { "pacing": "realistic", "nodes": [ {"name": "host", "port": {"line": {"baud": 9600}}}, {"name": "equipment", "port": {"line": {"baud": 9600}}} ], "media": [{"kind": "null_modem", "a": "host", "b": "equipment"}] }, "ports": [ {"node": "host", "backend": {"type": "pty", "link": "/tmp/pdcomm-host"}}, {"node": "equipment", "backend": {"type": "pty", "link": "/tmp/pdcomm-equipment"}} ]}The PDComm SECS-I endpoint (pdcomm_secs1_create) never opens a serial port itself. Your application opens the port and passes nonblocking read and write callbacks in PdcommSecs1Io. So with virtual ports, the only thing you change is the port name your application opens:
- On macOS and Linux, open the link path (
/tmp/pdcomm-host) withopen()and use termios to set 9600 bit/s 8N1, raw mode, and nonblocking reads. - On Windows, open
\\.\COM201withCreateFileand useSetCommStateandSetCommTimeoutsfor the same format and reads that return immediately.
Use fault injection to test SECS-I error handling. For example, {"command":"inject","node":"equipment","fault":{"xor":1}} corrupts the next character the equipment side receives, so the block checksum fails, the receiver rejects the block with NAK, and the sender retries. Unplugging a cable (attach) tests the timeout when ENQ gets no answer and the retry limit.
SECS-I background explains the block structure and timers. To learn block transfer inside the simulator first, without any serial port, use the SECS-I virtual pair. Virtual serial ports go one step further and also test the code with which your application opens a real serial port name. The SECS/GEM tutorials explain the API.