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.
See also my Mastodon page.
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
(...), Arduino, AVR, BaRef, Blosxom, Book, Busy, C++, Charity, Debian, Electronics, Examination, Firefox, Flash, Framework, FreeBSD, Gnome, Hardware, Inter-Actief, IRC, JTAG, LARP, Layout, Linux, Madness, Mail, Math, MS-1013, Mutt, Nerd, Notebook, Optimization, Personal, Plugins, Protocol, QEMU, Random, Rant, Repair, S270, Sailing, Samba, Sanquin, Script, Sleep, Software, SSH, Study, Supermicro, Symbols, Tika, Travel, Trivia, USB, Windows, Work, X201, Xanthe, XBee
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.
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:
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).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).
So, here's the list of libraries I found.
HardwareSerial
objects.boards.txt
.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
Hi, did you see this one? github.com/smarmengol/Modbus-Master-Slave-for-Arduino
Took me a while to recognize it, but I had indeed seen it before: the MCCI library is a fork of that Smarmengol library.
However, it seems that since I wrote this post, a little new development happened on the Smarmengol version, which now allows using arbitrary Streams to be used, which is useful. It still suffers from some of the other problems, but I have now included it separately in the list.
Thanks for pointing it out!
Check out the omzlo NoCAN plattform. It uses CAN and has already implemented a pub/sub functionality that is great for simple communication between nodes. It requires the NoCAN boards and a Raspberry Pi as a master.
Check out the omzlo NoCAN plattform. It uses CAN and has already implemented a pub/sub functionality that is great for simple communication between nodes. It requires the NoCAN boards and a Raspberry Pi as a master.
Written for ESP, asynchronous that can handle multiple sockets as a modbus TCP server. github.com/eModbus/eModbus I use this together with your library for reading a dsmr, and serving the data as a modbus server.
Hi, Good overview!
Hereby some more suggestions:
Slave library:
github.com/epsilonrt/modbus-arduino -- This library is a further development of the andresarmento slave library.
Master libraries:
github.com/Hantuch/ModbusMaster -- fork of the master library of 4-20ma, adding the possibility to address multiple slaves
github.com/EnviroDIY/SensorModbusMaster -- this library has very convenient functions, but is limited to 1 slave; works with softwareserial
github.com/CMB27/ModbusRTUMaster -- a very new library, has easy to use functions; supports multiple slaves and works with softwareserial
(I've only tested the slave library so far)