Master Slave network of Arduinos for long distances

Hi all,

I'm tasked with the project of building a sensor network containing lots of arduino powered sensors and connecting them to a computer. I've never done anything like this before and am hoping to get some ideas and suggestions for my approach.

The data is read in using analogRead functions to get integers and needs to be passed to a computer running matlab. Matlab then analyzes the data. I've done some searching and came up with this master slave solution. It's not a finished product but I believe it has most of the needed basics covered.

The slave devices obtain data from the environment and wait for the master to call on them. Then they send it to the master. The master sends the information out of the standard serial port to the computer. I'll build a matlab program to catch the data coming from the serial port.

Due to the distances involved in the real project I have to use RS485. I've chosen to use these adapters http://arduino-info.wikispaces.com/RS485-Modules

Please let me know if there is a better way to do this or if I'm missing anything.

I've heard modbus being tossed around and I wonder if it could help me?

here is the master code

#include <SoftwareSerial.h>

//initializing software serial pins
#define RX 10
#define TX 11
#define TXcontrol 3

#define Transmit HIGH
#define Recieve  LOW

SoftwareSerial mySerial(RX,TX);

int dataOne=1;
int dataTwo=1;
int one=501;     // first slave address
int two=502;      //second slave address

void setup() {
  // put your setup code here, to run once:
//(error,OUTPUT);
//digitalWrite(error,LOW);
pinMode(TXcontrol,OUTPUT);

mySerial.begin(4800); //start serial communication between arduinos
Serial.begin(9600);   //start communication with computer
delay(2500);
}

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(TXcontrol,Transmit);   //prepare to transmit to slaves
mySerial.write(one);                //call first slave
digitalWrite(TXcontrol,Recieve);    //prepare for first slave data
dataOne=mySerial.read();            //recieve first slave data
Serial.println(dataOne);            //send data to console

digitalWrite(TXcontrol,Transmit);   //prepare to transmit to slaves
mySerial.write(one);                //call second slave
digitalWrite(TXcontrol,Recieve);    //prepare for second slave data
dataTwo=mySerial.read();            //recieve second slave data
Serial.println(dataOne);            //send data to console
}

here is the slave code

#include <SoftwareSerial.h>

//initializing software serial pins
#define RX 10
#define TX 11
#define TXcontrol 3

#define Transmit HIGH
#define Recieve  LOW

SoftwareSerial mySerial(RX,TX);

int potpin=1;     //analog input pin
int ledpin=12;    //blinky pin
int error=4;      //error pin, turn on if there is an error
int data=1;       //initial data value for potentiometer 
int com=500;      //communications parameter
int address=501;  //slave address for communication purposes

void setup() {
  // put your setup code here, to run once:
//initialize all needed pins to output
pinMode(ledpin,OUTPUT);
digitalWrite(ledpin,LOW); 
pinMode(error,OUTPUT);
digitalWrite(error,LOW);
pinMode(TXcontrol,OUTPUT);

digitalWrite(TXcontrol,Recieve);  //prepare to recieve command from master
mySerial.begin(4800); //start serial communication
delay(2000);
}

void loop() {
  // put your main code here, to run repeatedly:
data=analogRead(potpin);    // read in data
if (data > 400){          // reduce data value if too high
  data=400;
}

digitalWrite(ledpin,HIGH);
delay(data);

digitalWrite(TXcontrol,Recieve);     // prepare to recieve command from master
if (mySerial.available()==0);{  // check to see data coming in
  digitalWrite(error,HIGH);
  delay(500);
}

com=mySerial.read();           // read in value from serial stream
if (com==address){             // check to see if master is calling this slave
  digitalWrite(TXcontrol,Transmit);  // allow transmission
  mySerial.write(data);        // send data to master
}

digitalWrite(TXcontrol,Recieve);    // set serial port to recieve
digitalWrite(error,LOW);       // reset error pin to off
com=500;
digitalWrite(ledpin,LOW);
delay(data);

}

Just found out this network doesn't work, my code seems to be wrong but that's not surprising because I'm still learning.

For RS-485, use twisted pair cable. That can be a pair of a CAT5 cable.

The SoftwareSerial is a risk. Lower baudrates are not adviced, and higher baudrates might not work. Perhaps you can use 9600 baud as well. If you use CAT5 cable, a higher baudrate will work just as well, and 9600 baud will even be low.
Could you use Arduino Leonardo boards (or Arduino Micro) ? Those boards have a spare hardware serial port.

Those RS-485 modules have all the resistors included when just two modules are used. However, the 120 ohm should be only at the begin and at the end of the long cable. All the modules connected half-way to the cable should not have that 120 ohm.

In the master sketch the second slave is called with ID 'one' instead of 'two'.

The Serial library (for hardware serial ports) has a Serial.flush() function that waits for the data to be transmitted.
https://www.arduino.cc/en/serial/flush
I don't know if such a function works the same in the SoftwareSerial library.

You can't just do a mySerial.read(), you have to check if something is received with mySerial.available().

I suggest to make a function that communicates with a slave. In that function you could make a timeout with millis(), in case the slave is not responding.

In the sketch for the slave are too many delay() functions. When the slave is doing a delay() it can not respond to the master. Remove every delay() from the slave sketch.
The way the incoming serial data is received is not okay. Check if something is available, and read data only if something is available.

You send a single byte to a Slave. I think that is okay, but often a packet of data is transmitted. Such a packet could start with a 'stx' byte, some data, a checksum and a 'etx' byte at the end.

Here is a tutorial : Gammon Forum : Electronics : Microprocessors : RS485 communications
Could you start with that tutorial ? instead of your own sketch. That is a working example.

Could you find a good RS-485 library for the Arduino ?

welcome to the forum Henradrie, you detail your project quite well and from my present understanding of your requirements, Modbus would seem to be the exact and perfect fit as part of your solution.

Modbus is fundamentally based on a master and slave network topology.
Modbus is now an open source data transfer protocol.

Some call the master a client and the slave a server, the terms are interchangeable.

Modbus has all the logic required to effectively handle all errors.
It has been around for many many years in industrial control systems and is gaining popularity in small embedded systems and boards like Arduino.

There are a few Modbus libraries about and depending on your design requirements, you may use either TCP - Ethernet or the RTU version, which is all things async serial such as TTL, RS-232, EIA-485 or even USB.

EIA-485 allows multi-drop of Modbus devices, which I think id what you are needing.

The idea mentioned above is another option of course, but you need to weigh up a few factors to determine your criteria.

I don't use MatLab, but I surely think it will have support or Modbus.

Interested to watch your progress.


Paul

Further to what Peter_n says, try to avoid use of SoftwareSerial, it may be problematic for you.
I have hear of many struggling with it, and I don't use it at all.

Rather, if you need additional ports, then best buy a bigger board such as a Mega.

Now, for an idea, you could use UNO's for all your slaves that are getting sensor data and simply use the one and only async port direct to EIA-485 module as you described.

Then, for the master, use either a Mega, where you have 4 serial ports to play with, or use a board that has Ethernet on it and use TCP to communicate to MatLab.

As you can see, there are many ways.


Paul

Further,
In your slave code:

data=analogRead(potpin);    // read in data
if (data > 400){          // reduce data value if too high
  data=400;
}

digitalWrite(ledpin,HIGH);
delay(data);

You have a delay, and that delay can be as high as 400mSec.
Any use of a delay is very likely to give you all sorts of grief, especially in communications applications like this.
You don't want or need it, really.

Imagine, the code is doing its delay, and during this time it gets a call from the master, but the master knocks on the door, but there is no answer, they are all asleep. Will the master wait or move on. If you make the master wait, then your speed of polling all other slaves is impacted.

Edit: Oh heck, you have more delays, more grief :fearful:


Paul

Thanks everyone, There were some solutions mentioned and resources revealed that I had no idea about. I'll give them a try.

I tried wiring this tutorial up exactly and it doesn't work what could be the problem? Gammon Forum : Electronics : Microprocessors : RS485 communications

I suspect the tutorial works perfectly, it is by Nick Gammon, who I would say, knows his stuff quite well.
If you did it up exactly, it would also work, so I can only guess you haven't done it exactly the same.

Only you can check for what you have not considered, as you have your code and circuit, we don't.


Paul