I'm looking for advice on whether there is an available notation in the Arduino IDE that would save me a bunch of copy-paste-modify effort. The Arduino is a MEGA 2560. I'm using the Arduino IDE 1.8.19. Here is a summary of what my code needs to do:
Software Overview:
The architecture of this code project is a Finite State Machine, and we make use of Jose Rullan's StateMachine library.
Note that this library, in turn, makes use of Ivan Seidel's LinkedList library.
We'll need seven state machines, one for each inverter.
Each FSM has four states and eight transitions, as shown in the diagram, "BPCC State Diagram R2.pdf" (sorry, I cannot upload this file as a new member; however it is simply four boxes representing four states, with a total of 8 arrows representing possible transitions between the states).
The state machines and their states are created during Initialization, and the transitions are added within the setup routine.
Functions define what happens within each state, and the conditions that trigger transitions; these functions are duplicated for the seven state machines.
[I cannot upload the diagram, being a new user.]
I tried using array notation (e.g., "machine[0]" for the name of the StateMachine instance), and it actually compiled, but didn't appear to be running successfully. This was using the code example provided, which otherwise worked fine with simple names for the multiple machines.
It may be that I need to learn more about using pointers in Arduino code??
Otherwise I guess I will need to write and debug the code for ONE inverter's state machine, then copy and paste the many lines that create and define states and transitions for that machine, then edit the names of the machines and transitions to reflect the six additional inverters.
Just as an example of what I'd like to be able to do, I'm already using array variables for the seven I/O pins and related parameters (e.g., voltages), with each array having seven members, so that I can create a "for" loop with an index that steps from 0 to 6, that can perform the transition testing within a Switch-Case code structure.
Kind of humorous: I had this in my original message, but deleted it after reading the posting guidelines on this forum, that said something about avoiding putting in links to other sites!
My bad!
Thanks for this tip, Bob.
I've browsed way more than that, but failed to create my account on this forum until I finally decided to ask a question!!
Shouldn't take me long to reach trust level 1...
Bob, that sounds like exactly how I should proceed.
I'm learning about classes and instances; kind of new to OO programming, I hate to admit (I'm an older mechanical engineer, with a smattering of simple coding experience over the years).
The StateMachine is already a class created by Rullan's library, if I understand his code correctly. I think what I'm missing is whether I want to create an array of instances of the class, vs creating an array of such classes, as you are suggesting.
I don't suppose you could point me (sorry for the pun) to a source for me to learn the basics of the notation for defining a class and creating the array of classes?
My next challenge will be how to refer to the four states and eight transitions, which pertain to each machine, with a similar array notation. As you can see in the code above, I just used unique names, e.g., state1 and state2 for one machine and stateA1 and stateA2 for the second machine. Since these are functions I'm defining (both the states and transitions are defined functions), can I similarly create an array of "state1" functions?
Why do you need an array of states and transitions ?
The states and transitions belong to their own instance of the state machine object and I would expect them to be private to that instance
If that is the case then each instance can use the same state and transition names without any danger of a change of state in one affecting any other state machine
gfvalvo: you are correct; I changed the code to delete the part you said is superfluous; still works fine.
The original line from Rullan's example code (in his github) is this:
StateMachine machine = StateMachine();
Is the "= StateMachine()" superfluous in his code as well?
I'm trying to understand the difference; declaring an array vs declaring a single instance of a class.
Each state is associated with a function. If I try to associate the same state to multiple instances of "machine", of course it throws a compiler error: redefinition of 'State* S0'. Thus:
Do I need to find a way to add the states to the StateMachine class, before creating the seven instances of it? It's not obvious to me how I could do that.
I'm also missing something here, regarding how to define the states and transitions, which are functions called by code in the StateMachine.h library, to handle the multiple instances of machines. If they could take an argument, we could pass them the machine array number, and their functions could use that to test the correct parameter array member.
#include <LinkedList.h>
#include "State.h"
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
class StateMachine
{
public:
// Methods
StateMachine();
~StateMachine();
void init();
void run();
// When a state is added we pass the function that represents
// that state logic
State* addState(void (*functionPointer)());
State* transitionTo(State* s);
int transitionTo(int i);
// Attributes
LinkedList<State*> *stateList;
bool executeOnce = true; //Indicates that a transition to a different state has occurred
int currentState = -1; //Indicates the current state number
};
And then from the referenced State.h:
#include <LinkedList.h>
#ifndef _STATE_H
#define _STATE_H
/*
* Transition is a structure that holds the address of
* a function that evaluates whether or not not transition
* from the current state and the number of the state to transition to
*/
struct Transition{
bool (*conditionFunction)();
int stateNumber;
};
/*
* State represents a state in the statemachine.
* It consists mainly of the address of the function
* that contains the state logic and a collection of transitions
* to other states.
*/
class State{
public:
State();
~State();
void addTransition(bool (*c)(), State* s);
void addTransition(bool (*c)(), int stateNumber);
int evalTransitions();
int execute();
int setTransition(int index, int stateNumber); //Can now dynamically set the transition
// stateLogic is the pointer to the function
// that represents the state logic
void (*stateLogic)();
LinkedList<struct Transition*> *transitions;
int index;
};
#endif
There are also .cpp files for both of these .h files; here is state.cpp:
#include "State.h"
State::State(){
transitions = new LinkedList<struct Transition*>();
};
State::~State(){};
/*
* Adds a transition structure to the list of transitions
* for this state.
* Params:
* conditionFunction is the address of a function that will be evaluated
* to determine if the transition occurs
* state is the state to transition to
*/
void State::addTransition(bool (*conditionFunction)(), State* s){
struct Transition* t = new Transition{conditionFunction,s->index};
transitions->add(t);
}
/*
* Adds a transition structure to the list of transitions
* for this state.
* Params:
* conditionFunction is the address of a function that will be evaluated
* to determine if the transition occurs
* stateNumber is the number of the state to transition to
*/
void State::addTransition(bool (*conditionFunction)(), int stateNumber){
struct Transition* t = new Transition{conditionFunction,stateNumber};
transitions->add(t);
}
/*
* Evals all transitions sequentially until one of them is true.
* Returns:
* The stateNumber of the transition that evaluates to true
* -1 if none evaluate to true ===> Returning index now instead to avoid confusion between first run and no transitions
*/
int State::evalTransitions(){
if(transitions->size() == 0) return index;
bool result = false;
for(int i=0;i<transitions->size();i++){
result = transitions->get(i)->conditionFunction();
if(result == true){
return transitions->get(i)->stateNumber;
}
}
return index;
}
/*
* Execute runs the stateLogic and then evaluates
* all available transitions. The transition that
* returns true is returned.
*/
int State::execute(){
stateLogic();
return evalTransitions();
}
/*
* Method to dynamically set a transition
*/
int State::setTransition(int index, int stateNo){
if(transitions->size() == 0) return -1;
transitions->get(index)->stateNumber = stateNo;
return stateNo;
}
Bob, if this is getting too far into the weeds, perhaps I should forget about using this StateMachine library, and instead I can study the several tutorials available teaching how to implement state machines using switch/case nomenclature. It might be a lot simpler to do that using array variables, rather than dealing with the class structure within this library.