Strategy for long duration subroutine

I am building a project that will run 3 or 4 different subroutines depending on which instruction is sent from a PC. One of the subroutines involves moving a heavy object with a servo so the servo must be instructed in short steps with gaps between them. There is also another servo that is used as part of that process to achieve precise positioning. The total servo movement time might be 10 or 20 seconds. This is working perfectly well.

With a simple approach to the program the Arduino will not check for another instruction from the PC until the servo movement is completed - i.e. a 10 to 20 second gap. For example a program structure like this (pseudo code)

loop {

    get instruction from PC
    call the appropriate subroutine
    }

shortRoutine1 {
    switch on LED
}

shortRoutine2 (
  turn off LED
}

longRoutine {
    use servos to move turntable to new position
}

It would be nice if the Arduino could respond to another command from the PC without waiting so long. Ideally an interrupt would be triggered when all 5 characters of a new command are received - but I haven't come across any way to do that. Interruptions that cause delays in the servo-move aren't a problem.

I have thought of peppering calls to the PC reading stuff throughout the long servo-move subroutine - perhaps wherever one would normally put a delay().

I am wondering if anyone else has other ideas about how to deal with this sort of issue.

Thanks

...R

You could always have a look at the blink without delay example for clues.

You could use a state machine to move the servo in between Serial checks.

There are two fundamental approaches to software design: blocking, and non-blocking.

In a blocking design, the code executes a sequence of actions, waiting for events as needed. A classic example is using a blocking delay: if you want an LED to flash on for a second, the blocking approach is to turn it on, wait for a second to elapse and then turn it off. This is conceptually very simple to code since you just program in the expected sequence of actions and events, and at run-time your position within the code tells you where you are within the sequence of actions/events. However, it is only suited for very simple applications because with this approach it’s hard to manage more than one thing at a time.

In a non-blocking design, the code looks for and handles any events that occur but does not stop and wait (block) for events to occur. The non-blocking approach results in more complex code because now your application has to explicitly remember where it is in each sequence of actions/events. Typically, this is achieved using a state machine. If you design it correctly then a state machine managing one aspect of your code (making an LED flash) can run independently of other aspects of your code (handling serial input, moving servos etc) and you can manage a large variety of independent functions without any more complexity. The ‘blink without delay’ is a very simple example of the non-blocking approach to program design, but the example doesn’t really explain why this is so important, or how to apply the same approach to the whole sketch.

Each state machine would consist of variables recording the current state, and some code to determine whether any action is needed.

For a servo controller, the state might be the current position of the servo and when it was last moved, and the event detection code might check whether the current position is different to the target position and enough time has elapsed since the last movement, implying that the servo needs to be moved again.

For a serial port handler, the state might be a buffer of characters received so far, and the event detection code might be a call to Serial.available().

If you organise your code around the different independent things that need to be managed then you can have very simple code with minimal coupling between all these different activities, and at the top level all you need to do is handle each functional area:

void loop()
{
  handleSerialInput();
  handleServos();
  handleLedFlashing();
  handleLcdOutput();
 // etc
}

I have been thinking of a state-machine but the nature of the servo movements (at least as I conceive them at the moment) suggest that it would get very complex and I am wondering is it worth the trouble. I will give it more thought in case there may be a different way of managing the servo movement.

I have found some websites that have Arduino threading systems but I haven't yet studied them closely. Has anyone any experience of any threading system?

If I wasn't already planning to use Timer1 to generate radio control PPM pulses and Timer2 to drive the servos (using the ServoTimer2 library) I would be tempted to set up an interrupt to call the PC reading routine periodically.

...R

Robin2: I have been thinking of a state-machine but the nature of the servo movements (at least as I conceive them at the moment) suggest that it would get very complex and I am wondering is it worth the trouble. I will give it more thought in case there may be a different way of managing the servo movement.

Why complex?

long routine(){
if(servos have not reached their destination){
step servos once
}
}
shortroutine1{
if(there's something useful for it to do now){
do it
}
}
shortroutine2{
if(there's something useful for it to do now){
do it
}

}

That way, all your routines get equal treatment each each time round the loop, no matter how long each routine takes.

Can you post the servo moving code so we can see what it is about it that takes up so much time?

The servo code takes a long time only because I want to move the turntable slowly (it's heavy and also trains might fall off) and because it needs to be stopped in precise alignment with the in/out track.

I'm not posting the code I already have as I don't want to get into that sort of discussion. I'm just looking for strategies for dealing with this sort of problem. Once I have a suitable idea I don't expect any problem turning it into code (and if there is I will come back with a specific question).

Overnight I have begun to get the glimmerings of an idea of how I could restructure it into something like a State machine. But that will remove a lot of what makes the initial approach attractive - complete in itself, easy to understand, easy to modify, easy to debug and clearly representative of the physical reality.

I must look further at Threading as that may allow me to leave the simple code intact.

Other strategies are very welcome.

...R

I don't share your view about state machines being complex. I find that finite state machines are a very effective way to make complex systems simple.

The trick is to choose states that represent something meaningful in the problem domain, and structure the implementation so that the order of states in your code corresponds to the order in which you expect the states to occur. In this way it is conceptually very similar to blocking code, in that you can look at the source and see the sequence of events and actions that are expected. Also make sure you abstract out the code to detect events and perform actions so that the top level code for each state is very short - this makes it easier to see the logical sequence instead of getting bogged down in the implementation details.

If the state machine ends up being repetitive with similar logic in different states, or repeated logic handling permutations of similar conditions, consider whether you should have a compound state machine instead of a single state machine.

Blink without delay will serve you well in this scenario. Keep a variable with your desired servo position and another one with the current position. Check every time around loop whether it's time to move the servo and if so, whether there is any movement needed.

Psuedo threading is going to bring a whole bunch of new problems - I'd suggest that it really isn't necessary or desirable in this case.

I've been thinking more about the State machine approach bearing in mind PeterH's view that it can be simple.

The business of moving the turntable involves 4 sequential tasks 1. Use a servo to lift a sensor out of the way - this needs a short delay but I haven't timed it yet - maybe 1 second. 2. Move the table from wherever it is to a position defined by a number of microseconds (in the range 580 to 2400). I have found that moving in steps of 10 microseconds with a 20 millisecond delay is about the right speed though a little slower might be better. 3. Lower the sensor and allow time for it to descend 4. Move the table from wherever it is until it touches the sensor - checking for contact on each step (contact grounds one of the digital pins).

In the simple model for this problem all of that can be included in a routine called moveTable which is called from the main loop if that is the instruction from the PC. But of course everything else must wait until all 4 steps are finished. (Other PC instructions will be to move trains and maybe (later) operate signals)

At the moment I can only envisage an alternative where each of these turntable tasks is scheduled from the main loop with each task only called when the appropriate time has elapsed. (For example a move will just move the table 10 microseconds and then return.) It also means that there must be a process to keep track of which task is in progress and, in the case of a move, whether all the steps have completed. To my mind this is considerably more complex.

My attitude to complexity is also coloured by the fact that the wait for all 4 tasks to continue isn't a deal breaker. On the other hand there will be two turntables and it might be nice to see them both moving at the same time!

Perhaps others have a different concept of how to deal with these tasks?

Thanks ...R

I’d start with the following states:

enum TurnTableState { IDLE, RAISING, MOVING, LOWERING, POSITIONING };

Define a variable holding the state of a single turntable.

Define a function to maintain the state machine by testing for events relevant to the current state. In a simple version you could just use a plain function operating on global data. Given that you eventually need more than one of these, if you’re comfortable dealing with classes then I would be strongly inclined to collect the state data and code into a class. But the simple non-OO implementation could be structured like this:

// incomplete, untested

TurnTableState state = IDLE;

void beginTurntableMovement()
{
  raiseServo();
  state = RAISING;
  startTime = millis();
}

void handleTurntable()
{
  switch(state)
  {
    case IDLE:
      // no action needed
      break; 
    
    case RAISING:
      if(millis() - startTime > RaiseTime)
      {
        // finished raising
        startTime = millis();
        state = MOVING;
      }
      break; 

    case MOVING:
      if(millis() - startTime > MoveInterval)
      {
        // time to advance the turntable by one step
        startTime  = millis(); // reset the timer for the next step
        incrementTablePosition();
        if(tablePosition >= EndPosition)
        {
          // movement is complete
          lowerServo();
          state = LOWERING;
        } 
      }
      break; 

    case LOWERING:
      if(millis() - startTime > LowerTime)
      {
        // finished lowering
        startTime = millis();
        state = POSITIONING;
      }
      break; 

    case POSITIONING:
      if(sensorContact())
      {
        // table movement cycle complete
        state = IDLE;
        // here we could trigger any other action that needed to follow the table movement
      }
      else
      {
        if(millis() - startTime > PositioningInterval)
        {
          // time to advance the turntable by one step
          startTime  = millis(); // reset the timer for the next step
          incrementTablePosition();
        }
      }
      break; 
  }
}

I don’t understand the details of how the movement occurs so I haven’t tried to define exactly how the position changes would be done but hopefully you can follow the overall structure of the state machine.

I have just posted a way to do lightweight cooperative multi-tasking. http://arduino.cc/forum/index.php/topic,164821.msg1230258.html#msg1230258

I just recently tested it with 3 'blink led' task's and it worked fine. See also http://arduino.cc/forum/index.php/topic,164707.0.html

Hope it helps.

PS: No state machines required :P

@peterH - thanks for all your work. You have captured my idea well and what you have suggested looks nice and clean.

@obiwanjacobi - I will study your code - thanks, also.

...R