Go Down

Topic: Polling multiple slaves, master outputs chattering (Read 682 times) previous topic - next topic

GCone

This project has a coordinator (MEGA 2560) that talks to several (2 so far to test) UNO stations.  I am using XBEE XSC 900MHz wireless chips and SainSmart shields.  Each field station has 2 inputs.  The master has 2 outputs to relays.  If 'Input1' on pin 8 or 'Input2' on pin 9 are true, I want the corresponding outputs to come on, energizing a relay.  All of the XBEE on each line have the same DT parameter (Destination Address), which is needed for them to talk, and each one has a StationID in its program.  This works perfectly when only one remote station is talking to the master, even using the same loops designed for multiple stations, but when I have 2 stations and with different input states, the corresponding output chatters the relay.

The master has a FOR loop that sends out an integer as a StationID.  The remote that has that ID recognizes it and replies with a '0', '1', '2', or '3' representing the state of the inputs.  The master does this for all stations (currently only 2) and reads the received data into the corresponding element of array StationStatus.  After doing this for all stations, I was looping through the array and bitwise ORing the elements into a result, LineStatus.  Then I used bitRead to examine the state of bits 0 and 1 and turn on the corresponding output.  Like I said, works fine if I unplug one of the stations, but with both stations on and opposite states on input 1 or 2, that matching output goes on and off and random intervals. =(  Anybody have any ideas?

I am new to Arduino and C, I've been programming ladder logic on PLCs (Programmable Logic Controllers for a long time.  Thanks!

UNO Remote Station code:
Code: [Select]
//function to invert input state so
//contacts closed = false and contacts
//open = true
boolean invert(boolean Pin)
{
  boolean result;
  if (Pin == true)
  {
    result = false;
  }
  else if (Pin == false)
  {
    result = true;
  }
  return result;
}
//====================================

const int inputPin1 = 8; 
//input from Problem Solve andon

const int inputPin2 = 9; 
//input from Water Spider andon

const int ledPin = 13;
boolean inputState1;  //inverted input 1
boolean inputState2;  //inverted input 2
int MyStationID = 2; 
//may make this configurable by dip switch

String TransmitData; 
//byte to be sent as status response to master unit

int ch = 0;  //recieved byte from master unit

//for testing
unsigned long StartBlink;
unsigned long StopBlink;

//-----------------------------------------
void setup()
{
  //configure digital I/O pins
  pinMode(ledPin,OUTPUT);      //for testing
  pinMode(inputPin1,INPUT);
  pinMode(inputPin2, INPUT);

  //Set input pins HIGH to enable Arduino's
  //internal pullup resistor
  digitalWrite(inputPin1,HIGH);
  digitalWrite(inputPin2,HIGH);

  Serial.begin(57600);
}

//------------------------------------------
void loop() {

  //invert input state so closed contact = true
  //and open contact = false
  //inputState1 = invert(digitalRead(inputPin1));
  //inputState2 = invert(digitalRead(inputPin2));

  inputState1 = digitalRead(inputPin1);
  inputState2 = digitalRead(inputPin2);

  //Clear TransmitData
  TransmitData = 0;




  /*
  //Read inputs on pins 8 & 9 and set corresponding
   // bits in TransmitByte to be sent to master.
   if (inputState1 == true)
   {
   bitSet(TransmitData, 0);
   }
   else
   {
   bitClear(TransmitData, 0);
   }
   
   
   if (inputState2 == true)
   {
   bitSet(TransmitData, 1);
   }
   else
   {
   bitClear(TransmitData, 1);
   }
   */
  //-----------------------------------------
  if (Serial.available())
  {
    ch = Serial.read();
    delay(20);
    //-----------------------------------------
    if ((ch - 48) == MyStationID)
    {
      if(inputState1 == false && inputState2 == false)
      {
        Serial.print(0);
      }
      else if(inputState1 == true && inputState2 == false)
      {
        Serial.print(1);
      }
      else if(inputState1 == false && inputState2 == true)
      {
        Serial.print(2);
      }
      else if(inputState1 == true && inputState2 == true)
      {
        Serial.print(3);
      }
    }
    delay(20);


  }



}


MEGA (Master unit) code:
Code: [Select]
//array of slave station status bytes received
int StationStatus[30];
int StationCount = 2;
int LineStatus;
boolean LineStatus0;
boolean LineStatus1;
int OutputPin1 = 8;   
int OutputPin2 = 9;   
unsigned long TimeoutMonitor;
boolean Timeout;
int ledPin = 13;
int i;
int x;
int z;
boolean WriteOutputs = false;
boolean Output1 = 0;
boolean Output2 = 0;
//==============================================
void setup()
{
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(ledPin, OUTPUT);

  Serial.begin(57600);
}
//==============================================
void loop()
{
  //Poll each station and wait to receive ascii status character
  for(i=1; i<=StationCount;i++)
  {
    Serial.print(i); 
    delay(60);
    if (Serial.available() > 0)
    {
      StationStatus[i] = (Serial.read()-48);
      TimeoutMonitor = 0;
      digitalWrite(ledPin, HIGH);
    }
    else if (Serial.available() <= 0)
    {
      if(TimeoutMonitor == 0)
      {
        TimeoutMonitor = millis() + 1000;
      }
      if (millis() >= TimeoutMonitor)
      {
        Timeout = true;
        digitalWrite(ledPin, LOW);
      }
    }
    if(i == StationCount)
    {
      WriteOutputs = true;   
    }
  }
  //=============================================
  if(WriteOutputs == true)
  {
    LineStatus = 0;
    for (x=1; x<=StationCount;x++)
    {
      int temp;
      temp = StationStatus[x];
      if bitRead(temp, 0)
      {
        bitSet(LineStatus, 0);
      }
      if bitRead(temp, 1)
      {
        bitSet(LineStatus, 1);
      }
      //LineStatus = LineStatus | StationStatus[x];
    }
    // if (LineStatus == 0)
    if((bitRead(LineStatus, 0)== 0) && (bitRead(LineStatus, 1) == 0))
    {
      Output1 = 0;
      Output2 = 0;
    }
    //else if(LineStatus == 1)
    if((bitRead(LineStatus, 0)== 1) && (bitRead(LineStatus, 1) == 0))
    {
      Output1 = 1;
      Output2 = 0;
    }
    if((bitRead(LineStatus, 0)== 0) && (bitRead(LineStatus, 1) == 1))
    {
      Output1 = 0;
      Output2 = 1;
    }
    //else if(LineStatus == 3)
    if((bitRead(LineStatus, 0)== 1) && (bitRead(LineStatus, 1) == 1))
    {
      Output1 = 1;
      Output2 = 1;
    }
    if(Output1 == true)
    {
      digitalWrite(OutputPin1, HIGH);
    }
    else
    {
      digitalWrite(OutputPin1, LOW);
    }
    if(Output2 == true)
    {
      digitalWrite(OutputPin2, HIGH);
    }
    else
    {
      digitalWrite(OutputPin2, LOW);
    }
    WriteOutputs = false;
  }

GCone

Note: I replaced the bitwise OR with a slightly different scheme for seeing if any bit 0 or bit 1 in the array was true.  I've actually tried a bunch of stuff and that doesn't seem to be the problem.

PeterH

It looks like quite a complex system and there are lots of things that need to work correctly for the overall result to be correct. The code looks a bit ragged in places and it's hard to be sure which bits work correctly. Does your system put the expected value into StationStatus for each station, for each possible input condition on the two stations? I notice that you are using the hardware serial to talk to the remotes. Do you have any way to print trace messages for debugging?
I only provide help via the forum - please do not contact me for private consultancy.

GCone

Let me just simply ask this:  I have multiple UNOs with 2 inputs each.  I have a MEGA that talks to all of the UNOs.  If the 1st input is true on any UNO station, I want to turn on the first output on the MEGA.  If the 2nd input is true on any UNO station, I want to turn on the 2nd output on the MEGA.  Communicating over XBEE XSC.  Does anyone know of a documented project like this? 

I am simply: 

1.Asking each station to send its status and storing the received data in an array.

2. Looping through the array to see if any 'Input1' is on and if any 'Input 2' is on.  If so I turn on the corresponding output.

3.  It doesn't work.  If some of the Input 1's are on and some are off the output toggles on and off at seemingly random intervals (same issue with Input 2.

PeterH


Does anyone know of a documented project like this? 


I don't.

What it is trying to do is conceptually simple. The overall design seems to be trying to do the right thing. Since it isn't working, some part of this process is clearly not working. Which is why I asked the question I did in my previous post. If your sketch is getting garbage in, then everything else that follows will be garbage.
I only provide help via the forum - please do not contact me for private consultancy.

GCone

#5
Aug 21, 2012, 09:15 pm Last Edit: Aug 21, 2012, 09:22 pm by GCone Reason: 1
I do not have a way of printing trace messages currently, although I do have a 2 line lcd display I could probably connect to one of the other serial ports on the MEGA.

I did find a bug - remote 1 & remote 2 are listening for a '1' or '2' respectively to prompt them to send their status, but if one of them sends a status equal to the other ones StationID, the other one would send out its status also, unsolicited by the master.  I changed so that the remotes send an 'a', 'b', 'c', or 'd' instead of 1, 2, 3, or 4 and I subtract a 97 from the raw ascii code instead of a 48.  Not chattering now but also not working.  For some reason now the stupid MEGA is turning the second output on and the first output off no matter what, even when I turn off the remotes!

Edit:  Oops, just realized my bitRead() was missing in the condition to do digitalWrites on the 'else if's. Didn't give compile error on  "else if ((LineStatus, 1) == false"

GCone

#6
Aug 21, 2012, 09:19 pm Last Edit: Aug 21, 2012, 09:32 pm by GCone Reason: 1
Current code, cleaned up a bit.  (I'm not using the Invert() function, might just get rid of it.  Thought I needed it but apparently not)

Remote:
Code: [Select]

//function to invert input state so
//contacts closed = false and contacts
//open = true
boolean invert(boolean Pin)
{
  boolean result;
  if (Pin == true)
  {
    result = false;
  }
  else if (Pin == false)
  {
    result = true;
  }
  return result;
}
//====================================

const int inputPin1 = 8; 
//input from Problem Solve andon

const int inputPin2 = 9; 
//input from Water Spider andon

const int ledPin = 13;
boolean inputState1;  //inverted input 1
boolean inputState2;  //inverted input 2
int MyStationID = 2; 
//may make this configurable by dip switch

String TransmitData; 
//byte to be sent as status response to master unit

int ch = 0;  //recieved byte from master unit

//for testing
unsigned long StartBlink;
unsigned long StopBlink;

//-----------------------------------------
void setup()
{
  //configure digital I/O pins
  pinMode(ledPin,OUTPUT);      //for testing
  pinMode(inputPin1,INPUT);
  pinMode(inputPin2, INPUT);

  //Set input pins HIGH to enable Arduino's
  //internal pullup resistor
  digitalWrite(inputPin1,HIGH);
  digitalWrite(inputPin2,HIGH);

  Serial.begin(57600);
}

//------------------------------------------
void loop() {

  //invert input state so closed contact = true
  //and open contact = false
  //inputState1 = invert(digitalRead(inputPin1));
  //inputState2 = invert(digitalRead(inputPin2));

  inputState1 = digitalRead(inputPin1);
  inputState2 = digitalRead(inputPin2);

  //Clear TransmitData
  TransmitData = 0;

  //-----------------------------------------
  if (Serial.available())
  {
    ch = Serial.read();
    delay(20);
    //-----------------------------------------
    if ((ch - 48) == MyStationID)
    {
      if(inputState1 == false && inputState2 == false)
      {
        Serial.print('a');
      }
      else if(inputState1 == true && inputState2 == false)
      {
        Serial.print('b');
      }
      else if(inputState1 == false && inputState2 == true)
      {
        Serial.print('c');
      }
      else if(inputState1 == true && inputState2 == true)
      {
        Serial.print('d');
      }
    }
    delay(20);
  }
}




Master:
Code: [Select]

//array of slave station status bytes received
int StationCount = 2;
int StationStatus[30];
int Received;
int LineStatus;
int OutputPin1 = 8;   
int OutputPin2 = 9;   
unsigned long TimeoutMonitor;
boolean Timeout;
int ledPin = 13;
int i;
int x;
boolean Output1 = 0;
boolean Output2 = 0;
//==============================================
void setup()
{
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(ledPin, OUTPUT);

  Serial.begin(57600);
}
//==============================================
void loop()
{
  //Poll each station and wait to receive ascii status character
  for(i=1; i<=StationCount;i++)
  {
    delay(100);
    Serial.print(i); 
    delay(100);
    if (Serial.available() > 0)
    {
      Received = Serial.read();
      StationStatus[i] = (Received - 97);
      TimeoutMonitor = 0;
      digitalWrite(ledPin, HIGH);
    }
    else if (Serial.available() <= 0)
    {
      if(TimeoutMonitor == 0)
      {
        TimeoutMonitor = millis() + 1000;
      }
      if (millis() >= TimeoutMonitor)
      {
        Timeout = true;
        digitalWrite(ledPin, LOW);
      }
    }
  }
  //=============================================
 
    LineStatus = 0;
   for (x=1; x<=StationCount;x++)
   {
    LineStatus = LineStatus || StationStatus[x];
   }
   
   if (bitRead(LineStatus, 0) == true)
   {
   digitalWrite(OutputPin1, HIGH);
   }
   else if (bitRead(LineStatus, 0) == false)
   {
   digitalWrite(OutputPin1, LOW);
   }
   if (bitRead(LineStatus, 1) == true)
   {
   digitalWrite(OutputPin2, HIGH);
   }
   else if (bitRead(LineStatus, 1) == false)
   {
   digitalWrite(OutputPin2, LOW);
   }
}


GCone

#7
Aug 22, 2012, 06:32 pm Last Edit: Aug 22, 2012, 06:35 pm by GCone Reason: 1
OK, I'm a n00b, but I found that by slowing the program way down with a 5 sec delay between sending out the request and checking for received data, and a 5 second delay between reading data and sending out a request to the next station, I can just pull the XBEE shield and simulate the remote station responses by typing them into the serial monitor.  I made a truth table and my observed results weren't what I expected, so I added a loop to clear out the StationStatus array each time:
Code: [Select]

  for(y=1; y<=StationCount; y++)
  {
   StationStatus[y] = 0;
  }


It seems to work now as I step through it, but a couple of times I let it cycle through both stations without inputing anything and it went to 01 or 10 or something and then to 00 the next time.  I don't know if I accidentally typed something and lost track of what I was putting in, but I am going to also clear out the receive buffer each main loop cycle too.  I understand that the Serial.flush() used to do this but doesn't in the current version, so I guess this should do the same thing:
Code: [Select]

  while (Serial.available())
  {
   bufferDump = Serial.read();
  }


Go Up