Managing program "states"

Working on a program with four main states and not sure if there is a better way to manage them.

State 1: Set pin outputs, init CAN shield State 2: Init SD card, make sure files required on SD card are there and copy data from them into local variables State 3: Downloading data from serial (via PC) to write data to SD card **If data is downloaded to SD card, program must go back to State 2 to copy new SD data into local variables State 4: Reading vehicle parameters over CAN

My thought is to handle these state changes via bool flag variables in loop():

void setup()
{
    // State 1:  Set pin outputs, init CAN shield
        // Should be a one time deal
}

bool bSDFileCheckAndCopy=true;
bool bDownloadDataFromPC=false;

void loop()
{
    if (bSDFileCheckAndCopy)
    {
        //State 2:  Init SD card, make sure files required on SD card are there and copy data from them into local variables
            //Reinit SD card would handle SD card being removed/reinserted
        bSDFileCheckAndCopy=false;
    }
    else if (bDownloadDataFromPC)
    {
        //State 3:  Downloading data from serial (via PC) to write data to SD card
        bDownloadDataFromPC=false;
        bSDFileCheckAndCopy=true;
    }
    else
    {
        if (Serial.available > 0) bDownloadDataFromPC=true;
    }
}

Thoughts on this method to handle the flow between states?

"States" is probably not the best way to do what you want. A state machine is for handling a complex number of inputs, output or internal processes that MUST be enabled in one state and disabled in another. I believe you want to have a CAN bus, Serial bus, SD (SPI) bus simultaneously and manage all three, passing some data to and from each bus. You can do that without states. The pseudocode would look something like this

// includes for libraries, such as CAN, SD etc.
// constant pin numbers
// other constants
// global variables
byte dataFlagCAN;
byte dataFlagSD;
byte dataFlagSerial;

void setup() {
  //setup pin modes
  //setup CAN
  //setup SD
  //setup serial
}

void loop {
  receiveCAN();
  receiveSD();
  receiveSerial();
  sendCAN();
  sendSD();
  sendSerial();
}

void receiveCAN() {
  //do receive CAN stuff
  //evaluate data received to set dataFlagCAN
}

void receiveSD() {
  //do receive SD stuff
  //evaluate data received to set dataFlagSD
}

void receiveSerial() {
  //do receive serial stuff
  //evaluate data received to set dataFlagSerial
}

void sendCAN() {
  //evaluate flags to send CAN data
}

void sendSD() {
  //evaluate flags to send SD data
}

void sendSerial() {
  //evaluate flags to send Serial data
}

The problem with using flags for program states is that if you accidentally set more than one bit you have an ambiguous state.

I prefer a single State variable and an enumeration to name the states:

enum States {
eSDFileCheckAndCopy,
eDownloadDataFromPC, 
eSDFileCheckAndCopy} State = eSDFileCheckAndCopy;

This also makes it possible to use a switch/case statement to process the current state:

    switch (State)
    {
    case eSDFileCheckAndCopy:
      // code here
      break;

    case eDownloadDataFromPC:
      // code here
      break;

    case eSDFileCheckAndCopy:
      // code here
      break;

    }

I agree with @JohnWasser.

Note that I would not do the init of the SD card in a state. I would put the first state as well as the init of the SD card in setup(); after all, init stuff usually only runs once. Your second state (which now becomes the first state) can simply be reading from SD in that case.

I am inclined towards @Perehama's general approach based on the description in the Original Post. That does not in any way invalidate the comments by @johnwasser - it's just that states may not be needed at this stage of the design.

Have a look at how things are organized in Planning and Implementing a Program

...R

Good points have definitely been made. Using the enum for state changes definitely makes more sense than how I have it set up.

I understand Perehama's method of doing things as well and think it might be better suited for my objective. Time to arrange the code!

johnwasser:
The problem with using flags for program states is that if you accidentally set more than one bit you have an ambiguous state.

I totally agree and would not use flags for program states. I was making the point that a state machine may NOT be desirable, for example, if there is Serial data received that is to be written to the SD card and the CAN bus. If a state machine IS necessary, Johnwasser’s approach is correct.

Moving some code around and back to thinking state machine logic better suits my needs.

The general loop should go:

void loop()
{
    If (Serial.available() > 0) 
    {
          // Means controller update required
          // This reads in new data over serial, then copies it to the SD card
    }
    Else
    {
          // Read from vehicle CAN and update local program variables (no serial or SD card interaction)
    }

}

Not necessarily everything simultaneously receiving/sending over CAN, SD, and Serial.

dtbingle:

    If (Serial.available() > 0) {

If your baud rate is 9600, this will be true every 1.04 milliseconds, or roughly every 16,666 machine instructions.
If your baud rate is 115200, this will be true every 8.68 microseconds, or roughly every 138 machine instructions.
To better understand how to help you, can you give a bit more details about what is the general purpose of the code and how the Serial, SD and CAN contribute to the general purpose?

Sure thing.

The core of the program is to read vehicle parameters over CAN and adjust an output (an active exhaust valve in this case) based on lookup tables stored on the SD card. These lookup tables are valve "calibrations". The second part of this project is a PC interface using Visual C#. On the PC interface, these lookup tables are able to be modified as desired and downloaded to the arduino controller via Serial. Given the size of the tables (total around 5600 bytes) and need to modify after compile time has led me to SD card storage.

Under normal operation (99% of the time), the arduino will be only reading CAN, reading the lookup tables on the SD card, and adjusting a PWM output pin based on the "found" value. For example, the controller will read CAN to determine current throttle pedal position and engine RPM. Then it will use a binary search method to find the closest pedal position/engine RPM combo and corresponding exhaust valve position it should be at. Then it will modify the PWM output to achieve this desired exhaust valve position.

The other 1% of the operation time is that the arduino needs to detect when a new "calibration" or lookup table is attempting to be sent over Serial via the PC Visual c# interface. So while the controller is pulling vehicle CAN signals and modifying the exhaust valve position, it must be constantly checking the Serial bus to determine that the PC is trying to update the arduino lookup tables.

Something like:

void loop()
{
    if (PC is trying to update controller via Serial bus)
    {
        // Stop reading CAN signals
        // Read Serial data
        // Copy new data from serial onto SD card
        // Set "Download Complete" trigger and go back to reading CAN
    }
    else
    {
        // Read CAN signal to determine vehicle parameters
        // Access lookup tables on SD card to determine new exhaust valve position
        // Set new valve position
    }
}

First thigs first, you know that ATmega chips are NOT recommended for automotive use, especially where their failure could cost life. That said, the structure may ultimately be your preference. I am imagining trying to update the lookup table while driving, so to prevent this, I might use a state machine to prohibit uploads while running or running while uploading. That's 2 states, uploading and running, enumerated as in Johnwasser's example.

Thanks for help everyone.

I've decided to use a state configuration since many of my program functions don't run simultaneously.

// Program states
enum States {
    eDownloadPCtoTempSD,
    eCheckTempSDFiles,
    eFileMaintenance,
    eCheckSDFiles,
    eReadCAN,
    eTableLookup,
    eSetValve
} state = eCheckSDFiles;

switch (state)
    {
        case eDownloadPCtoTempSD:
            break;

        case eCheckTempSDFiles:
            break;

        case eFileMaintenance:
            break;

        case eCheckSDFiles:
            break;

        case eReadCAN:
            break;

        case eTableLookup:
            break;

        case eSetValve:
            break;
    }

dtbingle: I've decided to use a state configuration since many of my program functions don't run simultaneously.

I'm still not convinced that is appropriate - the concept proposed in Reply #1 still seems more relevant.

What will cause your states to change?

You say "many of my program functions don't run simultaneously" which obviously implies that some of them do. And it seems to me that it will be appropriate to do the tasks in little inter-leaved chunks. As indicated in Reply #8 an Arduino can do a lot of work between the arrival of two serial characters.

If you design the program from the start to be able to do Several Things at a Time it will be much easier than trying to modify it later to include that capability.

...R

dtbingle: Thanks for help everyone.

I've decided to use a state configuration since many of my program functions don't run simultaneously.

Based on the code you present, I think you grossly misunderstand what a state machine is. What you list as states are how most people would break a program into functions. I agree with Robin. A state machine is a provision for a program to BE two things. You can put money into an ATM and someone else can take it out without those two things ever mixing because of state machines. Your car radio can BE a FM radio and an AM radio because of state machines. This was my point in my first post, so I am being repetitive. As you get further along, if you feel yourself "forcing" your code into states, it's likely because you are hamstringing yourself by using states.

Think of states like this

I am broke (spent all my money and waiting for my paycheck) I have money (paycheck has arrived) Repeat

Note how these are not actions. However the states usually regulate which actions can happen, For example the action of "spendingMoney()" cannot happen in the "broke" state. (Always excluding money-lenders :) )

...R