doc/help/Protocol-Setup-Guides.md
Step-by-step setup instructions for every communication protocol supported by Serial Studio. Each guide walks through the exact UI steps required to establish a connection. For protocol overviews and selection guidance, see Communication Protocols; for the driver model and multi-device projects, see Data Sources.
License: Free
Prerequisites: Device connected via USB or RS-232/RS-485 adapter. Driver installed if required (CH340, FTDI, CP210x).
/dev/ttyUSBx / /dev/ttyACMx (Linux/macOS) from the COM Port dropdown. If the port does not appear, check the Troubleshooting entries for this driver.dialout group: sudo usermod -aG dialout $USER, then log out and back in. On Windows, install the appropriate USB-serial driver. On all platforms, try a different USB cable or port.Full driver reference: UART.
License: Free
Prerequisites: Device and computer on the same network, or device accessible over the internet. Device IP address and port number known.
127.0.0.1.Serial Studio will resolve the hostname (if provided) and attempt a TCP connection. The Connect button stays disabled until the name resolves.
Serial Studio is a TCP client only: it dials out to a listening server and never accepts inbound connections. If the device expects to push data to a listener, run a small TCP relay in front of Serial Studio, or switch to UDP.
Full driver reference: Network.
License: Free
Prerequisites: Device IP address and port number known. For receiving, the device must be sending to your computer's IP.
127.0.0.1.0 (the default) for automatic port assignment; bind a fixed port when the device sends to a known destination.239.x.x.x). Serial Studio joins the group on connect.Full driver reference: Network.
License: Free
Prerequisites: Computer with Bluetooth 4.0+ adapter. BLE device powered on and in advertising mode (not connected to another host).
bluetoothd is running and the user is in the bluetooth group.Full driver reference: Bluetooth LE.
License: Pro
Prerequisites: Access to an MQTT broker. Broker hostname, port, and credentials (if any). Topic filter (subscriber) or base topic (publisher).
MQTT now has two distinct surfaces in Serial Studio. Pick the one that matches what you want to do; both can run in the same project.
For a single-device setup, select MQTT Subscriber from the I/O Interface dropdown in the Setup Panel and fill in the same fields there. For multi-device projects:
1883 plaintext, 8883 TLS; default 1883).sensors/#, device/+/telemetry, mydevice/data. Connecting is blocked while this is empty.Repeat with another source set to MQTT Subscriber to add a second broker connection (or a second topic on the same broker). Each MQTT source has its own broker session, credentials, and TLS configuration.
See MQTT Subscriber for the full protocol primer.
mqtt(frame) script hook.See MQTT Publisher for the full reference, including the mqtt(frame) script hook for frame parsers.
mosquitto_sub) to confirm the broker is reachable. On the Publisher form, Test Connection does the same probe inside Serial Studio.# as the topic filter to receive all messages for debugging.License: Pro
Prerequisites: RS-485-to-USB adapter connected to the computer. Device wired to the RS-485 bus with correct polarity (A-to-A, B-to-B) and 120-ohm termination resistors at each end.
Once you have configured your register groups, you can auto-generate a Serial Studio project file instead of building one by hand:
.ssproj file.The generated project includes one group per register block, one dataset per register/coil, and a generated Lua frame parser. See How Multi-Group Polling Works for details on the frame parser.
Full driver reference: Modbus.
License: Pro
Prerequisites: Device connected to the network with a known IP address and Modbus TCP port.
License: Pro
Instead of manually adding register groups one by one, you can import a register map file that describes all registers for your device. Serial Studio will auto-generate a complete project with register groups, datasets, a Lua frame parser, and appropriate dashboard widgets.
Serial Studio auto-detects the format from the file extension and accepts CSV, XML, and JSON files.
.ssproj file.CSV is the most common format for Modbus register maps. Most PLC vendors, SCADA tools, and device datasheets can export or be converted to this format.
Columns (header row required, order is flexible):
| Column | Aliases | Required | Default |
|---|---|---|---|
address | addr, register, reg | Yes | None |
name | label, tag | No | Register N |
type | register_type, function, fc | No | holding |
dataType | data_type, format | No | uint16 |
units | unit, eng_units | No | None |
min | minimum, range_min | No | 0 |
max | maximum, range_max | No | 65535 |
scale | factor, multiplier | No | 1.0 |
offset | No | 0.0 | |
description | desc, comment (used as name if no name column) | No | None |
Register type values: holding (or 0x03, 3, hr), input (or 0x04, 4, ir), coil (or 0x01, 1), discrete (or 0x02, 2, di).
Data type values: uint16, int16, uint32, int32, uint64, int64, float32, float64, bool. Unknown types fall back to uint16; bool defaults its range to 0-1.
Lines starting with # are treated as comments and skipped.
Example:
address,name,type,dataType,units,min,max,scale,offset
0,Temperature,holding,uint16,°C,0,150,0.1,0
1,Pressure,holding,uint16,PSI,0,5000,1,0
2,RPM,holding,uint16,RPM,0,3000,1,0
4,Flow Rate,holding,float32,GPM,0,50,1,0
0,E-Stop,coil,bool,,0,1,,
1,Motor Run,coil,bool,,0,1,,
Most Modbus device documentation provides register maps as tables in PDF or Excel. To convert:
float32, uint32, int32) occupies two consecutive 16-bit registers; a 64-bit value occupies four. Set the address to the first register. Serial Studio handles the rest.scale,0.1 to the CSV..csv with UTF-8 encoding.Tip: You only need the address and name columns to get started. Everything else has sensible defaults.
Two layouts are supported:
Flat (type as attribute):
<modbus>
<register address="0" type="holding" name="Temperature" dataType="uint16" units="°C" min="0" max="150" scale="0.1"/>
<register address="0" type="coil" name="E-Stop"/>
</modbus>
Nested (type from parent element):
<modbus>
<holding-registers>
<register address="0" name="Temperature" dataType="uint16" units="°C"/>
</holding-registers>
<coils>
<register address="0" name="E-Stop"/>
</coils>
</modbus>
Supported parent tags: holding-registers, input-registers, coils, discrete-inputs (with or without hyphens).
Two layouts are supported:
Flat (type per register):
{
"registers": [
{"address": 0, "type": "holding", "name": "Temperature", "dataType": "uint16", "units": "°C", "min": 0, "max": 150, "scale": 0.1},
{"address": 0, "type": "coil", "name": "E-Stop", "dataType": "bool"}
]
}
Grouped (type from key):
{
"holdingRegisters": [
{"address": 0, "name": "Temperature", "dataType": "uint16", "units": "°C"}
],
"coils": [
{"address": 0, "name": "E-Stop"}
]
}
Supported group keys: holdingRegisters, holding_registers, holding, inputRegisters, input_registers, input, coils, discreteInputs, discrete_inputs, discrete.
The importer produces:
Below is the same hydraulic test stand register map in all three supported formats.
CSV:
address,name,type,dataType,units,min,max,scale,offset
0,Temperature,holding,uint16,°C,0,150,0.1,0
1,Pressure,holding,uint16,PSI,0,5000,1,0
2,RPM,holding,uint16,RPM,0,3000,1,0
3,Valve Position,holding,uint16,%,0,100,0.1,0
4,Flow Rate,holding,float32,GPM,0,50,1,0
6,Motor Load,holding,uint16,%,0,100,0.1,0
7,Vibration,holding,uint16,mm/s,0,50,0.1,0
0,Pump Inlet Temp,input,int16,°C,-40,150,0.1,0
1,Pump Outlet Temp,input,int16,°C,-40,150,0.1,0
2,Ambient Temp,input,int16,°C,-40,80,0.1,0
0,E-Stop,coil,bool,,0,1,,
1,Motor Run,coil,bool,,0,1,,
2,Valve Open,coil,bool,,0,1,,
3,Alarm Active,coil,bool,,0,1,,
0,Door Sensor,discrete,bool,,0,1,,
1,Level Switch,discrete,bool,,0,1,,
JSON (flat format):
{
"registers": [
{"address": 0, "type": "holding", "name": "Temperature", "dataType": "uint16", "units": "°C", "min": 0, "max": 150, "scale": 0.1},
{"address": 1, "type": "holding", "name": "Pressure", "dataType": "uint16", "units": "PSI", "min": 0, "max": 5000},
{"address": 2, "type": "holding", "name": "RPM", "dataType": "uint16", "units": "RPM", "min": 0, "max": 3000},
{"address": 3, "type": "holding", "name": "Valve Position", "dataType": "uint16", "units": "%", "min": 0, "max": 100, "scale": 0.1},
{"address": 4, "type": "holding", "name": "Flow Rate", "dataType": "float32", "units": "GPM", "min": 0, "max": 50},
{"address": 6, "type": "holding", "name": "Motor Load", "dataType": "uint16", "units": "%", "min": 0, "max": 100, "scale": 0.1},
{"address": 7, "type": "holding", "name": "Vibration", "dataType": "uint16", "units": "mm/s", "min": 0, "max": 50, "scale": 0.1},
{"address": 0, "type": "input", "name": "Pump Inlet Temp", "dataType": "int16", "units": "°C", "min": -40, "max": 150, "scale": 0.1},
{"address": 1, "type": "input", "name": "Pump Outlet Temp", "dataType": "int16", "units": "°C", "min": -40, "max": 150, "scale": 0.1},
{"address": 2, "type": "input", "name": "Ambient Temp", "dataType": "int16", "units": "°C", "min": -40, "max": 80, "scale": 0.1},
{"address": 0, "type": "coil", "name": "E-Stop", "dataType": "bool"},
{"address": 1, "type": "coil", "name": "Motor Run", "dataType": "bool"},
{"address": 2, "type": "coil", "name": "Valve Open", "dataType": "bool"},
{"address": 3, "type": "coil", "name": "Alarm Active", "dataType": "bool"},
{"address": 0, "type": "discrete", "name": "Door Sensor", "dataType": "bool"},
{"address": 1, "type": "discrete", "name": "Level Switch", "dataType": "bool"}
]
}
XML:
<?xml version="1.0" encoding="UTF-8"?>
<modbus>
<holding-registers>
<register address="0" name="Temperature" dataType="uint16" units="°C" min="0" max="150" scale="0.1"/>
<register address="1" name="Pressure" dataType="uint16" units="PSI" min="0" max="5000"/>
<register address="2" name="RPM" dataType="uint16" units="RPM" min="0" max="3000"/>
<register address="3" name="Valve Position" dataType="uint16" units="%" min="0" max="100" scale="0.1"/>
<register address="4" name="Flow Rate" dataType="float32" units="GPM" min="0" max="50"/>
<register address="6" name="Motor Load" dataType="uint16" units="%" min="0" max="100" scale="0.1"/>
<register address="7" name="Vibration" dataType="uint16" units="mm/s" min="0" max="50" scale="0.1"/>
</holding-registers>
<input-registers>
<register address="0" name="Pump Inlet Temp" dataType="int16" units="°C" min="-40" max="150" scale="0.1"/>
<register address="1" name="Pump Outlet Temp" dataType="int16" units="°C" min="-40" max="150" scale="0.1"/>
<register address="2" name="Ambient Temp" dataType="int16" units="°C" min="-40" max="80" scale="0.1"/>
</input-registers>
<coils>
<register address="0" name="E-Stop"/>
<register address="1" name="Motor Run"/>
<register address="2" name="Valve Open"/>
<register address="3" name="Alarm Active"/>
</coils>
<discrete-inputs>
<register address="0" name="Door Sensor"/>
<register address="1" name="Level Switch"/>
</discrete-inputs>
</modbus>
When a Modbus connection has multiple register groups, the driver and the auto-generated frame parser coordinate through a simple sequential protocol. Understanding this mechanism helps when debugging or customizing the generated parser.
Each poll cycle, the Modbus driver iterates through the configured register groups in order:
The groups are always polled in the same fixed order (the order they appear in the register groups list). Each response is emitted as a separate frame to the data pipeline.
The generated Lua parser keeps a poll cursor that starts at the first register block. Each time a response frame arrives, the parser reads the Modbus function code (the second byte of the response) and matches it against the configured blocks, probing in cursor order. It decodes the matching block, publishes the extracted values into the block's data table, then advances the cursor to the block after the one it just decoded. Because matching is keyed on the function code rather than on cursor position alone, the parser snaps to the correct block even if a response is missing or arrives out of order.
Values are latched through the data tables: each register block updates only its own table registers, and every dataset reads its current value back from the table inside its transform, so values from other blocks carry over between frames. A single contiguous block decodes on every frame; with multiple blocks, the cursor walks through them as their responses arrive.
For two groups (e.g., holding registers at address 0 and coils at address 100), the response for the holding registers carries function code 0x03 and updates the temperature and pressure datasets, while the coils response carries function code 0x01 and updates the E-Stop and Motor Run datasets.
Frame with function 0x03 arrives → parser matches the holding-register block → extracts holding register values → advances the cursor.
Frame with function 0x01 arrives → parser matches the coil block → extracts coil values → advances the cursor.
BLOCKS spec (name, offset, type, optional scale/shift); to add a register, add a line there plus a matching data-table register and a dataset that reads it back with tableGet(). For larger changes, adjust the register groups and regenerate the project.License: Pro
Prerequisites: CAN-to-USB adapter (PEAK PCAN, Kvaser, CANable, SocketCAN-compatible) connected and drivers installed.
socketcan (Linux), peakcan, vectorcan, systeccan, tinycan, and virtualcan; Serial Studio adds its own canable_gsusb (CANable USB), slcan (Serial CAN), and seeed_usbcan (Seeed / Waveshare) backends.can0, PCAN_USBBUS1).To skip writing a project by hand, click Import DBC File… under DBC Database: the importer generates a complete project from a CAN signal database (see Auto-Generating Projects). Without a DBC, frames reach the frame parser as binary records (ID, DLC, payload); decode them with the Binary decoder and your own parse function.
Before connecting in Serial Studio, bring up the CAN interface from a terminal:
sudo ip link set can0 type can bitrate 500000
sudo ip link set up can0
For CAN FD, add the data bitrate:
sudo ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on
sudo ip link set up can0
ip link show can0. On Windows, check Device Manager for the CAN adapter.Full driver reference: CAN Bus.
License: Pro
Prerequisites: Audio input device (microphone, line-in, audio interface) connected and recognized by the OS.
Audio data flows into the pipeline as PCM samples, which can be visualized with Plot, FFT Plot, and Waterfall widgets.
Full driver reference: Audio.
License: Pro
Prerequisites: USB device with accessible bulk, control, or isochronous endpoints. On Linux, appropriate udev rules. On Windows, a WinUSB-compatible driver (installable via Zadig).
VID:PID – Manufacturer Product.udev rule granting your user access to the device's VID/PID, then run sudo udevadm control --reload-rules && sudo udevadm trigger. On Windows, use Zadig to install a WinUSB driver for the device. On macOS, the device should appear if no kernel driver has claimed it.udev rule is correct and the user is in the appropriate group. Running as root is a quick test but not recommended for production.Full driver reference: USB.
License: Pro
Prerequisites: USB HID-class device (gamepad, joystick, custom HID firmware, sensor) connected.
Product Name (VID:PID).udev rule for hidraw access: create a file in /etc/udev/rules.d/ with KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0666" (or a more restrictive rule targeting your VID/PID). On Windows and macOS, HID devices are generally accessible without additional configuration.Full driver reference: HID.
License: Pro
Prerequisites: A script or executable that writes data to stdout in a format Serial Studio can parse.
Serial Studio spawns the process, reads its merged stdout and stderr, and feeds the output into the data pipeline. You can write to the process's stdin via the console.
chmod +x script.py). For Python scripts, you may need to specify python3 as the executable and the script path as an argument.flush=True in print() or run with python3 -u). Buffered output will not appear until the buffer fills.Full driver reference: Process I/O.
License: Pro
Prerequisites: An external process writing data to a named pipe (FIFO on Linux/macOS, \\.\pipe\Name on Windows).
/tmp/myfifo (create with mkfifo /tmp/myfifo beforehand).\\.\pipe\MyPipeName.Serial Studio opens the named pipe for reading and streams data into the pipeline. The external process must open the pipe for writing.
mkfifo /path/to/pipe. On Windows, the pipe must be created by the writing process before Serial Studio connects.Full driver reference: Process I/O.
parse(frame) function for custom data formats.