ArduinoCMRI and RS485
A fun little side project of mine is Arduino C/MRI, a library that lets you easily connect your Arduino projects up to the JMRI layout control software, by pretending to be a piece of C/MRI hardware. Hence the name.
In previous episodes, we've looked at various methods of expanding the capabilities of Arduino C/MRI; be it by using shift registers, or emulating larger boards. But at some point you're going to need more than just one board, and that's where things get confusing.
The logical answer, you would think, is to connect multiple Arduino's to your computer. Simple. One USB cable for each one. The problem is, JMRI is not designed to address more than one C/MRI system at once. Trust me, I've done everything including hacking the XML config files to make it work! It doesn't.
So how then does one do it? Well, the original C/MRI system used a shared serial bus, and that is what we'll do to.
RS485
RS485 is a electrical standard designed for connecting multiple masters together on a single bus. We've all heard of RS232 which is ye olde tried-and-tested two device connection. RS485 bumps things up a notch by allowing more than just two devices on a network, and allowing any of them to be the boss; there are no fixed master and slave roles. It also uses differential signals, excellent for long distances or split grounds.
Each node connects to the bus using a small bus transceiver IC. These little 8 pin chips (such as the MAX485 and its many clones) have a pair of pins to control the direction, or mode of communication. That is, at any point in time you can be either talking or listening on the bus.
This is a half-duplex bus, which means you can't both talk and listen at the same time, but that's just fine for us. It also means that only one device may be transmitting at any point in time, and that's where things get a little complicated. We basically run the bus like we wish children would run: don't talk unless you're asked to.
RS485 is pretty common and you may well have used it already. Both NCE and Digitrax use RS485 for their cab busses, though in vastly different ways. DMX lighting control networks use it. Miniatur Wunderland use it for their lighting control networks. It's used in aircraft cabins, and was used on old Macs to connect printers. Even TV studios use it. It's well proven.
And now ArduinoC/MRI uses it too.
How to set up RS485 with Arduino C/MRI
Ingredients:
- 1 or more Arduinos
- 1x MAX485
- 1x USB to RS485 adapter, easily found on ebay
- Latest release of Arduino C/MRI, installed in Arduino/Libraries/ArduinoCMRI
- Latest release of Auto485, installed in Arduino/Libraries/Auto485
Instructions:
Connect the USB/485 adapter to your computer. Wire up the MAX485 as follows:
- VCC and GND to +5V and GND
- A and B to the respective terminals on the USB/RS485 adapter
- RO (read out) to RX on the Arduino
- DI (data in) to TX on the Arduino
- /RE and DE are both connected to pin 2 on the Arduino.
That's it.
Now we need to write some code for it:
#include <Auto485.h> #include <CMRI.h> #define CMRI_ADDR 0 #define DE_PIN 2 #define LED_PIN 13 Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins CMRI cmri(CMRI_ADDR, 24, 48, bus); // defaults to a SMINI with address 0. SMINI = 24 inputs, 48 outputs void setup() { pinMode(LED_PIN, OUTPUT); bus.begin(9600); } void loop() { // 1: main processing node of cmri library cmri.process(); // 2: update output. Reads bit 0 of T packet and sets the LED to this digitalWrite(LED_PIN, cmri.get_bit(0)); // 3: update input. Flips a bit back and forth every second cmri.set_bit(0, (millis() / 1000) % 2 == 0); }
So what does all this do? Well the magic starts on the very first line when we #include <auto485.h>
— Auto485 is a cute wee library I wrote that lets you transparently talk over an RS485 bus using just the standard Arduino Stream functions like .printl()
, .write()
, .available()
, .read()
, .peek()
, .poke()
, etc. Auto485 will sit in the background and handle toggling the /RE and DE pins to ensure the MAX485 is correctly in transmitting or receiving mode, all without us having to write any tedious state logic ourselves.
The next few lines are normal Arduino C/MRI stuff.
Auto485 bus(DE_PIN);
is where we actually set up the Auto485 bus. Into its constructor we pass the Arduino pin number that is connected to the /RE and DE pins on the MAX485. This is how it knows which pin to toggle. The resulting bus object implements Stream, so is just the same as creating additional HardwareSerial or SoftwareSerial objects.
When we set up the CMRI object, we pass in the Stream object created above, and so now the code knows to use this for all serial communications, instead of the default Arduino serial port.
The only other important line is bus.begin(9600);
this is exactly the same as how you would normally call Serial.begin(9600)
in a normal Arduino sketch. Without this line you will spend hours wondering why nothing is being read to or from the bus. Trust me, guess what I spent most of today trying to solve :(
The rest of the sketch is bog standard Arduino C/MRI stuff; nothing has changed here. The best part about this is that you can add RS485 support to your projects with only two new lines! That's pretty cool.
Practical Example
To prove this all works I wired up 3 Arduinos with the above sketch, changing the CMRI_ADDR for each one so that I now had 3 distinct nodes, each with their own address. Notice how each board only has the blue and white A/B wires of the RS485 bus going between them. The USB cables are just for power. At far right we have the USB to RS485 adapter board.
Then in JMRI I set up 3 inputs, and 3 outputs.
And that was it. Toggling the state buttons on the Lights panel toggles the LEDs on each board, and meanwhile the input states under the Sensors Panel toggle off and on each second, completely automatically.
I hope that explains roughly how to use Arduino C/MRI and Auto485 in order to connect up multiple Arduinos to JMRI. From here you have all the foundations to create a truly expansive control system for your railroad. Just imagine, each Arduino could control servos to toggle points, monitor RFID readers to track trains, read push buttons on the edge of the layout, drive relays or play sound files. Watch this space as I have some exciting developments in the works.
Comments