Issue using FSM and XBee libraries together.

Hi,

I think I am experiencing an issue using the FSM and XBee libraries together. Or I'm being an idiot, either way I would be greatful of some help.

I'm in the early stages of putting together some Arduino code for some home automation stuff, I've been focusing on the controller (Raspberry Pi) up till recently.

First up, some code that works. This just uses the XBee library, no FSM. (pastebin'd as post was too long)

When I run this on my Leonardo it connects to the coordinator fine and I get the following output on the serial monitor

Sending Message
9E <- FrameID
Waiting for TX confirmation
Something available
Message delivered
Sent

Which is exactly as I expect.

However If I add in some FSM code like so:

I get an output like this:

Updating State Machine
Entered Associated State
Updating State Machine
Attempting Registration
Sending Message
B7 <- FrameID
Waiting for TX confirmation
Something available
Message TX failed...maybe
A <- Delivery status
10 <- Remote Address
1 <- TX Retries
9A <- Discovery status
Updating State Machine
Attempting Registration
Sending Message
29 <- FrameID
Waiting for TX confirmation
Something available
Message TX failed...maybe
A <- Delivery status
10 <- Remote Address
1A <- TX Retries
9A <- Discovery status
...
... I will get this a few times with slight variations to some of the values
...
...
Updating State Machine
Attempting Registration
Sending Message
E0 <- FrameID
Waiting for TX confirmation
Something available
Received TX status for incorrect frame ID
E0 <- Expected
33 <- Received
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Timed out reading packet
looping
Looped too many times

Am I being an idiot somewhere in my code? I do have a headache right now so might not be noticing something.

Cheers.

OK, now I am completely confused.

I made a slightly simpler example that includes the FSM code and I can make it work/not work by just changing 1 if statement in a block of code that shouldn't even be being executed yet.

Here is the code:

#include <FiniteStateMachine.h>

#include <XBee.h>

XBee xbee;
XBeeAddress64 coord_addr = XBeeAddress64(0,0);

const byte NUMBER_OF_STATES = 2;

State state1 = State(f_enter, f_update, f_exit);
State state2 = State(f2_enter, f2_update, f2_exit);
FSM fsm = FSM(state1);

// send a message and return the frame ID of the sent message
uint8_t send_msg(uint8_t * msg, int msg_len, XBeeAddress64 dst_addr) {
  randomSeed(analogRead(0));
  uint8_t frame_id = random(1,254);
  ZBTxRequest req = ZBTxRequest(dst_addr, ZB_BROADCAST_ADDRESS, ZB_BROADCAST_RADIUS_MAX_HOPS, ZB_TX_UNICAST, msg, msg_len, frame_id);
  Serial.println("Sending Message");
  Serial.print(req.getFrameId(), HEX);
  Serial.println(" <- FrameID");
  xbee.send(req);
  return frame_id;
}

// Check if a specific frame ID message sent
boolean did_msg_send(uint8_t frame_id) {
  Serial.println("Waiting for TX confirmation");
  int max_loops = 10;
  int loop_count = 0;
  while(loop_count < max_loops) {
    if(xbee.readPacket(100)) {
    if(xbee.getResponse().isAvailable()) {
      Serial.println("Something available");
      if(xbee.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE) {
        ZBTxStatusResponse tx_resp;
        xbee.getResponse().getTxStatusResponse(tx_resp);
        if(tx_resp.getFrameId() == frame_id) {
          if(tx_resp.isSuccess() == true)
          {
            Serial.println("Message delivered");
            return true;
          } else {
            Serial.println("Message TX failed...maybe");
            Serial.print(tx_resp.getDeliveryStatus(), HEX);
            Serial.println(" <- Delivery status");
            Serial.print(tx_resp.getRemoteAddress(), HEX);
            Serial.println(" <- Remote Address");
            Serial.print(tx_resp.getTxRetryCount(), HEX);
            Serial.println(" <- TX Retries");
            Serial.print(tx_resp.getDiscoveryStatus(), HEX);
            Serial.println(" <- Discovery status");
            return false;
         }
        } else {
          Serial.println("Received TX status for incorrect frame ID");
          Serial.print(frame_id, HEX);
          Serial.println(" <- Expected");
          Serial.print(tx_resp.getFrameId(), HEX);
          Serial.println(" <- Received");
        }
      } else {
        Serial.println("Received something else");
        Serial.print(xbee.getResponse().getApiId(), HEX);
        Serial.println(" <- API ID");
      }
    }
    }
    else{
      Serial.println("Timed out reading packet");
    }
    delay(500);
    Serial.println("looping");
    loop_count += 1;
  }
  Serial.println("Looped too many times");
  return false;
}

void setup() {
  pinMode(13, OUTPUT);
  xbee = XBee();
  Serial1.begin(9600);
  xbee.setSerial(Serial1);
  Serial.begin(9600);
  while(!Serial) {}
  digitalWrite(13, HIGH);
}

void loop() {

  delay(2000);
  fsm.update();
}

void f_enter() { Serial.println("enter"); }
void f_update() {
  Serial.println("update");
  uint8_t registration_msg[] = {'R','E','G','I','S','T','R','A','T','I','O','N','1'};
  uint8_t msg_id = send_msg(registration_msg, sizeof(registration_msg), coord_addr);
  if (did_msg_send(msg_id)) {
    Serial.println("Sent");
    while(true) {
      xbee.readPacket(); 
      if(xbee.getResponse().isAvailable()) {
        Serial.println("Response available");
        if(xbee.getResponse().getApiId() == ZB_RX_RESPONSE) {
          ZBRxResponse rx_resp = ZBRxResponse();
          xbee.getResponse().getZBRxResponse(rx_resp);
          uint8_t rx123_data[4];
          rx123_data[0] = rx_resp.getData(0);
          rx123_data[1] = rx_resp.getData(1);
          rx123_data[2] = rx_resp.getData(2);
          rx123_data[3] = rx_resp.getData(3);
         if((rx123_data[0] == 0x30) && (rx123_data[1] == 0x31) && (rx123_data[2] == 0x30) && (rx123_data[3] == 0x31)){
    //       if(1 == 1) {
            Serial.println("Anything");
            fsm.transitionTo(state2);
            break;
           }
           else {
             Serial.println("Got something other than registration success");
           }
        }
      }
    }
  }
}
void f_exit() { Serial.println("exit"); }
void f2_enter() { Serial.println("enter2"); }
void f2_update() { Serial.println("update2"); }
void f2_exit() { Serial.println("exit2"); }

If I change the line

if((rx123_data[0] == 0x30) && (rx123_data[1] == 0x31) && (rx123_data[2] == 0x30) && (rx123_data[3] == 0x31)){

to something like if(1 == 1){ then it magically starts working!

It shouldn't have even got to evaluating this yet though as did_msg_send needs to return true and then it needs to receive a ZB_RX_RESPONSE, which it isn't.

Any insight would be very helpful.

Any insight would be very helpful.

Not going to happen until you attach your code to your post, instead of tossing it in the rubbish bin.

Cheers Paul, very helpful. :roll_eyes:

You clearly saw my second post as you quoted it. I guess you missed the block of new, simplified code that exhibits the issue.

What is your problem with pastebin? It has a couple of advantage over attaching the code directly to the post: it's quicker to view (especially on mobile devices) , offers syntax highlighting and doesn't require you to download anything potentially dodgy. The only downside I can see is that if you want to try the code you have to copy and paste rather than just open it.

What is your problem with pastebin?

Primarily that it is blocked by the proxy server that I have to use.

and doesn't require you to download anything potentially dodgy.

Really? Why do you suppose, then, that my company prevents access?

Well, perhaps the key is "require" vs. "enable". It certainly enables downloading viruses and trojan horses and other nasties.

Primarily that it is blocked by the proxy server that I have to use.

Why didn't you just say this to begin with?

I have no idea why your company chooses to block it, could be for any reason. I was once blocked from buying a pair of trainers because the website sold swimwear.

Please find the code attached.

works.ino (2.81 KB)

doesnt_work.ino (4.36 KB)

simplified.ino (4.16 KB)

I'm looking at the doesnt_work.ino file, and I see some stuff that really looks wrong.

uint8_t send_msg(uint8_t * msg, int msg_len, XBeeAddress64 dst_addr) {
  randomSeed(analogRead(0));
  uint8_t frame_id = random(1,254);

First, randomSeed() should be called once, in setup(), not every time you need a random number.

Second, frame_id is NOT supposed to be a random number. The first frame sent is 0, the next frame is 1, then 2, 3, 4...

You have a lot of string literals, occupying flash memory AND SRAM. You just might be running out of SRAM.

Before reviewing the code any further, we need to know if that is the case.
http://playground.arduino.cc/Code/AvailableMemory

You can keep the string literals out of SRAM by wrapping them in the F() macro:
Serial.println(F("Sending Message"));

First, randomSeed() should be called once, in setup(), not every time you need a random number.

I'll admit this is unnecessary, as I said in my opening post I'm still at the 'throwing stuff together' stage.

Second, frame_id is NOT supposed to be a random number. The first frame sent is 0, the next frame is 1, then 2, 3, 4...

The XBee spec states that if frame ID is 0 then you won't receive the transmit status back. It says nothing about any ordering.

You have a lot of string literals, occupying flash memory AND SRAM. You just might be running out of SRAM.

Before reviewing the code any further, we need to know

You might be on to something here, all the serial printing was put into help with debug.

Running the 'simplified' code I get the following results:
Not working - 1518B free
Working - 1564B free

Changing all the strings to use the F() macro gives a free memory of 2004B and works in both cases.

Given that I still had free memory in all cases, was memory usage the reason it wasn't working or has changing the code to use the F() macro altered something else?

Running the 'simplified' code I get the following results:
Not working - 1518B free
Working - 1564B free

Which Arduino are you using? Only the Due has that much memory.

Changing all the strings to use the F() macro gives a free memory of 2004B and works in both cases.

See above question.

The XBee spec states that if frame ID is 0 then you won't receive the transmit status back.

OK. So, don't use 0.

It says nothing about any ordering.

I guess it's an implicit assumption.

Given that I still had free memory in all cases

That isn't a given. If you are out of memory, the result of freeMemory() will be bogus.

As previously stated I'm using a Leonardo, which according to here http://arduino.cc/en/Main/ArduinoBoardLeonardo#.UyWHU_l_t8E has 2.5KB of SRAM.

geuben:
As previously stated I'm using a Leonardo, which according to here http://arduino.cc/en/Main/ArduinoBoardLeonardo#.UyWHU_l_t8E has 2.5KB of SRAM.

So, 2.5KB is 2048 + 512 = 2560 bytes. How can there possibly be 15,000+ bytes free of the 2,560 that you have?

Unless that last character is the letter B, not the number 8, your assumptions are incorrect. If it is, you should have your butt kicked for not putting a space between the number and the letter.

I made no assumption so it can't be incorrect.

It is indeed a B. Maybe you should kick your own butt for not being able to tell the difference between a B and an 8, even when they are right next to each other.

Are you always this rude when people ask for help? I know you've been on this forum for some time, have a lot of posts and have contributed a lot to this community but that is no excuse for being so rude.