NetBurner 3.5.7
PDF Version
Serial Receive Modbus RTU Timing

Example Path: examples/serial/SerialReceiveModbus

Serial Receive Modbus RTU Timing Example

Overview

This NetBurner embedded application demonstrates the timing requirements for receiving Modbus RTU serial data. It uses a serial receive callback to timestamp each incoming byte and software-based idle detection to identify frame boundaries. The console output shows per-byte timing analysis with inter-character gap measurements and violation detection.

Supported Platforms

  • MODM7AE70 (SAME70)
  • SBE70LC (SAME70)
  • SOMRT1061 (i.MX RT1061)
  • MODRT1171 (i.MX RT1171)

Application Description

The application monitors a serial port configured for Modbus RTU communication (9600 baud, 8E1) and provides detailed timing analysis of each received frame, including:

  • Per-byte inter-character gap measurement with violation detection
  • Inter-frame silence measurement (must be >= 3.5 character times)
  • Frame hex dump with Modbus RTU field identification
  • Frame validity assessment

Modbus RTU Timing Requirements

Modbus RTU uses timing-based frame delimiting rather than special start/end characters:

  • Inter-Frame Silence: A minimum of 3.5 character times (~4.01 ms at 9600 baud) of silence on the bus indicates the start or end of a frame
  • Inter-Character Limit: The gap between consecutive bytes within a frame must not exceed 1.5 character times (~1.72 ms at 9600 baud). If exceeded, the frame is considered invalid
  • Character Time: At 9600 baud with 8E1 framing (11 bits per character: 1 start + 8 data + 1 parity + 1 stop), one character time is approximately 1.146 ms

Architecture

The example uses a serial receive callback registered via UartData[].m_pPutCharFunc. This ISR-level callback:

  1. Timestamps each byte using StopWatch::GetTime() (sub-microsecond resolution)
  2. Checks for 3.5 char-time gaps to detect frame boundaries in software
  3. Posts a semaphore when a complete frame is detected

The main loop pends on the semaphore, then prints the complete frame with per-byte timing analysis.

Platform-Specific Serial Port Selection

The default serial port is selected automatically based on the platform:

MODM7AE70 / SBE70LC (SAME70)

Default: port 2 (UART0)

These platforms have two types of serial peripheral: USART (ports 0-1) and UART (ports 2-6). This example uses a UART port because USART ports use DMA for receive, which batches incoming bytes and prevents accurate per-byte timing measurement. UART ports at baud rates <= 230400 use per-byte RXRDY interrupts, giving true wire-level timestamps.

Port Peripheral Notes
0 USART0 Typically stdio. Uses DMA – not suitable for per-byte timing
1 USART1 Uses DMA – not suitable for per-byte timing
2 UART0 Default for this example
3 UART1
4 UART2
5 UART3
6 UART4

SOMRT1061 (i.MX RT1061)

Default: port 1 (LPUART2)

All LPUART ports use per-byte receive interrupts, so any port works for accurate timing.

Port Peripheral Notes
0 LPUART3 Typically stdio
1 LPUART2 Default for this example
2 LPUART4
3-6 LPUART5-8

MODRT1171 (i.MX RT1171)

Default: port 1 (LPUART2)

All LPUART ports use per-byte receive interrupts, so any port works for accurate timing.

Port Peripheral Notes
0 LPUART1 Typically stdio
1 LPUART2 Default for this example
2 LPUART3
3-10 LPUART5-12

To change the port, modify the MODBUS_SERIAL_PORT define in main.cpp.

Hardware Requirements

  • A supported NetBurner platform (see above)
  • Serial connection to the selected port at 9600 baud, 8E1
  • A Modbus RTU master, serial terminal, or the included Python test script

Usage

  1. Compile and flash the application to a NetBurner device
  2. Connect a Modbus RTU master or serial terminal to the selected serial port at 9600 baud, 8 data bits, even parity, 1 stop bit
  3. Send Modbus RTU frames to the device
  4. Observe the timing analysis output on the debug console

Python Test Script

A test script (test_modbus_timing.py) is included that sends various valid and invalid Modbus RTU frames to exercise the timing analysis. It requires a USB-to-serial adapter connected to the selected port.

pip install pyserial
python test_modbus_timing.py COM3 # Run all tests
python test_modbus_timing.py COM3 3 # Run only test 3
Definition test_modbus_timing.py:1

Console Output Example

--- Modbus RTU Frame Received (8 bytes) ---
Inter-frame silence: 5230 us (4.56 char times) [OK >= 3.5]
Byte Hex Delta(us) CharTimes Status
---- ---- --------- --------- ------
0 0x01 --- --- [START]
1 0x03 1148 1.00 ok
2 0x00 1147 1.00 ok
3 0x00 1148 1.00 ok
4 0x00 1147 1.00 ok
5 0x0A 1148 1.00 ok
6 0xC5 1147 1.00 ok
7 0xCD 1148 1.00 ok
Modbus RTU fields:
Slave address: 0x01 (1)
Function code: 0x03 (3)
Data: 4 bytes
CRC (received): 0xCDC5
Frame timing: VALID

Code Structure

main.cpp
Platform-specific port selection (#if defined)
Timing constants (character times, thresholds)
RxByte struct (byte + timestamp)
SerialRxCallback() - ISR-level byte capture with timestamp and frame detection
PrintFrameAnalysis() - Console output with per-byte timing analysis
UserMain() - Initialization and main processing loop

Key APIs Used

  • OpenSerial() – Opens the serial port with 8E1 Modbus RTU framing
  • UartData[].m_pPutCharFunc – ISR-level receive callback registration
  • StopWatch – High-resolution timestamping (sub-microsecond resolution)