I am struggling establishing a state machine in my Arduino program and I figured I had a simple enough program to learn finite state machines. Currently the problem I have is every time the program changes states, it floods the serial monitor with it's state or data being recorded. I created something that works at a very finite level but I feel like I can't expand on it in that this code only works if I have 3 states. But what if I want 4 or 5 states?
I would like to be prompted only once in the serial monitor every time a state is changed while the program continues to run. For example,
I would also like input as to how to better approach this because I feel as though this only works with a 3 state example. What happens if I add more switches or more states?
Quick Program Function:
I have this sort of working but feel it is inefficient and I only got here by luck. I have constantly struggled executing a finite state machine.
void setup() {
Serial.begin(57600); // Initiate serial
delay(100);
// Set pinmodes
// Start and adjust RTC
// Begin RTC
delay(100);
// Initialize SD Card
}
void loop() {
// Read to see if the switch has changed states
PIN_STATE = digitalRead(PIN_SWITCH);
currentMillis = millis();
// State 0 - Idle
if (currentMillis - previousMillis < interval) {
STATE = STATE; // State flag set
led_status(HIGH, LOW);
digitalWrite(PIN_READ, LED_READ);
}
// State 1 - Read Only
if ((PIN_STATE == HIGH) && (currentMillis - previousMillis >= interval)) {
if (CHANGE_STATE) {
STATE = 1; // State flag set
Serial.println("Changed state: Read");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, LOW); // Update LEDs
read(); // Read the pins
CHANGE_STATE = false;
}
//State 2 - Read and Record
else if ((PIN_STATE == LOW) && (currentMillis - previousMillis >= interval)) {
if (!CHANGE_STATE) {
Serial.println("Changed state: Read and Record");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, HIGH); // Update LEDs
read(); // Read the pins
if (STATE < 2) { // Was a file already created?
// Create new file
}
filename = filename;
open(); // Open the file
write(); // Write to the file
close(); // Close the file
STATE = 2; // State flag set
CHANGE_STATE = true;
}
}
void read() {
//Read Values
//Calculate Values
}
void open() {
// Open the file
}
void write() {
// Write to file
}
void close() {
// Close the file
}
void led_status(int led_one, int led_two) {
// Change the status of LEDS
Update, solved. From the article provided below by Arcadien, I was able to rethink the program flow and rewrite it into switch case state machine.
// Declare the states
enum state {
_readState,
_displayState,
_createState,
_openState,
_writeState,
_saveState,
_errorState
};
void loop() {
pinState = digitalRead(pinSwitch);
// Green LED HIGH
// Yellow LED LOW
currentMillis = millis();
if ((currentMillis - previousMillis) >= interval) {
switch (_currentState) {
case _readState:
//Serial.println("Current State: _readState");
// Green LED LOW
_currentState = _readState;
previousMillis = currentMillis;
//Read Values
case _displayState:
//Serial.println("Current State: _displayState");
_currentState = _displayState;
previousMillis = currentMillis;
if (pinState != LOW) { // If not recording, then break
filename = "";
_currentState = _readState;
break;
};
case _createState:
//Serial.println("Current State: _createState");
// Yellow LED HIGH
_currentState = _createState;
previousMillis = currentMillis;
if (filename == NULL) {
//Serial.print("Creating new file... ");
DateTime now = rtc.now();
filename = String(now.unixtime(), DEC);
filename = filename + ".txt";
//Serial.print(filename);
//Serial.println(" created!");
//Serial.print("Writing header to file... ");
//dataFile = SD.open(filename, FILE_WRITE);
dataFile = SD.open(filename, O_WRITE | O_CREAT);
dataFile.println("Time(ms), TPS, AFR");
//Serial.println("header written!");
dataFile.close();
};
case _openState:
//Serial.println("Current State: _openState");
_currentState = _openState;
dataFile = SD.open(filename, O_CREAT | O_APPEND | O_WRITE); // Open filename.txt
case _writeState:
//Serial.println("Current State: _writeState");
_currentState = _writeState;
// if the file is available, write to it:
if (dataFile) {
// Create a single line string of data
dataFile.println(_data); // Write data
}
// if the file didn't open, throw errors
else {
Serial.print("error opening ");
Serial.println(filename);
_currentState = _errorState;
}
case _saveState:
//Serial.println("Current State: _saveState");
_currentState = _saveState;
dataFile.flush();
dataFile.close();
_currentState = _readState;
break;
case _errorState:
_currentState = _errorState;
// Red LED HIGH
}
}
// Return to the begining
}
You should have a look at other answer here like this one. Roughly, you have to separate your machine and its states. Embedded code usually need to be compact and efficient, state machine can be implemented using transition tables.