Installation
MacTools Modbus Poll is a native macOS app built with Rust and Tauri. It runs natively on Apple Silicon and Intel Macs — no Rosetta, no Parallels, no Java, no Node.js. One-time purchase, no subscription.
Download
- Mac App Store: Modbus Poll on the App Store (recommended — handles updates automatically)
- Direct purchase: buy direct ($29.99 one-time) — you receive a download link by email
- Product page: /modbus-poll/
System Requirements
| Requirement | Minimum |
|---|---|
| macOS | 12 Monterey or newer (Apple Silicon & Intel) |
| For Modbus RTU | A USB-to-RS485 adapter (FTDI FT232, CH340/CH341, SiLabs CP2102, or Prolific PL2303) |
| For Modbus TCP | Network reachability to the device on TCP/502 |
First Launch
If you downloaded the direct build outside the App Store, macOS Gatekeeper may block the unsigned binary on first run. Right-click the app, choose Open, then confirm. This is only required once.
/dev/tty.usbserial-* or /dev/cu.usbserial-*.
Quick Start
1. Poll a Modbus TCP device (2 minutes)
This is the most common path — reading holding registers from a power meter, VFD, or PLC over Ethernet.
- Open the app and create a new TCP connection.
- Enter the device IP address and port
502. - Set the Unit ID (slave address). Power meters are usually
1; some devices use255or a configurable value. - Add a register range: type Holding Register, start address
0, count10. - Click Start Polling. Live values appear in the register table.
If every value reads 0 or you get an exception, jump to Troubleshooting — the most frequent cause is a wrong Unit ID or a 1-based vs 0-based addressing mismatch.
2. Poll a Modbus RTU device over serial
- Plug in your USB-to-RS485 adapter.
- Create a new RTU connection and select the serial port (
/dev/cu.usbserial-*). - Match the device's serial parameters exactly: baud rate, data bits, parity, stop bits.
9600 8N1and9600 8E1are the two most common industrial defaults. - Set the Unit ID and add a register range as above.
3. Try the built-in simulator (no hardware needed)
Switch the app to Slave mode and load a device template (Schneider PM5560, SMA inverter, ABB analyzer). The app answers Modbus requests locally on 127.0.0.1:502. Open a second window in Master mode, target 127.0.0.1, and poll — you now have a full loop without any physical device.
Configuration
Connection Types
| Transport | Parameters |
|---|---|
| Modbus TCP | IP address, port (default 502), Unit ID (1–247), timeout |
| Modbus RTU | Serial port, baud, data bits, parity, stop bits, Unit ID |
| Modbus ASCII | Same as RTU but ASCII framing (rare) |
RTU Serial Settings
RTU parameters must match the device exactly. A single mismatched parity bit produces CRC errors on every frame.
| Parameter | Common Values |
|---|---|
| Baud rate | 9600, 19200, 38400, 115200 |
| Data bits | 8 (effectively always) |
| Parity | None, Even, Odd |
| Stop bits | 1 or 2 |
| Typical defaults | 9600 8N1 (Schneider, ABB), 9600 8E1 (Siemens, many VFDs) |
Register Types & Function Codes
| Data Type | Access | Function Code |
|---|---|---|
| Coil | Read/Write (single bit) | FC01 read, FC05 write one, FC0F write many |
| Discrete Input | Read-only (single bit) | FC02 |
| Holding Register | Read/Write (16-bit) | FC03 read, FC06 write one, FC10 write many |
| Input Register | Read-only (16-bit) | FC04 |
Mapping 32-bit & Float Values
Modbus registers are 16-bit. Real process values (a 32-bit float voltage, a 32-bit integer energy counter) span two registers, and every vendor picks a different byte order. Pick the wrong one and your 230 V reading shows up as a tiny fraction or a huge number.
| Setting | Byte Order (2 registers) |
|---|---|
| Big-endian (ABCD) | High word first — Schneider, most power meters |
| Little-endian (DCBA) | Low word first |
| Swapped Big (BADC / CDAB) | Word-swapped — common on SolarLog, some inverters |
| Swapped Little | Both bytes and words reversed |
Per register you can also set a scale factor (e.g. 0.1) and a display unit (V, A, kW) so the table reads in engineering units instead of raw counts.
Polling
Configure the poll interval per register group. 1000 ms is a safe default for power meters; faster intervals stress slow devices and RTU buses. On RTU, leave the inter-frame gap (3.5 character times) enabled — turning the bus around too fast causes collisions.
Key Features
Master & Slave in one app
Master (client) mode polls physical devices. Slave (server) mode turns your Mac into a Modbus device that answers requests — ideal for testing SCADA/HMI software against a known-good slave without field hardware.
Device template library
Pre-built register maps with realistic auto-drifting values, so simulated telemetry looks live rather than frozen:
- Schneider PM5560 power meter — voltage, current, power, power factor, frequency, energy
- SMA STP 60-US-10 solar inverter — DC/AC power, yield, grid metrics
- ABB AZL402 analyzer
- Endress+Hauser Promag 10W flowmeter
You can also build custom templates from a blank register map and reuse them across projects.
Traffic capture & CSV export
Every Modbus frame is logged with millisecond timestamps, direction (request/response), function code, and raw bytes. Filter by direction or by exception responses, then export to CSV for post-mortem analysis of an intermittent comms failure.
Live trend charts
Plot one or more registers as a multi-series trend with pan and zoom — useful for watching a power factor drift or confirming a VFD ramp profile during commissioning.
Native serial support
User-space drivers for FTDI, CH340, CP2102, and Prolific adapters. No kernel extensions, no unsigned-driver warnings on Apple Silicon.
Troubleshooting
Modbus exception codes
When the slave refuses a request, it returns an exception code instead of data. These are the ones you will see in practice.
| Code | Name | Likely Cause |
|---|---|---|
| 01 | Illegal Function | Device doesn't support that function code (e.g. FC06 on a read-only meter) |
| 02 | Illegal Data Address | Register address out of range — check 0-based vs 1-based offset |
| 03 | Illegal Data Value | Value out of range for a write |
| 04 | Slave Device Failure | Device internal error / unrecoverable fault |
| 0A (10) | Gateway Path Unavailable | Wrong Unit ID, or gateway can't reach the downstream slave |
| 0B (11) | Gateway Target No Response | Downstream slave offline behind a gateway |
TCP connection refused / timeout
Symptom: "Connection refused" or no response on TCP.
Fix: Confirm the device listens on port 502 and that nothing blocks it. From Terminal:
nc -zv 192.168.1.100 502
If nc fails, the device is offline, on another subnet, or behind a firewall. If nc succeeds but Modbus times out, the Unit ID is almost certainly wrong.
RTU CRC errors on every frame
Symptom: All values read 0, comm log shows CRC errors.
Fix: Serial parameters don't match the device. Check baud, parity, and stop bits first. Then verify the RS-485 wiring: A/B (D+/D-) polarity is swapped on roughly half of all installations — if swapping the two wires fixes it, that was it. On long buses, confirm a 120 Ω termination resistor at each physical end.
USB-RS485 adapter not detected
Symptom: No /dev/cu.usbserial-* port appears.
Fix: Unplug/replug, check System Information → USB to confirm the adapter enumerates. Counterfeit Prolific chips are the usual culprit — a genuine FTDI or CP2102 adapter avoids this entirely.
32-bit values read as garbage
Symptom: A 230 V reading shows as 0.003 or 1.5e7.
Fix: Byte order mismatch. Cycle through Big / Little / Swapped Big / Swapped Little on that register until the value is sane — there are only four options and exactly one will be correct.
Addressing offset (off-by-one registers)
Modbus protocol addresses are 0-based, but many device manuals print 1-based "register numbers" (e.g. register 40001 = holding register 0). If the manual says register 30201, that is input register 200 (0-based). Subtract the base (30001, 40001) and then subtract 1.
Support
- Email: support@voltrus.id
- Product page: /modbus-poll/
When reporting a comms issue, attach the traffic capture CSV (redact any sensitive IPs) and the device's register map page from its manual.