Issue with serial input/output and multiple state machines.

Hello,

This project will ultimately result in a robot controller, hopefully controlling three DC motors and a short LED strip. What I am doing here is learning the basic control structure that I hope to use. The final project will have four robots, each with its own Arduino, all controlled over serial by a Raspberry PI. This is far outside my current programming capabilities, but the deadline is far enough out that I have some time to experiment, and I always tend to learn by jumping in the deep end first. The ‘bots will be articulated bases with a 20’ tall inflatable on top. In order to maintain the illusion of solidity in the inflatable, I don’t want hard starts and stops on the motors, and I’ve come up with some trig based easing formulas to handle the acceleration change. That’s what brings in the necessity of the state machine: an idle phase, a start phase, a run phase and a stop phase. The RPI will be handling all the coordination and hopefully sound generation via supercollider, so I didn’t want to overburden it with motor control and firmata as well, I’d like the arduino to pull as much of the weight as it can.

What my current problem is, I’ve set up this experimental sketch to learn how to use the SMlib simple state machine library. (http://playground.arduino.cc/Code/SMlib) When I start the sketch and send a state machine number over serial, it starts a timer that lets me know it’s rolling. If I type in another state machine number it will not start unless I have waited until the first request was processed and started displaying. My questions are: Is this just a noob misunderstanding of loop()? Will I ultimately have to go to protothreading in the end, so I should just start learning it now? I don’t quite understand interrupts, would they apply here?

Thanks for any advice. I’m running v1.0.5 on a Duemilanove.

#include <SM.h>

SM m1(m1s1);//the state machines
SM m2(m2s1);
SM m3(m3s1);

long int output[3];




const int NUMBER_OF_FIELDS = 3; // how many comma-separated fields we expect
int fieldIndex = 0;             // the current field being received
int values[NUMBER_OF_FIELDS];   // array holding values for all the fields.  From the Arduino Cookbook


void setup()
{
  Serial.begin(115200); 
}

void loop()
{
  EXEC(m1);
  EXEC(m2);
  EXEC(m3);

  if( Serial.available()) {    //this code is from the Arduino Cookbook.  I'm currently just using the first slot of the array.
    for(fieldIndex = 0; fieldIndex  < 3; fieldIndex ++)
    {
      values[fieldIndex] = Serial.parseInt(); // get a numeric value
      
    }
    fieldIndex = 0;  // ready to start over
   
  }
  
  /////////////////////////
  
  //this takes the values from the serial and fires up the appropriate state machine
  
  switch(values[0])
  {
    case 1:
            m1.Set(m1s2);
            break;
    case 2:
            m2.Set(m2s2);
            break;
    case 3:
            m3.Set(m3s2);
            break;  
    default:
            break;
  }  
  values[0]=0;  
  
  
  
  
  
  
  if(millis()%1000==0){
  
      Serial.print(output[0]);
      Serial.print("  ");
      Serial.print(output[1]);
      Serial.print("  ");
      Serial.println(output[2]);
     
  
  }
}

State m1s1()
{
  output[0]=0;//idle
}

State m1s2()
{
  output[0]=m1.Statetime();//this prints millis elapsed since the state was entered.
  
 
}

State m2s1()
{
  output[1]=0;
}

State m2s2()
{
  output[1]=m2.Statetime();
 
}

State m3s1()
{
  output[2]=0;
}

State m3s2()
{
  output[2]=m3.Statetime();
 
}

Serial.parseInt() is going to loop until the next non-numeric character arrives. This can take a significant amount of time and during that time the rest of the loop() function is not being processed. You can fix that by using a state machine to gather digits into numbers as they arrive.

So something to the tune of...(?)

//psuedocode StateMachine

State 1 : if (serial.available()) StateMachine.Set(State 2)

State 2: for(///etc){values[0]=serial.parseInt(); if (serial.read ==/n) StateMachine.Set(State1) }

Yep…that was it. Put it outside loop() and lost the serial.parseInt(). Still lifted the read function whole cloth from the cookbook. Thanks.

#include <SM.h>

SM m1(m1s1);//the state machines
SM m2(m2s1);
SM m3(m3s1);
SM Read(r1);

long int output[3];




const int NUMBER_OF_FIELDS = 3; // how many comma-separated fields we expect
int fieldIndex = 0;             // the current field being received
int values[NUMBER_OF_FIELDS];   // array holding values for all the fields.  From the Arduino Cookbook


void setup()
{
  Serial.begin(115200); 
}

void loop()
{
  EXEC(Read);
  EXEC(m1);
  EXEC(m2);
  EXEC(m3);
  

 
  
  
  
  
  
  if(millis()%100==0){
  
      Serial.print(output[0]);
      Serial.print("  ");
      Serial.print(output[1]);
      Serial.print("  ");
      Serial.println(output[2]);
     
  
  }
}

void serialEvent()
{
   if( Serial.available()) {    //this code is from the Arduino Cookbook.  I'm currently just using the first slot of the array.
    
   
  }
  
  /////////////////////////
  
  //this takes the values from the serial and fires up the appropriate state machine
  
  switch(values[0])
  {
    case 1:
            m1.Set(m1s2);
            break;
    case 2:
            m2.Set(m2s2);
            break;
    case 3:
            m3.Set(m3s2);
            break;  
    default:
            break;
  }  
  values[0]=0;  
  
}

State m1s1()
{
  output[0]=0;//idle
}

State m1s2()
{
  output[0]=m1.Statetime();//this prints millis elapsed since the state was entered.
  
 
}

State m2s1()
{
  output[1]=0;
}

State m2s2()
{
  output[1]=m2.Statetime();
 
}

State m3s1()
{
  output[2]=0;
}

State m3s2()
{
  output[2]=m3.Statetime();
 
}

State r1()
{
  

  if(Serial.available()>0)
  {
    
    Read.Set(r2);
  }
}

State r2()
  {
    char ch = Serial.read();
    if(ch >= '0' && ch <= '9') // is this an ascii digit between 0 and 9?
    {
      // yes, accumulate the value if the fieldIndex is within range
      // additional fields are not stored
      if(fieldIndex < NUMBER_OF_FIELDS) {
        values[fieldIndex] = (values[fieldIndex] * 10) + (ch - '0'); 
      }
    }
    else if (ch == ',')  // comma is our separator, so move on to the next field
    {
        fieldIndex++;   // increment field index 
    }
    else
    {
        fieldIndex = 0;  // ready to start over
        Read.Set(r1);
    }
  }