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,
- If the program is idle (State 0) show nothing in serial monitor.
- If the program is reading only, prompt the serial monitor (State 1) once, not every time a read is performed.
- If the program is reading and recording because the SPST switch state changed, prompt the serial monitor (State 2) once, not every time a read and record is performed.
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:
- Wait 100ms.
- LED1 state HIGH
- LED2 state LOW
- If SPST switch is HIGH, read analog input pins and return back to idle state.(There will be a display hooked at this point that will show values regardless if it is recording data or not).
- LED1 state LOW
- LED2 state LOW
- If a SPST switch is LOW, record data read to file.
- If this is the first time, create a file with unix name.
- If this isn't the first time (file exist), open it up and append to it.
- LED1 state LOW
- LED2 state HIGH
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
}