Using MODBUS RTU with NuttX

MODBUS is an old protocol used on industrial automation. Although it is an “old” protocol, it still used on many industrial devices.

There are many articles about this protocol in the Internet, but most are very shallow. This guy has a fair introduction about MODBUS:

https://www.lammertbies.nl/comm/info/modbus.html

I spent some time (few days) to get it working on NuttX. First I implemented the DIR pin control for SAMD21 microcontroller and submitted to mainline: https://bitbucket.org/nuttx/nuttx/commits/a34c9733bcac707be803a27c91fec7406bda310b and also fixed a serial issue that was preventing the serial driver from working: https://bitbucket.org/nuttx/nuttx/commits/db0b9b7c34ddcf098059bb51d73d03d45b3f69b7

Then after spending many hours trying to get my computer communicating with SAMD21 board I discovered that the guilt was the USB/RS485 Dongle I was using.

Replacing it with a common USB/Serial (CP2102) and with a MAX485 module, like this: https://www.aliexpress.com/item/2PCS-MAX485-module-RS-485-module-TTL-to-RS-485-module/2055143247.html , solved the issue.

I connected this way:

+-----------------------------------------+
| USB/Serial (CP2102)   |   MAX485 Module |
+-----------------------+-----------------+
|          RXD          |       RO        |
|          TXD          |       RI        |
|          RTS          |     DE + RE     |
|          GND          |       GND       |
|          +5V          |       VCC       |
+-----------------------+-----------------+

This is the listing of features enabled on NuttX:

Serial SERCOM5 Config:

CONFIG_SAMD2L2_SERCOM5_ISUSART=y
CONFIG_USART5_RS485MODE=y
CONFIG_USART5_RS485_DIR_POLARITY=1

We need support to POLL (so don’t select the disable POLL)

# CONFIG_DISABLE_POLL is not set

FreeModbus config:

CONFIG_MODBUS=y
CONFIG_MB_FUNC_HANDLERS_MAX=16
CONFIG_MODBUS_SLAVE=y
CONFIG_MB_ASCII_ENABLED=y
CONFIG_MB_RTU_ENABLED=y
# CONFIG_MB_TCP_ENABLED is not set
CONFIG_MB_ASCII_TIMEOUT_SEC=1
CONFIG_MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0
CONFIG_MB_FUNC_OTHER_REP_SLAVEID_BUF=32
CONFIG_MB_FUNC_OTHER_REP_SLAVEID_ENABLED=y
CONFIG_MB_FUNC_READ_INPUT_ENABLED=y
CONFIG_MB_FUNC_READ_HOLDING_ENABLED=y
CONFIG_MB_FUNC_WRITE_HOLDING_ENABLED=y
CONFIG_MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED=y
CONFIG_MB_FUNC_READ_COILS_ENABLED=y
CONFIG_MB_FUNC_WRITE_COIL_ENABLED=y
CONFIG_MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED=y
CONFIG_MB_FUNC_READ_DISCRETE_INPUTS_ENABLED=y
CONFIG_MB_FUNC_READWRITE_HOLDING_ENABLED=y
# CONFIG_MODBUS_MASTER is not set

Modbus example:

CONFIG_EXAMPLES_MODBUS=y
CONFIG_EXAMPLES_MODBUS_PORT=0
CONFIG_EXAMPLES_MODBUS_BAUD=38400
CONFIG_EXAMPLES_MODBUS_PARITY=2
CONFIG_EXAMPLES_MODBUS_REG_INPUT_START=1000
CONFIG_EXAMPLES_MODBUS_REG_INPUT_NREGS=4
CONFIG_EXAMPLES_MODBUS_REG_HOLDING_START=2000
CONFIG_EXAMPLES_MODBUS_REG_HOLDING_NREGS=130

After compiling and flashing the nuttx.bin, run the modbus:

NuttShell (NSH)
nsh> modbus -e

From computer side I used the “mbpoll” program:

$ mbpoll -a 10 -b 38400 -t 3 -r 1000 -c 4 /dev/ttyUSB0 -R
mbpoll 1.4-11 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU
Slave configuration...: address = [10]
start reference = 1000, count = 4
Communication.........: /dev/ttyUSB0,      38400-8E1
t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, input register table

-- Polling slave 10... Ctrl-C to stop)
[1000]: 	40122 (-25414)
[1001]: 	0
[1002]: 	0
[1003]: 	0
-- Polling slave 10... Ctrl-C to stop)
[1000]: 	832
[1001]: 	0
[1002]: 	0
[1003]: 	0
-- Polling slave 10... Ctrl-C to stop)
[1000]: 	24996
[1001]: 	0
[1002]: 	0
[1003]: 	0
-- Polling slave 10... Ctrl-C to stop)
[1000]: 	3747
[1001]: 	0
[1002]: 	0
[1003]: 	0

This is an image of pulseview sniffing the bus:

That is it, very easy after you get it working.

Thanks Daniel Carvalho for the help and suggestions to fix the issue.

I used this online tool to check my modbus packets:

http://modbus.rapidscada.net

2 thoughts on “Using MODBUS RTU with NuttX

  1. I just inspected the NuttX Modbus/RTU implementantion because I wanted to put my own Modbus code over the existing serial API.
    Short version: It is unuseable as it is.
    RTU defines t1.5 as packet end and t3.5 as minimum packet gap.
    That means an end has to be detected somewhere between t1.5 and t3.5
    t3.5 for 19200 is around 2ms.
    NuttX waits 50ms in the slave code and 5ms in the master plus possible layer overhead.
    There is code to calculate timer ticks, but the results isn’t used anywhere.
    It is time wasting on the master to wait longer and too small for speeds below 9600, but you can get away with this as the bus stays idle during this time.
    On a slave however this gets you into trouble because communication to other slaves can result in failing to detect the begin of a packet addressed to you.
    That said, for an RTU slave the existing serial API wouldn’t work.
    I usually put a hardware timer in addition to the uart code.
    On the Atmel SAM7 and similar series they have specific idle support in the USART itself, which makes it super easy, unfortunately this is not on the Sercom in the SAMD21 and many other controllers.
    I usually set packet end to t2.5 and gap to t4.5 to allow some margins.

    1. Hi Bernd,

      Thank you very much for this update.

      I tested both master and slave, it worked for basic tests, maybe it could fail for some devices.

      So, yes! It needs to be fixed!

      I will try to fix it.

      I really appreciated your comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s