munotes®

Blink: A TinyOS Application Read Line by Line

Get access to whole semester resourcesSemester Pass

Chapter Twenty-Nine

Syllabus topic Module 1, "WSN Operating Systems and Ad-hoc Networks: Examples of WSN operating systems (case/examples)" (and the paired practical, "Develop a TinyOS program that toggles LEDs using events and tasks to demonstrate split-phase execution")

Pages 157 to 160 of 862

In one line

Blink makes a mote's three LEDs flash at three different rates, and its ten or so lines contain the whole of TinyOS: a module and a configuration, a boot event, three generic timers, commands to start them, events when they fire, and, in our version, tasks to do the work.

In the wording a student can write in an examination: a TinyOS application consists of a module, which implements the behaviour, and a top-level configuration, which wires the module to system components. In Blink, the configuration wires the module to MainC (which signals Boot.booted() when the node has started), to LedsC (the LED driver) and to three instances of the generic timer TimerMilliC. On boot the module calls startPeriodic on each timer; each timer's fired() event toggles one LED. In a version that follows the practical's brief, each fired() event posts a task and the task toggles the LED, separating the short event from the deferred work.

TinyOS's own Blink, in outline

TinyOS ships Blink in its apps directory as two files, BlinkC.nc and BlinkAppC.nc. Its module uses three Timer<TMilli> interfaces, the Leds interface and the Boot interface. On Boot.booted() it starts the three timers with periods of 250, 500 and 1,000 milliseconds, and each timer's fired() event calls one of led0Toggle(), led1Toggle() and led2Toggle() directly. Its configuration wires the module to MainC, to LedsC and to three separate instances of TimerMilliC made with new. Each event handler also contains a dbg() statement, which prints only when the program runs in the simulator.

It is the smallest complete TinyOS program, and it deliberately has no tasks: toggling an LED is a tiny, fixed amount of work, so it is done in the event itself.

BlinkTask: the practical's version

The practical asks for LEDs toggled "using events and tasks". So in BlinkTask each fired() event does nothing but post a task, and the task does the toggling. The work is trivial, which is the point: the program shows the structure a real application uses when the work is not trivial. It also counts the toggles in the module's frame and prints debugging messages for the simulator.

#include "Timer.h"

module BlinkTaskC {
  uses interface Boot;
  uses interface Timer<TMilli> as Timer0;
  uses interface Timer<TMilli> as Timer1;
  uses interface Timer<TMilli> as Timer2;
  uses interface Leds;
}
implementation {
  uint16_t toggles = 0;          // how many toggles, in the frame

  task void toggle0() { toggles++; call Leds.led0Toggle();
    dbg("BlinkTask", "task toggle0 ran, toggle %u\n", toggles); }
  task void toggle1() { toggles++; call Leds.led1Toggle();
    dbg("BlinkTask", "task toggle1 ran, toggle %u\n", toggles); }
  task void toggle2() { toggles++; call Leds.led2Toggle();
    dbg("BlinkTask", "task toggle2 ran, toggle %u\n", toggles); }

  event void Boot.booted() {
    dbg("BlinkTask", "booted at %s\n", sim_time_string());
    call Timer0.startPeriodic(250);
    call Timer1.startPeriodic(500);
    call Timer2.startPeriodic(1000);
  }

  event void Timer0.fired() {
    dbg("BlinkTask", "Timer0 fired at %s\n", sim_time_string());
    post toggle0();
  }
  event void Timer1.fired() {
    dbg("BlinkTask", "Timer1 fired at %s\n", sim_time_string());
    post toggle1();
  }
  event void Timer2.fired() {
    dbg("BlinkTask", "Timer2 fired at %s\n", sim_time_string());
    post toggle2();
  }
}
munotes.in157

Blink: A TinyOS Application Read Line by Line

configuration BlinkTaskAppC { }
implementation {
  components MainC, LedsC, BlinkTaskC;
  components new TimerMilliC() as T0;
  components new TimerMilliC() as T1;
  components new TimerMilliC() as T2;

  BlinkTaskC.Boot -> MainC;
  BlinkTaskC.Timer0 -> T0;
  BlinkTaskC.Timer1 -> T1;
  BlinkTaskC.Timer2 -> T2;
  BlinkTaskC.Leds -> LedsC;
}

The module, line by line

#include "Timer.h". Brings in the definitions of timer precisions, among them TMilli.

module BlinkTaskC { ... }. The specification: this module uses five interfaces and provides none. Using them means it will call their commands and must implement their events: Boot.booted(), and fired() for each of the three timers.

uses interface Timer<TMilli> as Timer0. One interface type, used three times, so each use is renamed with as. The type parameter TMilli sets the timer's precision. TEP 102 defines it precisely: one second contains 1,024 binary milliseconds, and TMilli means "1024 ticks per second". A period of 250 therefore means 250 / 1,024 of a second, about 0.244 seconds, not a quarter of a second exactly. The next chapter's simulator log shows exactly that.

uint16_t toggles = 0. A variable in the module's frame: allocated when the program is compiled, private to this module, kept for the life of the program.

task void toggle0() { ... }. A task, declared with the task keyword, taking no parameters (TinyOS 2's basic tasks never do). It increments the counter, calls the command Leds.led0Toggle(), and prints a debugging line. Three separate tasks are needed because a basic task cannot be told which LED to toggle.

event void Boot.booted(). The first code of the application to run. TinyOS's boot sequence (TEP 107) initialises the system and then signals booted() through MainC. The handler calls startPeriodic on each timer, a command that returns at once; the timers now run on their own.

event void Timer0.fired(). Signalled every 250 binary milliseconds. It prints a line and posts toggle0. Posting returns immediately; the handler ends; the scheduler will run the task when its turn comes.

The configuration, line by line

configuration BlinkTaskAppC { }. The top-level configuration: an empty specification, because the whole application provides and uses nothing outside itself.

components MainC, LedsC, BlinkTaskC. Three single-instance components: the system's boot component, the LED driver, and our module.

components new TimerMilliC() as T0; ... Three generic timer components, each a separate instance, so each timer can run at its own period.

BlinkTaskC.Boot -> MainC. A link wire from the module's used Boot to MainC's provided Boot; the interface name on MainC is left out because it has only one Boot. The other four lines wire the three timers and the LEDs the same way.

munotes.in158

Blink: A TinyOS Application Read Line by Line

What happens, in order

  1. The node powers up. TinyOS initialises its components and signals Boot.booted().
  2. booted() starts three timers and returns. The scheduler finds no tasks and puts the processor to sleep.
  3. After 250 binary milliseconds the hardware timer interrupts; the timer component signals Timer0.fired(); the handler posts toggle0 and returns.
  4. The scheduler runs toggle0 to completion: LED 0 changes state.
  5. Nothing is queued: the processor sleeps until the next timer interrupt.
  6. At 500 binary milliseconds, Timer0 and Timer1 are both due. Their events are signalled one after the other, each posting its task; the scheduler runs the tasks in FIFO order.

So LED 0 changes every 250, LED 1 every 500 and LED 2 every 1,000 binary milliseconds: a three-bit binary counter in lights.

Worked example: the numbers

The compiled size. Built for the TelosB by this book's checker, BlinkTask compiled with no warning from its own files and reported 2,682 bytes of program memory and 60 bytes of RAM. The three timers, the LED driver, the scheduler and the boot sequence are all included. Against the 48 kB of flash on the TelosB's MSP430F1611, that is about 5.5 per cent.

The toggles in one second. Timer0 fires at 250, 500, 750 and 1,000 binary milliseconds, 4 times; Timer1 at 500 and 1,000, 2 times; Timer2 at 1,000, once. So by 1,000 binary milliseconds, which is 1,000 / 1,024 of a second, about 0.977 seconds, the counter reads 4 + 2 + 1 = 7. The next chapter's simulator run ends with exactly "toggle 7".

The timing unit, worked. 250 binary milliseconds is 250 / 1,024 of a second:

250 / 1,024 = 0.244140625

seconds, which is where the first toggle comes in the simulator.

Distinctions

TinyOS's BlinkBlinkTask (this chapter)
Timer periods250, 500 and 1,000250, 500 and 1,000
Where the LED is toggledDirectly in each fired() eventIn a task the event posts
TasksNoneThree
ShowsThe smallest complete applicationEvents and tasks, as the practical asks
In the event handlerIn the task
RunsWhen the timer firesLater, when the scheduler reaches it
Should doThe minimum: note the event, post the workThe work itself
Can be preempted byOther interrupts, if the event is asyncInterrupts, never by another task

What it does not mean

startPeriodic(250) is not a quarter of a second. TMilli is 1,024 ticks a second, so it is 0.244 seconds.

Posting a task does not run it. It queues it; the handler returns first.

munotes.in159

Blink: A TinyOS Application Read Line by Line

A task is not needed for every event. TinyOS's Blink toggles directly in the event, which is right for so little work. The task exists in BlinkTask to show the structure a larger job needs.

Three timers are not three threads. They are three instances of a timer component, all driven by one hardware timer and one scheduler.

Quick revision

  • TinyOS Blink: module BlinkC and configuration BlinkAppC; three timers at 250, 500, 1,000; each fired() toggles one LED directly.
  • BlinkTask: each fired() posts a task; the task toggles the LED and counts in the frame.
  • uses interface Timer<TMilli> as Timer0: one interface type used three times, renamed with as.
  • TMilli is 1,024 ticks a second (TEP 102): 250 means 0.244140625 s.
  • Configuration: MainC (Boot), LedsC, three new TimerMilliC(); wired with ->.
  • Order: boot, start timers, sleep; interrupt, fired(), post, return; task runs to completion; sleep.
  • Compiled for TelosB: 2,682 bytes ROM, 60 bytes RAM; toggles in the first 1,000 binary milliseconds: 4 + 2 + 1 = 7.

Test yourself

1. Explain the structure of the Blink application. A module that uses Boot, three Timer<TMilli> interfaces and Leds, and a top-level configuration that wires it to MainC, LedsC and three instances of TimerMilliC. On boot the module starts the timers with periods 250, 500 and 1,000; each timer's fired() event toggles one LED.

2. Modify Blink so that the LEDs are toggled by tasks. Why might you do this? Declare a task for each LED, and in each fired() event post the task instead of toggling; the task calls the toggle command. For an LED it makes no difference, but for real work it keeps the event handler short, so other events and tasks are not delayed.

3. What does TMilli mean, and how long is a period of 250? A timer precision of 1,024 ticks per second, binary milliseconds (TEP 102). 250 of them are 250 / 1,024 = 0.244140625 seconds.

4. Why is each timer declared with as? Because the module uses the same interface type three times, and each use needs its own name so that it can be wired separately and its events told apart.

5. By the end of the first 1,000 binary milliseconds, how many toggles has BlinkTask made? Timer0 has fired 4 times, Timer1 twice and Timer2 once: 7 toggles.

munotes.in160

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Issue
Done!