— Human-Readable Communication for Noisy & Low-Bandwidth Links
(Module 2 · Modbus Serial Protocols — The Original Workhorses)
Reading roadmap
§ | Topic | Bread-and-Butter | Deep-Dive Nuggets |
---|---|---|---|
6.1 | Why Modbus ASCII was invented | Dial-up modems, radio, teletype | How 1970-era acoustic couplers shaped the frame |
6.2 | Frame anatomy | Start “:”, ASCII hex, CR LF, LRC | Byte-by-byte with ASCII→binary mapping |
6.3 | Longitudinal Redundancy Check (LRC) | 8-bit two’s-complement checksum | Manual example + table-less C & Python code |
6.4 | Timing & throughput | 1-second inter-frame vs RTU’s 3.5-char | Bandwidth model & optimisation levers |
6.5 | ASCII vs RTU head-to-head | Pros, cons, mixed networks | When to tunnel RTU vs convert in gateway |
6.6 | Worked examples | Read & write frames with live decode | Wireshark TCP-over-serial capture, RealTerm log |
6.7 | Implementation patterns | UART config, buffers, error handling | Converting ASCII↔RTU inside microcontroller |
6.8 | Troubleshooting cookbook | Classic field failures & fixes | “Ghost colon”, CR LF polarity, HEX typo traps |
(diagram & listing placeholders appear as [Fig-6-x] / Listing x)
6.1 Why Modbus ASCII Exists
- Human readability
Every data byte is transmitted as two high-ASCII characters (0‥9 A‥F
). A technician armed with only a dumb serial terminal in 1984 could read frames and type commands by hand. - Character-oriented framing
Start sentinel:
and terminatorCR LF
remove dependence on inter-byte silence. Perfect for:- acoustic-coupler modems (300 Bd)
- half-duplex UHF/VHF radios with unknown turn-around delay
- RS-232 links that insert variable gaps (UART FIFOs, SCADA pollers)
- Noise resilience on mutilated channels
ASCII hex doubles the byte length but halves the probability that a single flipped bit converts one legal byte into another legal byte.
6.2 Frame Anatomy — Byte-for-Byte Breakdown
Mnemonic “S A F D … D LRC E”
Start : Address Function Data … Data Checksum End(CR LF)
Order | Element | Length (ASCII chars) | Purpose |
---|---|---|---|
① | Start | : (3A h) | Marks absolute frame start |
② | Slave Addr | 2 | Hex of 0–247 (00–F7) |
③ | Function Code | 2 | Hex of FC (01–06, 0F, 10…) |
④ | Data | N×2 | Parameters or payload |
⑤ | LRC | 2 | 8-bit checksum (two’s complement) |
⑥ | End | 0D 0A (CR LF ) | Frame terminator |
[Fig-6-1] Colour-coded ASCII frame -> binary field map.
6.2.1 ASCII → Binary Mapping
Every ASCII pair (3F
) == one binary byte (0x3F
).
Total binary data per register = 2 (bytes) × 2 = 4 ASCII characters.
Binary | ASCII High | ASCII Low | Comment |
---|---|---|---|
0x11 | '1' '1' | Slave address 17 | |
0x03 | '0' '3' | Function 03 |
6.3 Longitudinal Redundancy Check (LRC)
6.3.1 Definition
LRC = Two’s-complement (negated sum) of every binary byte from Slave Addr → Data inclusive. LRC=[−∑i=1nbytei]8 bit\text{LRC} = \left[ -\sum_{i=1}^{n} \text{byte}_i \right]_{8\,\text{bit}}
6.3.2 Hand Calculation Walk-Through
Assume request :110300000006
(Addr 17, FC03, Start 0000, Qty 0006).
Byte | Hex | Running sum (8-bit) |
---|---|---|
Addr | 11 | 0x11 |
FC | 03 | 0x14 |
Hi | 00 | 0x14 |
Lo | 00 | 0x14 |
QtyH | 00 | 0x14 |
QtyL | 06 | 0x1A |
Negate: -0x1A = 0xE6
(two’s complement).
ASCII E6
appended before CR LF
⇒ full frame:
:110300000006E6␍␊
6.3.3 Table-less Code (C) – Listing 1
uint8_t calc_lrc(const uint8_t *buf, size_t len) {
uint8_t lrc = 0;
while (len--) lrc += *buf++; // 8-bit add (auto wraps)
return (uint8_t)(-lrc); // two's complement
}
6.4 Timing & Throughput
6.4.1 Framing Rules
Parameter | Recommended | Notes |
---|---|---|
Inter-char timeout | ≤ 1 s | Longer aborts frame |
Inter-frame gap | ≥ 1 s | Gives slave parse window |
Baud rate range | 300 Bd … 38 400 Bd | ASCII decoding overhead becomes dominant above 19 200 Bd |
Unlike RTU, ASCII framing does not depend on character time; start/stop chars govern the parser.
6.4.2 Efficiency Equation
η=NdataNascii=(1+D+2)(1+D)×2+5\eta = \frac{N_\text{data}}{N_\text{ascii}} = \frac{(1 + D + 2)}{(1 + D) \times 2 + 5}
(where 1
=Addr, 2
=LRC, D
=Data bytes, 5
= Start+CRLF)
Example read of 60 regs (binary 125 B data):
ASCII size = (1+1+4+2) × 2 + 3 = 232 chars
(1.28× bigger than RTU).
At 9600 Bd = 232 × 11 / 9600 ≈ 266 ms vs 91 ms for RTU.
▶ Optimisation tip If you must stick with ASCII, crank baud to 19 200 or 38 400 to regain cycle time.
6.5 ASCII vs RTU — Choosing Wisely
Feature | ASCII | RTU |
---|---|---|
Human readable | ✅ | ❌ |
Works through ASCII-only media (GPRS SMS, e-mail tunnelling) | ✅ | ❌ |
Bandwidth efficiency | ❌ (≈½) | ✅ |
Parser complexity | ✅ (no timing) | ⚠ Gap-aware |
Interoperability with hobby tools (PuTTY) | ✅ | ❌ |
Field popularity (2020s) | 5 % of new builds | 95 % |
Decision matrix
Choose ASCII if:
- Communication traverses text-only channels (ASY-000 AT-modem, radio telemetry, SMS).
- A human will type configuration frames (legacy drive commissioning).
- Debugging takes place via simple terminal without protocol plug-ins.
Choose RTU otherwise; wrap RTU inside an ASCII-capable modem or tunnel over TCP if needed.
6.6 Worked Examples
6.6.1 Request — Write Single Coil (ON) to Slave 07
:0705 0013 FF00 79␍␊
Spaces added for clarity.
Field | ASCII | Bin | Meaning |
---|---|---|---|
Start | : | — | — |
Addr | 07 | 0x07 | Slave 7 |
FC | 05 | 0x05 | Write Single Coil |
Addr | 00 13 | 19 | Coil 19 |
Value | FF 00 | On | — |
LRC | 79 | — | Checksum |
Slave response echoes same PDU → :07050013FF0079\r\n
6.6.2 Terminal Capture [Fig-6-2]
Screenshot of RealTerm log, highlighting Start → LRC, colourising TX vs RX.
6.7 Implementation Patterns & Gateways
6.7.1 UART Configuration
Setting | Recommended |
---|---|
Data bits | 7 (ASCII) or 8 (if parity used for 7E1) |
Parity | Even (7E1 ) — optional |
Stop bits | 1 |
Why 7E1? Each ASCII char is 7 bits; parity gives simple per-char error check.
6.7.2 Buffer Strategy
- Scan for
:
; when found, switch to collect mode. - Store until
LF
; verify LRC before parsing. - Reject frames > 513 chars (spec safety guard).
6.7.3 ASCII ↔ RTU Conversion
Many modern gateways (e.g., Moxa, Anybus) transparently convert:
- Strips
:
and CR LF. - Converts ASCII pairs → binary bytes.
- Re-computes CRC-16 to send onto RS-485 RTU slaves.
Latency cost ≈ 1 ms + (ASCII length/baud). Ensure gateway FIFO can buffer full ASCII frame.
6.7.4 In-Microcontroller Conversion (bare metal)
Listing 2 Minimal C routine (STM32) to accept ASCII on UART 1, forward RTU on UART 2; includes interrupt state machine.
(full listing trimmed for brevity but complete in downloadable repo)
6.8 Troubleshooting Cookbook
Symptom | Possible Cause | Fix |
---|---|---|
Slave never responds | Start char missing (: ) | Verify terminal sends 0x3A; some GUIs strip |
“LRC error” only on long frames | Admin typed wrong HEX pair | Echo input, visual diff |
CRC errors after gateway | ASCII→RTU converter double-byte swapped | Update firmware / set little-endian CRC flag |
Frame splits across two TCP packets | Telnet/SSH path inserts CR only | Require full CR LF; enable binary mode |
⚠ Ghost colon Noise flips a line from 0→1 that equals ASCII ‘:’ (0x3A). Install EMI ferrite, verify shield ground.
Key Takeaways
- Modbus ASCII is text-mode RTU: same 6-byte PDU, human-visible hex envelope.
- Start
:
+ EndCR LF
provide timing-agnostic framing perfect for modems & radios. - LRC = –Σ(bytes) (two’s complement); send as ASCII high-low nibbles.
- ASCII costs ~2× bandwidth; mitigate with higher baud or migrate to RTU/TCP.
- Gateways make ASCII ↔ RTU conversion painless but watch latency & endian flags.
Assets to produce before publication
ID | Visual / Asset | Purpose |
---|---|---|
Fig-6-1 | Colour-coded ASCII frame | Teach field order |
Fig-6-2 | RealTerm TX/RX capture | Practical reference |
Fig-6-3 | LRC calc flowchart | Reinforce algorithm |
Fig-6-4 | ASCII vs RTU efficiency graph | Decision making |
Coming Up Next
With both serial variants under your belt, Module 3 catapults us onto Ethernet: Chapter 7 – Introduction to Modbus TCP/IP & the MBAP Header. You’ll learn how the same PDU sails through IP networks, what Transaction-ID means, and how Unit-IDs let one gateway feed 50 serial loops. Keep Wireshark open.