As the title says... Chat on...

User avatar
By SteveBaker
#55715
cwr wrote:A common way of handling small embedded applications is with a state machine:
Code: Select allwhile (true) {
    switch (state) {
    case a: do_a()
            state = b
    case b: do_b()
            state = c
    case c: do_c()
            state = a
    }
}


The fundamental problem is that the ESP8266 doesn't like to run continuously. It expects to wake up, do some small task, then go back to sleep again. So the "main loop" idea has to go. Finagling your way around this fundamental fact is a bad idea.

In your example, the do_a(), do_b(), and do_c() functions run continuously, which simply isn't something an ESP is designed to do. What you really want is to trap state CHANGES and have the state change code do the work. So instead of having that main loop and saying things like "state = b ;" when the state changes, instead have "state" be a hidden variable and change it by calling a function like this:
Code: Select allvoid changeState ( int new_state )
{
   static int state = a ;
   if ( new_state == state ) return ;   // Nothing changed, so no work to do.
   state = new_state ;
   switch ( state )
   {
      case a : do_a() ; break ;
      case b : do_b() ; break ;
      case c : do_c() ; break ;
   }
}

This doesn't do exactly what your example does - it only calls a "do_x()" function once, when the state change calls for it rather than continuously as your code does. That's not necessarily what you wanted - but it *is* what you have to do.

So, for example, suppose I want to drive a stepper motor under WiFi control - so my program is sleeping until a TCP packet arrives - that packet calls "changeState" to switch from "state=idle" to "state=spinMotor" or something. But driving a stepper motor isn't a one-time thing, you have to keep stepping it - so the "do_SpinMotor" code has to set up a timer event to cause the ESP to wake up every millisecond (or whatever) to step the stepper motor once....and then go back to sleep again. The "do_Idle" function has to cancel any existing stepper motor timer events.

Tricking the software into doing it the way you imagine is a really bad idea. This is a different way of thinking - but it doesn't prevent you from using a state machine. Quite the opposite, actually. Having a global 'state' makes this style of event-based programming much easier.

The *HUGE* win here is that if you do things like this, the ESP chip will sleep through all of the boring parts of it's day - and your battery life will almost certainly be MUCH higher!
User avatar
By cwr
#55803 Thanks - that's extremely helpful. After spending some time digging around in the lower
levels of the NodeMCU and Arduino code I was coming to the conclusion that some
sort of timer was the only way to get repetitive actions, and you've confirmed that.

The problem I couldn't solve was forcing an update of the ESP8266's underlying dispatcher;
there didn't seem to be any way to break out of the Lua interpreter. However, following
through the low-level code it seems that Lua callbacks are called indirectly via the dispatcher,
and that gives the housekeeping tasks a chance to run.

The Arduino implementation uses a yield() call into the firmware, and that can be built into
a Lua C module, but I haven't got it to work. Your approach looks much more reasonable.

Thanks again - Will