State Machine Not Properly Working

Riddle me this batman:

I'm in the process of writing a library to reliably transmit/receive packetized serial data. In this library there is a class method called "available()" that parses incoming data and lets the main code know if a packet has successfully arrived or if any packet corruption has occurred.

Within this "available()" method, I have a finite state machine with its states initialized as:

enum fsm {
	find_start_byte,
	find_payload_len,
	find_payload,
	find_checksum,
	find_end_byte
};

When I send a packet between two Arduinos using this library, the receiving Arduino successfully parses the entire packet through the "find_checksum state". Once it's done with the "find_checksum" I set the next state to be "find_end_byte", but the next iteration of the method doesn't execute the code associated with "find_end_byte" even though I know the state machine is in that state.

Here is the source code for "available()":

/*
 int8_t SerialTransfer::available()

 Description:
 ------------
  * Parses incoming serial data, analyzes packet contents,
  and reports errors/successful packet reception

 Inputs:
 -------
  * void

 Return:
 -------
  * int8_t - Error code
*/
int8_t SerialTransfer::available()
{
	if (port->available())
	{
		while (port->available())
		{
			uint8_t recChar = port->read();
			Serial.println(state);
			switch (state)
			{
			case find_start_byte://///////////////////////////////////////
				if (recChar == START_BYTE)
					state = find_payload_len;
				break;

			case find_payload_len:////////////////////////////////////////
				if (recChar <= MAX_PACKET_SIZE)
				{
					bytesToRec = recChar;
					state = find_payload;
				}
				else
				{
					state = find_start_byte;
					return PAYLOAD_ERROR;
				}
				break;

			case find_payload:////////////////////////////////////////////
				if (payIndex < bytesToRec)
				{
					rxBuff[payIndex] = recChar;
					payIndex++;

					if (payIndex == bytesToRec)
					{
						payIndex = 0;
						state = find_checksum;
					}
				}
				break;

			case find_checksum:///////////////////////////////////////////
				uint8_t calcChecksum = findChecksum(rxBuff, bytesToRec);
				
				if (calcChecksum == recChar)
				{
					Serial.println("next, finding end byte");
					state = find_end_byte;
				}
				else
				{
					state = find_start_byte;
					return CHECKSUM_ERROR;
				}
				break;

			case find_end_byte:///////////////////////////////////////////
				Serial.println("finding end byte");
				state = find_start_byte;

				if (recChar == STOP_BYTE)
					return NEW_DATA;

				return STOP_BYTE_ERROR;
				break;

			default:
				break;
			}
		}
	}
	else
		return NO_DATA;

	return CONTINUE;
}

Here is the output of the receiving Arduino when it is sent packets of data:

0
1
2
2
2
3
next, finding end byte
4
4
4
4
4
4
4
4
4
4
4
4
4
4

I'm expecting:

0
1
2
2
2
3
next, finding end byte
4
finding end byte
0
1
2
2
2
3
next, finding end byte
4
finding end byte

Any idea why my state machine isn't working?

        case find_end_byte:///////////////////////////////////////////
          Serial.println("finding end byte");
          state = find_start_byte;

On entering the find_end_byte case you almost immediately set the state to find_start_byte. Is that intentional or should it only happen when the STOP_BYTE has been received ?

UKHeliBob:
On entering the find_end_byte case you almost immediately set the state to find_start_byte. Is that intentional or should it only happen when the STOP_BYTE has been received ?

Yes, because checking the end byte is the end of the process - whether or not it errors out, the next state must be to find the start byte of the next packet.

I think that the code that you show does not match with the results that you show, or I might be overlooking something ::slight_smile:
If you are really sure that is what you have, then it could be memory overflow, or writing to memory locations beyond an array, or another variable called 'state', or a compiler bug, and so on.

Koepel:
I think that the code that you show does not match with the results that you show, or I might be overlooking something ::slight_smile:

Ikr, it doesn't make any sense. I know it's moving on to the correct state, but isn't executing the proper state code. So strange.

To give you guys a full picture, the complete code I'm debugging is here on GitHub. Inside "examples" are the two sketches I'm using for the test.

Again, any insight is appreciated.

TURN UP THE WARNINGS.

/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino: In function 'int8_t available()':
/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino:95:14: warning: jump to case label [-fpermissive]
         case find_end_byte: ///////////////////////////////////////////
              ^
/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino:81:19: note:   crosses initialization of 'uint8_t calcChecksum'
           uint8_t calcChecksum = findChecksum(rxBuff, bytesToRec);
                   ^
/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino:105:9: warning: jump to case label [-fpermissive]
         default:
         ^
/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino:81:19: note:   crosses initialization of 'uint8_t calcChecksum'
           uint8_t calcChecksum = findChecksum(rxBuff, bytesToRec);
                   ^
/Users/john/Documents/Arduino/sketch_jul13a/sketch_jul13a.ino:46:14: warning: enumeration value 'find_end_byte' not handled in switch [-Wswitch]
       switch (state)
              ^

It doesn't like any case after:

        case find_checksum: ///////////////////////////////////////////
          uint8_t calcChecksum = findChecksum(rxBuff, bytesToRec);


          if (calcChecksum == recChar)
          {
            Serial.println("next, finding end byte");
            state = find_end_byte;
          }
          else
          {
            state = find_start_byte;
            return CHECKSUM_ERROR;
          }
          break;

I think it doesn't want you to declare a variable and then jump past the declaration. Change it to:

        case find_checksum: ///////////////////////////////////////////
          {
          uint8_t calcChecksum = findChecksum(rxBuff, bytesToRec);


          if (calcChecksum == recChar)
          {
            Serial.println("next, finding end byte");
            state = find_end_byte;
          }
          else
          {
            state = find_start_byte;
            return CHECKSUM_ERROR;
          }
          }
          break;

Thanks, it works flawlessly now!

Yes indeed - if you need a locally scoped variable in a switch for one case, you need curly brackets.

@johnwasser, Thanks !
So I did indeed overlook something :-[
Normally I would get a real error and the compiler stops when I declare a variable in a case without brackets. So I did a test:

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

void loop()
{
  int i = random( 0, 2);

  switch( i)
  {
    case 0:
      Serial.println( "0");
      break;
    case 1:
      int j = random( 0, 1000);
      Serial.print( "1,");
      Serial.println( j);
      break;
    default:
      Serial.println( "default");
      break;
  }
  delay( 500);
}

It only gives a warning :cry:
("warning: jump to case label [-fpermissive]" and "note: crosses initialization of 'int j").

Is the solution to turn on all compiler warnings in the preferences with verbose output for the compiler and check the warnings ?

I get an error when compiling that code

sketch_jul22a:15: error: crosses initialization of 'int j'

       int j = random( 0, 1000);

Windows 10
IDE 1.8.5
Nano board

Looks like the behavior changed between 1.8.5 and 1.8.9. When I compile on 1.8.9 on a Mac I just get the warning.

Koepel:
@johnwasser, Thanks !
Is the solution to turn on all compiler warnings in the preferences with verbose output for the compiler and check the warnings ?

I recommend turning the "Compiler warnings:" preference to "All" and keeping it there. The warnings will often point out code that is syntactically valid but semantically questionable.
No need for verbose output.