5

This is about the third time I've had to write software to control a cellular modem. For those unfamiliar with the process, you have a sequence of steps you have to take. Each step takes a certain amount of time, and there's a few responses you should receive in that amount of time. There are also some responses you can receive at any time, regardless of the step you are on. Based on the response you have to go to another stage in the process. If it times out, you have to go to a different stage. In some cases, steps are attempted multiple times before going to another stage.

These have to be non-blocking functions, which I can run as a task on a single threaded machine. So the main program would be calling this modemTask() a few hundred times a second, it checks whether it needs to do something, then performs a function if needed, and exits.

In the past I've written this as a simple switch based state machine, with enumerated stages, somewhat like the following:

switch(stage){
   case Power:
      powerOn();                 // Turn the modem on
      nextstage = ResetCmd;      // Go perform a reset
      attemptsLeft = 5;          // Send the reset command up to five times
      break;
   case ResetCmd;
      modem.write("ATZ\n");      // ATZ - reset
      attemptsLeft--;            // Use one of our attempts
      nextstage = ResetReply;    // Next wait for a response (should be OK)
      timeout = millis() + 5000; // Wait for up to 5 seconds each attempt
      break;
   case ResetReply;
      if(receivedResponse() == OK)     // Success
      {
         nextStage = NetworkAttachCmd; // Attach to the cellular network
      } elseif(receivedResponse() == ERROR || timeout < millis())
      {        // If we get an error or timeout, reattempt if we can, power on if we can't
         if(attemptsLeft > 0)
         {
            nextStage = ResetCmd;
         } else {
            nextStage = Power;
         }
      } 
      break;
   case NetworkAttachCmd:
   ...
}
stage = nextstage;               // Assign stage indirectly for debug purposes - nice to know where we came from at this point

It's difficult to keep track of the whole flow of the system, inserting an additional step requires changes to the steps before and after, and it just seems like there should be an easier way. The largest one I've had to design had fewer than 60 stages, so it's not unmanageable, but I can't help but think that there's a better strategy or pattern for this type of work.

While I use a few #define for most timeouts and attempts, it would be a bit nicer if this weren't embedded in the state machine. Perhaps a structure of some sort could be made to hold each state, but since the responses vary it seems just as complicated. Most steps will have a simple "OK" but some contain status and data that have to be acted on, where the stage will change based on the exact response.

  • If it matters to the answer, assume you can use an object oriented language as well. In this case I have access to c/c++. The system is embedded, but it's a rather roomy ARM processor. – Adam Davis May 7 '14 at 1:27
  • 7
    What about graduating to an actual state machine or processing pipeline? – Robert Harvey May 7 '14 at 2:42
1

State machines are a common design pattern in embedded systems, and it seems that you have a typical use case here. What you can do is simply have an infinite loop that let the current state handle the incoming message, transition to a new state if needed, then wait a bit.

Here's a simple C++ try. In my code, you need to bind or registers the possible states in some way, but the nice thing is that the really fixed part (handle event, switch to next state if needed) is really fixed in subclasses.

Additionnally, a nice win of state machines is that you can check their correctness easily with a combination of peer review, careful datasheet reading and a few unit tests.

class State {
public:
    // Forgetting constructors, etc.

    // Assume event is an integer, could be tuned to your case
    State *handleEvent(int event) {
        switch (event) {
           case EVENT1:
               this->doAction1();
               break;
            // etc.
        }
        State *nextState = this->nextStateForEvent(event);
        return nextState;
    }

    private:
        // Force each subclass (= posisble state) to re-implement all these
        virtual void doAction1() = 0;
        // etc.

        virtual State *nextStateForEvent(int event) = 0;
}

Then, you get a main loop that becomes something like this:

#define DEFAULT_STATE 0

volatile int event = 0;

int main()
{
    // Create the states beforehand
    State *allStates = populate_the_possible_states;
    State *currentState = allStates[DEFAULT_STATE];

    while (1) {
        currentState = currentState->handleEvent(event);
        sleep(someMilliseconds);
    }
}
0

Abstract it on its function, not state. States receive messages, they do things in response to those messages, and they alter the state of some variables that are persisted across the states.

Your attempts, timeout, next state are persisted across. I'd put these in some kind of singleton type manager, or simply some static variables.

The functionality with the state is left is what to do when it receives a message. It needs to be able to do things like change the static variables, run functions based on other functions. Here's one example I can think of, but these ideas should be tailored to what it is you want to be able to write one time that works with all your different states. Is it the waiting loops? Is it the checking for the command to be in a valid list of commands based on the current state? Is it being able to keep track of the sessions?

public interface IState {
    void Process(string message);
}

Now any class you make can be one of your states by implementing this interface and adding a void Process(string) method to the class.

public class PowerOn : IState {
    public void Process(string message) {
        powerOn();
        StateManager.NextStage = new ResetCmd();
        StateManager.AttemptsLeft = 5;
    }
}

public class ResetCmd : IState {
    public void Process(string message) {
        modem.write("ATZ\n");
        StateManager.AttemptsLeft--;
        StateManager.NextStage = new ResetReply();
        StateManager.Timeout = millis() + 5000;
    }
}
0

You know how everyone is always saying that learning a functional programming language can help you in other languages, but you never know exactly how? This is one of those cases. Because of the way functional languages track state, they have come up with some innovative ways to do asynchronous programming tasks like state machines. To use these patterns in C++, you probably need C++11 and/or boost.

Futures

Futures are a way to specify a sequence of asynchronous tasks, passing a value from step to step, with different paths for success and failure. You could extend them for the retry behavior you want. A state machine implemented using futures might look something like:

retryFuture(5)
.then(powerOn)
.then(resetCmd)
.then(attachNetwork)
.timeout(5000)
.failure(printFailureToScreen)

Coroutines

Coroutines are a way to sort of interrupt a function in the middle and resume it from the same point later, like when a response is received. It's a cooperative type of multitasking. This allows you to specify a function in a more linear order.

powerOn()
yield
resetCmd()
yield
attachNetwork
yield

Actors

Actors are a way to do message passing between tasks in an asynchronous manner. You would set up an actor to do each step of the state machine, then pass a message to the next actor at the appropriate time. This is similar to what you're doing now with the state variable, but in a somewhat more structured manner.

None of these ideas may be a perfect fit for your application, but hopefully it will give you a push in a direction to find a combination that may be useful for you.

0

If you just want to step up from using a switch-based machine, I'd recommend a technique I learned back in school, which is splitting the machine up into 3 or 4 orthogonal functions:

  • A next-state determination function -- this would take the machine's current state and its current input and determine what the next machine state should be, returning that (no side effects)
  • A transition output function -- this would look at what state transition was being taken, along with the input, and react to it (comparable to a Mealy machine's output, running only when a transition occurs)
  • A state output function -- this would look at the current state and output for it (comparable to a Moore machine's output, running on each iteration of the machine's loop)
  • A transition function -- this would call the other three functions and set the machine's state

(This is copied from my answer here -- there's a code sample there too if you'd like an example.)

You might consider a fully OOP machine though. There are software packages which will allow you to diagram machines and generate the code for them, which should cut down on the tedium of implementing a state-machine.

If you're unsure which level to go with, I've successfully built into an OOP approach from the function approach.

Some of your requirements made me think of hierarchical state-machines (you might wanna start reading at the previous sub-section), so you might check that out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.