I've used the FiniteStateMachien library a few times very successfully, with triggers in each state to move on to the next one. In a new application it would be very helpful to be able to set up a list of states to progress through, which may involve using the same state several different times. ideally I'd like to be able to set up a list of states in the order I want them and then increment through.
I'm not an experienced programmer, and despite having read articles about pointers, I simply can't get this to work. Adding asterisks as I thought might be required changed the error message but the nextState() function has always brought a compiler error.
Here is a brief example of the kind of thing I'm trying, which has exactly the same error as my larger code.
void nextState(){
runState ++;
mainStateMachine.transitionTo(runningOrder[runState])
}
#include <FiniteStateMachine.h>
#include <LiquidCrystal_I2C.h> // LCD library
char command;
char* runningOrder[]= { "startTest",
"waitForSafety",
"actuateNest",
"waitForUp",
"actuateNest",
"mainNoOp"};
const int numPar = 8; // number of parameters in a command string
int parameters[numPar];
//pin assignments
const byte sendHeadUp = 22;
const byte headIsUp = 23;
const byte safetyIsOk = 25;
const byte itemDetected = 27;
boolean endComm;
byte runState;
State mainNoOp = State(mainNoOpEnter,mainNoOpUpdate,mainNoOpExit); //no operation
State startTest = State(startTestEnter,startTestUpdate,startTestExit);
State waitForSafety = State(waitForSafetyUpdate);
State waitForUp = State(waitForUpEnter,waitForUpUpdate,waitForUpExit);
State error = State(errorEnter,errorUpdate,errorExit);
FSM mainStateMachine = FSM(mainNoOp); //initialize state machine, start in state: mainNoOp
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
void setup(){
pinMode(headIsUp,INPUT);
pinMode(safetyIsOk,INPUT);
pinMode(itemDetected,INPUT);
pinMode(sendHeadUp,OUTPUT);
Serial.begin(115200,SERIAL_8N1); // serial used for PC comms through USB
lcd.init(); // initialize the lcd & turn on backlight
lcd.backlight();
lcd.clear();
lcd.print("Hello !");
delay(1000);
}
void loop(){
mainStateMachine.update(); // this line makes the state machine 'tick' for timer reaedings
}
void mainNoOpEnter(){
command = '0';
memset(parameters,0,sizeof(parameters)/sizeof(int));
digitalWrite(sendHeadUp,LOW);
}
void mainNoOpUpdate(){
readIncoming();
if (endComm) {
if (command ='s') mainStateMachine.transitionTo(startTest);
else mainStateMachine.transitionTo(error);
}
}
void mainNoOpExit(){
command='0';
endComm=!endComm; // reset end of communication string flag
}
void startTestEnter(){
runState = 1; // reset running state
}
void startTestUpdate(){
if (digitalRead(itemDetected)==LOW) nextState(); // wait for item detected sensor
}
void startTestExit(){
}
void waitForSafetyUpdate(){
if (digitalRead(safetyIsOk)==LOW) nextState(); // wait until safe to run
}
void waitForUpEnter(){
digitalWrite(sendHeadUp,HIGH);
}
void waitForUpUpdate(){
if (digitalRead(headIsUp) ==LOW) nextState(); // when head is raised, return to noOp
}
void waitForUpExit(){
}
void errorEnter(){
lcd.clear();
lcd.print("Error");
delay(1000);
}
void errorUpdate(){
//things to do if error occurred
}
void errorExit(){
}
void nextState(){
runState ++;
mainStateMachine.transitionTo(runningOrder[runState])
}
void readIncoming(){
switch (readPCSerial){
case 1:
if (Serial.available()>0) readPCSerial = 2; // something arrived from PC
break;
case 2:
if (Serial.read() == '*'){ // check for correct start command
memset(parameters,0,sizeof(parameters)/sizeof(int)); // zero all parameters
commsEnd = millis() + (commsTime);
readPCSerial = 3;
}
else{
readPCSerial =99; // error state
while (Serial.available()) Serial.read();
}
break;
case 3:
while(Serial.available()<2){ // wait for minimum size acceptable message to be available - blocking while waiting is no big deal here
if ((long)(millis() - commsEnd) >=0){ // jump out of waiting loop if insufficient data received in time - should comms retry ?
commsFailed = true;
readPCSerial =99;
errorState = 1; // 1=comms timeout
failWhy=1;
break;
}
}
if (readPCSerial !=99) readPCSerial=4;
break;
case 4:
{
command = Serial.read(); // read command letter - NEED TO CHECK IS VALID
int i=0;
endComm = false; // detect end of command string
while(!endComm && !commsFailed){ // keep looping to read all info
if (Serial.available()>0){ // check serial data available
incomingChar = Serial.read(); // read next part
if (incomingChar >= 48 && incomingChar <= 57){ // if is numeric
parameters[i] = parameters[i] * 10;
parameters[i] = parameters[i] + (incomingChar -'0');
}
else if (incomingChar == ',') i++; // check for comma separator and increment to next parameter
else if (incomingChar == '\r'){
endComm = true; // check for CR at end of command string and end loop
if (i=0) checkRequest=true; // if no parameters, is a request for a status update
else checkRequest=false;
}
//else {}//ERROR CONDITION
if ((long)(millis() - commsEnd) >=0){ // jump out of waiting loop if insufficient data received in time - should comms retry ?
commsFailed = true;
errorState = 1;
failWhy=1;
break;
} // end of if comms timeout
}// end of if avaialble
} // end of while(!endComm) loop
break;
}
case 99:
//things to do if comms error from PC
break;
}
}
It reports 'no matching function for call to 'FiniteStateMachine::transitionTo(char*&)'
I like the clarity that the FiniteStateMachine library brings, but it would be a pain to have to effectively duplicate states so that I can use them in different parts of the sequence. It would also be nice to be able to adjust the sequence by simply editing one list, rather than chasing through the entire code.
I'm sure this is a clear example of a gap in my knowledge, and there are several different ways to achieve the result. I'm open to ideas !