[Solved] Arduino with XBee Wifi communication problems

Hello,

I have a question. I have an Arduino UNO R3 with a XBee Wifi board. In between there is a logic level converter to convert the 5v communication of arduino to 3.3v that xbee uses.
Now i can make a connection with my pc and send and receive data. But the problems start appearing if i send more then 2 bytes at once from my pc to arduino. The serial.read either reads it in the wrong order but mostly i lose connection for half a second and then regain it. But on the other side i can easily send 12 bytes from arduino without problems.

The method i use to read the incoming data on arduino is.

if (Serial.available() > 0) {
          // read the incoming byte:
            Pressure = Serial.read();
            Mode = Serial.read();
            lastReceiveTime = millis();
        }

Though this works fine if i add more variables the problems start.
My Pc sends an array of bytes to Xbee all at once.

I send data from my arduino with the following:

Serial.print(bytearray[0]);
       Serial.print(bytearray[1]);
       Serial.print(bytearray[2]);
       Serial.print(bytearray[3]);
       Serial.print(bytearray[4]);
       Serial.print(bytearray[5]);   
       Serial.print(bytearray[6]);
       Serial.print(bytearray[7]);
       Serial.print(bytearray[8]);
       Serial.print(bytearray[9]);
       Serial.print(digitalRead(Pin));
       Serial.print(char(Crc_Lo));
       Serial.println(char(Crc_Hi));

If anyone is using Arduino with Xbee wifi and has resolved this problem i would appreciate any help. Even if you have a suggestion what i might try i would be really glad with it.

My pc is making the connection to XBee and is using tcp port 10000.

Serial.print(bytearray[0]);
       Serial.print(bytearray[1]);
       Serial.print(bytearray[2]);
       Serial.print(bytearray[3]);
       Serial.print(bytearray[4]);
       Serial.print(bytearray[5]);   
       Serial.print(bytearray[6]);
       Serial.print(bytearray[7]);
       Serial.print(bytearray[8]);
       Serial.print(bytearray[9]);

You've never heard of for loops?

Posting all of your code will be necessary.

Here is my entire code. I apologize if it looks a little bad.
I did add the loops but it did not help though it looks alot better.

//Import library
#include <ST7036.h>
#include <Wire.h>
#include <Keypad.h>

const byte ROWS = 5; //four rows
const byte COLS = 2; //four columns
char hexaKeys[ROWS][COLS] = {
  {'A', 'B'}, //Menu Links en Rechts
  {'C', 'D'}, //Druk Neer en Op
  {'E', 'F'}, //Waterjet Aan en Uit
  {'G', 'H'}, //Niets en Haspel op
  {'I', 'J'}, //Arduino uit en Injector aan
};

byte rowPins[ROWS] = {9, 8, 6, 5, 3}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, A2}; //connect to the column pinouts of the keypad
byte bytearray[9] ={0,0,0,0,0,0,0,0,0};
Keypad keypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 
String regel1, regel2, Regeldruk, Regelsignaal;
byte Druk = 0;
byte Modus = 0;
byte DrukStand = 0;
byte Checksum = 0;
byte ChecksumHi = 0;
byte ChecksumLo = 0;
byte Hulpbyte = 0;
byte Hulpbyte2 = 0;
byte Hulpbyte3 = 0;
byte Hulpbyte4 = 0;
byte Crc_Lo = 0;
byte Crc_Hi = 0;
int Uitkomst = 0;
unsigned long lastReceiveTime = 0;     // last time you connected to the server, in milliseconds
long SendInterval = 200;
long LastSend = 0;
long TimerPulseren = 0;
const unsigned long PulseInterval = 1000;
const unsigned long requestInterval = 3000;    // delay between requests in ms; 3 seconds
const int NoodstopPin = A3;     // the number of the NoodstopPin pin

boolean SwitchedOn = false;
boolean ToggleSwitch = false;
boolean DrukTrigger = false;
boolean ModusWaterjet = false;
boolean PulseTrigger = false;
String Noodstopregel = "Noodstop        ";

ST7036 lcd = ST7036 ( 2, 16, 0x7C ); 

void setup(){
  //Initialize LCD
  lcd.init();
  lcd.setCursor(0, 0);
  lcd.print("AquaPowerCleaner");
  lcd.setCursor(1, 0);
  lcd.print("Version: 0.3    ");
  
  //Regels voor LCD
  regel1 = String("Druk:");
  regel2 = String("Modus:");
  
  //Noodstopknop init
  pinMode(NoodstopPin, INPUT);
  digitalWrite(NoodstopPin, HIGH);
  
  //Initialize serial
  Serial.begin(9600);
 
  //Add key events
  keypad.addEventListener(keypadEvent); //add an event listener for this keypad 
  }

void loop() {
  //Check the keys pressed
  char key = keypad.getKey();
  if ( digitalRead(NoodstopPin) == HIGH ){
    lcd.setCursor(0, 0);
    lcd.print(Noodstopregel);
    
      if ( LastSend < (millis() - SendInterval) ) {
       LastSend = millis();
       for(int x = 0; x < 10; x++){
         Serial.print(0);
       }
       Serial.print(digitalRead(NoodstopPin));
      }
  }
  else if ( SwitchedOn == false ) {
     lcd.setCursor(0, 0);
     lcd.print("                ");
     lcd.setCursor(1, 0);
     lcd.print("                ");
   }
  else{
     if ( LastSend < (millis() - SendInterval) ) {
       LastSend = millis();
       //Sending the keys to PLC
       Uitkomst = (bytearray[0]+bytearray[1]+bytearray[2]+bytearray[3]+bytearray[4]+bytearray[5]+bytearray[6]+bytearray[7]+bytearray[8]+bytearray[9]+byte(digitalRead(NoodstopPin))) % 255;
       Hulpbyte = byte(Uitkomst);
       Hulpbyte2 = Hulpbyte && 15;
       Crc_Lo = Hulpbyte + 48;
       Hulpbyte3 = char(Hulpbyte) >> 4;
       Hulpbyte4 = Hulpbyte3 && 15;
       Crc_Hi = Hulpbyte4 + 48;
       if ( bytearray[5] == true ) {
         PulseTrigger = true;
       }
       else{
         TimerPulseren = millis();
         PulseTrigger = false;
       }    
       for(int x = 0; x < 10; x++){
         Serial.print(bytearray[x]);
       }
       Serial.print(digitalRead(NoodstopPin));
       Serial.print(char(Crc_Lo));
       Serial.println(char(Crc_Hi));
       
       if (Modus >= 4 && Modus < 15){
         ModusWaterjet = true;
       }
       else{
         ModusWaterjet = false;
       }

        if (Serial.available() > 0) {
          // read the incoming byte:
            Druk = Serial.read();
            Modus = Serial.read();
            //DrukStand = Serial.read();
            lastReceiveTime = millis();
        }
        if (millis() - lastReceiveTime > requestInterval) {
         lcd.setCursor(0, 0);
         lcd.print("                ");
         lcd.setCursor(1, 0);
         lcd.print("Disconnected    ");
        }
        else{
         LCDPrint();            
        }
     }
  }
   if (bytearray[8] == 1) {
      if (ToggleSwitch == false) {
        if (SwitchedOn == true) {
            SwitchedOn = false;
        }
        else if (SwitchedOn == false) {
            SwitchedOn = true;
        }
      }
      ToggleSwitch = true;
    }
   else if (bytearray[8] == 0) {
     ToggleSwitch = false;
   }
}

void LCDPrint() {
  if (Modus == 1){
    Regelsignaal = regel2 + " Neutraal  ";
    DrukStand = 0;
  }
  else if (Modus == 2){
    Regelsignaal = regel2 + " Generator";
    DrukStand = 0;
  }
  else if (Modus == 3){
    Regelsignaal = regel2 + " Injector";
    DrukStand = 0;
  }
  else if (Modus == 4){
    Regelsignaal = regel2 + " Water    ";
    DrukStand = 1;
  }
  else if (Modus > 4 && Modus < 15){
    Regelsignaal = regel2 + " Water    ";
    DrukStand = Modus - 4;
  }
  else if (Modus == 15){
    Regelsignaal = regel2 + " Noodstop ";
    DrukStand = 0;
  }
  else if (Modus == 16){
    Regelsignaal = regel2 + " LaagWater";
    DrukStand = 0;
  }
  else if (Modus == 17){
    Regelsignaal = regel2 + " HoogWater";
    DrukStand = 0;
  }
  else if (Modus == 18){
    Regelsignaal = regel2 + " DrukLaag ";
    DrukStand = 0;
  }
  else if (Modus == 19){
    Regelsignaal = regel2 + " DrukHoog ";
    DrukStand = 0;
  }
  else if (Modus == 20){
    Regelsignaal = regel2 + " MotorFout";
    DrukStand = 0;
  }
  else if (Modus == 21){
    Regelsignaal = regel2 + " Rust     ";
    DrukStand = 0;
 }
  else if (Modus == 22){
    Regelsignaal = regel2 + " MotorStop";
    DrukStand = 0;
  }
 else {
    Regelsignaal = regel2 + " Init.    ";
    DrukStand = 0;
 }
  Regeldruk = regel1 + Druk;
  lcd.setCursor(0, 0);
  if ( TimerPulseren < (millis() - PulseInterval) ){
    lcd.print(Regeldruk + " Pulseren   ");
  }
  else{
    lcd.print(Regeldruk + " Speed:" + DrukStand + "  ");
  }
  lcd.setCursor(1, 0);
  lcd.print(Regelsignaal);  
}

void keypadEvent(KeypadEvent key){
  if (digitalRead(NoodstopPin) == LOW){  
    switch (keypad.getState()){
        case HOLD:
          switch (key){
            case 'A': bytearray[0] = 1; break; //KnoppenArray[0] MenuOp
            case 'B': bytearray[1] = 1; break; //KnoppenArray[1] MenuNeer
            case 'C': bytearray[2] = 1; break; //KnoppenArray[2] DrukOp
            case 'D': bytearray[3] = 1; break; //KnoppenArray[3] DrukNeer
            case 'E': bytearray[4] = 1; break; //KnoppenArray[4] WaterJetAan
            case 'F': bytearray[5] = 1; break; //KnoppenArray[5] WaterJetUit
            case 'G': bytearray[6] = 1; break; //KnoppenArray[6] HaspelAan/Uit
            case 'H': bytearray[7] = 1; break; //KnoppenArray[7] 
            case 'I': bytearray[8] = 1; break; //KnoppenArray[8] VullenAan/Uit
            case 'J': bytearray[9] = 1; break; //KnoppenArray[9] LCD scherm uit
          }
        break;
        case RELEASED:
          switch (key){
            case 'A': bytearray[0] = 0; break;
            case 'B': bytearray[1] = 0; break;
            case 'C': bytearray[2] = 0; break;
            case 'D': bytearray[3] = 0; break;
            case 'E': bytearray[4] = 0; break;
            case 'F': bytearray[5] = 0; break;
            case 'G': bytearray[6] = 0; break;
            case 'H': bytearray[7] = 0; break;
            case 'I': bytearray[8] = 0; break;
            case 'J': bytearray[9] = 0; break;
          }
        break;
      }
  }
  else{
    bytearray[0] = 0;
    bytearray[1] = 0;
    bytearray[2] = 0;
    bytearray[3] = 0;
    bytearray[4] = 0;
    bytearray[5] = 0;
    bytearray[6] = 0;
    bytearray[7] = 0;
    bytearray[8] = 0;
    bytearray[9] = 0;
  }
}
void LCDPrint() {
  if (Modus == 1){
    Regelsignaal = regel2 + " Neutraal  ";
    DrukStand = 0;
  }
  else if (Modus == 2){

This function really needs to be using a switch statement, with cases.

void loop() {
  //Check the keys pressed
  char key = keypad.getKey();

Why are you reading a key here? You've registered a callback to be called when a key is pressed. You don't need to test it here, especially since you never use this value.

       Uitkomst = (bytearray[0]+bytearray[1]+bytearray[2]+bytearray[3]+bytearray[4]+bytearray[5]+bytearray[6]+bytearray[7]+bytearray[8]+bytearray[9]+byte(digitalRead(NoodstopPin))) % 255;

Another place where a for loop would be good...

byte bytearray[9] ={0,0,0,0,0,0,0,0,0};

The indices range from 0 to 8. Reading bytearray[9] is reading beyond the end of the array.

        if (Serial.available() > 0) {
          // read the incoming byte:
            Druk = Serial.read();
            Modus = Serial.read();
            //DrukStand = Serial.read();
            lastReceiveTime = millis();
        }

If there is one byte available, don't try to read more than one. This seems like a strange place (time) to be reading serial data.

PaulS:

void LCDPrint() {

if (Modus == 1){
    Regelsignaal = regel2 + " Neutraal  ";
    DrukStand = 0;
  }
  else if (Modus == 2){



This function really needs to be using a switch statement, with cases.

I have added this thank you.

PaulS:

void loop() {

//Check the keys pressed
  char key = keypad.getKey();



Why are you reading a key here? You've registered a callback to be called when a key is pressed. You don't need to test it here, especially since you never use this value.

I think this is a leftover from making the matrix keyboard work. I have removed it. Thank you.

PaulS:

       Uitkomst = (bytearray[0]+bytearray[1]+bytearray[2]+bytearray[3]+bytearray[4]+bytearray[5]+bytearray[6]+bytearray[7]+bytearray[8]+bytearray[9]+byte(digitalRead(NoodstopPin))) % 255;

Another place where a for loop would be good...

Loop added.

PaulS:

byte bytearray[9] ={0,0,0,0,0,0,0,0,0};

The indices range from 0 to 8. Reading bytearray[9] is reading beyond the end of the array.

The array was 12 bytes when i was testing but i figured i only needed 9. So i changed it before i posted here and missed bytearray[9]. I have added it.

PaulS:

        if (Serial.available() > 0) {

// read the incoming byte:
            Druk = Serial.read();
            Modus = Serial.read();
            //DrukStand = Serial.read();
            lastReceiveTime = millis();
        }



If there is one byte available, don't try to read more than one. This seems like a strange place (time) to be reading serial data.

I'd like to change this into a better way to read date, but i need to read the proper bytes to the variables. My pc atm sends 3 bytes but they do not always come in the proper place and i really need to read more then that. If i change it into what you say will it read into the proper variables.

Another way i just thought of, is that my pc sends one byte at a time and have a counter in arduino reading the data one by one. And the pc sends a byte of 255 to reset the counter (PC will never send a byte this high in normal operation)

example:

if (Serial.available() > 0) {
  Counter = Counter + 1;
  if (Serial.Peek == 255){
     Counter = 0;
  }
  switch (Counter ) {
     case 1: Druk = Serial.read(); break;
     case 2: Modus = Serial.read(); break;
     etc....
  }
}

I'd like to change this into a better way to read date, but i need to read the proper bytes to the variables. My pc atm sends 3 bytes but they do not always come in the proper place and i really need to read more then that. If i change it into what you say will it read into the proper variables.

The best way to send serial data is with start and end markers, and in ASCII format. That way, you can ensure that the start and end markers are not mistaken for valid data.

How often is serial data sent? Can you afford the overhead of sending ASCII data instead of binary data? The number of bytes sent goes up, making the process take longer, and the storing, parsing, and converting to ints takes time. But, if time isn't critical, ASCII is a better way to go.

The data is sent every second. The data from pc to arduino is not that time bound, so a second should be ok. But the data from arduino to pc needs to be a bit quicker aprox. half a second.

From what i understand the serial start and end markers are ‘<’ and ‘>’. Am i correct to understand that in the stream of bytes i receive there should be a ‘<’ and a ‘>’ and in between those the data i need is stored? And in sending data over serial should i send the markers as well or will Arduino do that for me?

ASCII data should be fine i think, i just need to see on my pc which button is pressed. Whether it is in ASCII or binary, as long as i can read that i am fine. It is just my PC only receives bytes, so i need to check which value represents a ‘<’ and a ‘>’.

I am currently not able to test anything until tomorrow.

From what i understand the serial start and end markers are ‘<’ and ‘>’.

There are no defined start and end markers. They can be whatever makes sense for you.

Am i correct to understand that in the stream of bytes i receive there should be a ‘<’ and a ‘>’

Not unless the sender puts them there.

and in between those the data i need is stored?

That’s the usual way.

And in sending data over serial should i send the markers as well or will Arduino do that for me?

The sending application needs to add them. If the Arduino could, there would be no reason to have them.

It is just my PC only receives bytes, so i need to check which value represents a ‘<’ and a ‘>’.

Characters are bytes. This comment doesn’t make a lot of sense.

PaulS:

It is just my PC only receives bytes, so i need to check which value represents a ‘<’ and a ‘>’.

Characters are bytes. This comment doesn’t make a lot of sense.

Sorry it was late for me and wasn’t thinking clearly anymore. I have changed some code and it does seem to work better now.

One odd thing i found was that if i send 3 bytes from my pc the XBee will close the connection. But if i send 6 bytes all is well. The idea of start and end markers was really helpful. Thank you.

I am using values for the start and end markers that my system would never be able to send in normal operation.

Alright i am close now. When i first make a connection between arduino and my pc everything goes smoothly and i can transfer data without problems. But once i close the connection from my pc and try to open it again without restarting my arduino i am getting issues. The connection is made without problems and arduino is able to send its data to my pc but the other way around is not working.

This is part of the code that listens to data from XBee

  if ( Serial.available() == 6){
    Serial.readBytes(ReceivedArray, 6);
    lastReceiveTime = millis();
  }

  //Check array for valid data
  if ( ReceivedArray[0] == char(255) && ReceivedArray[5] == char(250) ) {
      Druk = char(ReceivedArray[1]);
      Modus = char(ReceivedArray[2]);
      Afstand = char(ReceivedArray[3]);
      Waterniveau = char(ReceivedArray[4]);
  }

Now i suspect that at the moment i close the connection, data is being sent and once i make the connection again that data is still waiting on the arduino. So i think that the buffer is being filled without arduino being able to read it because the of the ( Serial.available() == 6) i wrote. But i am currently drawing a blank on how to make it work.

I tried clearing the stream once it goes over 6 bytes but if i do that the data is never being sent.

Btw. the data from my pc is being sent once it receives data from arduino.

tyvm so far :slight_smile:

Serial data delivery is not guaranteed. Waiting until there are 6 bytes to read, and then assuming that they are the correct 6 bytes that make up a packet, feels wrong. Using start and end of packet markers and ASCII data between them will probably solve your problems.

At a minimum, you should be testing for >=6, not exactly equal 6.

Sorry for the long wait but i got it working now.
I am checking if there are 6 or more bytes available and then use serial.find to locate my start marker and then read the 5 bytes after it. If the last byte is my end marker i read the data in between.

  if ( Serial.available() >= 6){
    if ( Serial.find( "~" ) ) {
      Serial.readBytes(ReadArray, 6);
     if ( ReadArray[5] == char(250) ){
       lastReceiveTime = millis();
       Druk = ReadArray[0];
       Modus = ReadArray[1];
       Afstand = ReadArray[2];
       Waterniveau = ReadArray[3];
       Brandstofniveau = ReadArray[4]; 
     }
    }
  }

This seems to work very well and no matter how often i break the connection, it works after i make the connection again. And i have not get any faulty connection yet.

Thank you very much PaulS for your help.
I know i got alot to learn about programming regarding structure and using better functions like loop instead of what i did. But we all got to start somewhere :slight_smile: and i am getting better at it.

  if ( Serial.available() >= 6){
    if ( Serial.find( "~" ) ) {
      Serial.readBytes(ReadArray, 6);

Suppose that the serial buffer contains "12345~". What will be in ReadArray when Serial.readBytes() returns? What value will Serial.readBytes() have returned, that you have discarded?

I'm sorry, but this is still not good code. You need to peak at the buffer. If the first character is not ~, read and discard it. If it is, you need a time-wasting while loop that waits for there to be at least 6 characters. Then, you can read all 6 of them.

Sending ASCII data with start and end of packet markers is much more robust, and far easier to implement.

You need to peak at the buffer.

"peek" would be better.

"peek" would be better.

You try typing with carpal tunnel. It's a miracle that I can get as many words spelled right as I do. :roll_eyes: