Switch case statement stop working mid execution

Hi everyone,
I usually only need to use google to debug all my problems, but this time is different, I've been pulling my hair over this bug for 2 days and really can't figure out what I did wrong.

I'm programming a finite state machine, using a switch case statement to execute the code corresponding to the current state. Everything is working fine until the system reaches "state = 6". At this point, nothing in the switch case is getting executed. I've put print statements in all cases, including a default one, and nothing is getting printed.

The void loop is still getting run, I put a print statement outside the switch case and sure enough, it's printed every time.

Without further ado, here is my code :

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

#define CRqst 0
#define AAdrs 1
#define StpEN 2
#define SrtEN 3
#define Rst 4

RF24 radio(9, 10); // CE, CSN
byte self[6] = "0Node";
byte child[6] = "0Node";
byte gate[6] = "0Node";
byte voidAddress[6] = "00000";
int address = 0;


int state = 0;
int prevState = 0;
unsigned long timer = 0;
unsigned long ackTimeout = 10000;
unsigned long sendNewCRqstDelay = 100;

uint8_t sender = 0; 

struct Payload {
  unsigned int address;
  unsigned int instruction;
  unsigned long data;
};

Payload res;


void setup() {
  Serial.begin(9600);
  radio.begin();
  while(!Serial.available()){};
  }
  

void loop(){
  prevState = state;
  switch (state) {
    case 0:
      Serial.println("State 0");
      Serial.println("State 0 - Initializing");
      radio.openWritingPipe(gate);
      radio.stopListening();
      state = 1;
      break;
      
    case 1:
      Serial.println("State 1");
      if(millis()-timer > sendNewCRqstDelay){
        timer = millis();
        Payload P = {0,0,0};
        Serial.println("Sending CRqst");
        if(radio.write(&P,sizeof(P))){
          Serial.println("Ack received");
          radio.openReadingPipe(1,gate);
          radio.startListening();
          timer = millis();
          state = 2;
          Serial.println("Waiting for AAdrs");
        }
        else{
          Serial.println("Ack not received");
        }
      }
      break;

    case 2:
      Serial.println("State 2");
      if (radio.available()){
        Serial.println("Received payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if(res.address == 0 && res.instruction == AAdrs){
            Serial.println("Payload is a AAdrs");
            state = 3;
          }
        }
      }
      else if (millis()-timer > ackTimeout){
        Serial.println("CRqst timed out");
        state = 0;
      }
      break;

    case 3:
      Serial.println("State 3");
      Serial.println("Updating addresses and setting up");
      updateAddresses();
      radio.openReadingPipe(1,self);
      radio.openReadingPipe(2,gate);
      radio.startListening();
      state = 4;
      break;
    
    case 4:
      Serial.println("State 4");
      if (radio.available(&sender)){
        Serial.println("Received Payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if (sender == 1 && res.instruction == StpEN){
            Serial.println("Instructed to stop expanding the network by self pipe");
            radio.openReadingPipe(2,voidAddress);
            state = 6;
            break;
          }
          if (sender == 2 && res.instruction == CRqst){
            Serial.println("Payload is a CRqst from gate pipe");
            radio.openWritingPipe(gate);
            radio.stopListening();
            state = 5;
            break;
          }
          if (sender == 1 && res.instruction == Rst){
            Serial.println("Instructed to reset by self pipe");
            delay(res.data);
            state = 0;
            break;
          }
        }
      }
      break;

    case 5:
      Serial.println("State 5");
      Payload p = {0,AAdrs,address+1};
          if (radio.write(&p,sizeof(p))){
            Serial.println("Address assignment successful");
            radio.openWritingPipe(child);
            radio.startListening();
            state = 6;
            break;
          }
          else{
            Serial.println("Address assignment failed");
            state = 3;
            break;
          }
      break;

    case 6:
      Serial.println("State 6");
      if (radio.available()){
        Serial.println("Received Payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if (res.instruction == SrtEN){
            Serial.println("Instructed to start expanding network by self pipe");
            state = 3;
            break;
          }
          if (res.instruction == Rst){
            Serial.print("Instructed to reset by self pipe with delay : ");
            Serial.println(res.data);
            delay(res.data);
            state = 0;
            break;
          }
        }
      }
      break;

    default:
      Serial.println("Default state");
      break;
      
  }
  if (prevState != state){
    Serial.println("------");
    Serial.println(state);
    Serial.println("------");
  }
  delay(5);
}



void updateAddresses(){
  address = int(res.data);
  self[0] = address;
  child[0] = address+1;
}

bool readIncoming(){
  Serial.println("Reading Incoming");
  radio.read(&res, sizeof(res));    //Reading the data
  if(res.address == address or res.address == 0){
    Serial.println(res.instruction);
    Serial.println(res.data);
    Serial.println("**************************");
    return true;
  }
  else if(state == 6){
    radio.stopListening();
    if(radio.write(&res,sizeof(res))){
      Serial.println("Message forwarded");
    }
    else{
      Serial.println("Transmission to child failed");
    }
    radio.startListening();
    return false;
  }
}

Initially, I wanted to delete some of the code for readability's sake, but the bug is so obscure that I don't want to miss including any information.

If someone can help me that would be awesome, my project is currently stalled because of this.

Thanks for reading !

PS: below is an example of output from the arduino, with all current print statement. I've made sure to send the correct data to the arduino via NRF24L01 module to make it reach state 6. Not sure if that will help, but I'll include it anyway

------
0
------
State 0
State 0 - Initializing
------
1
------
State 1
Sending CRqst
Ack received
Waiting for AAdrs
------
2
------
State 2
Received payload
Reading Incoming
1
2
**************************
Payload is addressed to this node
Payload is a AAdrs
------
3
------
State 3
Updating addresses and setting up
------
4
------
State 4
<bunch of them repeating, I cut them out>
State 4
Received Payload
Reading Incoming
2
0
**************************
Payload is addressed to this node
Instructed to stop expanding the network by self pipe
------
6
------

Just a guess, but in state 6, if if (readIncoming()) evaluates false, state is unchanged.

Shouldn't default: set a known state?

Also, upon entry, state is an undefined value- likely zero, but no guarantee. Set it in setup().

I don't see the print statement that shows loop being called, so please post the code you're running.

What are those breaks in the ifs in state four needed for?

Thanks for you replies !

SteveMann:
Just a guess, but in state 6, if if (readIncoming()) evaluates false, state is unchanged.

Shouldn't default: set a known state?

Also, upon entry, state is an undefined value- likely zero, but no guarantee. Set it in setup().

Yes this is correct, if readIncoming() evaluates false, state remains unchanged and the machine stays in state 6, this is expected.

I just added a default state to see if the switch case didn't manage to match "state" to any case I've coded, for debugging purposes. It's not useful for the program.

I don't know what you mean by "state is not defined". It's defined at the beginning as a global variable and given an initial value of 0.

wildbill:
I don't see the print statement that shows loop being called, so please post the code you're running.

What are those breaks in the ifs in state four needed for?

Yes I've included the code with that print statement removed, because it does not serve any purposes and was removed because it just spammed the serial monitor.

The breaks in the ifs are there as a last ditch effort, I thought maybe there could be cases where both if got ran. But it didn't change anything to add or remove them, so they can be ignored.

A thing I want to make clear is that it seems like the switch case statement doesn't do anything after the state variable becomes 6.

There are print statements at the beginning of every case, and in the default case, so no matter the value of "state", there should be something showing up in the serial monitor, yet there is nothing ...

It really looks like the switch statement is either ignored or not run at all

There seems to be stray "break" after 6. That would sure cause your problem.

Paul

Paul_KD7HB:
There seems to be stray "break" after 6. That would sure cause your problem.

Paul

Hi, thanks for the reply. I'm not sure to understand why this is so. In all my cases I put a break at the end, my understanding is that If I don't do so, then the program will run all subsequent cases, which is not what I want.

If I remove this "break" at the end of case 6, how would that resolve my issue, given that the 6th case doesn't seem to even be run at all? What is weird is that I put a break at the end of every case and it seems to work fine, until the 6th one.

Hi, quick update for my problem.

So I've been basically trying random things to see what could fix my issue, and I've come to the conclusion that the problem lies before state 6, as I never see the print situated in state 6 appearing on the serial monitor.

So I tried to comment-out state 5, and it fixed the problem. It seems like state 5 si the one making all the problems.

I still don't understand why state 5 causes this issue, so if anyone can figure it out, I'll be glad to know. State 5 is important in my program and I can't just remove it ...

Thank you for anyone who is spending time trying to help me !

You do have a lot of extra breaks, and that could be the problem. There only needs to be one break at the end of each case statement as you do in case 0-3.

Before the break statement, print the state so that you know the logic is working as expected.

SteveMann:
You do have a lot of extra breaks, and that could be the problem. There only needs to be one break at the end of each case statement as you do in case 0-3.

Before the break statement, print the state so that you know the logic is working as expected.

New update.
This is my current code

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

#define CRqst 0
#define AAdrs 1
#define StpEN 2
#define SrtEN 3
#define Rst 4

RF24 radio(9, 10); // CE, CSN
byte self[6] = "0Node";
byte child[6] = "0Node";
byte gate[6] = "0Node";
byte voidAddress[6] = "00000";
int address = 0;


int state = 0;
int prevState = 0;
unsigned long timer = 0;
unsigned long ackTimeout = 10000;
unsigned long sendNewCRqstDelay = 100;

uint8_t sender = 0; 

struct Payload {
  unsigned int address;
  unsigned int instruction;
  unsigned long data;
};

Payload res;


void setup() {
  Serial.begin(9600);
  radio.begin();
  while(!Serial.available()){};
  }
  

void loop(){
  prevState = state;
  switch (state) {
    case 0:
      Serial.println("State 0");
      Serial.println("State 0 - Initializing");
      radio.openWritingPipe(gate);
      radio.stopListening();
      state = 1;
      break;
      
    case 1:
      Serial.println("State 1");
      if(millis()-timer > sendNewCRqstDelay){
        timer = millis();
        Payload P = {0,0,0};
        Serial.println("Sending CRqst");
        if(radio.write(&P,sizeof(P))){
          Serial.println("Ack received");
          radio.openReadingPipe(1,gate);
          radio.startListening();
          timer = millis();
          state = 2;
          Serial.println("Waiting for AAdrs");
        }
        else{
          Serial.println("Ack not received");
        }
      }
      break;

    case 2:
      Serial.println("State 2");
      if (radio.available()){
        Serial.println("Received payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if(res.address == 0 && res.instruction == AAdrs){
            Serial.println("Payload is a AAdrs");
            state = 3;
            updateAddresses();
          }
        }
      }
      else if (millis()-timer > ackTimeout){
        Serial.println("CRqst timed out");
        state = 0;
      }
      break;

    case 3:
      Serial.println("State 3");
      Serial.println("Setting up");
      radio.openReadingPipe(1,self);
      radio.openReadingPipe(2,gate);
      radio.startListening();
      state = 4;
      break;
    
    case 4:
      if (radio.available(&sender)){
        Serial.println("Received Payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if (sender == 1 && res.instruction == StpEN){
            Serial.println("Instructed to stop expanding the network by self pipe");
            radio.openReadingPipe(2,voidAddress);
            state = 6;
          }
          else if (sender == 2 && res.instruction == CRqst){
            Serial.println("Payload is a CRqst from gate pipe");
            radio.openWritingPipe(gate);
            radio.stopListening();
            state = 5;
          }
          else if (sender == 1 && res.instruction == Rst){
            Serial.println("Instructed to reset by self pipe");
            delay(res.data);
            state = 0;
          }
        }
      }
      break;


      
    case 6:
      if (radio.available()){
        Serial.println("Received Payload");
        if (readIncoming()){
          Serial.println("Payload is addressed to this node");
          if (res.instruction == SrtEN){
            Serial.println("Instructed to start expanding network by self pipe");
            state = 3;
          }
          if (res.instruction == Rst){
            Serial.print("Instructed to reset by self pipe with delay : ");
            Serial.println(res.data);
            delay(res.data);
            state = 0;
          }
        }
      }
      break;

    case 5:
      Serial.println("State 5");
      Payload p = {0,AAdrs,address+1};
      
      if (radio.write(&p,sizeof(p))){
        Serial.println("Address assignment successful");
        radio.openWritingPipe(child);
        radio.startListening();
        state = 6;
      }
      else{
        Serial.println("Address assignment failed");
        state = 3;
      }
      break;
    
    default:
      Serial.println("Default state");
      break; 
  }
  if (prevState != state){
    Serial.println("------");
    Serial.println(state);
    Serial.println("------");
  }
  delay(5);
}



void updateAddresses(){
  address = int(res.data);
  self[0] = address;
  child[0] = address+1;
}

bool readIncoming(){
  Serial.println("Reading Incoming");
  radio.read(&res, sizeof(res));    //Reading the data
  if(res.address == address or res.address == 0){
    Serial.println(res.instruction);
    Serial.println(res.data);
    Serial.println("**************************");
    return true;
  }
  else if(state == 6){
    radio.stopListening();
    if(radio.write(&res,sizeof(res))){
      Serial.println("Message forwarded");
    }
    else{
      Serial.println("Transmission to child failed");
    }
    radio.startListening();
    return false;
  }
}

Removing the extra breaks didn't help, but somehow swapping the order of case 5 and 6 did the trick ...
I still don't understand why, as every case is terminated by a break :confused:

I'll try printing something just before every break, that's a good idea.

If you have any idea as to why this swap solved anything, I'll be glad to hear :slight_smile:

i have not seen where you SerialPrint the value of "state" at the top of Loop to see what the actual value is. I am sure you must have done this, so does it ever show -6-?

Paul

Paul_KD7HB:
i have not seen where you SerialPrint the value of "state" at the top of Loop to see what the actual value is. I am sure you must have done this, so does it ever show -6-?

Paul

If you scroll down the main loop, there is a little if statement that checks whether prevState and State are equal, if not it prints state. I did this to make sure the state is only printed when a change occurs.
I hope this answers your question.

I don't have much hope for this post as it's already getting buried, but I'll update anyway.

So when I take the code initially posted and comment out everything in the case 5, the program works as expected. If uncomment just the line

Payload p = {0,AAdrs,address+1};

it stops working. I don't understand how this line can mess up the program :confused:
It's valid syntax and everything is well defined (or so I think).

I think the issue is variable initialization within a case. I always avoid declaring variables within a case UNLESS I create a new code block (therefore scope) otherwise I always seem to find issues. Definitely declaring an initialized variable within a case without a code block is a no-no. Try the following.

Declare payloads at the top of loop():

void loop(){
  Payload P = {0,0,0};
  Payload p;

In case 5: replace:

      Payload p = {0,AAdrs,address+1};

with:

      p.address = 0;
      p.instruction = AAdrs;
      p.data = address+1;
3 Likes