Pages: [1]   Go Down
Author Topic: Arduino RS485 communication for home automation project  (Read 1439 times)
0 Members and 1 Guest are viewing this topic.
Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

With Nicks Gammons  RS485 Master code listed below (all be it played with by me)

http://www.gammon.com.au/forum/?id=11428  (from Nicks Site)

I want to have the slave send a response that contains data from a variable that when received by the Master can be used in my application,
the slave sends a response to a masters message but it only has a boolean response - successful transmission true/false, switching on pin 13 if false.

How can i get a decimal value passed back

My network works great for sending digital commands and i can change the channel and communicate with other slaves indepentantly.
i now want to iterogate a slave that will respond with value read from an analoge input.

Code:
#include "WConstants.h"
#include <NewSoftSerial.h>
#include "RS485_protocol.h"

NewSoftSerial rs485 (2, 3);  // receive pin, transmit pin
const byte ENABLE_PIN = 4;
int channel = 0;
int data=0;
const byte LED_PIN = 13;
int incomingByte=0; // for incoming serial data
int thousands=0; int hundreds =0; int tens =0; int ones=0;
int inByte=0;
// callback routines
 
void fWrite (const byte what)
  {
  rs485.print (what); 
  }
 
int fAvailable ()
  {
  return rs485.available (); 
  }

int fRead ()
  {
  return rs485.read (); 
  }

void setup()
{
  rs485.begin (28800);
   Serial.begin(9600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
}  // end of setup
 
byte old_level = 0;

void loop()
{

  // read potentiometer

 // byte level = analogRead (0) / 4;// commented out as it is now variable being written
  byte level = (incomingByte);
  // Serial.print(level);// print to serial monitor - for testing only
   
   if (Serial.available() > 0) {       //read value from host PC
    hostprotocol();
   }
  // no change? forget it
  if (level == old_level)
    return;
     
  // assemble message
  byte msg [] = {
     1,    // device 1
     2,    // turn light on
     level // to what level
  };

  // send to slave 
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  delayMicroseconds (660);
  digitalWrite (ENABLE_PIN, LOW);  // disable sending

The following code is where i beleive the response would arrive.
i have tried adding lines of code to read a particular buffer location ie Rxdata = (buf [1]);
and then do a println to see content but nothing happens.
any suggestions would be greatly appreciated :-

Code:

// receive response 
  byte buf [20];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
 
  digitalWrite (LED_PIN, received == 0);  // turn on LED if error 
 
  // only send once per successful change
  if (received)
{
    if (buf [0] != channel)
      return;  // not my device
     
    if (buf [1] != 2)
      return;  // unknown command
   
byte msg [] = {
       0,  // device 0 (master)
       data,  // turn light on command received
       //data

};
 
    old_level = level;

}

}  // end of loop

rest of master code

Code:
void hostprotocol()      //Serial commands received from host
{
  inByte = Serial.read();
    switch (inByte)
    {
     
 case 79: //O  out
 
        DigitalOut();
   break;
 
  }
}
void DigitalOut() {
   
 // send data only when you receive data:
Serial.print("Digital OK");
 
 // only if there are bytes in the serial buffer execute the following code
  if(Serial.available()) {
   
     //keep reading and printing from serial untill there are bytes in the serial buffer
     //while (Serial.available()>0){
       if (Serial.available()>2){ // got 3 characters
       
// assume comes in as hundreds, tens, ones
hundreds = Serial.read() - 0x30;  // read the byte & convert from ASCII to a number
tens = Serial.read() - 0x30;
ones = Serial.read() - 0x30;

// now make into a digit
incomingByte =  hundreds*100 + tens*10 + ones;
     
 }
 Serial.print(incomingByte);
    }
 
//int value = (incomingByte);
 }
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 551
Posts: 46256
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  byte level = (incomingByte);
(Why) (are) (there) (parentheses) (around) (incomingByte)?

Which of those codes is on the master and which is on the slave?

Code:
// byte level = analogRead (0) / 4;// commented out as it is now variable being written
This comment makes no sense. That the line is commented out means that the variable is not defined. It is then not possible to send that variable anywhere.

Post ALL of your code, not part of it.
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Paul
The parentheses probably dont need to be there as the code works, i dont know if Nicks variable which i replaced with incommingByte ( the read serial input value) had them. it could just be one of my elderly moment things.

The commented out analogue read is because Nicks master module reads an analogue value which it transmits to the slave.
i send this value from the host PC to the 485 master for transmission via the bus.

the code above is all of my master module code that i have modified from Nicks original - just add all three code segments together.
the slave is not that dissimilar to Nicks (on his web page)

i really would appreciate someone explaining the Receive part
As mentioned above i want to be able to ask a slave to get an analogue value and reply back with that value.
Jason

Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

still hoping some kind individual will help by providing an answer
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 551
Posts: 46256
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
just add all three code segments together.
I've never been very good at adding code. Somehow my calculator just can't figure out what code 1 + code 2 is supposed to be.

Nor have you provided the receiver code.
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It works but i can only send digital outputs, i want to acheive getting values back.
The master code:
Code:
#include "WConstants.h"
#include <NewSoftSerial.h>
#include "RS485_protocol.h"

NewSoftSerial rs485 (2, 3);  // receive pin, transmit pin
const byte ENABLE_PIN = 4;
int channel = 0;
const int NUMBER_OF_FIELDS = 4; // t_west, t_back, t_up, t_down
int fieldIndex = 0;
int values[NUMBER_OF_FIELDS]; // temp * 10
int deviceID=0;
int function =2;
int data=0;
int RXdata=0;
int data2=0;
const byte LED_PIN = 13;
int incomingByte=0; // for incoming serial data
int thousands=0; int hundreds =0; int tens =0; int ones=0;
int inByte=0;
byte Cmd = 2;
byte old_level = 0;

// callback routines
 
void fWrite (const byte what)
  {
  rs485.print (what); 
  }
 
int fAvailable ()
  {
  return rs485.available (); 
  }

int fRead ()
  {
  return rs485.read (); 
  }

void setup()
{
  rs485.begin (28800);
   Serial.begin(9600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
}  // end of setup
 
void loop()
{

byte level = (data);

   if (Serial.available() > 0) {       //read value from host PC
    hostprotocol();
   }
  // no change? forget it
  if (data == old_level)
    return;
     
  // assemble message
  byte msg [] = {
     deviceID,    // device 1
     function,    // turn light on
     data // to what level
  };

  // send to slave 
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  delayMicroseconds (660);
  digitalWrite (ENABLE_PIN, LOW);  // disable sending
 

  // receive response 
  byte buf [20];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
 
  digitalWrite (LED_PIN, received == 0);  // turn on LED if error 
 
 
  // only send once per successful change
  if (received)
{
    if (buf [0] != channel)
      return;  // not my device
     
    if (buf [1] != Cmd)
      return;  // unknown command
   
byte msg [] = {
       0,  // device 0 (master)
       RXdata,
 }; 
     
       data2 = buf [2];
//delay (5);  // give the master a moment to prepare to receive     
//Serial.print (data2);
 
    old_level = data;
}
}  // end of loop

void hostprotocol()      //Serial commands received from host
{
 
 if (Serial.available()) { // Read message
    delay(100);
    while (Serial.available()) {
      char ch = Serial.read();
      if (ch >= '0' && ch <= '9') {
        values[fieldIndex] = (values[fieldIndex] * 10) + (ch - '0');
      } else if (ch == ',') {
        if (fieldIndex < NUMBER_OF_FIELDS - 1) {
          fieldIndex++;
        }
      } else if (ch == '-') {
        values[fieldIndex] = values[fieldIndex] * -1;
      } else {
       
        deviceID=(values[0]);
        values[0] = 0;
     
       function=(values[1]);
        values[1] = 0;
       
        data=(values[2]);
        values[2] = 0;
       
        data2=(values[3]);
        values[3] = 0;
   
        fieldIndex = 0;
       
      }
    }
  }
}

and slave
Code:
#include "WConstants.h"
#include <NewSoftSerial.h>
#include "RS485_protocol.h"

NewSoftSerial rs485 (2, 3);  // receive pin, transmit pin
const byte ENABLE_PIN = 4;
int channel = 1;// Device ID

int Inputvalue =0; // Input value from Master

int ledPin = 12;// digital bit weighted outputs
int ledPin1 = 11;
int ledPin2 = 10;
int ledPin3 = 9;
int ledPin4 = 8;
int ledPin5 = 7;
int ledPin6 = 6;
int ledPin7 = 5;


void fWrite (const byte what)
  {
  rs485.print (what); 
  }
 
int fAvailable ()
  {
  return rs485.available (); 
  }

int fRead ()
  {
  return rs485.read (); 
  }
 
void setup()
{
  rs485.begin (28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  //Bit weighted gigital outputs
  pinMode(ledPin, OUTPUT);  // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin2, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin3, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin4, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin5, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin6, OUTPUT); // initialize the LED pin as an output:
  pinMode(ledPin7, OUTPUT); // initialize the LED pin as an output:
}

void loop()
{
  byte buf [20];
 
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
 
  if (received)
    {
    if (buf [0] != channel)
      return;  // not my device
     
    if (buf [1] != 2)
      return;  // unknown command
   
    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };
   
    delay (5);  // give the master a moment to prepare to receive
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
   
    //analogWrite (11, buf [2]);  // set light level AS WAS JT
    Inputvalue =buf [2];// write coil value to a variable for use later JT
   // digital bit weighted outputs
   digitalWrite(ledPin, (Inputvalue >> 0 )% 2);// LSB
   digitalWrite(ledPin1, (Inputvalue >> 1)% 2);
   digitalWrite(ledPin2, (Inputvalue >> 2)% 2);
   digitalWrite(ledPin3, (Inputvalue >> 3)% 2);
   digitalWrite(ledPin4, (Inputvalue >> 4) % 2);
   digitalWrite(ledPin5, (Inputvalue >> 5) % 2);
   digitalWrite(ledPin6, (Inputvalue >> 6) % 2);
   digitalWrite(ledPin7, (Inputvalue >> 7) % 2); // MSB
   
   }  // end if something received
   
}  // end of loop
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 551
Posts: 46256
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
It works but i can only send digital outputs
What does this mean? You are sending data, not "digital outputs".

Code:
int values[NUMBER_OF_FIELDS]; // temp * 10
The comment makes no sense in terms of the code that it goes with.

Code:
void fWrite (const byte what)
  {
  rs485.print (what); 
  }
 
int fAvailable ()
  {
  return rs485.available (); 
  }

int fRead ()
  {
  return rs485.read (); 
  }
These functions are useless. Get rid of them. Simply call rs485.print(), rs485.available(), and rs485.read().

Code:
byte msg [] = {
       0,  // device 0 (master)
       RXdata,
 }; 
What is this for?

You really should use Tools + Auto Format. That code that jerks all over the place is hard to read.

Get rid of the useless parentheses. They contribute nothing. They interfere with understanding the code.

Is recvMsg a blocking function? What does the master get back?

If the slave is always going to respond with the same values, is it necessary to call the slave at all?
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK,
Im not totally responsible for 70% of this code, its the sketches i am using written by Nick Gammon - this should already have been established.
I can send a message that operates Leds on the slave device.
in Nicks code the slave sends an acknowledge back which is an echo of the received message
the master unfortunately handels that as being a boolean response, either it received the message back or it didnt.

getting back to the very start, i want to know if the returned message contains the data which the slave built up in the echoed message (msg).
and if i can get the master to split the returned message into variables which i can then send back to the host pc.
unfortunately it appears the acknowledge message is queried to see if its tru or false then the function (received message) returns zero.

That in a nutshell is what i want to know
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 551
Posts: 46256
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'd suggest that you refrain from using the word "it" in any future posts. When you use that pronoun, it is never clear what you are referring to.

I suggest, too, that you cite particular parts of you code when you say "it does this". It is not clear what part of the code you are referring to, or whether your understanding of the code is correct.

Quote
the master unfortunately handels that as being a boolean response, either it received the message back or it didnt.
So, stop doing that. The recvMsg function does not return a boolean (true or false). It returns a value. You need to look at the source code or the documentation to figure out what that value represents. The name, though, implies that it is the number of bytes received. Print it out and find out. Change the number of bytes sent by the slave to see if the value in received changes.

Quote
getting back to the very start, i want to know if the returned message contains the data which the slave built up in the echoed message (msg).
How? By using your psychic powers? Where are the Serial.print() statements that confirm what you actually get on the slave or back on the master?
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I removed the Println statements to make the code clearer for any assisting guru's to see the code as stands
i tried to place a serial.print in the received message (function) and nothing happens, no response.

as i understand it there is a response because the led does not light indicating an error.
the function works because if i remove the RS485 data pair and send a message the error led lights

so can i deduce from your comments that i am infact looking in the right area of code to see a response
and that it is your view that i should be able to view the returned message within the received function
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

still not got a satifactory answer.

can some one please tell me how i can read the acknowledge
so that i can use it to pass messages back to the master

J.
Logged

Pages: [1]   Go Up
Jump to: