0
votes

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,

  1. If the program is idle (State 0) show nothing in serial monitor.
  2. If the program is reading only, prompt the serial monitor (State 1) once, not every time a read is performed.
  3. 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:

  1. Wait 100ms.
    • LED1 state HIGH
    • LED2 state LOW
  2. 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
  3. If a SPST switch is LOW, record data read to file.
    1. If this is the first time, create a file with unix name.
    2. 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
}
1

1 Answers

0
votes

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.