This page documents my attempts to use Modbus over RS-485 for a "sensor-net" type application, with both off-the-shelf and self-built devices. "Lipstick" is the name I've given to my home-made Modbus board that uses an LPC2119 microcontroller.
Modbus is a long-standing protocol for industrial automation and similar tasks, which can run over RS-485. RS-485 is a serial standard that is half-duplex, uses differential signalling, and supports multiple devices with a multi-drop bus topology. Compared to Ethernet, Modbus over RS-485 is slower, can support longer cables, and can be implemented with simpler hardware.
For my application I have an embedded Linux controller, which needs to connect to the Modbus using a USB adaptor, and need a small number of devices for sensors and relays.
I've looked at a handful of cheapo RS-485 and Modbus devices and most of them suffer from problems, including the following:
RS-485 needs termination resistors at the extremities of the bus, but not at intermediate devices. Ideally each device would provide a link to enable an optional resistor, but some of the devices have a fixed termination resistor.
The bus also needs pull-up and pull-down resistors somewhere so that it idles at defined levels when no device is driving it. Some of the devices seem to have weak pull-up and pull-downs so that they don't see noise when not connected, or when connected to an unterminated bus, but these aren't sufficient to make the bus work when it has termination resistors. No device as a link to enable stronger pull-up/down resistors.
Some devices don't have a proper enable signal for their driver, and instead use a hack that turns the driver on only when the transmit data is low; this relies on the UART output being high between bytes, and the bus being biased to idle in that state. Here's a scope screenshot of the result:
The upper half of the screen shows a "good" driver on the left and a "bad" driver on the right; yellow and blue are the two bus signals. The "good" signals are nice and symetric and have a voltage difference of about 3 V; the "bad" signals are asymetric and have a much smaller voltage difference. (To be fair, the "good" driver has a 5 V supply while the "bad" driver has a 3 V supply.)
The lower half of the screenshot is zoomed in on a transition from the "bad" driver. To the left of the screen, a 0 is being sent and there is an acceptable voltage difference of about 1.25 V between the two signals. To the right of the screen a 1 is being sent, but the driver has been turned off and the bus is idling with a voltage difference of about 0.5 V. While this is clearly wrong, note that this does work in my setup - the receiving device correctly recovers the bits; I believe RS-485 requires a voltage difference of at least 0.2 V, so this allows a noise margin of 0.3 V. Noise of more than this magnitude is visible in the left third of the top zoomed-out trace, and noise transients are also visible in the right half of the bottom zoomed-in trace, but this noise seem to be common-mode and so does not corrupt the signal.
A TTL or RS-232 to RS-485 converter doesn't have a good way to determine when to enable its driver. It could use a timer, but that requires a fixed baud rate. It could require an input that controls the enable (RTS is sometimes used for this) but that requires software support. A USB device should know when it is transmitting, but if the USB-to-serial chip doesn't have the required output signal then it is in the same situation as a TTL converter.
The Modbus serial spec names the differential signal lines D0 and D1, but none of the devices that I've looked at use those names. RS-485 instead calls them A and B, and the Modbus spec says A is D0 and B is D1.
Datasheets for driver chips like the MAX485 also call the differential signals A and B, with A being the non-inverting and B the inverting output - but this is confusing because it's making an assumption about the polarity of its connections to the UART. In practice, UART signals can be connected to non-differential devices like the MAX232 which are inverting, so if the same signals are connected to a MAX485 that needs to invert too, which means reversing the A and B signals. But generally, devices simply name their terminals after the MAX485 pins.
In the idle state, D1 is supposed to be pulled up to the supply and D0 pulled down to ground; as noted above devices sometimes have high-value resistors to do this, and checking how they are connected to the A and B pins can help identify which is which. If the A pin is pulled down, the device is using the RS-485 A/B naming (I've never seen this); if the A pin is pulled up, the device is using the MAX485 naming.
Of course if all the devices on a bus get the polarity wrong, it will work!
Wikipedia has a section about this mis-naming issue. It seems to be so common that I wonder if it is an error in the modbus spec.
I've also seen A+ and B-, and D+ and D-. Here is how I believe those match up, plus ground and optional power wires, along with how they should be connected to an RJ45 connector and CAT5 cable:
Modbus name | D1 | D0 | Common | Power |
---|---|---|---|---|
RS-485 name | B | A | C | |
MAX485 name | A | B | ||
or | D+ | D- | ||
or | A+ | B- | ||
Pull-up/down | Up | Down | ||
RJ45 pin | 5 | 4 | 8 | 7 |
CAT5 colour | Blue/white | Blue | Brown | Brown/white |
(Note that the CAT5 colours are the ones that do not change for EIA-568A/B, so that's one fewer things to worry about.)
The modbus spec requires devices to support 9600 and 19200 baud, and that 19200 must be the default. It also requires a default of 8 data bits, even parity and 1 stop bit, and says that no parity requires 2 stop bits.
None of the devices I've tried seems to implement these defaults: they all seem to default to 9600 baud, 8 data bits, no parity and 1 stop bit. (To be fair, it's possible that some may auto-detect the settings.)
The modbus spec requires that devices wait for at least 3.5 character times between receiving a command and sending a response; this is to give the sender time to disable its driver. In practice, some devices seem to send their reply immediately. This could cause problems particularly when the driver enable is software-controlled.
Below are the cheapo RS-485 / Modbus devices that I've bought from eBay etc.
This is now quite an old ARM microcontroller chip (from NXP) and board (from Futurlec in Thailand) and it may not be ideal for new designs, but the boards are still sold. The core is an ARM7TDMI; it has 128 kB of flash and 16 kB of RAM, and peripherals include two UARTs, I2C, SPI, timers, 4 analogue inputs, GPIO etc. Both the core and the microcontroller are well documented. Tools are available for compiling and programming via the board's serial interface, e.g. Debian has packages for a suitable compiler, libc and programming.
If I wanted something more modern, I'd consider the Sammy-D21 Plus from Chip45 because it has a built-in RS485 driver, or the Teensy LC from Sparkfun because it's small and cheap. Or for something more exotic, a RISC-V board would be interesting (note that one has no analogue inputs).
Some points to note:
I have made the software that I am using on my board available here. You can view it online, or check it out with svn co https://svn.chezphil.org/lipstick/trunk lipstick
See the README file for more information.