Why not while(true)?

Many programs have a basic structure of:

 while (true)
{
    // Program goes here
}

This approach is logical if your program is responsible for monitoring user input, device I/O, threads, etc.  However, it's not an optimal approach for most Gadgeteer programs.  The rest of this post will explore the Gadgeteer event dispatcher, execution model, and best practices for utilizing their benefits.

How Gadgeteer Programs Run

When you write a Gadgeteer program, the code you want to run first should go into the ProgramStarted method.  The system calls this method after the hardware is booted and the event dispatcher is running.  ProgramStarted is a good place to create event handlers, set initial state in modules, and show a welcome screen if you're using a display.

However, you don't want to run an infinite loop in ProgramStarted.  Why not?  Because none of your event handlers will get called until after ProgramStarted exits.  That includes any timers you've created.

This happens because modules send events to a queue which is processed by the same thread that executes ProgramStarted. If ProgramStarted never exits, the dispatcher never starts.

The .NET Micro Framework Dispatcher

Gadgeteer uses an event dispatcher to funnel events from modules and timers to your program.  Many modules do processing on their own threads, but they send events to a dispatcher on the main program thread.  That dispatcher routes events to the appropriate event handlers in your code.

That means you don't need to worry about multi-threading (and its pitfalls like race conditions) unless you choose to create multiple threads yourself.  Any multithreading done by modules and timers is strictly behind the scenes.

The dispatcher makes it easy to work with devices that work asynchronously, too.  For example, many camera modules take a while (in microprocessor clock cycle terms) to capture a picture, encode it into a useful format and send the data to the main processor.  Networking events happen sporadically, and your application shouldn't spend time polling the network if it's not active.  Same with user input components - you want to know when the user presses or releases a button, but you don't want to spend processor resources on it otherwise.

The tricky thing is that the dispatcher runs on the same thread as your program code, so you have to give it time to run.  Any method which contains an infinite loop (or which takes a very long time to complete) will prevent the dispatcher from notifying your program about important events.

Care and Feeding of the Dispatcher

There are two design patterns which work very well for Gadgeteer programs.

1)  Register event handlers whenever possible. Don't block on user or device input, as this will prevent other parts of your project from functioning.

2)  Create a Gadgeteer.Timer and listen for its Tick event.  Any operation which you want to do at regular intervals is a good candidate for a timer. 

Here's an example of a program which controls a Button module that has an onboard LED.  The LED is set to blink on and off at a 1 second interval, and the program prints debug text when the user presses the button.

 public partial class Program
    {
        GT.Timer ledBlinkTimer = new GT.Timer(1000);
 
        void ProgramStarted()
        {
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            ledBlinkTimer.Tick += new GT.Timer.TickEventHandler(ledBlinkTimer_Tick);
             ledBlinkTimer.Start();
         }
 
        void ledBlinkTimer_Tick(GT.Timer timer)
        {
            if (button.IsLedOn == true)
            {
                button.TurnLEDOff();
            }
 
            else
                button.TurnLEDOn();
        }
 
        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button Pressed");   
        }
    }

This program will not block the dispatcher from running, and it won't spend a lot of processor cycles listening for button events.

The corresponding anti-pattern for Gadgeteer looks like this:

     public partial class Program
    {
        GT.Timer ledBlinkTimer = new GT.Timer(1000);
 
        // Don't do this with Gadgeteer.  It won't work!
        void ProgramStarted()
        {
            ledBlinkTimer.Tick += new GT.Timer.TickEventHandler(ledBlinkTimer_Tick);
            ledBlinkTimer.Start();
 
            // This while loop prevents the timer event from firing
            while (true)
            {
                if (button.IsPressed == true)
                {
                    Debug.Print("Button pressed");
                }
            }
            
        }
 
        // This event handler is never called because the program thread is blocked!
        void ledBlinkTimer_Tick(GT.Timer timer)
        {
            if (button.IsLedOn == true)
            {
                button.TurnLEDOff();
            }
 
            else
                button.TurnLEDOn();
        } 

This pattern is quite common, especially for embedded devices.  It's not wrong, but it doesn't work with the Gadgeteer dispatcher, which is essentially a while(true) loop itself!

Questions?

The Microsoft Research team who created Gadgeteer hangs out on the Gadgeteer forums.  We're also on Twitter (@netgadgeteer) and Facebook if you want to find us.