Infinite loops in Squirrel

Squirrel programs, both on the device and as agents, have to be written in an “event-driven” style, where basically the system runs the main loop for you, and all the code that you write hangs off it as handlers for individual events. (That’s because there are other, system, event handlers also hung off the same main loop – for handling network messages, for instance.) While this is a well-known way of writing, say, UI code or server software, it’s less common in embedded programming, where everyone’s more used to writing their own main loop. If you attempt to do that on an imp, though, you’ll find that other important things, including network messages, don’t get handled.

Fortunately for the status quo, there is a way to write (what looks very like) your own main loop – yet still leave the system event handlers running. It relies on an obscure (and, frankly, under-documented) feature of Squirrel: generators.

Let’s start by looking at the code:

`local looper;

function pause(sec)
{
return imp.wakeup(sec, function() { resume looper; });
}

function mainloop()
{
while (1) {
hardware.pin1.write(1);
yield pause(1.0); // Line 12
hardware.pin1.write(0);
yield pause(1.0); // Line 14
}
}

looper = mainloop(); // Line 18
resume looper; // Line 19
`

That function “mainloop” has a while(1) in it, which is normally a no-no – but inside that loop it repeatedly yields (lines 12 and 14). Every time Squirrel executes a “yield”, the current function stops executing – but keeps all its internal state, such as local variables – and it starts again when you “resume” it.

The syntax is a bit unusual. The “mainloop” function is what Squirrel calls a “generator” (which means, a function that includes one or more “yield” statements). Calling such a function the first time (line 18) doesn’t actually cause the function to run: it just returns an object (the generator object), which you have to “resume” (line 19) to start it running.

Once “mainloop” is running, every time it yields it also calls that “pause” function, which sets a timer wakeup for N seconds later – a wakeup which, when it fires, resumes the generator again and continues execution. But meanwhile, before the wakeup fires, the rest of the system continues to run and to process network messages and so on.

Squirrel generators are similar to facilities in other languages known by names such as “co-routines” or “co-operative threads”.

Peter

Peter, this is interesting, thanks for writing this up.

Is there an advantage to the “generator” technique as opposed to calling imp.wakeup in a main function recursively like the code below.

realizing that in the case below there are other events that may occur such as pin state changes and serial data coming in.

?

`
mynextwakeup <- null;

function main () {

//do something here
server.log(“run main”);

mynextwakeup = imp.wakeup(1.0, main);  

}

main();`

The advantage is only that it might look a bit more familiar: to have a main loop and not lose all its local variables each time round the loop. You don’t gain anything from using generators if you’re happy writing in the pure event-driven style shown in your piece of code.

Peter

Peter thanks for this. Any chance of adding Arduino after …common in embedded or somewhere else in text. As this will help those searching/coming from that platform to e.imp

[deleted]

Sounds like GOTO … oh the memories of BASIC
We have been discussing yield internally lately, trying to work out how to use it properly.

A little while back I resorted to GOTO in a Mac OS app I was working on. It just seemed to make it easier to mentally track the flow into, round a number of times, and then out of the loop. I am a fan.

Just had a play with it and it works as promised.
a/ I assume the “looper” variable just needs to be in scope, so an object could hold that just the same?
b/ Can you have multiple generator objects? Could you keep a stack of them in an array?
c/ Pause is handy but can you think of a less contrived example of when to use yield/resume?
d/ What is the overhead like? Is this an efficient way to handle wait timing in comms (uart/spi/i2c) routines?

A.

@Aron

a/ Yes – generators are first-class objects in Squirrel and can be stored in tables, objects or arrays.

b/ Yes – in fact that’s another advantage I should have mentioned. Not only can you write an infinite loop as a generator – you can write several, and have them not interfere. On traditional embedded platforms you’d have to do that by using multiple threads.

c/ I’m not sure I’d describe that example as “contrived” – it closely resembles many people’s first program, which turns an LED on and off. If you’d like another example, it lets you write code as if a UART were blocking:

local byte = uart.read(); local cmd = ""; while (byte != '\ ') { // Blocking loop -- DON'T DO THIS! if (byte != -1) { cmd += byte.tochar(); } byte = uart.read(); } processLine(cmd);

but without actually blocking the rest of the system:

uart.configure(115200, 8, PARITY_NONE, 1, NO_CTSRTS, function() { resume looper; }); local byte = uart.read(); while (byte != '\ ') { if (byte == -1) { yield; // the UART callback will resume us } else { cmd += byte.tochar(); } byte = uart.read(); } processLine(cmd);

(I haven’t tested this one – you might have to yield a dummy value instead.) Again, it doesn’t technically offer any more functionality than a pure-callback solution, but it might read more naturally to some, especially if you have to wait for several UART things in a row.

d/ The overhead should be low – no more than a normal imp.wakeup(). Maybe even less, because the generator will be restarted with all its local variables intact.

Peter

(Edited to move the uart.read() to the bottom of the loop where it belongs.)

Thanks, Peter. These are helpful examples. I am interested in using generators in some applications, but I’ve been scared to use it with so little documentation in circulation.
I have a requirement to wait for a sequences of bytes through a uart port. However, in the event that the bytes do not arrive, I need a timer to timeout and abort the process. How would I adjust the example above, so that I’m not waiting for a terminating char, but instead exiting after an idle period?

I’ve tried doing this with imp.wakeup(timeout, function() { idleTimeout=true; resume looper; } ); uart.configure(115200, 8, PARITY_NONE, 1, NO_CTSRTS, function() { resume looper; }); ... local byte; while (!idleTimeout){ if ((byte = uart.read()) == -1) { yield; // the UART callback will resume us } else { cmd += byte.tochar(); } }

But the timer callback is never called. How would I structure it to work correctly?

have a look at line 18 and line 19 in peter’s example. He is showing that the while loop should be in a ‘main’ function. That function main gets assigned to ‘looper’.

maybe you have that in your code and snipped it out?

is your serial data coming in non-stop?

No, the serial data is a packet of variable length that should arrive within a certain timeframe. I am receiving the serial data just fine. The uart callback is invoked and it resumes the generator. After the packet is received (or if no packet is ever received), the routine just sits there ‘yeilding’ ad infinitum.

It turns out that there’s nothing new under the sun.

Here http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/ is an article on using generators for reading serial data. The code uses Python, whose syntax is, to put it mildly, not the same as Squirrel – but the concept of generators is similar. (Although Python additionally has generator-expressions, and yield-expressions, which Squirrel doesn’t.)

The linked articles http://www.dabeaz.com/generators/index.html and http://www.dabeaz.com/coroutines/index.html might also be worth a look, though they’re substantially more hardcore, and also spend more time with those Python features that Squirrel doesn’t share.

Peter