"In het verleden behaalde resultaten bieden geen garanties voor de toekomst"
About this blog

These are the ramblings of Matthijs Kooijman, concerning the software he hacks on, hobbies he has and occasionally his personal life.

Most content on this site is licensed under the WTFPL, version 2 (details).

Questions? Praise? Blame? Feel free to contact me.

My old blog (pre-2006) is also still available.

Sun Mon Tue Wed Thu Fri Sat
Powered by Blosxom &Perl onion
(With plugins: config, extensionless, hide, tagging, Markdown, macros, breadcrumbs, calendar, directorybrowse, entries_index, feedback, flavourdir, include, interpolate_fancy, listplugins, menu, pagetype, preview, seemore, storynum, storytitle, writeback_recent, moreentries)
Valid XHTML 1.0 Strict & CSS
Reliable long-distance Arduino communication: RS485 & MODBUS?

Arduino connected to other things

For a customer, I've been looking at RS-485 and MODBUS, two related protocols for transmitting data over longer distances, and the various Arduino libraries that exist to work with them.

They have been working on a project consisting of multiple Arduino boards that have to talk to each other to synchronize their state. Until now, they have been using I²C, but found that this protocol is quite susceptible to noise when used over longer distances (1-2m here). Combined with some limitations in the AVR hardware and a lack of error handling in the Arduino library that can cause the software to lock up in the face of noise (see also this issue report), makes I²C a bad choice in such environments.

So, I needed something more reliable. This should be a solved problem, right?


A commonly used alternative, also in many industrial settings, are RS-485 connections. This is essentially an asynchronous serial connection (e.g. like an UART or RS-232 serial port), except that it uses differential signalling and is a multipoint bus. Differential signalling means two inverted copies of the same signal are sent over two impedance-balanced wires, which allows the receiver to cleverly subtract both signals to cancel out noise (this is also what ethernet and professional audio signal does). Multipoint means that there can be more than two devices on the same pair of wires, provided that they do not transmit at the same time. When combined with shielded and twisted wire, this should produce a very reliable connection over long lengths (up to 1000m should be possible).

However, RS-485 by itself is not everything: It just specifies the physical layer (the electrical connections, or how to send data), but does not specify any format for the data, nor any way to prevent multiple devices from talking at the same time. For this, you need a data link or arbitration protocol running on top of RS-485.


A quick look around shows that MODBUS is very commonly used protocol on top of RS-485 (but also TCP/IP or other links) that handles the data link layer (how to send data and when to send). This part is simple: There is a single master that initiates all communication, and multiple slaves that only reply when asked something. Each slave has an address (that must be configured manually beforehand), the master needs no address.

MODBUS also specifies a simple protocol that can be used to read and write addressed bits ("Coils" and "Inputs") and addressed registers, which would be pretty perfect for the usecase I'm looking at now.

Finding an Arduino library

RS-485 Adapter

So, I have some RS-485 transceivers (which translate regular UART to RS-485) and just need some Arduino library to handle the MODBUS protocol for me. A quick Google search shows there are quite a few of them (never a good sign). A closer look shows that none of them are really good...

There are some more detailed notes per library below, but overall I see the following problems:

  • Most libraries are very limited in what serial ports they can use. Some are hardcoded to a single serial port, some support running on arbitrary HardwareSerial instances (and sometimes also SoftwareSerial instances, but only two librares actually supports running on arbitrary Stream instances (while this is pretty much the usecase that Stream was introduced for).
  • All libraries handle writes and reads to coils and registers automatically by updating the relevant memory locations, which is nice. However, none of them actually support notifying the sketch of such reads and writes (one has a return value that indicates that something was read or written, but no details), which means that the sketch should continuously check all register values and update them library. It also means that the number of registers/coils is limited by the available RAM, you cannot have virtual registers (e.g. writes and reads that are handled by a function rather than a bit of RAM).
  • A lot of them are either always blocking in the master, or require manually parsing replies (or both).

Writing an Arduino library?

Ideally, I would like to see a library: - That can be configured using a Stream instance and an (optional) tx enable pin. - Has a separation between the MODBUS application protocol and the RS-485-specific datalink protocol, so it can be extended to other transports (e.g. TCP/IP) as well. - Where the master has both synchronous (blocking) and asynchronous request methods.

The xbee-arduino library, which also does serial request-response handling would probably serve as a good example of how to combine these in a powerful API. - Where the slave can have multiple areas defined (e.g. a block of 16 registers starting at address 0x10). Each area can have some memory allocated that will be read or written directly, or a callback function to do the reading or writing. In both cases, a callback that can be called after something was read or writen (passing the area pointer and address or something) can be configured too. Areas should probably be allowed to overlap, which also allows having a "fallback" (virtual) area that covers all other addresses.

These areas should be modeled as objects that are directly accessible to the sketch, so the sketch can read and write the data without having to do linked-list lookups and without needing to know the area-to-adress mapping. - That supports sending and receiving raw messages as well (to support custom function codes). - That does not do any heap allocation (or at least allows running with static allocations only). This can typically be done using static (global) variables allocated by the sketch that are connected as a linked list in the library.

I suspect that given my requirements, this would mean starting a new library from scratch (using an existing library as a starting point would always mean significant redesigning, which is probably more work than its worth). Maybe some parts (e.g. specific things like packet formatting and parsing) can be reused, though.

Of course, I do not really have time for such an endeavor and the customer for which I started looking at this certainly has no budget in this project for such an investment. This means I will probably end up improvising with the MCCI library, or use some completely different or custom protocol instead of MODBUS (though the Arduino library offerings in this area also seem limited...). Maybe CANBus?

However, if you also find yourself in the same situation, maybe my above suggestions can serve as inspiration (and if you need this library and have some budget to get it written, feel free to contact me).

Existing libraries

So, here's the list of libraries I found.

  • Official library from Arduino.
  • Master and slave.
  • Uses the RS485 library to communicate, but does not offer any way to pass a custom RS485 instance, so it is effectively hardcoded to a specific serial port.
  • Offers only single value reads and writes.
  • Slave stores value internally and reads/writes directly from those, without any callback or way to detect that communication has happened.

  • Master-only library.
  • Latest commit in 2016.
  • Supports any serial port through Stream objects.
  • Supports idle/pre/post-transmission callbacks (no parameters), used to enable/disable the transceiver.
  • Supports single and multiple read/writes.
  • Replies are returned in a (somewhat preprocessed) buffer, to be further processed by the caller.

  • Slave-only library.
  • Last commit in 2015.
  • Supports single and multiple read/writes.
  • Split into generic ModBus library along with extra transport-specific libraries (TCP, serial, etc.).
  • Supports passing HardwareSerial pointers and (with a macro modification to the library) SoftwareSerial pointers (but uses a Stream pointer internally already).
  • Slave stores values in a linked list (heap-allocated), values are written through write methods (linked list elements are not exposed directly, which is a pity).
  • Slave reads/writes directly from internal linked list, without any callback or way to detect that communication has happened.
  • is a fork that has some Due-specific fixes.

  • Fork of (6 additional commits).
  • Slave-only-library.
  • Last commit in 2018.
  • Supports passing HardwareSerial pointers.
  • Slave stores values external to the library in user-allocate arrays. These arrays are passed to the library as "areas" with arbitrary starting addresses, which are kept in the library in a linked list (heap-allocated).
  • Slave reads/writes directly from internal linked list, without any callback or way to detect that communication has happened.

  • Master and slave.
  • Last commit in 2019.
  • Fork of old (2016) version of with significant additional development.
  • Supports passing arbitrary serial (or similar) objects using a templated class.
  • Slave stores values external to the library in a single array (so all requests index the same data, either word or bit-indexed), which is passed to the poll() function.
  • On Master, sketch must create requests somewhat manually (into a struct, which is encoded to a byte buffer automatically), and replies returns raw data buffer on requests. Requests and replies are non-blocking, so polling for replies is somewhat manual.

  • Master and slave.
  • Last commit in 2019.
  • Hardcodes Serial object, supports SoftwareSerial in slave through duplicated library.
  • Supports single and multiple read/writes of holding registers only (no coils or input registers).
  • Slave stores values external to the library in a single array, which is passed to the update function.

  • Master and slave.
  • Last commit in 2020.
  • Supports any serial port through Stream objects.
  • Slave stores values external to the library in a single array (so all requests index the same data, either word or bit-indexed), which is passed to the poll() function.
  • On Master, sketch must create requests somewhat manually (into a struct, which is encoded to a byte buffer automatically), and replies returns raw data buffer on requests. Requests and replies are non-blocking, so polling for replies is somewhat manual.

  • Unlike what the name suggests, this actually implements ModBus
  • Master and slave.
  • Started very recently (October 2020), so by the time you read this, maybe things have already improved.
  • Very simple library, just handles modbus framing, the contents of the modbus packets must be generated and parsed manually.
  • Slave only works if you know the type and length of queries that will be received.
  • Supports working on HardwareSerial objects.

  • This is not a MODBUS library, but a very thin layer on top of RS485 that does collision avoidance and detection that can be used to implement a multi-master system.
  • Last commit in 2016, repository archived.
  • This one is notable because it gets the Stream-based configuration right and seems well-written. It does not implement MODBUS or a similarly high-level protocol, though.

  • Also not MODBUS, but also a collision avoidance/detection scheme on top of RS485 for multi-master bus.
  • Last commit in 2015.
  • Replaces HardwareSerial rather than working on top, requiring a customized boards.txt.

  • This is not a MODBUS library, but a communication library for data communication over radio. It also supports serial connections (and is thus an easy way to get framing, checksumming, retransmissions and routing over serial).
  • Seems to only support point-to-point connections, lacking an internal way to disable the RS485 driver when not transmitting (but maybe it can be hacked internally).

Update 2020-06-26: Added smarmengol/Modbus-Master-Slave-for-Arduino to the list
Update 2020-10-07: Added asukiaaa/arduino-rs485 to the list

2 comments -:- permalink -:- 12:42
Copyright by Matthijs Kooijman - most content WTFPL