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
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);
}
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.
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)
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.
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.