Lipstick - Modbus on an LPC2119 microcontroller board

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.

RS-485 and Modbus Devices

I've looked at a handful of cheapo RS-485 and Modbus devices and most of them suffer from problems, including the following:

Resistors

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.

Bodged Driver Enables

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.

Signal naming not to spec

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.)

Default serial settings not to spec

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.)

Inadequate turnaround time

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.

Other differences

Below are the cheapo RS-485 / Modbus devices that I've bought from eBay etc.

"ZK-U485 Converter USB <-> RS-485"

Type
USB adaptor.
Chip
CH341. There is a Linux driver for this chip which is perhaps less widely available than some of the other USB-serial drivers but does seem to function. Either this chip doesn't have a transmitter enable output, or it's not used in this adaptor.
Connections
Screw terminals for A, B, ground and 5 V out.
A/B Polarity
B is pulled down, so they are the "wrong" way around.
Resistors
Has a fixed 120 Ω termination resistor.
Protection
Has anti-surge components.
Driver Enable
Seems to enable the driver from the data signal, so probably has edge time issues, but I've not measured this.

"FTDI USB to RS485"

Type
USB adaptor.
Chip
FTDI FT232 and SP485.
Connections
Small screw terminals for D+, D-, ground and 5 V out.
Resistors
None.
Protection
Nothing apparent.
Driver Enable
The FTDI chip can supply a proper driver enable signal, which this device seems to use.
Note
This is the device that I'm actually using to connect my Linux board to the Modbus.

"RS485 to TTL"

Type
TTL to RS-485 converter.
Chip
MAX485.
Supply
MAX485 specifies 5 V, but it seems to work OK with 3.3 V.
Connections
Has holes for screw terminals for A, B and ground, but is supplied without.
A/B Polarity
There are no pull-ups/downs, but A/B are connected to the driver's A/B pins so these are the "wrong" way around.
Resistors
It seems that a 120 Ω termination resistor can be enabled by soldering.
Protection
Has anti-surge components.
Driver Enable
Enables the transmit driver when the transmit data input is low; the scope screenshot above is from this device.

"TTL to RS485 module"

Type
TTL to RS-485 converter.
Chip
MAX485.
Supply
MAX485 specifies 5 V, but it seems to work OK with 3.3 V.
Connections
Screw terminals for A and B.
A/B Polarity
A is pulled up, so these are the "wrong" way around.
Resistors
Has a fixed 120 Ω termination resistor.
Protection
None.
Driver Enable
Has transmit and receive driver enable inputs, so software or UART hardware can enable at the right times.
Note
This is the device that I'm actually using to connect my LPC microcontroller board to the Modbus.

"WTR10-E"

Type
Modbus Temperature and Humidity Sensor.
Chip
SP3485, STM8S003F3P6 microcontroller, SHT21 sensor.
Supply
6-48 V; doesn't work at 5 V.
Connections
Screw terminals for A, B, ground and power in.
A/B Polarity
A is pulled up, so these are the "wrong" names (despite actually labelling them "485A" and "485B").
Resistors
No termination resistor.
Protection
None.
Driver Enable
Driver enable comes from microcontroller.
Address Setting
DIP switches.
Default Serial Settings
9600 8N1, I believe.
Commands
It's possible to read temperature with a one-register read command or both temperature and humidity with a single two-register read command. No other commands work.
Documentation
None supplied.
Hackability
Appears to have a programming header; microcontroller is a mainstream 8-bit device with 8 kB flash and 1 kB RAM.

"R46CA01"

Type
Modbus Temperature Sensor (external sensor) (photo shows panel of three modules).
Chip
SP3485, N76E003AT20 microcontroller, connects to DS18B20 one-wire sensor.
Supply
5 V; other versions have a regulator.
Connections
A, B, ground and power in.
A/B Polarity
A is pulled up, so these are the "wrong" way around.
Resistors
No termination resistors.
Protection
None.
Driver Enable
Controlled by microcontroller.
Address Setting
Software.
Default Serial Settings
9600 8N1, I believe.
Commands
TODO
Documentation
Available on request.
Hackability
Some pads on the bottom might be a programming header; microcontroller is a chinese 8051 clone with 18 kB flash and 1 kB RAM.
Note
At the time of writing I've not actually tried to use these boards.

"2 Channel RS485 Relay"

Type
Modbus Relay Board.
Chips
SP485, STC15W204S microcontroller.
Supply
5 V (other versions of the board have regulators).
Connections
Screw terminals for A+, B-, ground and power in.
A/B Polarity
A+ is pulled up, so these are the "wrong" names.
Resistors
No termination resistor.
Protection
None.
Driver Enable
Microcontroller enabled. Insufficient turn-around time.
Address Setting
DIP switches.
Default Serial Settings
9600 8N1, I believe.
Commands
Commands to turn relays on and off, and various delay features.
Documentation
Available on request.
Hackability
Appears to have a programming header; microcontroller is a chinese 8051 clone with 4 kB flash and 256 B RAM.

LPC2119 "ARM STAMP" Board

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.