Assistance with network problem please [solved]

Task: I am trying to build a system to control 5 servoes using Arduino Nano Every and Max485 for communication.

The system has been simulated in Tinkercad with Arduino Uno though and without Max485, and it works without any problem.

In real life the situation is very different.

  • With Max 485 connected I get only response to the first signal sent, after that none of the servoes respond. – I have included a picture which shows the signal as sent from the Arduino master, and the same signal as received at the Slave i.e. the signals are not the signals between the two Max 485 units but the signals between the Nano every’s.
    Picture of Signals:
    image
    Green is Arduino TX
    Blue is Arduino Rx.

  • Trying the system without Max 485 and using the TX/RX ports on the Nano every, I get random success, in that sometimes the servoes work but other times I get no response for a long time.

To isolate the issue to network only, I have made a dummy system, with two servoes only and without any buttons etc. and only the timer in the master controls when to run the servos.

The timer is set so they both run each 5 second, but with 2.5 second difference.

The diagram is below.

The code used to handle the communication is borrowed from: hackster.io, with the modification that I have added a line at the end to make sure, that the Rx buffer if fully cleard every time.

********************************
#define Btn1 2
#define Btn2 3
#define Btn3 4


int Btns[4] ={0,Btn1,Btn2,Btn3}; //array til aktivering af buttones

int btnState[4] = {0,0,0,0}; //array til buttonstate element "0" bruges ikke
int btnAction[4] = {0,0,0,0}; //array der indeholder seneste bevægelse ON/OFF

int x =0; //håndterer trykknapper er sat fast til 1 her
int y =0; //håndterer status på skiftespor åben eller lukket.

long Timecheck1 = 5000; //mSekunder = 1 minutter
long LastTime1 = 0;
long Timecheck2 = 5000; //mSekunder = 1 minutter
long LastTime2 = 0;
bool OK = false;
bool Run1 = false;
bool Continue = false;
bool Flag = false;

void setup()
{
Serial.begin(4800);
//Serial.setTimeout(250);

//sæt alle knapper til input
  
//pinMode(Btns[1],INPUT_PULLUP);
//pinMode(Btns[2],INPUT_PULLUP);

}

void loop()
{    
  	if ((millis()-LastTime1) >Timecheck1)
    {OK = HandleEvent(2); // Kører spor 2
     LastTime1 = millis();}
  	if(Flag == false && (millis())>2500){
  	LastTime2 = millis();
    Flag = true;}//Forskyder 2 2,5 sekund
  	if ((millis()-LastTime2) >Timecheck2)
     {OK = HandleEvent(3);//Kører spor 3
      LastTime2 = millis();}
    
}


// Begin new function - ikke i brug

bool TimerCount(int k)//Timeren kan benyttet til flere ting.
{int LastTrigger = 0;
	if(millis() - LastTrigger >= k){
	return true;}
}

bool HandleEvent(int k)//input = "i" modtagere til test.
  
{ 
   
  	const int x = 1;//konstant i denne udgave.
     int y = btnState[k];
  
		if (x==HIGH && btnState[k] == 0) {
		btnState[k] = 1;
        Serial.print("I"); //Forsøg kan nok slettes
        Serial.print(k);//Sender adresse
        Serial.print("O");//sender"O" for open
        Serial.print("F");//Slutkarakter
        
        }

		else if (x==HIGH && btnState[k] == 1) {
		btnState[k] = 0;
        Serial.print("I"); //Forsøg kan nok slettes
        Serial.print(k);//Sender adresse
        Serial.print("C");//sender "C" for lukket.
        //Serial.print("F");//Slutkarakter
        
        
        }  		
//  Serial.println(); 	//Debug linie
		Serial.flush();	
  		return true;
}
//End new function

**********************************

Code Slave (they are identical except for Slave number)

//Slave 2
#include<Servo.h>
        
//Fjernet alle delays
const int SlaveNumber = 2;
Servo servo1;  // create servo object to control a servo
int closed=75; //Skiftesport lukket Kører lige ud
int open=135;  //Skiftespor åben = drejer
        
void setup()
{
servo1.attach(9);  //n, min, max)
servo1.write(closed);  //Flyt servo til Spor lukket
  
Serial.begin(4800);
Serial.setTimeout(250);
pinMode(13, OUTPUT);
pinMode(A0, INPUT);
}
void loop()
{
	if(Serial.available()>0)
    {delay(20);
      	if(Serial.read()=='I')
        {int Slave = Serial.parseInt();
     		if(Slave == SlaveNumber)
			{char command = Serial.read();
             char slut = Serial.read();
     		if(command == 'O' && slut =='F')
        	{servo1.write(open);
        	}// Åben skiftespor
     			else if(command == 'C'){
     			servo1.write(closed);
       			delay(200);}
			}
		}
     if (Serial.available()>0){
       Serial.read();}//Tømmer serial	
  }
}
******************************

Any ideas what is going wrong and what I can do about it would be appreciated.

John

Hello John,

Welcome and thank you for asking a question that includes plenty of relevant information, including a schematic and code.

I am not familiar with the MAX485 so my answer might not be complete, but the obvious thing from your oscilloscope traces is that the receive out from the remote MAX485 is inverted. Have you got A and B reversed between the transmitting MAX485 and the one receiving?

What distance are you communicating over?

Hi Perry
Thanks for coming back so quickly.
As it happens I managed to attach the wrong picture. The picture I have enclosed is actually the Arduino Tx & the signal between the MAX 485.
Here is the picture showing Arduino Tx and Arduino Rx where you will see the signals are not inverted.
image

Sorry for the confusion.

The distance is approx. 10 meters.

John

Thank you.

OK, so the signals match. The spikes worry me...

I would think 10m between 2 serial ports would work without the MAX485, certainly worth trying a direct connection between the Tx and Rx of the 2 Nano Everys.

I'm not very good at reading other people's code but there are some things in there that I think would be a problem. The way you read the serial port is not going to work, for example do you realise that:

if(Serial.read()=='I')

Removes the character from the serial buffer, so it's gone? You can't access it again. Next time you do a read you will get the next character.

I don't think you understand how to do serial communications. There is nothing I can type here that would be better than this tutorial:

I suggest you follow that tutorial and put what you learn into your project. Start with 2 Nano Everys next to each other linked directly with Tx > Rx using a short wire. When that works to your satisfaction connect them over a longer cable, no MAX485, and see if that works. Only introduce the MAX485s if you demonstrate they are really needed.

The next question is why you think you even need multiple Nano Everys, rather than just one. I'm not seeing any good reason for doing so, and generally having more than one Arduino adds unnecessary complexity.

Hi Perry

  1. I have tried directly between the master and the slaves without Max485. It is better, but still very erratic and no clear response.
  2. I am aware that the 'I' is removed when reading, which is fine, as it is not used.
    The next - parseint - takes the slavenumber, after which I take the "command" which is open/close, followed by the "F" which is used to indicate End of message.

I then check that I have the Slavenumber plus the end of message, before, the event is being handled.
That part of the code is borrowed from Hackster.io, so not really something I came up with.

  1. Simulating the system without the Max 485 on Tinkercad works perfectly.

John

I can only repeat my advice to read the Serial input basics tutorial and learn how to do serial communications, then try with the Nano Everys next to each other.

That's all I have to offer at this point, maybe someone else can suggest something. I'll monitor this topic and if I see something new I can help with I'll comment.

You may also find something interesting here on RS485:

Not sure if this could be an issue but you are making timing assumptions here

you only test if there is at least 1 byte available to read and then - after a wait for 20ms - if that byte is 'I' then you assume everything else has arrived

I second the recommended read for Serial Input Basics

Hi J-M-L
Thanks for you input.
And just to confirm that I have already spent several hours on Serial input Bacis and find nothing really in my code that violates anything there.
As to the delay - this is just at new line I will be trying out to ensure that everything sent (4-characters) has actually arrived. It has not been testet yet.
20 ms is on the high side, and most likely the lines not necessary at all, since I already test the beginning as well as the end of the message

John

You have implied that your problem is with the MAX485 network. Why not start with a much simpler sketch pair. Say one that sends a character every second and another which prints what it receives ?
Use software serial for the MAX485 connection to allow you to use the default serial for debug printing.

say I'm sending I2 and pause for 300 ms then send more data.

Your code will read 'I' so will proceed to parse an int, will get the 2 and timeout, so will return 2. You'll compare 2 with the SlaveNumber, say it's matching, then you proceed to read the command and slut (whatever that is ... :slight_smile: ) bytes which are not there yet and so they will be set at 0xFF

also

is just removing 1 byte, what if you have many ? (like 2 — \r and \n from the command line)

this might not be an issue if you send manually through the serial monitor a full line, but it's not robust

Hi 6v6gt
Thanks for your suggestion.
Since this is an erratic error, which means that it actually works perhaps 5 out of 6-8 times, after which it stalls for say 30 - 40 seconds or more, I don't really believe it is a problem with what is sent or received on the Rx input of the nano every but more what the every does or does not do.
I have a 2 minute recording of sent and received - and there are absolutely nothing lost during those two minutes, but for some reason the every ignoes some of the messages, which is why i
end the loop by making sure that there is no left-over in the buffer before next message.

I think actually my next step will be to try to borrow 3 arduino Uno's, since that is the one which is used in the tinkercad simulation and with absolutely no issues.

John

log every character received. then you'll see if there is an issue.

without start and or end marker, it's easy to get desynchronised and really hard to get in sync again

OK. But I'd still recommend using SoftwareSerial for the MAX485 connection so you can more easily debug the code.

Hi J-M-L

I take you point regarding the timeout. I don't know if that could be part of the problem, since I use Serial.flush(), andre is 5 seconds between each send.
"Slut" is "end" in danish and test that the end character is "F" and if not it does not respond.

And yes if I still want the line at the end if (Serial.....) then it should be changed to while. In principle. It is there if anything should obscure the message, which could led to the action never to happen if there are leftovers or "dirt" in the message

6v6gt

Thanks - I will have a look and give it at go :slight_smile:

John

when you send your data

in case of 'I' you have an int followed by OF or followed by 'C' and nothing

but on the receiving side you have

        char command = Serial.read();
        char slut = Serial.read();

➜ you always try to read two bytes after the I and the slave number when only one might be available

if you are unlucky and the next command has been fired, you will get the first byte of the next command

This is telling you not to look at the RS485. Get basic serial working before you introduce RS485.
As others have said, you're not reading the Serial communication robustly, and so your synchronization is failing, leading to long delays. Those long delays come from Serial.parseInt(), which has a 1second timeout that is being invoked sometimes(and, in that case, it returns zero, which can really confuse the issue). You can test this by simply changing the timeout then observing if it changes the behavior, though it could be time consuming to do so due to the erratic nature of the problem. I can't see exactly where you've gone wrong in your code, but I'll back the advice of the others and suggest you dig deeply into it. Something is off.

try much simpler code with 'I' as start marker and '$' as end marker and the slave ID on one char

the master

void setup() {
  Serial.begin(4800);
}

void loop() {
  Serial.write("I2O$");         // t0
  Serial.flush(); delay(2500);
  Serial.write("I3O$");         // t0 + 2500
  Serial.flush(); delay(2500);
  Serial.write("I2C$");         // t0 + 5000
  Serial.flush(); delay(2500);
  Serial.write("I3C$");         // t0 + 7500
  Serial.flush(); delay(2500);
                                // t0 + 10000
}

the slaves (change slave ID - make sure you use a character and not a number)

#include<Servo.h>
const byte servoPin = 9;
Servo servo;
const int closedAngle = 75;
const int openAngle = 135;

const char slaveID = '2'; // or '3' make sure it's a char not a number so use simple quotes

enum {START, SLAVEID, COMMMAND, END} state = START;

void setup() {
  servo.write(closedAngle);
  servo.attach(servoPin);
  Serial.begin(4800);
}

void loop() {
  static char commandReceived = '\0';

  if (Serial.available() != 0) {
    char r = Serial.read();
    switch (state) {

      case START:
        if ('I' == r) {
          commandReceived = '\0';
          state = SLAVEID;
        }
        break;

      case SLAVEID:
        if (r ==  slaveID) state = COMMMAND; else state = START;
        break;

      case COMMMAND:
        commandReceived = r;
        state = END;
        break;

      case END:
        if ('$' == r) {
          switch (commandReceived) {
            case 'O': servo.write(openAngle); break;
            case 'C': servo.write(closedAngle); break;
            default: break;
          }
        }
        state = START;
        break;
    }
  }
}

typed here, fully untested

Hi, Here is a lastest update.

J-M-L I have not testet your code yet, but it will come.

I have however changed the line to clear the buffer from an if statement to a while.
I still do not use any delay for reveiving.

The result has been encouraging in that I now get 8 consecutive shifts form each turnout without any problems, indicates that the code so far is working.

The 8 consecutive shifts = 16 times send/receive = 64 characters. If I am not wrong that is the size of the buffer of the nano every. Therefore I begin to believe this may be where the problem is. For some reason I may not get the buffer cleared properly.
John