Receiving XBee acknowledgements

Hi, I am trying to get acknowledgement packets from my XBee End-device on whether my sent frame was received successfully, if not, the same frame will be retransmitted. However, when the XBees are not on the same network, frames will fail to transmit, and the End-device will not continue sending the frames it has missed. Also, when the reset button is pressed, a few frames are being skipped after it is rebooted.
For example, frames 1, 2, 3 are sent successfully, then on frame 4, the devices lose connection (nwk ack failure, not joined to network) / reset button is pressed, the code continues to run (frame 5, 6…), and when the devices are back on the same network / reboot completed, frames will continue to be sent from lets say frame 8.
How can I make the device send the frame (say frame 4, according to above example) from when frames fail to transmit and when the reset button is pressed?

I am reading the reset button state, but the XBee3 document (page 24) states that the reset pin is an input pin. I am getting correct button states, but after the button is pressed, it will toggle between 0 and 1 randomly, so now I am not sure if it is correct. Any help is greatly appreciated :drooling_face:

This is my code:

byte basicFrame[273] = {0x7E, 0x01, 0x0D, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

byte rfData[48] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
                   0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};

byte lastFrame[184] = {0x7E, 0x00, 0xB4, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x98};

int wakePin = 2;                                    //xbee pin 10 (sleep) to arduino pin 2
int resetPin = 4;                                   //xbee pin 6 (reset)
int resetState;                                     //state of reset button (active low reset)

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(resetPin, INPUT);
  pinMode(wakePin, OUTPUT);
  delay(300);
  Serial.println("Setup Complete");                               //debug
}

void loop()
{
  digitalWrite(wakePin, LOW);

  for (int i = 0 ; i < 48 ; i ++)                                 //frames 1-48
  {
    basicFrame[17] = rfData[i];                                   //increment header from 1-48
    //Serial.print(basicFrame[17], HEX);

    for (int x = 19 ; x < 272 ; x ++)                             //rf data [19]-[272]
    {
      basicFrame[x] = rfData[i];                                  //Serial.println(basicFrame[x], HEX);
    }

    byte s = 0;                                                   //checksum frame 1(A7)-frame 48(35)
    s += rfData[i];
    long chexsum = 0x10 + 0x01 + 0x49 + (s * 254);
    byte checksum = 0xFF - (chexsum & 0xFF);                      //keep last 8 bits, subtract from 0xFF
    basicFrame[272] = checksum;                                   //replace checksums from frame 1-48
                                                                  //Serial.println(checksum, HEX);
                                                              
                                                                  //for (int y = 0 ; y < 273 ; y ++)
                                                                  //{
                                                                  //  Serial.print(basicFrame[y], HEX);
                                                                  //}
                                                                  //Serial.println();

    resetState = digitalRead(resetPin);                           //read state of reset button
    Serial.println(resetState);
    delay(50);

    Serial1.write(basicFrame, sizeof(basicFrame));                //send frames 1-48
    delay(50);                                                    //for stability
    
    //acknowledgement coding:
    if (resetState == 1)                                          //if button not pressed (11 bytes)
    {
      if (Serial1.available() >= 11)
      {
        for (int d = 0 ; d < 8 ; d ++)                            //skip to read 9th byte
        {
          byte discard = Serial1.read();
        }

        byte deliveryStat = Serial1.read();                       //9th byte
        byte junk1 = Serial1.read();                              //read remaining 2 bytes to prevent errors
        byte junk2 = Serial1.read();

        if (deliveryStat == 0x00)                                 //success frame: 7E 00 07 8B 01 00 00 00 00 00 73
        {
          Serial.println("Success, Sending next frame... ");      //move on to send next frame
          delay(500);
        }
        else if (deliveryStat == 0x21)                            //failure frame: 7E 00 07 8B 01 00 00 00 21 00 52
        {
          Serial.println("Failure, Retransmitting...");
          Serial1.write(basicFrame, sizeof(basicFrame));          //re-transmit same frame
          delay(500);
        }
        else if (deliveryStat == 0x22)                            //wait for end device to be in nwk
        {
          Serial.println("Not joined to network");
          delay(300);
        }
      }
    }
    else if (resetState == 0)                                     //button pressed (6 bytes)
    {                                                             //reset frame: 7E 00 02 8A 00 75
      Serial.println("Hardware reset");
      Serial.println("Device associated, sending frames");        //approx 1-5 secs to send packets
      Serial.println("Please wait...");
      delay(300);
      Serial1.write(basicFrame, sizeof(basicFrame));              //send frame again
      delay(500);
    }
    delay(20);
  }

  Serial1.write(lastFrame, sizeof(lastFrame));                    //send frame 49

  while(1)
  {}
}

You are attempting to send data while the Xbee is still in reset or going through its boot process.

Generally speaking you only need to reset a device shortly after power up, or if you suspect it has frozen.

I want to stop the data transmission when XBee is under reboot, and then continue transmitting from the frame where it stopped, how do I do that?

Another way is to read the acknowledgements received which can be 11 bytes or 6 bytes (when reset pressed). However, I am facing problems with reading them due to the difference in length. I have tried to put the read values into an array then determine the acknowledgements by reading the bytes, but when the 6 bytes are stored into the array, there will be "blank spaces" behind, how do I clear them?

As seen from XCTU, these are a few acknowledgement frames I will get:
Success (11 bytes): 7E 00 07 8B 01 00 00 00 00 00 73
Failure (11 bytes): 7E 00 07 8B 01 00 00 00 21 00 52
Reset (6 bytes): 7E 00 02 8A 00 75
Modem status (6 bytes): 7E 00 02 8A 02 73

The modem status frame will appear twice after the reset frame, not sure why.

Any help is appreciated, thank you.

Having the Arduino react to an Xbee dev board 'manual button press reset' makes no sense.
The Arduino is driving the communication process and therefore it should be in control of the Xbee reset line.

If you need a manual press button control for testing purposes then connect it to an Arduino input pin. You'll need to write Arduino code that monitors the button and then uses another Arduino pin to pull the Xbee reset pin low for 1 ms (via a level shifter), and then waits for the Xbee to boot.

If the documentation doesn't give a precise value for the Xbee boot time then wait 500 ms, clear the Arduino Rx buffer and try talking to the Xbee. If you get a valid response then carry on sending data or whatever else it needs to do.

If you have a problem with Xbee communication periodically failing for whatever reason then you need to keep track of which packets have been successfully sent and which have failed and therefore need to be resent.

Thank you for your explanation. I have changed my code:

byte basicFrame[273] = {0x7E, 0x01, 0x0D, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

byte rfData[48] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
                   0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};

byte lastFrame[184] = {0x7E, 0x00, 0xB4, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 
                       0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x98};
                       
const byte numBytes = 11;
byte receivedAck[numBytes];
byte numRecvd = 0;
boolean newAck = false;

int wakePin = 2;                                      //xbee pin 10 (sleep)
//int resetPin = 4;                                   //xbee pin 6 (reset)
//int resetState;                                     //state of reset button (active low reset)

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  //pinMode(resetPin, INPUT);
  pinMode(wakePin, OUTPUT);
  digitalWrite(wakePin, LOW);
  delay(300);
  Serial.println("Setup Complete");                               //debug
}

void loop()
{
  for (int i = 0 ; i < 48 ; i ++)                                 //frames 1-48
  {
    basicFrame[17] = rfData[i];                                   //increment header from 1-48
                                                                  //Serial.print(basicFrame[17], HEX);

    for (int x = 19 ; x < 272 ; x ++)                             //rf data [19]-[272]
    {
      basicFrame[x] = rfData[i];                                  //Serial.println(basicFrame[x], HEX);
    }

    byte s = 0;                                                   //checksum frame 1(A7)-frame 48(35)
    s += rfData[i];
    long chexsum = 0x10 + 0x01 + 0x49 + (s * 254);                
    byte checksum = 0xFF - (chexsum & 0xFF);                      //keep last 8 bits, subtract from 0xFF
    basicFrame[272] = checksum;                                   //replace checksums from frame 1-48
                                                                  //Serial.println(checksum, HEX);
    Serial1.write(basicFrame, sizeof(basicFrame));                //send frames 1-48
    delay(200);                                                   //for stability

    recvAck();
    showNewData();
    checkAck();
    delay(300);
  }
  delay(100);

  Serial1.write(lastFrame, sizeof(lastFrame));           //send frame 49
  checkAck();
  
  while (1)
  {}
}

void recvAck() 
{
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '~';                               // ascii for 7E
  char endMarker = '~';                                 
  byte rb;                                              //read bytes

  while (Serial1.available() > 0 && newAck == false)    //more than 1 char and no new data
  {
    rb = Serial1.read();

    if (recvInProgress == true)
    {
      if (rb != endMarker)                              // check if rched end
      {
        receivedAck[ndx] = rb;  
        ndx++;
        if (ndx >= numBytes) 
        {
          ndx = numBytes - 1;
        }
       }
       else                                             // if rched end
       {
         receivedAck[ndx] = '\0';                       // terminate the string
         recvInProgress = false;
         numRecvd = ndx;                                // save number for use when printing
         ndx = 0;
         newAck = true;
       }
    }
    else if (rb == startMarker)                         // recv in prog true, reading from start byte 7E
    {
      recvInProgress = true;
    }
  }
  Serial.println();
}

void showNewData() 
{
  if (newAck == true) 
  {
    Serial.print("This just in (HEX)... ");
    for (byte i = 0; i < numRecvd; i ++)                //print out acks received
    {
      Serial.print(receivedAck[i], HEX);
      Serial.print(' ');
    }
    Serial.println();
    newAck = false;
  }
}

void checkAck()
{
  if (receivedAck[3] == 0x00)                                 // 7E 00 02 8A 00 75
  {
    Serial.println("Device is being reset");
    //Serial1.write(basicFrame, sizeof(basicFrame));          //send same frame after reboot
  }
  else if (receivedAck[3] == 0x02)                            // 7E 00 02 8A 02 73
  {
    Serial.println("Device Associated");
  }
  else if (receivedAck[7] == 0x00)                            // 7E 00 07 8B 01 00 00 00 00 00 73
  {
    Serial.println("Success");
  }
  else if (receivedAck[7] == 0x21)                            // 7E 00 07 8B 01 00 00 00 21 00 52
  {
    Serial.println("Network Ack Failure");
  }
  else if (receivedAck[7] == 0x22)                            // 7E 00 07 8B 01 00 00 00 22 00 51
  {
    Serial.println("Device is not joined to network");
  }
}

If the documentation doesn’t give a precise value for the Xbee boot time then wait 500 ms

The boot time is quite random as well, ranging from 3 seconds to 7 seconds.

and then waits for the Xbee to boot

I tried adding a delay, but it the code still seems to be running, so when the XBee is done rebooting, it has already missed out on the previous frames. How can I go about solving this issue?

Thank you.

Is the Xbee continuously powered?

Connect an oscilloscope or LED/resistor to the Associate pin.
https://www.digi.com/resources/documentation/Digidocs/90000991/reference/r_associate_led.htm?TocPath=Work%20with%20networked%20devices|Test%20links%20between%20adjacent%20devices|_____6

Does it change while the sketch is running?

mikb55:
Is the Xbee continuously powered?

Yes, it is. I have attached a diagram that shows the connections between the XBee3 (End-device) and Arduino Mega2560, through a voltage translator, with the Arduino connected to the pc. The other XBee3 (Coordinator) is connected to the pc too.

I have an associate LED on my development board, with a SMT XBee3. The LED blinks faster than the Coordinator's LED, so I assume a fast blink according to the link you provided. When reset button is pressed, all LEDs are off until the button is released.

Here's my XBee configurations:

Parameter Coordinator End-device
ID 2015 2015
JV - Enabled [1]
CE Enabled [1] -
NI COORD END-DEVICE
AP API Mode Without Escapes [1] API Mode Without Escapes [1]
SM - Pin Hibernate [1]
SP - 1F4

(-) keep default value

Refering to the attached "XBee Pin Locations":

  • Pin 1 – GND
  • Pin 2 – VCC
  • Pin 3 – DOUT (TX)
  • Pin 4 – DIN (RX)
  • Pin 10 – Sleep_RQ (low = wake)