Creating a state machine for unit testing

I am making a state machine with the Arduino to control an ATC CNC router spindle. I am trying to implement this using the state machine library from Jose Rullan V1.0.13. In order to make sure that the state machine is operating correctly I'd like to implement some unit tests. My idea was to define the state machine with it's transitions and state functions in a library, and then call this from another project that does the unit testing. I am currently looking to test the state transitions, state logic, and digital I/O to and from the SM.

Unfortunately my lack of experience is holding me up, and I can't get it to compile when I try to put the code that defines the state machine in header files or a library. This is even before the issue of doing unit testing on the Arduino code that has digital reads and writes.

How should this be done ?

Here is what the code looks like:Preformatted text

#include <StateMachine.h>

StateMachine machine = StateMachine();
State* S1 = machine.addState(&state1);  // Unclamped
State* S2 = machine.addState(&state2);  // Clamping
State* S3 = machine.addState(&state3);  // Stopped
...
State* S8 = machine.addState(&state8);  // error

void setup() {
   S1->addTransition(&transitionS1S2,S2);   // Unclamped

    S2->addTransition(&transitionS2S3,S3);  // Clamping
    S2->addTransition(&transitionS2S7,S7);
    S2->addTransition(&transitionS2S8,S8);
....
}
void loop() {
    machine.run();
    delay(STATE_DELAY);
}

/************** Logic executed in state ********************/

void state1(){              // Unclamped
    if(machine.executeOnce){
      startTime = millis();    
      Serial.println("State 1");
      digitalWrite(LED,HIGH);
    }// execute only once
} //end state0 function

void state2(){            // Clamping
    if(machine.executeOnce){
      startTime = millis();    
      Serial.println("State 1");
      digitalWrite(LED,HIGH);
    }// execute only once
} //state0 function
.....

/* ******************** Transition functions *********** */
bool transitionS1S2(){
    if((masterTime > (startTime + 1000))  || (masterTime < startTime) ){
      return true;
    }
    return false;
}// end transition 1-> 2

bool transitionS2S3(){
    if((masterTime > (startTime + 1000))  || (masterTime < startTime) ){
      return true;
    }
    return false;
}// end transition 2-> 3

.......

Not sure about unit testing, but you could do yourself a favor (and others) by posting your code in code tags.
Also, rather than having functions called "state1" with a comment that it is unclamped, why not name it

void unClamped() {
 ...
}

Your tracking of elapsed time is also flawed, the only reliable way to track elapsed time is

millis() - startTime >= durationDesired

and since you don't show the declaration of your time variables, they need to be unsigned long to match millis()

i'm not familiar with the state machine library you mention. i just write my own code using tables. i wrote this example code years ago on windows laptop (cygwin)

the value of this approach is the tables help visualize the state transitions and actions for each transition.

consider

//
// example state machine
//

#include <stdio.h>

#include "stateMach.h"

// ------------------------------------------------
Status
a (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
b (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
c (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
__ (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// --------------------------------------------------------------------

#define N_STATE     3
#define N_STIM      2

typedef enum { S0, S1, S2 } State_t;

typedef Status(*Action_t)(void*) ;

State_t smTransitionTbl [N_STATE] [N_STIM] = {
    {   S1, S2, },
    {   S0, S2, },
    {   S2, S1, },
};


Action_t smActionTbl [N_STATE] [N_STIM] = {
     {  a,  b },
     {  c, __ },
     { __,  a },
};

// ------------------------------------------------
char *msg1 [] = {
    "State machine has 3 states and 2 stimuli.",
    "It has the following state transistion and action routine tables.",
    "A __() represents do nothing and is easily identified in table.",
    "Each row is indexed by a state and each column a stimulus.",
    "Of course, meaningful action routine names are helpful.",
};

char *msg2 [] = {
    "Enter valid stimuli [0-1]:"
};

void
smHelp (void)
{
    for (int i = 0; i < sizeof(msg1)/sizeof(char*); i++)
        printf ("  %s\n", msg1 [i]);

    for (int state = 0; state < N_STATE; state++)  {
        printf ("%8s", "");
        for (int stim = 0; stim < N_STIM; stim++)
            printf (" %d", smTransitionTbl [state][stim]);

        printf ("%8s", "");
        for (int stim = 0; stim < N_STIM; stim++)  {
            if (a == smActionTbl [state][stim])
                printf ("  a()");
            else if (b == smActionTbl [state][stim])
                printf ("  b()");
            else if (c == smActionTbl [state][stim])
                printf ("  c()");
            else
                printf (" __()");
        }

        printf ("\n");
    }

    for (int i = 0; i < sizeof(msg2)/sizeof(char*); i++)
        printf ("  %s\n", msg2 [i]);

}

// ------------------------------------------------
static State_t _smState  = S0;

Status
smEngine (Stim_t stim)
{
    Action_t   func;
    State_t    last = _smState;

    if (N_STIM <= stim)
        return ERROR;

    func        = smActionTbl [_smState] [stim];
    _smState    = smTransitionTbl [_smState] [stim];

    printf ("    stim %d, transition from state %d to %d, exec ",
        stim, last, _smState);

    return (*func) (NULL);
}

How do I do the code tags ?
For the time tracking.. yes that code was just a rough outline to show the general idea.

I have also implemented state machine code in the past. I was hoping to use something more standard that had fewer bugs than something that I would write.

Here is the diagram for the state machine so that I can visualize it. It is a first draft, and I expect that it will change as I work on it.

post using </>

so you need something that can easily be enhance and verified. the tables aid verification

looking at your state diagram. not sure what you mean by events?

i would have expected the arcs to be labeled with stimuli. more properly they would be labeled with both a stimuli and action.

do you have 13 unique stimuli?

is there an unclamp stimuli? are 7 & 9 different or the same stimuli?

i see an error state. but no stimuli that results in an error

So back to the original issue: I would like to have as much of the definition of the state machine in a library so that I can test it standing alone. I can then use it without modification in the code that downloads to the Arduino.

If I put the function that will be called by the state machine in a library, then it does not recognize the machine object.

void state1(){              // Unclamped
  if(machine.executeOnce){
    startTime = millis();    
    Serial.println("State 1");
    digitalWrite(LED,HIGH);
  }// execute only once
} //state0 function

error: 'machine' was not declared in this scope
   if(machine.executeOnce){

How do I fix this? (this perhaps is a simple C++ question)

Note that I have

StateMachine machine = StateMachine();

In the .ino file and not in the library .cpp file

Hello
I´ve a short question:
Why do you don´t brew a own FSM by yourself?

part of what i've suggested, not using an Arduino specific library, is that it can be testing in simulation on a laptop and then moved to an embedded platform for use. of course it can subsequently be modified and tested

You will need an extern definition for machine in the file you're trying to use it in.

The problem with state machine libraries is that few people here use them, so the pool of people who can help you without having to study the library you chose is either very small or non-existent. By contrast, any of the regular crew who do answer questions here can read and understand a switch driven state machine.

If I did just write my own state machine library, how would this help with the ability to test it ?

Hello
Generate test case for the FSM.

It does not look like there is anything Arduino specific with this library. But I do understand the implicit comment that there really may be no straight forward way to do testing on the Arduino, and as a result do that development on the PC instead, and then port it. Did I misunderstand your comments?

it's common practice to build an off target simulation for code. usually need to simulate I/O. on the arduino, you also need to create stubs for all those helpful arduino specific routines (e.g. digitalWrite(), millis(), Serial.print(), ....)

you could try compiling state machine library on the PC and developing the code there. but as i've said, i believe the code i presented will be easier to develop with because of the tables.

otherwise, it seems that you're on your own