Saturday, 15 August 2009

An Arduino and RS485

I love being a student! It gives you tonnes of perks, such as free industrial samples from semiconductor companies. I just got my order of MAX485 ICs through the post this morning, so I've spent the day playing around with Arduino and RS485 comms. I'm now going to share my experiences with you


The aim of this demo is to simulate some serial data on an Arduino and send it via RS485 to a computer.

RS485? Say what?
485 is a standard very similar to RS232 (the serial port used on an Arduino and your PC). RS485 improves upon 232 by removing the limitation of distance (up to 4000ft) and allows more than two devices to communicate using a single line, though it does this at the cost of using more wires between the devices

What you'll need for this demo:
  • 2x MAX485 ICs (or equivalent)
  • 1x Arduino
  • 1x breadboard (2 preferably)
  • Jumper wires
  • 2x 150R resistors
  • FTDI cable or breakout board

Ok, so how's it done?
For this you're going to need a Maxim MAX485 (or equivalent) IC. This IC will take RS232 signals and perform the voltage level conversions required to turn them into 485 signals.

The MAX485 has 8 pins:
  1. RO - Receiver out
  2. RE - Receiver enable (enabled when this pin is LOW)
  3. DE - Driver enable (enabled when this pin is HIGH)
  4. DI - Driver in (the transmitter pin)
  5. GND - Ground (0V)
  6. A - Connect to pin A of the other 485 IC
  7. B - Connect to pin B of the other 485 IC
  8. Vcc - Power, in my case +5V
I'm not going to go into the basics on breadboarding, but you should place one MAX485 on each breadboard (or on two halves of one breadboard).
  • Wire up the power and ground. Try to use a battery for the transmitter. You can take power from the FTDI board for the receiver. This proves that there's only two wires (the comm) wires going between the devices.
  • Wire pin A to the other pin A
  • Wire pin B to the other pin B
  • On each IC, a 150R resistor goes between pin A and B
  • On the receiver side, wire pin RE to 0V
  • On the transmitter side, wire pin DE to 5V
  • On the transmitter, wire digital pin 0 (TX) from the Arduino to DI
  • On the receiver, wire pin RO on the 485 to the RX pin of the FTDI board
Ok.. that's about it as far as wiring goes. Now for some code.

Code:
The code for this project is reallllly simple. We're just wanting to simulate some data coming from the Arduino, and pick it up using the FTDI. I uploaded this simple sketch to my Arduino:

#define LED_PIN 13;
int i;
boolean b;

void setup() {
Serial.begin(57600);

i = 0;
b = false;
pinMode(LED_PIN, OUTPUT);

Serial.println("Init() complete");
}

void loop() {
i ++;

Serial.println(i);

b = !b;
digitalWrite(LED_PIN, b);

delay(1000);
}



Upload that to your Arduino and you should see the LED flashing roughly every 1 second. This means that it's sending serial data every 1 second. Now plug in your FTDI board to the PC and open up serial monitor in the Arduino IDE. If you have everything wired correctly, you'll see a number appearing in the window each time the Arduino sends something. Cool eh?


[UPDATE]
Handling two way communications:
The MAX485 and equivalents are only half duplex, meaning that they only support sending data in one direction at a time. This is fine, but it means you're going to need to account for that in your code.

It is possible to get full duplex versions of the MAX485 (I can't remember the part number right now). These bypass the need for any of the stuff below, but you will need four wires running between each device. Not a problem if you're running short distances, but it gets expensive if you have ~4000ft of cable. Anyway, it's pretty easy to get past the limitations of half duplex...

Remember the RE and DE pins? They specify whether the chip is in receive (RX) or transmit (TX) mode. If we control these from a pin on the Arduino we can leave the chip in RX mode, wait till something is received, and then change to TX mode to reply. Luckily the DE pin has a NOT gate on it, so you can drive both pins from one pin on your Arduino.

Note: From this point on you will need two Arduinos, as the FTDI board can't control the MAX485 in the way we want.

If you take a wire from your Arduino's digital pin 2, and wire it to DE, and then take another jumper wire and wire DE to RE, that will handle the TX/RX mode of the chip. Do this for both chips.

One of your Arduinos will need to be the master of the "network". This is typical in 485 applications, where there are several slave devices taking cues from a master. The master will query a slave, then change to RX mode. The slave will receive the command, and change to TX mode to send its response. After the response both devices change back to their normal mode.

At this point in your project you should probably specify exactly what a "command" is. They usually go along the lines of:
[START BYTE][COMMAND][PARAMS][END BYTE]
or even:
[START BYTE][DEVICE NUMBER][COMMAND][PARAMS][END BYTE]

It's up to you to specify what the start byte and end byte are. You also have to specify how long and what the commands are (1 byte will provide 255 commands), and how long the parameters are (whether they're a fixed length or not).

Below, I'll use 0xFF (255) as the start and end bytes, and 0x01 as the command (send status). The slave device will be number 0x05. Params are optional and may be any length (zero or more whole bytes).

The resulting communications between the two devices would look something like this:
StepMasterSlave
1Prepare command
0xFF 0x05 0x01 0xFF
Waiting
2Send commandWaiting
3Change to RX mode
Set pin 2 to LOW
Receive serial data
0xFF 0x05 0x01 0xFF
4WaitingParse serial data
Start byte ok, ID byte OK, command = send status, end byte ok
5WaitingChange to TX mode
Set pin 2 to HIGH
6WaitingSend response (0x05 = ID, 0x01 = status ok)
0xFF 0x05 0x01 0xFF
7Receive serial dataChange back to RX mode
Set pin 2 to LOW
9Change back to TX mode
Set pin 2 to HIGH
Waiting
10Parse data....Waiting


This is just an example of very basic comms between two device, but you can do a lot with it. I'm using something similar for my Arduino Backplane Experiment, only with I2C instead of serial.

I haven't gotten around to writing the code for two-way communications yet. Hopefully I've explained it well enough so you can have a stab at it. If anyone's feeling really stuck, give me a shout in the comments or on Twitter (@paddypluggedin) and I'll try and throw some code together

Saturday, 8 August 2009

Arduino Backplane Experiment

Just as an experiment I thought I'd try this...


My motivation:

Pictured above is an Allen Bradley SLC-500 PLC. We use these a lot in work to automate industrial processes. They're very expensive, and all they are is a simple motherboard connecting daughter boards. The first slot is the CPU and does all the processing, and the rest of the cards are "dumb" cards which add different kinds of inputs or outputs, counters, comms, etc. Have I mentioned that they're very expensive?

My hope is to copy this modular, "slot in" design, so someone could just slide in a new pre-programmed module in their rack. This concept could be used to add:
  • Additional digital or analog IO
  • A real time clock
  • EEPROM card
  • Pre-built sensor cards
  • Dedicated counter modules
  • GPS modules
  • Ethernet, or XBee or other RF comms
  • Flash storage cards
  • Motor, servo and stepper drivers
  • RS485 comms card
Each of these "cards" would be a board with the appropriate pins brought out and connectors to suit its configured purpose. They would come pre-programmed, so you just slot them in and they work.

The whole idea is not dissimilar to the Arduino "shield" idea, but I'm using I2C to connect them instead of all the pins. The trouble with the stackable shield idea is that pretty soon you'll get two cards that want to use the same pin, and then they can't be used together. Using I2C you have a theoretical limit of 255 devices


Above you can see my half-finished product. I've used a bunch of Arduino Pros, which have a 6 pin header on one side supplying power and serial comms. This plugs into an 8-pin female header on the motherboard. The remaining two pins are the I2C pins (analog 4 and 5), which I had to bring down using some wire and a couple of pins.

Each of these cards will eventually be configured to perform a particular task

Motherboard
The motherboard itself is a bit of strip board, connecting each of the pins to the corresponding pin of every daughter board (with the exception of the serial pins, 'cos serial can't work that way).

There's a set of male headers beside every card, this basically brings out the serial pins so I can connect my FTDI board to each card for debugging (not all are wired yet).

There should also be a 3-pin header beside each board for addressing (see the next section)

Oh I almost forgot, hiding away between the two leftmost boards is an I2C RTC module. I do plan to use this later...

Addressing
In order for I2C to work, each slave device needs a unique address. I don't want to have to reprogram the individual daughter cards after they've been configured, so I've brought down digital pins 11-13 to the motherboard. These pins will be either wired to +5v or 0v, depending on which slot they're in. These 3 inputs are then used to calculate the I2C address, giving 8 possible addresses.

Demo
In the picture you can see three of the Pros connected to a Duemilanove as the main CPU. As a very simple test program I have the CPU turning on the LED on each of the expansion boards in turn, then waiting, and turning them off again, to create a kind of chaser effect.

Eventually there'll be 5 Pros connected to the backplane (I don't have enough female header strips to complete the board today). I honestly don't know what I'm going to do with it afterwards, but I think this demonstrates a good proof of concept and I hope someone can think of a good use for this arrangement :).

[UPDATED] Source code:
The code's still not quite finished yet, but it now supports all the basic pin commands, except with an extra parameter to specify the card you want to address:
  • digitalRead()
  • digitalWrite()
  • analogRead()
  • pinMode()
I've left in the "turn on LED" and "turn off LED" commands for good measure. The hardware addressing still isn't implemented yet either because I've run out of PCB headers.

CPU code - here
Expansion board code - here

More photos


As always, comments, suggestions and fan mail are greatly appreciated

Saturday, 1 August 2009

iPhones are great things

Just spent £1.79 on an app to blog from my phone. Lets see how it goes...



-- Post From My iPhone