Serial send and receive - timing or coding issue?

Hello,

I've been struggling with this for quite a while, so I'm hoping for a few clues. The following is just a simplified test I'm running. I've got 2 Arduinos communicating via hardware serial, and sharing pretty much the same code (see below). In principle the master sends 111 then waits for a response (112 from slave). Yet even though the enable pins (I'm using RS485) are set low on the slave, no value seems to be getting through. I've added a delay on the slave before it sends a response, so the master has time to bring the enable pin low; 300 ms, maybe too long?

I've tested the Arduinos with two other sketches designed to just send a byte array from master to slave, and that works (the slave prints a confirmation response). It works when I reverse the roles too, so I'm guessing this has to do with the high/low timing on the enable pins.

Any help would be vastly appreciated, I'm trying to complete an art installation by the end of August. Thank you so much!

MASTER

const int pinEnable = 2;
byte hello = 111;
byte inRead = 0;
boolean received = true;

void setup ()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
}

void check () {
  digitalWrite(pinEnable, LOW);
  inRead = Serial.read();
  if (inRead == 112) {
    received = true;
  }  
}
  
void loop() {
  check();
  if (received == true) {
    Serial.write(inRead);
    digitalWrite(pinEnable, HIGH);
    Serial.write(hello);
    digitalWrite(pinEnable, LOW);
    received = false;
  }
}

SLAVE

const int pinEnable = 2;
byte helloBack = 112;
byte inRead = 0;
boolean received = true;

void setup ()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
}

void check () {
  digitalWrite(pinEnable, LOW);
  inRead = Serial.read();
  if (inRead == 111) {
    received = true;
  }  
}
  
void loop() {
  check();
  if (received == true) {
    delay(300);
    Serial.write(inRead);
    digitalWrite(pinEnable, HIGH);
    Serial.write(helloBack);
    digitalWrite(pinEnable, LOW);
    received = false;
  }
}

How are the two connected? What RS485 buffer are you using? Are the grounds connected?

I'm using two MAX485s (CPA1121) between the Arduinos, each grounded to their respective Arduino (the setup is described here: Gammon Forum : Electronics : Microprocessors : RS485 communications )

With prior sketches this was working fine. I suspect it's more of a code issue. Do you see anything wrong on that end?

Anyone?

You should take out the lines where you send inRead before you enable the transmitters.

    Serial.write(inRead);    ///////// Get rid of this line
    digitalWrite(pinEnable, HIGH);
    Serial.write(hello);
    digitalWrite(pinEnable, LOW);

You can probably take the pinEnable line out of check() an put it in setup() since it only has to be done once.

Hey John,

Absolutely, I was being redundant largely out of paranoia.

In the meantime, your initial hunch was right. I added a second pair of 620 Ohm resistors on the slave-end MAX485, and it did the trick! The Gammon diagram only showed one pair. Thanks for putting the bug in my ear!

I also simplified the code. Here it is:

MASTER

const int pinEnable = 2;
byte hello = 111;

void setup()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
}
  
void loop() {
  digitalWrite(pinEnable, LOW);
  byte inRead = Serial.read();
  if (inRead = 112) {
    Serial.write(inRead);
    delay(500);
    digitalWrite(pinEnable, HIGH);
    delay(1);
    Serial.write(hello);
    digitalWrite(pinEnable, LOW);
  }
}

SLAVE

const int pinEnable = 2;
byte helloBack = 112;

void setup()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
   digitalWrite(pinEnable, HIGH);
   Serial.write(helloBack);
   digitalWrite(pinEnable, LOW);
}
  
void loop() {
  byte inRead = Serial.read();
  if (inRead = 111) {
    delay(500); // adjust this delay as needed
    digitalWrite(pinEnable, HIGH);
    delay(1); // give time for master to go LOW
    Serial.write(helloBack);
    digitalWrite(pinEnable, LOW);
    }
}

each grounded to their respective Arduino

But there also needs to be a GND connection between the two. You are probably getting away with it now because the two Arduinos are running of the same PC or something and therefore getting a similar/same GND but if your intention is to have them more independent you need the third wire.

Technically Nick's schematic is correct but for newbies I think it should show the third wire to make it obvious.

If your Arduinos are close together and/or the bit rates are slow I don't see why you needed the extra resistors, in fact they are failsafe resistors not terminating resistors . But I won't argue with success :slight_smile:

EDIT:

In the slave setup() function you have this

digitalWrite(pinEnable, HIGH);
Serial.write(helloBack);
digitalWrite(pinEnable, LOW);

As there's no delay I doubt that byte ever sees the light of day.

ANOTHER EDIT:

    digitalWrite(pinEnable, HIGH);
    delay(1);
    Serial.write(hello);
    digitalWrite(pinEnable, LOW);

Same problem, the delay should be after the write().

Are you sure this code is working :slight_smile: Have you verified that the master actually sees the return byte?


Rob

Graynomad, I'm glad you mentioned that.

Just to make sure I understand what you mean by "third wire":

I have a 2-wire + ground (threaded wire) cable at home I planned on using. I guess I would connect two of the wires to A & B on the MAX485, but then would the receive enable pin connect to GND on the Arduino, whereas the actual GND pin on MAX485 would connect back to the master via the cable?

Also, whereas with the above sketches I could easily send bytes back to the master, when I change the sketch to use analogRead() (read as an int, then converted to byte), it doesn't read correctly on the master end - it always reads 255:

SLAVE:

int sensor = analogRead(A0)/4;
Serial.write((byte)sensor);

MASTER:

byte inRead = Serial.read();
if (inRead > 0) {
Serial.print(inRead, DEC);
}

Short answer, ALL grounds should be connected together, that cable should be good, use the "third wire" to connect the grounds at both ends.

The MAX485 RE pins at both ends should be pulled low (grounded) as well.

byte inRead = Serial.read();
  if (inRead > 0) {
    Serial.print(inRead, DEC);
}

This is wrong, you should wait for a character to be available then read it. As it is the Serial.read() is returning -1 (no characters to read) which happens to be 0xFF or 255. Try

byte inRead;
  if (Serial.available() > 0) {
    inRead = Serial.read();
    Serial.print(inRead, DEC);
}

Rob

The device to device grounds must be in place for a reference for the '485 serial differential data transfer, what if there were a 10 to 20 volt differential in the local grounds (Very Possible) now the question is without that ground... Referenced to what? and since the Max485 shares the same power supply as the Arduino again the ground from the '485 device is necessary so the Arduino can read/write to the device. It must also be explained that the ground is usually not explained but rather explicit. We all that use RS485 regularly Assume the ground rather that explain what is to us obvious. I hope this helps.

Doc

Graynomad,

I just read your edit, and indeed, I'm realizing the value I'm apparently getting back on the Master is a phantom. If I try incrementing the sent value from the slave, I see on the master that it doesn't change; d'oh!

I'll set the delay so it's after the write function and see what happens.

Docedison, I don't know who this 'we that use RS485' is, but to a non-engineer artist, let me tell you, the ground issue is not obvious at all! I'm a big fan of explicit :wink: But your point is well taken.

OK, the code below works, since the incremented values on the slave are being received by the master. I have to reset the slave to get the thing going, and oddly enough when I open Serial Monitor on the master the thing stops(!) But at least I know the data is coming in correctly. I also made sure the RE and GND pins from the slave MAX485 were connected to the master Arduino's GND. That tip on placing the delay after the write function was awesome! Thanks again guys.

Here's the code:

const int pinEnable = 2;
byte hello = 111;
byte inRead;

void setup()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
}
  
void loop() {
  digitalWrite(pinEnable, LOW);
  if (Serial.available() > 0) {
    inRead = Serial.read();
  if (inRead >= 112) {
    Serial.println(inRead, DEC);
    delay(500);
    digitalWrite(pinEnable, HIGH);
    Serial.write(hello);
    delay(10); // delay should be AFTER the write
    digitalWrite(pinEnable, LOW);
  }
}
}

SLAVE

const int pinEnable = 2;
byte helloBack = 112;
byte inRead;

void setup()
{
  pinMode(pinEnable,OUTPUT);
   Serial.begin(57600);
   digitalWrite(pinEnable, HIGH);
   Serial.write(112);
   delay(10);
   digitalWrite(pinEnable, LOW);
}
  
void loop() {
  if (Serial.available() > 0) {
  inRead = Serial.read();
  if (inRead == 111) {
    helloBack += 1;
    Serial.println(inRead, DEC);
    delay(500); // adjust as needed
    digitalWrite(pinEnable, HIGH);
    Serial.write(helloBack);
    delay(10); // give time for master to go LOW
    digitalWrite(pinEnable, LOW);
    }
}
}

inoukdemers:
oddly enough when I open Serial Monitor on the master the thing stops(!)

Starting Serial Monitor, like starting an Upload, resets the Arduino.

Good to know, thanks John.