Could use some advice on my networking project (although this could be about ignorance when it comes to language, not networking)!
I'm attempting to get three (knock-off) Mega2560's communicating over RS485 (using Nick Gammon's libraries), one as a master, the other two as slaves. I've been able to work through bugs with Uno's and the Mega's using his normal (blocking) library, but am having trouble getting things working with the non-blocking version. I'm suspicious this is ignorance in my understanding of C/C++ and how I'm manipulating memory.
Communication is over Serial1. For inputs and outputs on my master, each slave has its own button and green and red LED (so 2 buttons, 4 LED's total). The slaves each have one LED connected. The goal is to be able to press the button at the master, and have the corresponding slave receive the signal that it's LED should be on, and then it send a reply that it acted on that command, which would turn on the corresponding green LED on the master. A second button press on the master tells the slave to turn off; the slave turns it's LED off, and the reply turns the red LED on (and the green off) on the master. The slaves are designated "left" and "right" in my code, and shouldn't interfere with the other slave when they communicate.
I'm using a struct with four data types to send my info (not all are necessary, but I want to know I can send all types of data, particularly the integer). Everything is getting transmitted successfully going from master to slaves: the light on the slaves is turning on and off when it should. But when I send a signal to one slave, and then a signal to the other, the last reply coming back to the master, still contains the address from the slave in the second last transmission. If I alternate the button presses from there, the master is always one behind in who it's receiving a reply from (so the wrong green and red LED's will be switching on the master). However after sending two consecutive signals to the same slave, the address will be correct on the second transmission.
The glitch is very consistent. I've only been looking at the Serial feedback from one board at a time, but I don't think the wrong slave is replying, I think it's something in my code on the master.
What am I missing? I believe memset is completely wiping the memory of the struct I'm using to send my data, but is something being left over? When I check what's contained in it after wiping it, I notice that no value is given for my char (I just get 3 zero's); has that got something to do with it?? Maybe that comment explains my level of knowledge on all this!
Master code:
#include <RS485_non_blocking.h>
/* The current issue with this is (along with it's receiving partner) is that when I send a message to one
base but then switch who I'm sending to, the first transmission BACK from the receiver still has the old
receivers address in it. However after a couple (and this happens consistently) cycles, then the receiver's
address will show up correctly. I don't know why this is...*/
//easy to get confused on this: base LEFT = 1
// base RIGHT = 2
// callback routines
size_t fWrite(const byte what) {
return Serial1.write(what);
}
int fAvailable() {
return Serial1.available();
}
int fRead() {
return Serial1.read();
}
const byte ENABLE_PIN = 2;
const byte LEFT_GREEN = 3;
const byte LEFT_RED = 4;
const byte RIGHT_GREEN = 5;
const byte RIGHT_RED = 6;
const byte RIGHT_BUTTON = 8;
const byte LEFT_BUTTON = 9;
//variables for buttons pressing
unsigned long debounceDelay = 50; // the debounce time
bool leftState = false;
bool lastLeftState = false;
unsigned long lastLeftDebounceTime = 0; // the last time the output pin was toggled
bool rightState = false;
bool lastRightState = false;
unsigned long lastRightDebounceTime = 0; // the last time the output pin was toggled
//data for sending
byte leftAddress = 1;
bool leftActive = false;
char leftStatus = 'X';
byte rightAddress = 2;
bool rightActive = false;
char rightStatus = 'X';
int gameTime = 300;
//the structure of our message
struct message {
byte address;
bool active;
char status;
int theTime;
};
//default data for our message
struct message commPacket;
RS485 myChannel(fRead, fAvailable, fWrite, 20);
//-------------------------------------------------------------------------------------------------------------------------------------
// SETUP SETUP SETUP SETUP SETUP
//-------------------------------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
Serial1.begin(28800);
myChannel.begin();
pinMode(ENABLE_PIN, OUTPUT); // driver output enable
pinMode(LEFT_GREEN, OUTPUT);
pinMode(LEFT_RED, OUTPUT);
pinMode(RIGHT_GREEN, OUTPUT);
pinMode(RIGHT_RED, OUTPUT);
pinMode(RIGHT_BUTTON, INPUT_PULLUP);
pinMode(LEFT_BUTTON, INPUT_PULLUP);
}
//-------------------------------------------------------------------------------------------------------------------------------------
// LOOP LOOP LOOP LOOP LOOP
//-------------------------------------------------------------------------------------------------------------------------------------
void loop() {
userInterface();
updateLights();
}
//-------------------------------------------------------------------------------------------------------------------------------------
// UPDATE LIGHTS UPDATE LIGHTS UPDATE LIGHTS
//-------------------------------------------------------------------------------------------------------------------------------------
void updateLights() {
if (leftStatus == 'X') {
digitalWrite(LEFT_GREEN, LOW);
digitalWrite(LEFT_RED, LOW);
}
if (leftStatus == 'A') {
digitalWrite(LEFT_GREEN, HIGH);
digitalWrite(LEFT_RED, LOW);
}
if (leftStatus == 'D') {
digitalWrite(LEFT_GREEN, LOW);
digitalWrite(LEFT_RED, HIGH);
}
if (rightStatus == 'X') {
digitalWrite(RIGHT_GREEN, LOW);
digitalWrite(RIGHT_RED, LOW);
}
if (rightStatus == 'A') {
digitalWrite(RIGHT_GREEN, HIGH);
digitalWrite(RIGHT_RED, LOW);
}
if (rightStatus == 'D') {
digitalWrite(RIGHT_GREEN, LOW);
digitalWrite(RIGHT_RED, HIGH);
}
}
//-------------------------------------------------------------------------------------------------------------------------------------
// USER INTERFACE USER INTERFACE USER INTERFACE USER INTERFACE
//-------------------------------------------------------------------------------------------------------------------------------------
void userInterface() {
//int leftReading = !(PINH & B00100000); //read digital pin 9 (H6)
int leftReading = digitalRead(LEFT_BUTTON);
if (leftReading != lastLeftState) { //if the button changed...
lastLeftDebounceTime = millis(); //...remember the time
}
if ((millis() - lastLeftDebounceTime) > debounceDelay) { //if the time since the last change was large enough
if (leftReading != leftState) { //if the state of the pin is different than it was before
leftState = leftReading; //remember the new state
if (leftState == LOW) { //once the button goes off?? (pulldown resistor)
leftActive = !leftActive; //flip the active status of the base
Serial.print("AFTER press, LEFT base, local variables are: ");
Serial.print(leftAddress);
Serial.print(leftActive);
Serial.print(leftStatus);
Serial.println(gameTime);
messageLeft();
}
}
}
lastLeftState = leftReading;
//int rightReading = !(PINH & B00010000); //read digital pin 8 (H5)
int rightReading = digitalRead(RIGHT_BUTTON);
if (rightReading != lastRightState) { //check if the button changed due to noise or pressing
lastRightDebounceTime = millis(); //if so remember the time
}
if ((millis() - lastRightDebounceTime) > debounceDelay) {
if (rightReading != rightState) {
rightState = rightReading;
if (rightState == LOW) {
rightActive = !rightActive;
Serial.print("AFTER press, local variables for RIGHT: ");
Serial.print(rightAddress);
Serial.print(rightActive);
Serial.print(rightStatus);
Serial.println(gameTime);
messageRight();
}
}
}
lastRightState = rightReading;
}
//-------------------------------------------------------------------------------------------------------------------------------------
// MESSAGE BASES MESSAGE BASES MESSAGE BASES MESSAGE BASES
//-------------------------------------------------------------------------------------------------------------------------------------
void messageLeft() {
memset(&commPacket, 0, sizeof commPacket); //zeros data
//fill message for base with data
commPacket.address = leftAddress;
commPacket.active = leftActive;
commPacket.status = leftStatus;
commPacket.theTime = gameTime;
delay(1);
digitalWrite(ENABLE_PIN, HIGH); // enable sending
myChannel.sendMsg((byte *)&commPacket, sizeof commPacket);
Serial1.flush();
digitalWrite(ENABLE_PIN, LOW); // disable sending
Serial.print("Sent to LEFT: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
getReply();
}
void messageRight() {
memset(&commPacket, 0, sizeof commPacket);
//fill message for base with data
commPacket.address = rightAddress;
commPacket.active = rightActive;
commPacket.status = rightStatus;
commPacket.theTime = gameTime;
delay(1);
digitalWrite(ENABLE_PIN, HIGH); // enable sending
myChannel.sendMsg((byte *)&commPacket, sizeof commPacket);
Serial1.flush();
digitalWrite(ENABLE_PIN, LOW); // disable sending
Serial.print("Sent to RIGHT: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
getReply();
}
//-------------------------------------------------------------------------------------------------------------------------------------
// GET REPLY GET REPLY GET REPLY GET REPLY
//-------------------------------------------------------------------------------------------------------------------------------------
void getReply() {
if (myChannel.update()) {
memset(&commPacket, 0, sizeof commPacket); //zero's out data...?
Serial.print("after memset: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
int len = myChannel.getLength(); //this saves the length of the message as an int
if (len > sizeof commPacket) {
len = sizeof commPacket;
}
memcpy(&commPacket, myChannel.getData(), len); //this actually dumps the data into message
Serial.print("Received: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
if (commPacket.address == 1) { //if message was from base 2
leftStatus = commPacket.status; // save the data from what we received
}
if (commPacket.address == 2) { //if message was from base 2
rightStatus = commPacket.status; // save the data from what we received
}
}
}
Slave code:
#include <RS485_non_blocking.h>
// callback routines
size_t fWrite(const byte what) {
return Serial1.write(what);
}
int fAvailable() {
return Serial1.available();
}
int fRead() {
return Serial1.read();
}
//local data
byte myAddress = 1;
bool myActive;
char myStatus;
int gameTime = 200;
const byte ENABLE_PIN = 2;
const byte BLUE_STATUS = 8;
//the structure of our message
struct message {
byte address;
bool active; //inverted order from controller: what control sends, we receive...
char status; //...and what we send, control receives
int theTime;
};
//messages
struct message commPacket;
RS485 myChannel(fRead, fAvailable, fWrite, 20);
//-------------------------------------------------------------------------------------------------------------------------------------
// SETUP SETUP SETUP SETUP SETUP
//-------------------------------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
Serial1.begin(28800);
myChannel.begin();
pinMode(ENABLE_PIN, OUTPUT); // driver output enable
pinMode(BLUE_STATUS, OUTPUT);
}
//-------------------------------------------------------------------------------------------------------------------------------------
// LOOP LOOP LOOP LOOP LOOP
//-------------------------------------------------------------------------------------------------------------------------------------
void loop() {
if (myChannel.update()) {
memset(&commPacket, 0, sizeof commPacket); //I believe this zero's out commPacket...
int len = myChannel.getLength(); //this saves the length of the message as an int
if (len > sizeof commPacket) {
len = sizeof commPacket;
}
memcpy(&commPacket, myChannel.getData(), len); //dumps RS485 data into message
Serial.print("This is what we received: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
if (commPacket.address == myAddress) { //if message was for us
myActive = commPacket.active; // save the data from what we received
if (myActive == true) {
digitalWrite(BLUE_STATUS, HIGH);
myStatus = 'A';
}
if (myActive == false) {
digitalWrite(BLUE_STATUS, LOW);
myStatus = 'D';
}
Serial.print("This is our local data: ");
Serial.print(myAddress);
Serial.print(myActive);
Serial.print(myStatus);
Serial.println(gameTime);
;
sendReply();
}
}
}
void sendReply() {
//zero out commPacket data
memset(&commPacket, 0, sizeof commPacket);
//fill message for controller with updated data
commPacket.address = myAddress;
commPacket.active = myActive;
commPacket.status = myStatus;
commPacket.theTime = gameTime;
Serial.print("This is what we're sending: ");
Serial.print(commPacket.address);
Serial.print(commPacket.active);
Serial.print(commPacket.status);
Serial.println(commPacket.theTime);
Serial.println();
delay(1);
digitalWrite(ENABLE_PIN, HIGH); // enable sending
myChannel.sendMsg((byte *)&commPacket, sizeof commPacket);
Serial1.flush();
digitalWrite(ENABLE_PIN, LOW); // disable sending
}