Pages: [1]   Go Down
Author Topic: Arduino Finite State Machine Library  (Read 2802 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey all,

I've been working with the Arduino for quite some time now, and it bugged me that I didn't have a standardized way of developing state machines. So, I took some libraries originally written by a professor of mine, edited them somewhat, and ported to the Arduino.

I'd call this a beta revision of the code. I still need to deal with the timer overflowing; right now it'll break when that happens (after about an hour of runtime). There's also some trace/debug functionality that I need to include. However, it seems right now that everything works as intended.

For those who don't know, finite state machines are a way of organizing mechatronics projects in a logical manner and implementing cooperative multitasking. Once things get big enough, you have tons and tons of stuff going on; this allows you to break it down into pieces. For example, if you were making a robot, you might have a task to control the wheels, a task to read and filter sensor data, a task to control the gripper attachment, and a task to read data coming in over a radio signal.

You'd then break these tasks down into states. For the wheels, you could have a brake state, a "going forward" state, a reverse state, turn right, and turn left. For radio, however, you'd probably have a "waiting for data" state and a "read" state.

Then, you determine the conditions that cause your tasks to transition between states. For example, in the radio task you would transition to "reading data" when a check to the buffer shows that there's data there to be read, and you'd transition back when all the data has been dealt with.

Here are diagrams of a few tasks from a project I completed a few months back involving building an automated security camera system:

http://www.ecphrasis.org/task_logic.pdf
http://www.ecphrasis.org/task_motor.pdf
http://www.ecphrasis.org/task_sensor.pdf

===============================

Implementing in code:

Here's where this library comes in. For each task you define, you need to make a new class by creating [taskname].h and [taskname].cpp as new tabs in the Arduino IDE. This class needs to inherit from the task class, and define a new run function like so

Code:
#include <Task.h>
 
 class task_blink : public task
    {
    private:
        int duration, outputPin;
        unsigned int timer;
    public:
        // The constructor creates a new task object
        task_blink(int pin, int theDuration);
 
        // The run method is where the task actually performs its function
        char run(char);
    };

Also declare any variables you need (such as duration in this example).

Now, to define these methods:

Code:
#include "task_blink.h"
#include <Task.h>
#include "WProgram.h"

//States
const char OFF = 0;
const char ON = 1;

bool ledState = false;

task_blink::task_blink(int pin, int theDuration) : task(10,0){
  duration = theDuration;
  outputPin = pin;
  timer = 0;
  pinMode(outputPin,OUTPUT);
}

char task_blink::run(char state){
    /*Serial.print("Running pin ");
    Serial.print(outputPin, DEC);
    Serial.print(" -> timer = ");
    Serial.print(timer, DEC);
    Serial.print(", duration = ");
    Serial.println(duration, DEC);*/
  switch(state){
    case(OFF):
      if(timer++ == duration){
        timer = 0;
        digitalWrite(outputPin, HIGH);
        /*Serial.print("Turning on pin ");
        Serial.println(outputPin, DEC);*/
        return(ON);
      }
      return(OFF);
    case(ON):
      if(timer++ == duration){
        timer = 0;
        digitalWrite(outputPin, LOW);
        /*Serial.print("Turning off pin ");
        Serial.println(outputPin, DEC);*/
        return(OFF);
      }
      return(ON);
    default:
      return(OFF);
  };
}

Your constructor has to give two arguments to the superclass constructor defining how often you want the task to run: milliseconds and microseconds. In this example, we want the task to run every 10 milliseconds, so we passed (10,0). Keep in mind, you could also pass arguments from the constructor through to the superclass constructor.

Then, the run method. This method should contain a switch statement, with each case being a different state. The value you return is the case that will be run the next time the task is called. In this example, we sit in the OFF state until our timer hits our duration value. It then transitions to the ON state and stays there until timer hits duration again, before transitioning back.

Finally, in the main file:

Code:
#include "task_blink.h"
#include <Task.h>


task_blink pin0(0,9);
task_blink pin1(1,19);
task_blink pin2(2,29);
task_blink pin3(3,39);
task_blink pin4(4,49);
task_blink pin5(5,59);
task_blink pin6(6,69);
task_blink pin7(7,79);
  
void setup ()
{
    Serial.begin(9600);
}

void loop ()
{
  pin0.schedule();
  pin1.schedule();
  pin2.schedule();
  pin3.schedule();
  pin4.schedule();
  pin5.schedule();
  pin6.schedule();
  pin7.schedule();
}

Just initialize the objects you need, and inside the loop simply call schedule() on everything.

The advantage to this approach is that it's completely non blocking, as long as your states and such don't take very long to run. You could run 40 led's, all blinking at different rates, by setting stuff up like this. It also makes it easy to precisely time stuff, as you know how often each of your tasks is going to be called. The library pulls timer data from the Arduino libraries, so as long as the board you're using is supported the timer will work correctly.

Anyhow, this is my beta revision of the library, and I figured I'd post it up here. Let me know if you have any questions/comments, and I'll keep updating this as I fix up the library more smiley

Files can be found here; just drop into the libraries folder like always:

http://www.ecphrasis.org/Task.zip

--John
« Last Edit: November 23, 2008, 08:45:04 pm by johnw188 » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This looks great John.  I've been writing bits and pieces of my own state machines with the arduino, but haven't created anything approaching a library yet.  I'll definitely try this out and let you know how it goes.

Taiya
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 18
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I realize this is an old topic, but I was wondering if this project has been updated to deal with the timer overflows.  This sounds like fun to play with.
Logged

Norway@Oslo
Offline Offline
Edison Member
*
Karma: 13
Posts: 2033
loveArduino(true);
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Just a minor commercial:

Just made a FSM library that do not expect the user to implement new classes. It do however rely on user defined functions.

FSM Library 1.0 <Beta>


Silly Simple Example Sketch:
Quote

/*
|| A Simple Sample State Machine Client
||
|| This sketch {
||   Turn LED on
||   Wait a second
||   Turn LED off
||   Wait a second
||   Fade in and out in a second
||   Wait a second
|| }
||
|| Contributed:
|| Alexander Brevig
*/
//FSM_beta.zip
#include <FiniteStateMachine.h>
//LED.zip
#include <LED.h>

//initialize states
State onState = State(on);
State offState = State(off);
State fadeState = State(fade);
//initialize state machine
FSM ledStateMachine = FSM(onState);

//initialize the LED
LED led = LED(11);

void setup(){ /*nothing*/ }

//poor example, but then again; it's just an example
void loop(){
  ledStateMachine.transitionTo(onState);
  ledStateMachine.update();
  delay(1000);
  
  ledStateMachine.transitionTo(offState);
  ledStateMachine.update();
  delay(1000);
  
  ledStateMachine.transitionTo(fadeState);
  ledStateMachine.update();
  delay(1000);
}

//utility functions
void on(){ led.on(); }

void off(){ led.off(); }

void fade(){
  led.fadeIn(500);
  led.fadeOut(500);
}

PostScriptum:
An FSM would not've been the best choise for implementing the problem: 'Make an LED go HIGH, then LOW, then dade in and out in a second.'
This was simply the first thing I though of. But in this case, it is over-engeneering.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This looks like although it seems too complicated to run just an LED, actually for the frame work I'm going to need, I would love to learn to use this code.  

How would the code differ if there was a behavior tied to the state transition.  This looks like every 1000 ms it calls the next state function.  Say we have an extra pin assigned called buttonpin6 (a 0-5V button) and if it goes high, then it goes to the next step.  

INSTEAD OF:  

void loop(){
  ledStateMachine.transitionTo(onState);
  ledStateMachine.update();
  delay(1000);

IT COULD BE:

void loop(){
  ledStateMachine.transitionTo(onState);
  ledStateMachine.update();
  buttonpin6(HIGH);


Or something to this effect?

I am not very good at programming and am actually using the Arudino to try to teach myself code.   :o  there are a lot of great examples but I'm still learning.  Please excuse my ignorance!   :'(


But what I eventually want to accomplish is controlling several servos to move to pre-determined positions at each "step".  This sounds like the FSM architecture is perfect for this!
Logged

Norway@Oslo
Offline Offline
Edison Member
*
Karma: 13
Posts: 2033
loveArduino(true);
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You see the code that looks like this:
Code:
State offState = State(off);

Now see here: http://www.arduino.cc/playground/Code/FiniteStateMachine#Creation

Notice this:
Code:
State ethernetDebugState = State( connectToHost , debug , closeConnectionToHost );


The syntax is like this:

State stateName = State( functionToExecuteWhenStateEnters , functionToExecuteWhenStateUpdates , functionToExecuteWhenStateExits );



Your project sounds like a perfect task for an FSM.

Do not hesitate to contact me (preferably on gmail).
[edit]OriginalPoster: Sorry for hijacking this thread.[/edit]
« Last Edit: June 29, 2009, 02:38:17 pm by AlphaBeta » Logged

Pages: [1]   Go Up
Jump to: