an introduction to nesc and tinyos, or, a really complicated way to build very simple applications

95
An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications CENS Summer Internship 07/08/05 Ben Greenstein – [email protected] Contributors: Tom Schoellhammer, Deepak Ganesan, David Culler’s Lab (UCB) CENS Systems Lab

Upload: clover

Post on 20-Mar-2016

44 views

Category:

Documents


1 download

DESCRIPTION

An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications. CENS Summer Internship 07/08/05 Ben Greenstein – [email protected] Contributors : Tom Schoellhammer, Deepak Ganesan, David Culler’s Lab (UCB) CENS Systems Lab. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

An Introduction to nesC and TinyOS, or,A really complicated way to build very simple applications

CENS Summer Internship07/08/05

Ben Greenstein – [email protected]

Contributors: Tom Schoellhammer, Deepak Ganesan, David Culler’s Lab (UCB)

CENS Systems Lab

Page 2: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

2

Range of platforms with different constraintsCa

pabi

litie

s

Size, Power Consumption, Cost

MICA Mote

MK - II

StarGate

Spec

Ample resources

Solutions: Linux, uCos, Emstar…

Highly constrained (memory, CPU, storage, power)

Solutions: TinyOS,…

Page 3: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

3

Why do we use utterly constrained platforms?

• No wires = batteries and radios

• Resources drain batteries– Radio, Flash, ADC, RAM, MCU, etc.

• Deployments are often remote– Don’t always have summer interns to change batteries

• Constrained platforms are smaller• Constrained platforms are cheaper

Page 4: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

4

Technology for low-power, cheap nodes

• CMOS miniaturization– 1 M trans/$ tiny (~mm2), inexpensive processing and storage– 1-10 mW active, 1 W passive (at 1% use 100 W average)

• Micro-sensors (MEMS, Materials, Circuits)– acceleration, vibration, gyroscope, tilt, magnetic, heat, motion,

pressure, temp, light, moisture, humidity, barometric– chemical (CO, CO2, radon), biological, micro-radar, ...– actuators too (mirrors, motors, smart surfaces, micro-robots)

• Communication– short range, low bit-rate, CMOS radios (1-10 mW)

• Power– batteries remain primary storage (1,000 mW/mm3), fuel cells 10x– solar (10 mW/cm2, 0.1 mW indoors)

• 1 cm3 battery 1 year at 10 msgs/sec

Page 5: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

5

Characteristics of Network Sensors

• Small physical size and low power consumption

• Concurrency-intensive operation– multiple flows, not wait-command-respond=> never poll, never block

• Limited Physical Parallelism and Controller Hierarchy

– primitive direct-to-device interface– Asynchronous and synchronous devices=> interleaving flows, events, energy

management• Diversity in Design and Usage

– application specific, not general purpose– huge device variation=> efficient modularity=> migration across HW/SW boundary

• Robust Operation– numerous, unattended, critical=> narrow interfaces

sensorsactuators

network

storage

Page 6: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

6

What is a mote?

• Jason Hill’s Master’s Thesis (UCB)• PhD Dissertation was supposed to be a prototype for a smart-

dust system on a chip– Small physical size: 1 mm3

– Low Power Consumption: < 50 mW

Page 7: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

7

Anatomy of a 3rd generation mote (mica2)

• Constraints– 4KB RAM– 128KB Program Flash Memory– >25mA (Tx), <15uA (sleep) at 3.3V– 8MHz Microcontroller– 19.2Kbps (at 433 or 916MHz)

• Other exciting details– 512KB Measurement Flash– 4KB Configuration EEPROM– 10bit ADC– 3 LEDs– 51pin expansion connector– Transmission range ~500ft outdoor– Runs on 2 AA batteries

Page 8: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

8

Processing Sub-System

• Functions– Application Execution– Resource Management– Peripheral Interaction

• Atmel AVR ATMEGA128L– RISC Architecture– 8 bit ALU/data-path– 128 Kb FLASH - Code– 4 Kb SRAM - Data– Multiple peripherals

Details are available in the ATMEGA128L Datasheet

Page 9: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

9

Sensing Sub-System

• Functions– Sampling physical

signals/phenomena• Different types of sensors

– Photo-sensor– Acoustic Microphone– Magnetometer– Accelerometer

• Sensor Processor Interface– 51 Pin Connector– ON-OFF switches for individual

sensors– Multiple data channels

Sensors consume powerTurn them off after sampling !

Useful Link/Resources• http://www.tinyos.net/ Look under Hardware Designs tab• Crossbow website http://www.xbow.com

Page 10: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

10

Mica Weather Board – Weather monitoring applications

• Total Solar Radiation• Photosynthetically Active Radiation

– Resolution: 0.3A/W• Relative Humidity

– Accuracy: ±2%• Barometric Pressure

– Accuracy: ±1.5mbar• Temperature

– Accuracy: ±0.01oC• Acceleration

– 2 axis– Resolution: ±2mg

• Designed by UCB w/ Crossbow and UCLA

Revision 1.5

Revision 1.0

Page 11: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

11

Communication Sub-System• Functions

– Transmit – Receive data packets wirelessly

– Co-ordinate/Network with other nodes

• Implementation– Radio

• Modulation – Demodulation• Two types of radios: RFM, ChipCon

CC1000• RFM: Mica & predecessors• CC1000: Mica2 onwards

– AVR• Protocol Processing

Page 12: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

12

Wireless Comm. Basics

• RFM Radio– Simple radio, only modulates-demodulates bits• CC1000 Radio– Performs Manchester coding-decoding and synchronization also

Page 13: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

13

AVR Peripherals

• UART– Serial communication with the PC

• SPI – Serial Peripheral Interface– Synchronous serial communication– Interface to Radio in the Mote

• ADC– Analog – Digital Converter– Digitizing sensor readings

• I/O Ports– General Purpose Input Output pins (GPIO)– Used to light up LEDs in Mote

Page 14: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

14

Radio Power Management

• Radio has very high power consumption– Tx power is range dependant - 49.5 mW (0 dBm)– Rx power is also very high - 28.8 mW– Power-down sleep mode - 0.6 uW– Above data for CC1000, 868 MHz (Check out CC1000 data-sheets for more

numbers)• Radio power management critical

– Idle state channel monitoring power = Rx Power– Put radio to sleep when not in use– But then, how do we know when somebody is trying to contact us ?

Page 15: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

15

AVR Power Management

• Low Power operation – 15 mW @ 4 MHz• Multiple Sleep Modes

– Sleep Modes: Shutdown unused components– Idle Mode – 6 mW

• CPU OFF, all peripherals ON• CPU “woken up” by interrupts

– Power Down Mode – 75 uW• CPU and most peripherals OFF• External Interrupts, 2 Wire Interface, Watchdog ON

– Power Save Mode – 120 uW• Similar to Power Down• Timer0 continues to run “asynchronously”

Page 16: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

16

Typical sensor network operation

• Sensing Subsystem– Keep the very low power sensors on all the time on each node in the network

• Processing subsystem– Low-power sensors interrupt (trigger) processor when “events” are identified OR– Processor wakes up periodically on clock interrupt, takes a sample from sensor,

processes it, and goes back to sleep.• Radio subsystem

– Processor wakes up radio when event requires collaborative processing or multi-hop routing.

• Tiered architectures of above subsystems can be envisaged in other platforms

Page 17: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

17

Traditional Systems

• Well established layers of abstractions

• Strict boundaries• Ample resources• Independent apps at endpoints

communicate pt-pt through routers

• Well attended

User

System

Physical LayerData LinkNetwork

TransportNetwork Stack

Threads

Address Space

Drivers

Files

Application

Application

Routers

Page 18: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

18

by comparison ...

• Highly Constrained resources– processing, storage, bandwidth, power

• Applications spread over many small nodes– self-organizing Collectives – highly integrated with changing environment and network– communication is fundamental

• Concurrency intensive in bursts– streams of sensor data and network traffic

• Robust– inaccessible, critical operation

Page 19: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

19

So, how do we write programs for motes?

• Write C-style programs using a language for embedded software development (e.g., nesc)

• Use a cross-compiler to build a binary image for a mote MCU (e.g., avr-gcc).

• Use a programmer (e.g., uisp) to load the binary onto a mote

• Event driven execution:– Messages received over radio, discrete event sensors generate

interrupts when they detect things, timers go off.– We write handlers (functions) that are called in response to various

events.– We write tasks to do background processing

Page 20: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

20

What are TinyOS and nesC?

• TinyOS is a collection of software modules that can be glued together to build applications. Examples:

– GenericComm: send and receive radio packets– TimerC: start timers and get notified when they expire– ADC: sample light and temperature data, among others– UART: communicate over the serial interface– LedsC: make pretty lights blink

• TinyOS is also a FIFO scheduler:– Interrupts are handled immediately– Background tasks are scheduled (put on a queue are are executed when

there’s nothing more important to do)

• NesC is the language in which TinyOS modules are written– To define modules and the interfaces that connect them– Can create configurations, which are hierarchies of glued together modules

Page 21: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

21

TinyOS Goals (claims)

• Flexibility– new sensor network nodes keep emerging

• Telos, iMote, mica2, mica2Dot, etc.– Flexible hardware/software interface

• Future designs may require different HW/software interfaces and may move service (MAC, e.g.) into hardware or software

• Modularity– Component model

• Sensor Network Challenges– Address the specific and unusual challenges of sensor networks:

• limited resources, concurrency- intensive operation, a need for robustness, and application-specific requirements.

Page 22: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

22

Each mote runs a single application

• Properties– All memory resources are known statically– Rather than employing a general-purpose OS, applications are built from a suite of

reusable system components coupled with application-specific code – The hardware/software boundary varies depending on the application and hardware

platform; it is important to design software for flexible decomposition• Challenges:

– Driven by interaction with the environment (interrupts)– Limited resources (motes)– Reliability– Soft real-time requirements (radio management and sensor polling)– Lacking common service

• Time synchronization

Page 23: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

23

TinyOS

● application = scheduler + graph of components● event-driven architecture● single shared stack● NO kernel, process/memory management, virtual memory

Page 24: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

24

Application = Graph of Components

RFM

Radio byte

Radio Packet

UART

Serial Packet

ADC

Temp photo

Active Messages

clocksbit

byte

pack

et

Route map router sensor appln

appl

icat

ion

HW

SW

Page 25: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

25

The nesC programming language: moving away from perl

• NesC started with a perl script– Pretended to be a lexer and parser– Offered no compile-time support for error detection– Component model wasn’t there yet

• Now it is a systems programming language for motes• Interrupt-driven programming model

– integrates reactivity to the environment, concurrency, and communication• Provides whole-program optimizations and compile-time data race detection

(does it work?) – Does it simplify application development?– reduce code size?– eliminate many sources of potential bugs?

Page 26: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

26

How to periodically sense light and transmit it

Photo.nc

TimerC.nc

GenericComm.nc

I know how to return a light value

I know how to tell someone

when some time has gone by

And I can transmit

messages over a radio!

Page 27: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

27

How to periodically sense light and transmit it

Photo.nc

TimerC.nc

GenericComm.nc

I know how to return a light value

I know how to tell someone

when some time has gone by

And I can transmit

messages over a radio! SendLightM.nc

I’m the glue that ties

these modules together

Page 28: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

28

How to periodically sense light and transmit it

Photo.nc

TimerC.nc

GenericComm.nc

SendLightM.nc

I am a user of the services these other

modules provide

Page 29: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

29

How to periodically sense light and transmit it

Photo.nc

TimerC.nc

GenericComm.nc

SendLightM.nc

Specifically, I use the interfaces that

these modules provide

ADC

Timer

SendMsg

Page 30: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

30

How to periodically sense light and transmit it

Photo.nc

TimerC.nc

GenericComm.nc

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { // . . . }

Page 31: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

31

Modules

• A module has:– Frame (internal state)– Tasks (computation)– Interface (events, commands)

• Frame :– one per component– statically allocated– fixed size

• Keyword “includes” goes before module declaration

– Semantic equivalent of #include, with caveats: No cpp directives allowed

– Practical hack: put #include in implementation block

• Commands and events are function calls

• Application: linking/glueing interfaces (events, commands)

includes Foo;module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation {#include “FooConstants.h” int x; // a frame variable

task void work(){ // do something }

command result_t StdControl.start(){ call Timer.start(TIMER_REPEAT, 1000); return SUCCESS; } event result_t Timer.fired(){ post work(); }}

Page 32: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

32

How to periodically sense light and transmit it

configuration TimerC { provides { interface StdControl; interface Timer[uint8_t id]; } uses { // . . . }}Implementation { // manage one shot & periodic // timers}

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { // use timer to // periodically sample // light }

Page 33: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

33

How to periodically sense light and transmit it

configuration TimerC { provides { interface StdControl; interface Timer[uint8_t id]; } uses { // . . . }}Implementation { // manage one shot & periodic // timers}

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { // use timer to // periodically sample // light }

configuration SenseLightC {}implementation { components Main, SendLightM, TimerC; Main.StdControl -> SendLightM.StdControl; Main.StdControl -> TimerC; SendLightM.Timer -> TimerC.Timer[unique(“Timer”)];}

Page 34: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

34

How to periodically sense light and transmit it

configuration TimerC { provides { interface StdControl; interface Timer[uint8_t id]; } uses { // . . . }}Implementation { // manage one shot & periodic // timers}

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { // use timer to // periodically sample // light }

configuration SenseLightC {}implementation { components Main, SendLightM, TimerC; Main.StdControl -> SendLightM.StdControl; Main.StdControl -> TimerC; SendLightM.Timer -> TimerC.Timer[unique(“Timer”)];}

interface Timer { // type is: TIMER_ONE_SHOT // or TIMER_REPEAT command result_t start(char type, uint32_t period); command result_t stop(); event result_t fired();}

Page 35: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

35

Parameterized Interfaces

• Can associate a port with an interface, so that a provider can distinguish users• Used because the provider doesn’t know how many users will be connecting to it

configuration SenseLightC {}implementation { components Main, SendLightM, TimerC; Main.StdControl -> SendLightM.StdControl; Main.StdControl -> TimerC; SendLightM.Timer -> TimerC.Timer[unique(“Timer”)];}

configuration TimerC { provides { interface StdControl; interface Timer[uint8_t id]; } uses { // . . . }}Implementation { // manage one shot & periodic // timers}

TimerC can provide up to 256 instances of the Timer interface.

Page 36: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

36

Interfaces

• Define “public methods that a component can use

• used for grouping functionality, like:– standard control interface (init, start,

stop)• describe bidirectional interaction:

– interface provider must implement commands

– interface user must implement events• commands:

– deposit request parameters into the frame– are non-blocking– need to return status postpone time

consuming work by posting a task– can call lower level commands

• events:– can call commands, signal events, post

tasks, can not be signaled by commands– preempt tasks, not vice-versa– interrupt trigger the lowest level events– deposit the information into the frame

interface Timer { // type is: TIMER_ONE_SHOT // or TIMER_REPEAT command result_t start(char type, uint32_t period); command result_t stop(); event result_t fired();}

Page 37: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

37

Interface Examples

interface StdControl { command result_t init (); command result_t start (); command result_t stop ();}

interface Timer { command result_t start (char type, uint32_t interval); command result_t stop (); event result_t fired ();}

interface SendMsg { command result_t send (uint16_t addr, uint8_t len, TOS_MsgPtr p); event result_t sendDone ();}

interface ReceiveMsg { event TOS_MsgPtr receive (TOS_MsgPtr

m);}

StdControl.nc Timer.nc

ReceiveMsg.ncSendMsg.nc

Page 38: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

38

How to periodically sense light and transmit it

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { command result_t StdControl.init() { return SUCCESS; } command result_t StdControl.start(){ call Timer.start(TIMER_REPEAT, 1000); // 1000ms return SUCCESS; } command result_t StdControl.stop(){ call Timer.stop(); return SUCCESS; } event result_t Timer.fired(){ call ADC.getData(); }}

interface Timer { // type is: TIMER_ONE_SHOT // or TIMER_REPEAT command result_t start(char type, uint32_t period); command result_t stop(); event result_t fired();}

interface ADC { async command result_t getData(); async command result_t getContinuousData(); async event result_t dataReady(uint16_t data);}

Page 39: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

39

How to periodically sense light and transmit it

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { command result_t StdControl.init() { return SUCCESS; } command result_t StdControl.start(){ call Timer.start(TIMER_REPEAT, 1000); // 1000ms return SUCCESS; } command result_t StdControl.stop(){ call Timer.stop(); return SUCCESS; } event result_t Timer.fired(){ call ADC.getData(); } async event result_t ADC.dataReady(uint16_t data){ // send a message with the data }}

interface Timer { // type is: TIMER_ONE_SHOT // or TIMER_REPEAT command result_t start(char type, uint32_t period); command result_t stop(); event result_t fired();}

interface ADC { async command result_t getData(); async command result_t getContinuousData(); async event result_t dataReady(uint16_t data);}

Split-phase interface

Page 40: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

40

Split-phase Interfaces

• Operation request and completion are separate functions:

• No blocking operations because tasks execute non-preemptively• The usual way to do this is to register a callback by passing a function pointer

interface SendMsg { command result_t send (uint16_t address, uint8_t length, TOS_MsgPtr p); event result_t sendDone (TOS_MsgPtr msg, result_t success);} SendMsg.nc

Page 41: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

41

How to periodically sense light and transmit it

• In a configuration, modules and perhaps sub-configurations are connected together by the interfaces they use and provide

• Interfaces have commands and events

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { command result_t StdControl.init() { return SUCCESS; } command result_t StdControl.start(){ call Timer.start(TIMER_REPEAT, 1000); // 1000ms return SUCCESS; } command result_t StdControl.stop(){ call Timer.stop(); return SUCCESS; } event result_t Timer.fired(){ call ADC.getData(); } async event result_t ADC.dataReady(uint16_t data){ // send a message with the data }}

Page 42: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

42

How to periodically sense light and transmit it

• So, what’s this “async” word?

• Some functions, like dataReady are called directly from a hardware interrupt handler.

• Async means that this function might be called straight from the IH.

• Why do we care whether or not a function is called from the IH?

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { command result_t StdControl.init() { return SUCCESS; } command result_t StdControl.start(){ call Timer.start(TIMER_REPEAT, 1000); // 1000ms return SUCCESS; } command result_t StdControl.stop(){ call Timer.stop(); return SUCCESS; } event result_t Timer.fired(){ call ADC.getData(); }

async event result_t ADC.dataReady(uint16_t data){ // send a message with the data }}

Page 43: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

43

Concurrency Model

• There are Interrupt Handlers and Background Tasks

• We can lose interrupts, so we get out of their handlers as fast as possible by saving state and posting a task

• Posting a task tells the scheduler to put a task on a queue and to call it when the MCU is idle and it is its turn

module SendLightM{ provides { interface StdControl; } uses { interface ADC; interface Timer; interface SendMsg; }}implementation { // . . . // global variables bool dataPending; uint16_t dataValue;

// tasks take no parameters and return void task void sendData(){ // send a message with the data in it dataPending = FALSE; } async event result_t ADC.dataReady(uint16_t data){ if (!dataPending){

dataPending = TRUE; dataValue = data; post sendData(); } }}

Page 44: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

44

Concurrency Model

• Since tasks (and even some interrupts) can be interrupted, we sometimes need extra support to prevent race conditions

• Use the keyword atomic to prevent interruption…

• Note, try to avoid calling from within an atomic block

module SendLightM{ // . . .}implementation { // . . . // global variables bool dataPending; uint16_t dataValue;

// tasks take no parameters and return void task void sendData(){ // send a message with the data in it dataPending = FALSE; } async event result_t ADC.dataReady(uint16_t data){ bool doWork = FALSE; atomic { if (!dataPending){ dataPending = TRUE; doWork = TRUE; } } if (doWork) post sendData(); } }}

Page 45: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

45

Configurations

• Configurations refer to configurations and modules

• No distinction between an included configuration and an included module

• Configuration can expose an underlying module’s interface

• An application must connect a Main component to other components

• connected elements must be compatible (interface-interface, command-command, event-event)

• 3 wiring statements in nesC:– endpoint1 = endpoint2

– endpoint1 -> endpoint2

– endpoint1 <- endpoint2

configuration CntToLeds {}implementation { components Main, Counter, IntToLeds, TimerC; Main.StdControl -> IntToLeds.StdControl; Main.StdControl -> Counter.StdControl; Main.StdControl -> TimerC.StdControl; Counter.Timer -> TimerC.Timer[unique("Timer")]; Counter.IntOutput -> IntToLeds.IntOutput;}

configuration TimerC { provides interface Timer[uint8_t id]; provides interface StdControl;}

implementation { components TimerM, ClockC, NoLeds, HPLPowerManagementM;

TimerM.Leds -> NoLeds; TimerM.Clock -> ClockC; TimerM.PowerManagement -> HPLPowerManagementM;

StdControl = TimerM; Timer = TimerM;}

Page 46: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

46

Concurrency

• Tasks and interrupts (foreground and background operations)– Tasks cannot preempt other tasks

• Low priority for performing computationally intensive work– Interrupts can preempt tasks– Interrupts can preempt other interrupts, but not important for this course

• TOSH_INTERRUPT() – interrupt allowed• TOSH_SIGNAL() – interrupt forbidden

• Scheduler– Two level scheduling - interrupts (vector) and tasks (queue)– Queue of tasks

• No associated priorities• FIFO execution

– No local state associated with tasks• Programmer must manage own internal state when tasks need it

– Danger: task queue overflows (because no dynamic memory)

Page 47: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

47

What the scheduler does…

• Operation– When no tasks pending, sleep– Wake on interrupt, lookup interrupt handler

and execute– Before sleeping again, check task queue, call

task in FIFO order• Practices

– Statically defined maximum queue length means code should be carefully written to avoid posting too many tasks

– E.g., if we have a list of thing to process (such as messages), rather than post a separate task for each message, post the next processing task at the end of the task handler

Task void send(){ //get head of send queue //send message if (queue.size > 0) post send()}

Hardware

Interrupts

events

commands

FIFOTasks

POSTPreempt

Time

commands

while(1) {while(more_tasks)

schedule_task;sleep;

}

Page 48: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

48

Posting Tasks

module BlinkM {…}implementation {… task void processing () { if(state) call Leds.redOn(); else call Leds.redOff(); }

event result_t Timer.fired () { state = !state; post processing(); return SUCCESS; }…} BlinkM.nc

Page 49: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

49

Language features for concurrency

• Post – Puts a function on a task queue– Must be void foo(void)– void task do-work() { //do something }– post do-work();

• Atomic– Interrupts can preempt execution– Turn off interrupts with atomic{ }– E.g. to implement a semaphore

• Async– Use async to tell compiler that this code can be called from an

interrupt context – used to detect potential race conditions• Norace

– Use norace to tell compiler it was wrong about a race condition existing (the compiler usually suggests a million possible race conditions)

Page 50: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

50

Debugging using print statements

• TinyOS’s printf:– dbg(DBG_USR1, “%s [%d] – link quality is %d\n”,

__FILE__, __LINE__, link_quality);• DBG_USR1, DBG_USR2, DBG_USR3, and DBG_ERROR are defined for your

enjoyment

Page 51: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

51

Lesson 1: Make that LED Blink

• Every module you write should have this form:

module FooM { provides {

interface a; interface b; interface c;

} uses {

interface x; interface y; interface z;

} } implementation {

/* Your code goes here. */ /* Implement all commands for interfaces that I provide. */ /* Implement all events for the interfaces that I use. */

}

Page 52: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

52

Lesson 1: Make that LED Blink

• Every module you write will need to have an interface called StdControl (standard control). It has three methods:

interface StdControl { /* * Initialize the component and its subcomponents. */ command result_t init(); /* * Start the component and its subcomponents. */ command result_t start(); /* * Stop the component and pertinent subcomponents (not all * subcomponents may be turned off due to wakeup timers, etc.). */ command result_t stop();

}

Page 53: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

53

Lesson 1: Make that LED Blink

• We're going to need the LED, and there already is a module written that implements the functionality that we want. The LED interface looks like:

interface Leds {

async command result_t init(); async command result_t redOn(); async command result_t redOff(); async command result_t redToggle(); async command result_t greenOn(); async command result_t greenOff(); async command result_t greenToggle(); async command result_t yellowOn(); async command result_t yellowOff(); async command result_t yellowToggle(); async command uint8_t get(); async command result_t set(uint8_t value);

}

Page 54: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

54

Lesson 1: Make that LED Blink

• Let's add these interfaces and the required methods to our module :

module FooM { provides { interface StdControl; } uses { interface Leds; }

} implementation {

/* StdControl Interface. */

command result_t StdControl.init() { // Initialize the LEDs.

dbg(DBG_USR3, "%s [%d] - Initialization started.\n", __FILE__, __LINE__);

call Leds.init();

dbg(DBG_USR3, "%s [%d] - Initialization finished.\n", __FILE__, __LINE__);

return SUCCESS; } command result_t StdControl.start() { return SUCCESS; } command result_t StdControl.stop() { return SUCCESS; }

}

Page 55: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

55

Lesson 1: Make that LED Blink

• Now we need to be able to generate a periodic event so that we can toggle the LEDs. There is already a component written that does what we want and the interface that exports this functionality is called Timer:

interface Timer {

/* 'type' takes value of either TIMER_REPEAT or TIMER_ONE_SHOT

*/

/* 'interval' is the number of milliseconds until the timer will expire

*/ command result_t start(char type, uint32_t interval);

command result_t stop();

/* EVENT! */ event result_t fired();

}

Page 56: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

56

Lesson 1: Make that LED Blink• Since we're going to use the timer we're going to need to provide the callbacks (or event handlers) :

module FooM { provides { interface StdControl; } uses { interface Leds; interface Timer; }

} implementation { /* StdControl Interface. */ command result_t StdControl.init() { // Initialize the LEDs. call Leds.init(); call Leds.redOff(); call Leds.greenOff(); call Leds.yellowOff(); return SUCCESS; } command result_t StdControl.start() { call Timer.start(TIMER_REPEAT, 1000); return SUCCESS; } command result_t StdControl.stop() { call Timer.stop(); return SUCCESS; } /* Timer Event */ event result_t Timer.fired() { /* Performing computation within the interrupt might be bad ... */ call Leds.redToggle(); call Leds.greenToggle(); call Leds.yellowToggle(); return SUCCESS; }

Page 57: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

57

Lesson 1: Make that LED Blink

• Notice that we're doing our "processing" inside the timer event. This amount of processing is negligable, but it is possible to spend too much time inside the interrupt, causing you to miss interrupts, which causes bizaar behavior.

• That's all the code we have to write, but we still need to create a wiring file that explains what other modules will use those interfaces that we provide, and those modules that we will use. Here it is:

configuration Foo { } implementation {

components Main, FooM, LedsC, TimerC; Main.StdControl -> FooM.StdControl; Main.StdControl -> TimerC.StdControl; FooM.Timer -> TimerC.Timer[unique("Timer")]; FooM.Leds -> LedsC.Leds;

}

Page 58: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

58

Lesson 2: Using Tasks

• We're going to move our computation into a task that will run in the background :

includes AM; module FooM {

provides { interface StdControl; } uses { interface Leds; interface Timer; }

}implementation {

/* Tasks must be void! */

task void task_toggleLeds() { call Leds.redToggle(); call Leds.greenToggle(); call Leds.yellowToggle(); }

/* StdControl Interface. */

command result_t StdControl.init(){ // Initialize the LEDs. call Leds.init(); return SUCCESS;

} command result_t StdControl.start(){

call Timer.start(TIMER_REPEAT, 1000);

return SUCCESS; }

command result_t StdControl.stop(){ call Timer.stop(); return SUCCESS;

}

/* Timer Event */ event result_t Timer.fired() {

post task_toggleLeds(); return SUCCESS;

} }

Page 59: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

59

Lesson 3: Using the Radio

• So now we want to use the radio to send a simple message, like whether the LEDs are on or off. So we're going to augment our previous code to add a

• tinyos-1.x/tos/interfaces/SendMsg.nc :

interface SendMsg { command result_t send(uint16_t address, uint8_t length, TOS_MsgPtr msg); event result_t sendDone(TOS_MsgPtr m, result_t success);

}

Page 60: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

60

Lesson 3: Using the Radio

• Here is FooM.nc, augmented to add the SendMsg interface :

includes AM; module FooM {

provides { interface StdControl; } uses { interface Leds; interface Timer; interface SendMsg; }

} implementation {

TOS_Msg* myMsgPtr; TOS_Msg myMsg; uint8_t state;

/* Tasks must be void! */

task void task_toggleLeds() { myMsgPtr->addr = TOS_BCAST_ADDR;myMsgPtr->length =sizeof(this_data); ((uint16_t*) (myMsgPtr->data))[0] = state; if ( state == 0 ) { state = 1; } else { state = 0; }

call SendMsg.send(myMsgPtr->addr, myMsgPtr->length, myMsgPtr); call Leds.yellowToggle(); }

/* StdControl Interface. */ command result_t StdControl.init() { // Initialize the LEDs. call Leds.init(); state = 0; myMsgPtr = &myMsg; return SUCCESS; } command result_t StdControl.start(){call Timer.start(TIMER_REPEAT, 500); return SUCCESS; } command result_t StdControl.stop() {call Timer.stop(); return SUCCESS; }

/* Timer Event */ event result_t Timer.fired() {/* Minimize computation */ post task_toggleLeds(); return SUCCESS; }

/* SendDone event */ event result_t SendMsg.sendDone( TOS_MsgPtr msg, result_t success) {// We're not guaranteed to get the // same buffer back!!! myMsgPtr = msg; return SUCCESS; }

}

Page 61: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

61

Lesson 3: Using the Radio

• Here’s the new configuration:

configuration Foo { } implementation {

components Main, FooM, LedsC, TimerC, GenericComm; Main.StdControl -> FooM.StdControl; Main.StdControl -> TimerC.StdControl; Main.StdControl -> GenericComm; FooM.Timer -> TimerC.Timer[unique("Timer")]; FooM.Leds -> LedsC.Leds; FooM.SendMsg -> GenericComm.SendMsg[5];

}

Page 62: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

62

Lesson 4: Adding Receive

• GenericComm has more than just a send interface. It also has a receive interface that looks like this:

• tinyos-1.x/tos/interfaces/ReceiveMsg.nc :

interface ReceiveMsg { // Return a pointer to an empty TOS_Msg. event TOS_MsgPtr receive(TOS_MsgPtr m);

}

Page 63: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

63

Lesson 4: Adding Receive

• So let's add the event handler. First we'll augment our interfaces, then we'll add in the handler, then we'll add the wiring :

includes AM; module FooM {

provides { interface StdControl; } uses { interface Leds; interface Timer; interface SendMsg; interface ReceiveMsg; }

} implementation {// ...

/* ReceiveMsg Event handlers */ event TOS_MsgPtr ReceiveMsg.receive(TOS_MsgPtr m) { dbg(DBG_USR3, "%s [%d] - Received a message data == %d.\n", __FILE__, __LINE__, ((uint16_t*)(m->data))[0]); call Leds.redToggle(); return m; }

}

Page 64: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

64

Lesson 4: Adding Receive

• And the resulting configuration :

configuration Foo { } implementation {

components Main, FooM, LedsC, TimerC, GenericComm; Main.StdControl -> FooM.StdControl; Main.StdControl -> TimerC.StdControl; Main.StdControl -> GenericComm; FooM.Timer -> TimerC.Timer[unique("Timer")]; FooM.Leds -> LedsC.Leds; FooM.SendMsg -> GenericComm.SendMsg[5]; FooM.ReceiveMsg -> GenericComm.ReceiveMsg[5];

}

Page 65: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

65

Lesson 5: Using Sensors

• It wouldn't be a sensor network if you weren't able to use sensors. So now we're going to add sensing functionality to our application. First, we'll explore the interface to a standard sensor, then we'll add the interface to your application, and finally we'll add the additional wiring.

• tinyos-1.x/tos/interfaces/ADC.nc :

interface ADC { async command result_t getData(); async command result_t getContinuousData(); async event result_t dataReady(uint16_t data);

}

Page 66: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

66

Lesson 5: Using Sensors

includes AM; module FooM {

provides { interface StdControl; } uses { interface Leds; interface Timer; interface SendMsg; interface ReceiveMsg; interface ADC; }

} implementation { // . . .

/* Tasks must be void! */ task void task_toggleLeds() { call ADC.getData(); call Leds.yellowToggle(); }

// . . ./* Timer Event */ event result_t Timer.fired() { /* Minimize computation */ post task_toggleLeds(); return SUCCESS; }

// . . .

/* ADC Data ready event */

async event result_t ADC.dataReady(uint16_t this_data) {

dbg(DBG_USR3, "%s [%d] – DataReady == %d.\n",

__FILE__, __LINE__, this_data);

call Leds.greenToggle(); myMsgPtr->addr = TOS_BCAST_ADDR; myMsgPtr->length =

sizeof(this_data); ((uint16_t*)(myMsgPtr->data))[0]

= this_data; call SendMsg.send(myMsgPtr->addr,

myMsgPtr->length,

myMsgPtr); return SUCCESS;

} }

Page 67: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

67

Lesson 5: Using Sensors

• And the resulting configuration :

configuration Foo { } implementation {

components Main, FooM, LedsC, TimerC, GenericComm, Photo; Main.StdControl -> FooM.StdControl; Main.StdControl -> TimerC.StdControl; Main.StdControl -> GenericComm; FooM.Timer -> TimerC.Timer[unique("Timer")]; FooM.Leds -> LedsC.Leds; FooM.SendMsg -> GenericComm.SendMsg[5]; FooM.ReceiveMsg -> GenericComm.ReceiveMsg[5];FooM.ADC -> Photo;

}

Page 68: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

68

Lesson 6: Assigning Node IDs

• Assigning a unique identifier to a sensor node is very useful. TinyOS and it's build system already support unique IDs through a special symbol called TOS_LOCAL_ADDRESS.

• We're going to change our application so that in addition to sending a packet containing photo-sensor data, it will include it's own ID so that someone receiving the packet knows where it came from. This won't require any new modules or wiring (Foo.nc will remain the same), only a few lines of code. :

Page 69: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

69

Putting it all together

includes AM; module FooM {

provides { interface StdControl; } uses { interface Leds; interface Timer; interface SendMsg; interface ReceiveMsg; interface ADC; }

} implementation {

/* Tasks must be void! */

TOS_Msg* myMsgPtr; TOS_Msg myMsg; uint8_t state;

/* Tasks must be void! */

task void task_toggleLeds() { dbg(DBG_USR3, "%s [%d] - Call Get Data.\n", __FILE__, __LINE__); call ADC.getData(); dbg(DBG_USR3, "%s [%d] - Toggle Yellow.\n", __FILE__, __LINE__); call Leds.yellowToggle(); }

Page 70: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

70

Putting it all together

/* StdControl Interface. */

command result_t StdControl.init() { // Initialize the LEDs. dbg(DBG_USR3, "%s [%d] - Initialization started.\n",

__FILE__, __LINE__); call Leds.init(); state = 0; myMsgPtr = &myMsg; dbg(DBG_USR3, "%s [%d] - Initialization finished.\n",

__FILE__, __LINE__); return SUCCESS;

} command result_t StdControl.start() {

dbg(DBG_USR3, "%s [%d] - Starting timer.\n", __FILE__, __LINE__);

call Timer.start(TIMER_REPEAT, 500); return SUCCESS;

} command result_t StdControl.stop() {

dbg(DBG_USR3, "%s [%d] - Stopping timer.\n", __FILE__, __LINE__);

call Timer.stop(); return SUCCESS;

}

Page 71: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

71

Putting it all together

/* Timer Event */

event result_t Timer.fired() { dbg(DBG_USR3, "%s [%d] - Timer fired.\n", __FILE__, __LINE__); /* Minimize computation */ post task_toggleLeds(); return SUCCESS; }

/* SendDone event */

event result_t SendMsg.sendDone(TOS_MsgPtr msg, result_t success) { dbg(DBG_USR3, "%s [%d] - Send complete.\n", __FILE__, __LINE__); // We're not guaranteed to get the same buffer back!!! myMsgPtr = msg; return SUCCESS; }

/* ReceiveMsg Event handlers */

event TOS_MsgPtr ReceiveMsg.receive(TOS_MsgPtr m) { dbg(DBG_USR3, "%s [%d] - Received a message data == %d.\n", __FILE__, __LINE__, ((uint16_t*)(m->data))[0]); call Leds.redToggle(); return m; }

Page 72: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

72

Putting it all together

/* ADC Data ready event */

async event result_t ADC.dataReady(uint16_t this_data) { dbg(DBG_USR3,

"%s [%d] - Data Ready == %d.\n", __FILE__, __LINE__, this_data); call Leds.greenToggle(); myMsgPtr->addr = TOS_BCAST_ADDR; myMsgPtr->length = sizeof(this_data); ((uint16_t*)(myMsgPtr->data))[0] = TOS_LOCAL_ADDRESS; ((uint16_t*)(myMsgPtr->data))[1] = this_data; call SendMsg.send(myMsgPtr->addr, myMsgPtr->length,

myMsgPtr); return SUCCESS; }

}

Page 73: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

73

Programming Environment

• OS: cygwin/Win2000 or gcc/Linux• Software: atmel tools, java, perl

mote

programming board

mote-PC comms Code

download

Page 74: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

74

nesC details

• naming conventions:– nesC files suffix: .nc– C stands for Configuration (Clock, ClockC)– M stands for Module (Timer, TimerC, TimerM)

?

Clock.nc

?

ClockC.nc

configuration ClockC { ...}

implementation {…}

ClockC.nc

interface Clock { ...}

Clock.nc

?

Timer.nc

interface Timer { ...}

Timer.nc

?

TimerC.nc

?

TimerM.nc

configuration TimerC { ...}

implementation {…}

TimerC.nc

module TimerM { ...}

implementation {…}

TimerM.nc

● clarifications:– “C” distinguishes between

an interface and the component that provides it

– “M” when a single component has both: a configuration, a module

Page 75: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

75

Compiling an application

• Avr cross-compilation– Specify platform, programming interface, mote ID– To make and install a binary with ID of 3:

• make mica2 install.3– To install a preexisting binary with ID of 12:

• make mica2 reinstall.12• Pc native compilation

– Create a binary that runs on your pc for debugging and simulation– Make pc, make emstar

Page 76: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

76

Directory structure

• tos-contrib– make-contrib – where all contributed projects go– project-name – a particular project that has been contributed

• apps – the applications written for this project– Makefile

» Directives for all application in this project– Makerules– Foo-App

» Makefile – define TOSH_DATA_LENGTH, sensorboards, etc» Foo-App.nc – top-level configuration for the application» Foo-AppM.nc - non-reusable application-specific code

• lib - Configurations and modules defined for the project• interfaces - interfaces defined to be used with the modules and

configurations in lib

• tinyos-1.x has same structure (apps, lib, interfaces) as tos-contrib

Page 77: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

77

Build system

• An application’s Makefile…

COMPONENT=MultihopDseCONTRIB_INCLUDES += EmTos hostmote sensorIB dse#SENSORBOARD=basicsbSENSORBOARD=mda300caCFLAGS += -DCC1K_DEFAULT_FREQ=2CFLAGS += -DTOSH_DATA_LENGTH=50CFLAGS += -DLPL_MODE=0CFLAGS += -DSTATUS_DBGCFLAGS += -DHOSTMOTE_MAX_SEND_DATA_LENGTH=66#CFLAGS += -DHOSTMOTE_DEBUGinclude ../Makerules

Page 78: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

78

Build system integration with Emstar

• Tricky to get TinyOS and Emstar to play nice• Read: http://cvs.cens.ucla.edu/emstar/install.html• Basically, a few symbolic links need to be established• To build in emstar:

– $EMSTAR_ROOT points to your emstar distribution– $TOSDIR points to your TinyOS distribution– in Make.conf set BUILD_EMTOS to 1– make [ARCH=mica2]

Page 79: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

79

What happens when you build a TinyOS program?

ben@garrot:~/cvs/sourceforge/tinyos-1.x/apps/SenseToRfm$ make mica2mkdir -p build/mica2 compiling SenseToRfm to a mica2 binaryncc -o build/mica2/main.exe -Os -I%T/lib/Counters -finline-limit=100000

-Wall -Wshadow -DDEF_TOS_AM_GROUP=0x7d -Wnesc-all -target=mica2 -fnesc-cfile=build/mica2/app.c -board=micasb -DIDENT_PROGRAM_NAME="SenseToRf" -DIDENT_PROGRAM_NAME_BYTES="83,101,110,115,101,84,111,82,102,0" -DIDENT_USER_HASH=0x8e23d3f4L -DIDENT_UNIX_TIME=0x42014318L SenseToRfm.nc -lm

compiled SenseToRfm to build/mica2/main.exe 11376 bytes in ROM 455 bytes in RAMavr-objcopy --output-target=srec build/mica2/main.exe

build/mica2/main.srecavr-objcopy --output-target=ihex build/mica2/main.exe

build/mica2/main.ihex

What does –finline-limit=100000 mean?

Page 80: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

80

The main() scheduler loop(tos/system/RealMain.nc)

int main() __attribute__ ((C, spontaneous)) { call hardwareInit(); call Pot.init(10); TOSH_sched_init(); call StdControl.init(); call StdControl.start(); __nesc_enable_interrupt();

while(1) { TOSH_run_task(); } }}

How are tasks handled? How are interrupts handled?

Page 81: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

81

Under the hood: app.c

SenseToInt.nc: event result_t Timer.fired() { call ADC.getData(); return SUCCESS; }

# 75 "/home/ben/cvs/sourceforge/tinyos-1.x/tos/lib/Counters/SenseToInt.nc"

result_t SenseToInt$Timer$fired(void)#line 75{ SenseToInt$ADC$getData(); return SUCCESS;}

nesC compilation

Page 82: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

82

Mapping callers to callees

• apps/SenseToRfm/SenseToRfm.nc:components Main, SenseToInt, IntToRfm, TimerC, DemoSensorC as Sensor;SenseToInt.ADC -> Sensor;

• tos/sensorboards/micasb/DemoSensorC.nc:components Photo as Sensor;ADC = Sensor;

• tos/sensorboards/micasb/Photo.nc:provides interface ADC as PhotoADC;components PhotoTemp;PhotoADC = PhotoTemp.ExternalPhotoADC;

• tos/sensorboards/micasb/PhotoTemp.nc:components PhotoTempM, ADCC, TimerC;ExternalPhotoADC = PhotoTempM.ExternalPhotoADC;PhotoTempM.InternalPhotoADC -> ADCC.ADC[TOS_ADC_PHOTO_PORT];

app.c:# 52 "/home/ben/cvs/sourceforge/tinyos-

1.x/tos/interfaces/ADC.nc“inline static result_t

SenseToInt$ADC$getData(void){#line 52 unsigned char result;#line 52#line 52 result =

PhotoTempM$ExternalPhotoADC$getData();

#line 52#line 52 return result;#line 52}

How does nesC know we wanted to call PhotoTempM.nc’s getData()?

Page 83: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

83

Posting a task

async command result_t ExternalPhotoADC.getData(){

atomic { photoSensor =

stateReadOnce; }; post getSample(); return SUCCESS; }

static inline# 329

"/home/ben/cvs/sourceforge/tinyos-1.x/tos/sensorboards/micasb/PhotoTempM.nc"

result_t PhotoTempM$ExternalPhotoADC$getData(void)

#line 329{ { __nesc_atomic_t __nesc_atomic =

__nesc_atomic_start();#line 330 { PhotoTempM$photoSensor =

PhotoTempM$stateReadOnce; }#line 332 __nesc_atomic_end(__nesc_atomic); }#line 332 ; TOS_post(PhotoTempM$getSample); return SUCCESS;}

• No priorities• No parameters

Page 84: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

84

Adding a task to the scheduler(./tos/system/sched.c)

bool TOS_post(void (*tp)(void)){ __nesc_atomic_t fInterruptFlags; uint8_t tmp;

fInterruptFlags = __nesc_atomic_start();

tmp = TOSH_sched_free;

if (TOSH_queue[tmp].tp == (void *)0) { TOSH_sched_free = (tmp + 1) & TOSH_TASK_BITMASK; TOSH_queue[tmp].tp = tp; __nesc_atomic_end(fInterruptFlags); return TRUE; } else { __nesc_atomic_end(fInterruptFlags); return FALSE; }}

Where are the parameters to the task being posted?

Page 85: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

85

The main() scheduler loop(tos/system/RealMain.nc)

int main() __attribute__ ((C, spontaneous)) { call hardwareInit(); call Pot.init(10); TOSH_sched_init(); call StdControl.init(); call StdControl.start(); __nesc_enable_interrupt();

while(1) { TOSH_run_task(); } }}

# 54 "/home/ben/cvs/sourceforge/tinyos-1.x/tos/system/RealMain.nc"

int main(void)#line 54{ RealMain$hardwareInit(); RealMain$Pot$init(10); TOSH_sched_init();

RealMain$StdControl$init(); RealMain$StdControl$start(); __nesc_enable_interrupt();

while (1) { TOSH_run_task(); }}

Page 86: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

86

TOSH_run_task()

void TOSH_run_task() { while (TOSH_run_next_task()) ; TOSH_sleep(); TOSH_wait();}

bool TOSH_run_next_task (){ __nesc_atomic_t fInterruptFlags; uint8_t old_full; void (*func)(void); fInterruptFlags = __nesc_atomic_start(); old_full = TOSH_sched_full; func = TOSH_queue[old_full].tp; if (func == NULL) { __nesc_atomic_end(fInterruptFlags); return 0; }

TOSH_queue[old_full].tp = NULL; TOSH_sched_full = (old_full + 1) &

TOSH_TASK_BITMASK; __nesc_atomic_end(fInterruptFlags); func();

return 1;}

static inline#line 135void TOSH_wait(void){ __asm volatile ("nop"); __asm volatile ("nop");}

static inline void TOSH_sleep(void){ * (volatile unsigned char *)(unsigned

int )& * (volatile unsigned char *)(0x35 + 0x20) |= 1 << 5;

__asm volatile ("sleep");}

Page 87: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

87

Atomic operations

void task outputTask() { uint16_t rCopy; atomic { rCopy = reading; } call IntOutput.output(rCopy >> 7); }

void SenseToInt$outputTask(void)#line 80{ uint16_t rCopy;

#line 82 { __nesc_atomic_t __nesc_atomic =

__nesc_atomic_start();#line 82 { rCopy = SenseToInt$reading; }#line 84 __nesc_atomic_end(__nesc_atomic); } SenseToInt$IntOutput$output(rCopy >>

7);}

Why should we use atomic blocks?

Page 88: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

88

Atomic operations

typedef uint8_t __nesc_atomic_t;

inline __nesc_atomic_t __nesc_atomic_start(void) __attribute__((spontaneous))

{ __nesc_atomic_t result = inp(SREG); cli(); return result;}

inline void __nesc_atomic_end(__nesc_atomic_t oldSreg) __attribute__((spontaneou

s)){ outp(oldSreg, SREG);}

#line 153__inline __nesc_atomic_t __nesc_atomic_start(void

){ __nesc_atomic_t result = * (volatile unsigned

char *)(unsigned int )& * (volatile unsigned char *)(0x3F + 0x20);

#line 156 __asm volatile ("cli"); return result;}#line 160__inline void __nesc_atomic_end(__nesc_atomic_t

oldSreg){ * (volatile unsigned char *)(unsigned int )& *

(volatile unsigned char *)(0x3F + 0x20) = oldSreg;

}

cli() disables all interrupts by clearing the global interrupt mask. sei() enables.inp() reads a specified 8-bit port. outp() writes.

Page 89: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

89

Interrupt handling (in the AVR-GCC environment)

• Vector table is predefined to point to interrupt routines with predetermined names

• By using the appropriate name, your routine will be called when the corresponding interrupt occurs

• The device library provides a set of default interrupt routines, which will be used if you don't define your own

• The compiler uses a set of registers when it's normally executing compiler-generated code

– These registers, as well as the status register, must be saved and restored – The extra code needed to do this is enabled by tagging the interrupt function with

__attribute__((interrupt))• An interrupt routine is defined with one of two macros, INTERRUPT() and

SIGNAL()– E.g., SIGNAL(SIG_ADC){ /* handler code */ }– TOSH_INTERRUPT (enabled) and TOSH_SIGNAL (disabled) in nesC – These macros register and mark the routine as an interrupt handler for the specified

peripheral.

Page 90: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

90

Interrupt handlers

TOSH_SIGNAL(SIG_ADC) { uint16_t data = inw(ADCL); data &= 0x3ff; sbi(ADCSR, ADIF); cbi(ADCSR, ADEN); __nesc_enable_interrupt(); signal ADC.dataReady(data); }

#define TOSH_SIGNAL(signame)

void signame() __attribute__ ((signal, spontaneous, C))

# 144 "/home/ben/cvs/sourceforge/tinyos-1.x/tos/platform/mica2/HPLADCM.nc"

void __attribute((signal)) __vector_21(void)

#line 144{ uint16_t data = * (volatile unsigned

int *)(unsigned int )& * (volatile unsigned char *)(0x04 + 0x20);

#line 146 data &= 0x3ff; * (volatile unsigned char *)(unsigned

int )& * (volatile unsigned char *)(0x06 + 0x20) |= 1 << 4;

* (volatile unsigned char *)(unsigned int )& * (volatile unsigned char *)(0x06 + 0x20) &= ~(1 << 7);

__nesc_enable_interrupt(); HPLADCM$ADC$dataReady(data);}

Page 91: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

91

Memory management

• Static allocation in TinyOS• No cross-module protection• Scary avr-glib implementation of malloc()

Page 92: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

92

Static memory allocation: pointer swap

event TOS_MsgPtr Foo.receive(TOS_MsgPtr m) {if (gMsgToProcess != NULL) return m;else { gMsgToProcess = m; post process_the_message();

return an_available_tosmsg ();}

• Simple rule: If you take a pointer, return a blank one• The upper layer, therefore, manages buffer allocation• Can be VERY tricky if more than 2 components are involved

What are the potential problems with this scheme?

Page 93: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

93

Dynamic memory allocation(avr-libc-1.2.1/stdlib/malloc.c)

/* Step 3: If the request could not be satisfied from a freelist entry, * just prepare a new chunk. This means we need to obtain more memory * first. The largest address just not allocated so far is remembered in * the brkval variable. Under Unix, the "break value" was the end of the * data segment as dynamically requested from the operating system. Since * we don't have an operating system, just make sure that we don't collide * with the stack. */

//...

cp = STACK_POINTER() - __malloc_margin; // margin is 32 by defaultavail = cp - __brkval;

What are the potential problems with this scheme?

Page 94: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

94

User-implemented dynamic memory pools

uint8_t mem[MemoryPoolM____size];

command void *Memory_p.malloc(uint8_t len){ mem_hdr_t *f = get_first_fit(len); if (f == NULL){ dbg(DBG_ERROR, "%s [%d] - can't allocate %d bytes: out of memory\n", __FILE__,__LINE__, len); return NULL; } allocate(f,len); dbg(DBG_USR1, "%s [%d] memory malloc %p len %d total bytes %d total ptrs %d\n", __FILE__,__LINE__,f->data,len,gAlloc, gPtrs); return f->data; }mem_hdr_t *get_first_fit(uint8_t len){ mem_hdr_t *f = (mem_hdr_t *)mem; while (f != NULL){ dbg(DBG_USR1, "%s [%d] - %p size %d len to fit %d\n", __FILE__,__LINE__,f, get_size(f), len); if (get_size(f) >= len && f->allocated == FALSE){ return f; } f = f->next; } return NULL; }

What are the potential problems with this scheme?

Page 95: An Introduction to nesC and TinyOS, or, A really complicated way to build very simple applications

The End