TinyOS Tutorial Dr. WenZhan Song Professor, Computer Science Acknowledgement: Thank Greg Hackmann at Washington University in St. Louis for sharing the tinyos tutorial slides. Sensorweb Research Laboratory -1- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 2 Sensorweb Research Laboratory -2- Georgia State University TinyOS Installation TinyOS Documentation Wiki: http://docs.tinyos.net/ Pre-compiled .rpm and .deb packages for Fedora and Ubuntu Linux users Various installation options listed under “Getting started” section Ubuntu users: be sure to remove brltty package All necessary drivers already included with Linux kernel 3 Sensorweb Research Laboratory -3- Georgia State University TinyOS Installation (cont.) OS X unofficially supported but works well Precompiled packages available at http://www.cse.wustl.edu/~gwh2/tinyos-2.1.0.dmg.bz2 Need to install pySerial (http://pyserial.sourceforge.net) and FTDI FT232R serial drivers (http://www.ftdichip.com/Drivers/VCP.htm) separately Can also compile by hand: see TinyOS Wiki 4 Sensorweb Research Laboratory -4- Georgia State University TinyOS Installation (cont.) Windows installation uses Cygwin to emulate Linux software layer Problematic under XP, refuses to work on some Vista machines (updating Cygwin after the installation may help) gcc is also very slow under Cygwin “Running a XubunTOS Virtual Machine Image in VMware Player” tutorial recommended instead Or pick your favorite VM software and install Ubuntu yourself 5 Sensorweb Research Laboratory -5- Georgia State University TinyOS Directory Structure /opt/tinyos-2.1.0 ($TOSROOT) apps support make sdk tools tos 8 Sensorweb Research Laboratory -8- Georgia State University make System $TOSROOT/support/make includes lots of Makefiles to support the build process Create a simple stub Makefile in your app directory that points to main component COMPONENT=[MainComponentC] SENSORBOARD=[boardtype] # if needed include $(MAKERULES) make [platform] in app directory Builds but does not install program platform: one of the platforms defined in $TOSROOT/tos/platforms (mica2, micaz2, telosb) 9 Sensorweb Research Laboratory -9- Georgia State University make System make [re]install.[node ID] [platform] [programming options] node ID: 0 - 255 programming options: mica2/micaz: mib510,/dev/ttyXYZ telosb: bsl,/dev/ttyXYZ make clean make docs [platform] Generates HTML documentation in $TOSROOT/doc/nesdoc/[platform] 10 Sensorweb Research Laboratory -10- Georgia State University Build Stages Preprocess .nc to .c, then compile .c to binary Set AM address and node ID in binary Program mote 11 Sensorweb Research Laboratory -11- Georgia State University How to Get Help TinyOS Documentation Wiki: http://docs.tinyos.net TinyOS Programming Manual: 139-page PDF intro to nesC and TinyOS 2.x: http://www.tinyos.net/tinyos-2.x/doc/pdf/tinyosprogramming.pdf TinyOS Tutorials: short HTML lessons on using parts of TinyOS (sensors, radio, TOSSIM, etc.):http://docs.tinyos.net/index.php/TinyOS_Tutorials 12 Sensorweb Research Laboratory -12- Georgia State University How to Get Help nesdoc: annotated API for all interfaces and components in TinyOS: http://docs.tinyos.net/index.php/Source_Code_Docume ntation TinyOS Enhancement Protocols (TEP): formal documentation for TinyOS features: http://docs.tinyos.net/index.php/TEPs 13 Sensorweb Research Laboratory -13- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 14 Sensorweb Research Laboratory -14- Georgia State University MicaZ CC2420 - IEEE 802.15.4 Radio ATmega 128L microcontroller 250kbps 2.4 GHz 4kB RAM, 128kB program flash, 512 kB storage flash, Need a separate programming board and debug board 15 Sensorweb Research Laboratory -15- Georgia State University Tmote Sky (aka TelosB) CC2420 - IEEE 802.15.4 Radio TI MSP430 microcontroller 250kbps 2.4GHz 8MHz, 16 MIPS, 10kB RAM, 1MB storage flash, 48K program flash Integrated antenna & USB interface Low power utilization 1.8mA/5.1µA vs. Mica 2’s 8mA/15µA 16 Sensorweb Research Laboratory -16- Georgia State University TelosW = Telos + Wake-on CC1101 – wake-on radio TI MSP430 microcontroller 16MHz, 16 MIPS, 10kB RAM Integrated antenna & USB interface Low power utilization 1.2kbps - 500kbps 868-915 MHz 1.8mA/5.1µA vs. Mica 2’s 8mA/15µA Wake-on sensor/ADC design Onboard energy meters More details at http://sensorweb.cs.gsu.edu/sites/default/files/research/projects/Te losW/images/TELOSW.pdf Sensorweb Research Laboratory -17- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 18 Sensorweb Research Laboratory -18- Georgia State University TinyOS Execution Model To save energy, node stays asleep most of the time Computation is kicked off by hardware interrupts Interrupts may schedule tasks to be executed at some time in the future TinyOS scheduler continues running until all tasks are cleared, then sends mote back to sleep zZz handlePacket readSensor sendResponse 19 Sensorweb Research Laboratory -19- Georgia State University TinyOS Execution Model Sensorweb Research Laboratory -20- Georgia State University TinyOS Memory Model STATIC memory allocation! Global variables No heap (malloc) No function pointers Available on a per-frame basis Local variables Saved on the stack Declared within a method Sensorweb Research Laboratory -21- Georgia State University TinyOS Thread Model Tasks: Events: Time flexible Longer background processing jobs Atomic with respect to other tasks (single threaded) Preempted by events Time critical Shorter duration (hand off to task if need be) Interrupts task Last-in first-out semantics (no priority among events) Do not confuse an event from the NesC event keyword!! TinyOS-1.x supports up to 7 pending tasks by default, you can add -DTOSH_MAX_TASKS_LOG2=n to makefile’s PFLAGS line to get 2^n tasks, where n=8 is maximum. TinyOS-2.x supports up to 255 tasks by default. Task overflow is still possible – make sure to check whether task “post” was SUCCESS or not. Sensorweb Research Laboratory -22- Georgia State University TinyOS Programming Model Separation of construction and composition Programs are built out of components Each component is specified by an interface Provides “hooks” for wiring components together Components are statically wired together based on their interfaces Increases runtime efficiency Sensorweb Research Laboratory -23- Georgia State University TinyOS Component Model event command startDone() start() provides SplitControl NetworkHandlerP uses Receive provides Receive provides SplitControl ActiveMessageC NetworkHandlerC 24 Sensorweb Research Laboratory -24- Georgia State University TinyOS Component Model Components use and provide interfaces, commands, and events Specified by a component’s interface The word “interface” has two meanings in TinyOS Components implement the events they use and the commands they provide: Sensorweb Research Laboratory -25- Georgia State University TinyOS Component Model There are two types of components: Modules: Implement the application behavior Configurations: Wires components together A component does not care if another component is a module or configuration A component may be composed of other components Sensorweb Research Laboratory -26- Georgia State University Components != Objects AppLogicP NetworkHandlerP AnotherHandlerP ActiveMessageC ActiveMessageC ActiveMessageC ✗ ✓ 27 Sensorweb Research Laboratory -27- Georgia State University Interfaces List of exposed events and commands Like ordinary C function declarations, except with event or command in front interface Receive { event message_t * Receive(message_t * msg, void * payload, uint8_t len); command void * getPayload(message_t * msg, uint8_t * len); command uint8_t payloadLength(message_t * msg); } 28 Sensorweb Research Laboratory -28- Georgia State University Modules Modules provide the implementation of one or more interfaces They may consume (use) other interfaces to do so module ExampleModuleP { provides interface SplitControl; uses interface Receive; uses interface Receive as OtherReceive; } implementation { ... } “Rename” interfaces with the as keyword -- required if you are using/providing more than one of the same 29 interface! Sensorweb Research Laboratory -29- Georgia State University Modules implementation block may contain: Variable declarations Helper functions Tasks Event handlers Command implementations 30 Sensorweb Research Laboratory -30- Georgia State University Modules: Variables and Functions Placed inside implementation block exactly like standard C declarations: ... implementation { uint8_t localVariable; void increment(uint8_t amount); ... void increment(uint8_t amount) { localVariable += amount; } } 31 Sensorweb Research Laboratory -31- Georgia State University Modules: Tasks Look a lot like functions, except: Prefixed with task Can’t return anything or accept any parameters implementation { ... task void legalTask() { // OK } task bool illegalTask() { // Error: can’t have a return value! } task void anotherIllegalTask(bool param1) { // Error: can’t have parameters! } } 32 Sensorweb Research Laboratory -32- Georgia State University Modules: Task Scheduling Tasks are scheduled using the post keyword error_t retval; retval = post handlePacket(); // retval == SUCCESS if task was scheduled, or E_FAIL if not TinyOS guarantees that scheduled task will eventually run Default scheduling policy: FIFO task1 task2 task1 task3 task1 ... ✗ ✓ 33 Sensorweb Research Laboratory -33- Georgia State University Modules: Commands and Events Commands and events also look like C functions, except: they start with the keyword command or event the “function” name is in the form InterfaceName.CommandOrEventName e.g. implementation { command error_t SplitControl.start() { // Implements SplitControl’s start() command } event message_t * Receive.receive(message_t * msg, void * payload, uint8_t len) { // Handles Receive’s receive() event } } 34 Sensorweb Research Laboratory -34- Georgia State University Modules: Commands and Events Commands are invoked using the call keyword: call Leds.led0Toggle(); // Invoke the led0Toggle command on the Leds interface Event handlers are invoked using the signal keyword: signal SplitControl.startDone(); // Invoke the startDone event handler on the SplitControl interface 35 Sensorweb Research Laboratory -35- Georgia State University Modules: Commands and Events A command, event handler, or function can call or signal any other command or event from any interface wired into the module: module ExampleModuleP { uses interface Receive; uses interface Leds; } implementation { event message_t Receive.receive(message_t * msg, void * payload, uint8_t len) { // Just toggle the first LED call Leds.led0Toggle(); return msg; } ... } 36 Sensorweb Research Laboratory -36- Georgia State University Synchronous vs. Asynchronous Commands and event handlers normally run in synchronous context i.e., cannot be reached by an interrupt handler The async keyword notifies nesC that the command/event handler may run in an asynchronous context: implementation { async event void Alarm.fired() { // Handle hardware alarm interrupt } } 37 Sensorweb Research Laboratory -37- Georgia State University Reminder: Race Conditions Use atomic blocks to avoid race conditions implementation { uint8_t sharedCounter; async event void Alarm.fired() { sharedCounter++; } event void Receive.receive(...) { ... atomic { Interrupts are disabled here -- use sparingly sharedCounter++; and make as short as practical } } } 38 Sensorweb Research Laboratory -38- Georgia State University Reminder: Race Conditions Tasks are always synchronous If timing isn’t crucial, defer code to tasks to avoid race conditions implementation { uint8_t sharedCounter; task void incrementCounter() { sharedCounter++; } async event void Alarm.fired() { post incrementCounter(); } event void Receive.receive(...) { ... sharedCounter++; } Task is scheduled immediately, but executes later. This is just an illustration of ‘post’, not efficient in this particular example! } 39 Sensorweb Research Laboratory -39- Georgia State University nesC and Race Conditions nesC can catch some, but not all, potential race conditions If you’re absolutely sure that there’s no race condition (or don’t care if there is), use the norace keyword: implementation { norace uint8_t sharedCounter; async event void Alarm1.fired() { sharedCounter++; call Alarm2.start(200); } async event void Alarm2.fired() { sharedCounter--; call Alarm1.start(200); } Race condition is impossible; async events are mutually exclusive } 40 Sensorweb Research Laboratory -40- Georgia State University TOSThreads New in TinyOS 2.1: the TOSThreads threading library Threads add a third execution context to TinyOS’s concurrency layer Lowest priority: only run when TinyOS kernel is idle Threads are preemptable by anything: sync, async, or other threads Also adds a library of synchronization primitives (mutex, semaphore, etc.) and blocking wrappers around non-blocking I/O Described in TOSThreads Tutorial (http://docs.tinyos.net/ index.php/TOSThreads_Tutorial) or TEP 134 41 Sensorweb Research Laboratory -41- Georgia State University Configurations List interfaces that the component imports & exports configuration NetworkHandlerC { provides interface SplitControl; Give comma-separated list(s) of constituent } components implementation { components NetworkHandlerP as NH, ActiveMessageP as AM; //NH.Receive -> AM.Receive; //NH.SplitControl = SplitControl; Wire two components’ NH.Receive -> AM; interfaces together using an arrow pointing from NH = SplitControl; Wire external user= to provider interfaces using } 42 Sensorweb Research Laboratory -42- Georgia State University Configuration Wires A configuration can bind an interface user to a provider using -> or < Bounce responsibilities using = User.interface -> Provider.interface Provider.interface <- User.interface User1.interface = User2.interface Provider1.interface = Provider2.interface The interface may be implicit if there is no ambiguity e.g., User.interface -> Provider == User.interface -> Provider.interface Sensorweb Research Laboratory -43- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 44 Sensorweb Research Laboratory -44- Georgia State University High-Level Summary nesC includes a lot of complex features that try to alleviate design problems with TinyOS 1.x The good news: you will probably never have to write code that incorporates these features The bad news: you’re almost certain to use code that incorporates these features First, an abstract look at what these features are and what their syntax means Second, a concrete example of how to use them to build components 45 Sensorweb Research Laboratory -45- Georgia State University Interfaces with Arguments Creating new interfaces to support different data types can get redundant fast interface ReadUint16 { command error_t read(); event void readDone(error_t error, uint16_t value); } interface ReadBool { command error_t read(); event void readDone(error_t error, bool value); } 46 Sensorweb Research Laboratory -46- Georgia State University Interfaces with Arguments If you want to make an interface adapt to different underlying types, then put a placeholder in angle brackets: interface Read<type> { command error_t read(); event void readDone(error_t error, type value); } module SixteenBitSensorP { provides interface Read<uint16_t>; } module BooleanSensorP { provides interface Read<bool>; } 47 Sensorweb Research Laboratory -47- Georgia State University Fan-In: No Big Deal AppLogicP NetworkHandlerP AnotherHandlerP uses Receive uses Receive uses Receive Many-to-one calls work like you’d expect ... provides Receive RadioP 48 Sensorweb Research Laboratory -48- Georgia State University Fan-Out: Bad Things Happen return &buffer1; return &buffer2; return &buffer3; AppLogicP NetworkHandlerP AnotherHandlerP uses Receive uses Receive uses Receive … but what about oneto-many calls? provides Receive RadioP 49 Sensorweb Research Laboratory -49- Georgia State University Fan-Out: What Bad Things Happen? If different return values come back, nesC may not be able to make sense of the contradiction and will arbitrarily pick one Avoid designs where this is possible If you can’t avoid it, see TinyOS Programming Guide 5.2 for more info on combining return values 50 Sensorweb Research Laboratory -50- Georgia State University Parameterized Wiring Consider the following way to avoid fanout: module RadioP { provides interface Receive as Receive0; provides interface Receive as Receive1; provides interface Receive as Receive2; uses interface LowLevelRadio; ... } implementation { event void LowLevelRadio.packetReceived( uint8_t * rawPacket) { ... uint8_t type = decodeType(rawPacket); if(type == 0) signal Receive0.receive(...); else if(type == 1) signal Receive1.receive(...); ... } ... } Sensorweb Research Laboratory -51- AppLogicP Network HandlerP Another HandlerP uses Receive uses Receive uses Receive RadioP 51 Georgia State University Parameterized Wiring The idea works in concept, but isn’t maintainable in practice But nesC can approximate the behavior in a much more maintainable way: module RadioP { provides interface Receive[uint8_t id]; ... } implementation { event void LowLevelRadio.packetReceived(uint8_t * rawPacket) { ... uint8_t type = decodeType(rawPacket); signal Receive[type].received(...); } ... } 52 Sensorweb Research Laboratory -52- Georgia State University Using Parameterized Wiring You can wire parameterized interfaces like so: AppLogicP -> RadioP.Receive[0]; NetworkHandlerP -> RadioP.Receive[1]; AnotherHandlerP -> RadioP.Receive[2]; If each component is wired in with a unique parameter, then fan-out goes away 53 Sensorweb Research Laboratory -53- Georgia State University Unique Parameters In most cases, it’s unreasonable to expect the user to count the number of times (s)he is using the interface and wire accordingly nesC can automatically generate a unique parameter for you using the unique() macro (which is a hash function): AppLogicP -> RadioP.Receive[unique(“RadioP”)]; // unique(“RadioP”) expands to 0 NetworkHandlerP -> RadioP.Receive[unique(“RadioP”)]; // unique(“RadioP”) expands to 1 AnotherHandlerP -> RadioP.Receive[unique(“RaadioP”)]; // unique(“RaadioP”) expands to 0 (oops) ... 54 Sensorweb Research Laboratory -54- Georgia State University uniqueCount() What if your component needs to store different state for each unique parameter? uniqueCount(X) module RadioP { expands to # of times ... unique(X) appears in } the application implementation { int16_t state[uniqueCount(“RadioP”)]; ... } 55 Sensorweb Research Laboratory -55- Georgia State University Defaults If you provide a parameterized interface and signal an event on it, you must also give a default event handler: module SharedComponentP { ... } implementation { event void LowLevelRadio.packetReceived(uint8_t * rawPacket) { ... signal Receive[type].received(...); } default event void Receive.received[uint8_t id](...) { // e.g., do nothing } ... } 56 Sensorweb Research Laboratory -56- Georgia State University Generic Components What if you have a component where different users absolutely should not share any state? Generic components let you “instantiate” a single component multiple times generic module HashTableP() { provides interface HashTable; } ... components new HashTableP() as H1, new HashTableP() as H2; AppLogicP.HashTable -> H1; NetworkHandlerP.HashTable -> H2; 57 Sensorweb Research Laboratory -57- Georgia State University Generic Components But wait ... didn’t I say earlier that components aren’t objects? nesC internally creates a complete second copy of the component AppLogicP NetworkHandlerP HashTableP$0 HashTableP$1 58 Sensorweb Research Laboratory -58- Georgia State University Generic Components with Parameters You can give each instantiation of the component slightly different behavior by adding compile-time parameters: generic module ListP(typedef type, uint8_t size) { provides interface List<type>; } implementation { type data[size]; command void List.clear() { for(uint8_t i = 0; i < size; i++) data[i] = 0; } } components new ListP(bool, 16); 59 Sensorweb Research Laboratory -59- Georgia State University Putting It All Together: Building a Timer Consider an AlarmC component that exposes a 32 KHz hardware clock using the following interface: interface Alarm { async event void fired(); } We want to create a high-level timer component that: Runs outside of the asynchronous context Can be hooked into multiple components Each consumer can choose a custom firing interval (every n ticks) Can be transformed into lower frequencies (16 KHz, 1 Hz, etc.) 60 Sensorweb Research Laboratory -60- Georgia State University Step 1: Get Out of Asynchronous Context interface Timer { event void fired(); } module AlarmToTimerP { provides interface Timer; uses interface Alarm; } implementation { task void timerTask() { signal Timer.fired(); } Timer async event void Alarm.fired() { post timerTask(); } AlarmToTimerP Alarm } Alarm AlarmC 61 Sensorweb Research Laboratory -61- Georgia State University Step 2: Virtualize the Timer module VirtualizeTimerP { uses interface Timer as SubTimer; provides interface Timer[uint8_t id]; } implementation { event void SubTimer.fired() { uint8_t i; for(i = 0; i < 255; i++) { signal Timer.fired[i](); } } Timer VirtualizeTimerP SubTimer Timer default event void Timer.fired[uint8_t id]() { // Do nothing } } AlarmToTimerP Alarm Alarm AlarmC 62 Sensorweb Research Laboratory -62- Georgia State University Step 3: Reprogram the Timer interface Timer /* v. 2 */ { event void fired(); command void startPeriodic(uint16_t interval); } Timer VirtualizeTimerP SubTimer Timer AlarmToTimerP Alarm Alarm AlarmC 63 Sensorweb Research Laboratory -63- Georgia State University Step 3: Reprogram the Timer module VirtualizeTimerP /* v. 2 */ { ... } implementation { uint16_t currentTime = 0; uint16_t nextTimeToFire[255]; uint16_t intervals[255]; event void SubTimer.fired() { uint8_t i; for(i = 0; i < 255; i++) { if(nextTimeToFire[i] == currentTime) { signal Timer.fired[i](); nextTimeToFire[i] += intervals[i]; } } currentTime++; } command void Timer.startPeriodic[uint8_t id](uint16_t interval) { nextTimeToFire[id] = currentTime + interval; intervals[id] = interval; } ... Timer VirtualizeTimerP SubTimer Timer AlarmToTimerP Alarm Alarm AlarmC } 64 Sensorweb Research Laboratory -64- Georgia State University Step 3.5: Tidy Up the Wiring generic configuration VirtualizedTimerC() { provides interface Timer; } implementation { components AlarmC; components AlarmToTimerP to AtoT; components VirtualizeTimerP as Virt; AtoT.Alarm -> AlarmC; Virt.SubTimer -> AtoT; Timer = Virt.Timer[ unique(“VirtualizedTimerC”) ]; } VirtualizedTimerC Timer VirtualizeTimerP SubTimer Timer AlarmToTimerP Alarm Alarm AlarmC 65 Sensorweb Research Laboratory -65- Georgia State University Step 3.5: Tidy Up the Wiring [0] [1] VirtualizedTimerC [2] VirtualizedTimerC VirtualizedTimerC Timer VirtualizeTimerP SubTimer Timer AlarmToTimerP Alarm Alarm AlarmC 66 Sensorweb Research Laboratory -66- Georgia State University Step 3.5: Tidy Up the Wiring module VirtualizeTimerP /* v. 2.5 */ { ... } implementation { enum { NUM_SLOTS = uniqueCount(“VirtualizedTimerC”); } uint16_t currentTime = 0; uint16_t nextTimeToFire[NUM_SLOTS]; uint16_t intervals[NUM_SLOTS]; VirtualizedTimerC Timer VirtualizeTimerP SubTimer Timer AlarmToTimerP event void SubTimer.fired() { uint8_t i; for(i = 0; i < NUM_SLOTS; i++) { ... Alarm Alarm AlarmC 67 Sensorweb Research Laboratory -67- Georgia State University Step 4: Transform the Timer’s Frequency generic module TransformTimerP(uint16_t multiplier) { uses interface Timer as SubTimer; provides interface Timer; } implementation { event void SubTimer.fired() { signal Timer.fired(); } command void Timer.startPeriodic(uint16_t interval) { call SubTimer.startPeriodic(interval * multiplier); } Timer Timer Transform TimerP(2) Transform TimerP(32) SubTimer SubTimer } Timer Timer Timer Virtualized Virtualized Virtualized TimerC TimerC TimerC 68 Sensorweb Research Laboratory -68- Georgia State University Step 5: Add Type Safety interface Timer<frequency> /* v. 3 */ { event void fired(); command void startPeriodic(uint16_t interval); } typedef struct { bool unused; } T32Khz; enum { T32Khz, T16Khz, …, TMilli, }; typedef struct { bool unused; } T16Khz; typedef struct { bool unused; } TMilli; Timer<T32Khz> Timer<T32Khz> Timer<T32Khz> Virtualized Virtualized Virtualized TimerC TimerC TimerC 69 Sensorweb Research Laboratory -69- Georgia State University Step 5: Add Type Safety generic module TransformTimerP( typedef frequency, uint16_t multiplier) { uses interface Timer<T32Khz> as SubTimer; provides interface Timer<frequency>; } implementation { event void SubTimer.fired() { signal Timer.fired(); } command void Timer.startPeriodic(uint16_t interval) { call SubTimer.startPeriodic(interval * multiplier); } } Timer<TMilli> Timer<T16Khz> TransformTimer P(TMilli, 32) TransformTimer P(T16Khz, 2) SubTimer<T32Khz> SubTimer<T32Khz> Timer<T32Khz> Timer<T32KHz> Timer<T32Khz> Virtualized Virtualized Virtualized TimerC TimerC TimerC 70 Sensorweb Research Laboratory -70- Georgia State University The Good News This is just an example! It’s already been implemented for you TimerMilliC component provides Timer<TMilli> interface 71 Sensorweb Research Laboratory -71- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 72 Sensorweb Research Laboratory -72- Georgia State University Slight Diversion: App Bootstrapping Each app has a “main” configuration which wires together the app’s constituent components But how do these components start running? TinyOS includes a MainC component which provides the Boot interface: interface Boot { event void booted(); } 73 Sensorweb Research Laboratory -73- Georgia State University Slight Diversion: App Bootstrapping Create one module which initializes your application, then wire MainC’s Boot interface into it: component MyAppC { } implementation { components MyAppP; components MainC; ... MyAppP.Boot -> MainC; } module MyAppP { uses interface Boot; } implementation { event void Boot.booted() { // Initialize app here } ... } 74 Sensorweb Research Laboratory -74- Georgia State University Slight Diversion #2: error_t Data Type TinyOS defines a special error_t data type that describes several different error codes Often given as return values to commands or event handlers Commonly used values: SUCCESS (everything’s OK) FAIL (general error, deprecated) EBUSY (subsystem is busy with another request, retry later) ERETRY (something weird happened, retry later) Others defined in $TOSROOT/types/TinyError.h 75 Sensorweb Research Laboratory -75- Georgia State University Message Addressing Each node has a unique 16-bit address (am_addr_t) specified by the make command make install.[address] platform Two special address constants: TOS_BCAST_ADDR (0xFFFF) is reserved for broadcast traffic TOS_NODE_ID always refers to the node’s own address Each message also has an 8-bit Active Message ID (am_id_t) analogous to TCP ports Determines how host should handle received packets, not which host receives it 76 Sensorweb Research Laboratory -76- Georgia State University TinyOS Active Messages message_t structure defined in $TOSROOT/tos/types/message.h Each platform defines platform-specific header, footer, and metadata fields for the message_t Applications can store up to TOSH_DATA_LENGTH bytes payload in the data field (28 by default) typedef nx_struct message_t { nx_uint8_t header[sizeof(message_header_t)]; nx_uint8_t data[TOSH_DATA_LENGTH]; nx_uint8_t footer[sizeof(message_footer_t)]; nx_uint8_t metadata[sizeof(message_metadata_t)]; } message_t; Header Payload (TOSH_DATA_LENGTH) Footer Metadata 77 Sensorweb Research Laboratory -77- Georgia State University Split-Phase Operation Many networking commands take a long time (ms) for underlying hardware operations to complete -- blocking would be bad TinyOS makes these long-lived operations split-phase Application issues start...() command that returns immediately An event is signaled when it’s actually done Error code here indicates whether TinyOS couldError startcode here indicates processing request whether TinyOS could interface SplitControl { complete processing request command error_t start(); event void startDone(error_t error); command error_t stop(); event void stopDone(error_t error); } 78 Sensorweb Research Laboratory -78- Georgia State University Active Messaging Interfaces interface AMSend { command error_t send(am_addr_t addr, message_t * msg, uint8_t len); command error_t cancel(message_t * msg); event void sendDone(message_t * msg, error_t error); command uint8_t maxPayloadLength(); command void* getPayload(message_t * msg, uint8_t len); } interface Receive { event message_t* receive(message_t * msg, void * payload, uint8_t len); } 79 Sensorweb Research Laboratory -79- Georgia State University Other Networking Interfaces interface Packet { command void clear(message_t * msg); command void* getPayload(message_t * msg, uint8_t len); command uint8_t payloadLength(message_t * msg); command void setPayLoadLength(message_t * msg, uint8_t len); command uint8_t maxPayloadLength(); } 80 Sensorweb Research Laboratory -80- Georgia State University Other Networking Interfaces interface AMPacket { command am_addr_t address(); command am_group_t localGroup(); command command command command am_addr_t destination(message_t* amsg); am_addr_t source(message_t* amsg); am_group_t group(message_t* amsg); bool isForMe(message_t* amsg); command am_id_t type(message_t* amsg); } 81 Sensorweb Research Laboratory -81- Georgia State University Message Buffer Ownership Transmission: AM gains ownership of the buffer until sendDone(...) is signaled Reception: Application’s event handler gains ownership of the buffer, but it must return a free buffer for the next message Application Active Messaging msg 82 Sensorweb Research Laboratory -82- Georgia State University Network Types Radio standards like 802.15.4 mean that you could have communication among different types of motes with different CPUs nesC defines network types (nx_uint16_t, nx_int8_t, etc.) that transparently deal with endian issues for you nesC also defines an nx_struct analogous to C structs typedef struct { uint16_t field1; bool field2; } bad_message_t; // Can have endianness problems // if sent to a host with a // different architecture typedef nx_struct { nx_uint16_t field1; nx_bool field2; } good_message_t; // nesC will resolve endian // issues for you 83 Sensorweb Research Laboratory -83- Georgia State University Sending a Message First create a .h file with an nx_struct defining the message data format, and a unique active message ID (127–255) enum { AM_SENSORREADING = 240, }; typedef nx_struct sensor_reading { nx_int16_t temperature; nx_uint8_t humidity; } sensor_reading_t; 84 Sensorweb Research Laboratory -84- Georgia State University Sending a Message Declare a message_t variable in your module to store the packet’s contents Get the packet’s payload using the Packet interface; cast it to your message type; and store whatever you want to send implementation { ... message_t output; task void sendData() { sensor_reading_t * reading = (sensor_reading_t *)call Packet.getPayload(&output, sizeof(sensor_reading_t)); reading->temperature = lastTemperatureReading; reading->humidity = lastHumidityReading; ... } } 85 Sensorweb Research Laboratory -85- Georgia State University Sending a Message Finally, use the AMSend interface to send the packet task void sendData() { ... if(call AMSend.send(AM_BROADCAST_ADDR, &output, sizeof(sensor_reading_t)) != SUCCESS) post sendData(); // Try to send the message, and reschedule the task if it // fails (e.g., the radio is busy) } 86 Sensorweb Research Laboratory -86- Georgia State University Sending a Message The AM subsystem will signal AMSend.sendDone() when the packet has been completely processed, successfully or not event void AMSend.sendDone(message_t * msg, error_t err) { if(err == SUCCESS) { // Prepare next packet if needed } else { post sendTask(); // Resend on failure } } 87 Sensorweb Research Laboratory -87- Georgia State University Receiving a Message When messages with the correct AM ID are received, the Receive interface fires the receive() event implementation { ... event message_t * Receive.receive(message_t * msg, void * payload, uint8_t len) { am_addr_t from = call AMPacket.source(msg); sensor_reading_t data = (sensor_reading_t *)payload; ... return msg; } } 88 Sensorweb Research Laboratory -88- Georgia State University Networking Components Note that we didn’t mention the packet’s AM ID anywhere in the code That’s because TinyOS includes generic components to manage the AM ID for you when you send/receive: components new AMSenderC(AM_SENSORREADING); components new AMReceiveC(AM_SENSORREADING); MyAppP.AMSender -> AMSenderC; // AMSenderC provides AMSend interface MyAppP.Receive -> AMReceiveC; // AMReceiverC provides Receive interface MyAppP.Packet -> AMSenderC; MyAppP.AMPacket -> AMSenderC; // AMSenderC and AMReceiveC provide Packet and AMPacket // interfaces (pick one or the other) 89 Sensorweb Research Laboratory -89- Georgia State University Networking Components Before you can send/receive, you need to turn the radio on ActiveMessageC component provides a SplitControl interface to control the radio’s power state components ActiveMessageC; MyAppP.RadioPowerControl -> ActiveMessageC; 90 Sensorweb Research Laboratory -90- Georgia State University What About Multi-Hop? Until recently, TinyOS did not include a generalpurpose, point-to-point multi-hop routing library Two special-purpose algorithms instead: Collection Tree Protocol (CTP) Dissemination Experimental TYMO point-to-point routing library added to TinyOS 2.1 (http://docs.tinyos.net/index.php/Tymo) blip: work-in-progress IPv6 stack (http://smote.cs.berkeley.edu:8000/tracenv/wiki/blip) 91 Sensorweb Research Laboratory -91- Georgia State University Collection Tree Protocol (CTP) 92 Sensorweb Research Laboratory -92- Georgia State University Dissemination 93 Sensorweb Research Laboratory -93- Georgia State University For More Information CTP & Dissemination APIs are beyond the scope of this talk For more information, see: TinyOS Tutorial 12: Network Protocols (http://docs.tinyos.net/ index.php/Network_Protocols) TEP 123: Collection Tree Protocol (http://www.tinyos.net/ tinyos-2.x/doc/html/tep123.html) TEP 118: Dissemination (http://www.tinyos.net/ tinyos-2.x/doc/html/tep118.html) 94 Sensorweb Research Laboratory -94- Georgia State University Sending Data to a PC TinyOS apps can also send or receive data over the serial/USB connection to an attached PC The SerialActiveMessageC component provides an Active Messaging interface to the serial port: components SerialActiveMessageC; MyAppP.SerialAMSend -> SerialActiveMessageC.Send[AM_SENSORREADING]; MyAppP.SerialReceive -> SerialActiveMessageC.Receive[AM_SENSORREADING]; // SerialActiveMessageC provides parameterized AMSend and // Receive interfaces MyAppP.SerialPowerControl -> SerialActiveMessageC; 95 Sensorweb Research Laboratory -95- Georgia State University Displaying Received Data Java application: net.tinyos.tools.Listen To specify which mote to read from, use the commandline parameter -comm serial@[port]:[platform] header payload 96 Sensorweb Research Laboratory -96- Georgia State University Disseminating Received Data Java application: net.tinyos.sf.SerialForwarder Other PCs on the network can connect to the Serial Forwarder to access the sensor data 97 Sensorweb Research Laboratory -97- Georgia State University Java PC-to-Mote Interface MIG: Message Interface Generator Generates a Java class representing a TOS message Usage: mig java -java-classname=[classname] [header.h] [message-name] –o [classname].java TinyOS Java SDK includes a net.tinyos.message.MoteIF class for interfacing with motes using Java See $TOSROOT/apps/tests/TestSerial/TestSerial.java for an example 98 Sensorweb Research Laboratory -98- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 100 Sensorweb Research Laboratory -100- Georgia State University Obtaining Sensor Data Each sensor has components that provides one or more split-phase Read interfaces interface Read<val_t> { command error_t read(); event void readDone(error_t result, val_t val); } Some sensor drivers provide additional interfaces for bulk (ReadStream) or low-latency (ReadNow) readings See TEPs 101 and 114 for details 101 Sensorweb Research Laboratory -101- Georgia State University Sensor Reading Example module MyAppP { uses interface Read<uint16_t> as AccelX; ... } configuration MyAppC { implementation { } ... implementation { task void readAccelX() { components MyAppP; if(call AccelX.read() != SUCCESS) components new AccelXC(); post readAccelX(); // X axis accelerator component } // defined by mts300 sensorboard event void AccelX.readDone(error_t err, MyAppP.AccelX -> AccelXC; uint16_t reading) { … if(err != SUCCESS) { } post readAccelX(); return; } // Handle reading here } ... } 102 Sensorweb Research Laboratory -102- Georgia State University Sensor Components Sensor components are stored in: $TOSROOT/tos/platform/[platform] (for standard sensors) Note that telosb “extends” telosa, so look in both directories if you’re using a TelosB or Tmote Sky mote! $TOSROOT/tos/sensorboard/[sensorboard] (for add-on sensor boards) Additional sensor board components may be available from TinyOS CVS in tinyos-2.x-contrib Unfortunately, some third-party sensor board drivers have yet to be ported from TinyOS 1.x to 2.x 103 Sensorweb Research Laboratory -103- Georgia State University External Sensors interface HplMsp430GeneralIO { command void makeInput(); command void makeOutput(); command bool get(); command void clr(); command void set(); command void toggle(); } 104 Sensorweb Research Laboratory -104- Georgia State University External Sensors Digital I/O: wire directly into HplMsp430GeneralIOC component component HplMsp430GeneralIOC { provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO provides interface HplMsp430GeneralIO ... } as as as as as as as as as as ADC0; ADC1; ADC2; ADC3; ADC4; ADC5; ADC6; ADC7; DAC0; DAC1; Analog I/O: read TEP 101 (Analog-to-Digital Converters) 105 Sensorweb Research Laboratory -105- Georgia State University Outline Installing TinyOS and Building Your First App Hardware Primer Basic nesC Syntax Advanced nesC Syntax Network Communication Sensor Data Acquisition Debugging Tricks and Techniques 106 Sensorweb Research Laboratory -106- Georgia State University Hard-Learned Lessons Be sure to check return values -- don’t assume SUCCESS! At the very least, set an LED when something goes wrong The TinyOS toolchain doesn’t always warn about overflowing integers uint8_t i; for(i = 0; i < 1000; i++) { ... } // This loop will never terminate Not all the Tmote Sky motes have sensors 107 Sensorweb Research Laboratory -107- Georgia State University msp430-gcc Alignment Bugs If you’re unlucky, msp430-gcc will crash with internal errors like these: /opt/tinyos-2.x/tos/interfaces/TaskBasic.nc: In function `SchedulerBasicP$TaskBasic$runTask': /opt/tinyos-2.x/tos/interfaces/TaskBasic.nc:64: unable to generate reloads for: (call_insn 732 3343 733 (set (reg:SI 15 r15) (call (mem:HI (symbol_ref:HI ("AsyncQueueC$1$Queue$dequeue")) [0 S2 A8]) (const_int 0 [0x0]))) 14 {*call_value_insn} (nil) (nil) (nil)) /opt/tinyos-2.x/tos/interfaces/TaskBasic.nc:64: Internal compiler error in find_reloads, at reload.c:3590 It’s almost always because of alignment bugs (msp430-gcc doesn’t always like it when fields straddle 16-bit boundaries) typedef nx_struct my_msg { nx_uint8_t field1; nx_uint8_t pad; nx_uint16_t field2; } my_msg_t; 108 Sensorweb Research Laboratory -108- Georgia State University 802.15.4 Radio Channels The CC2420 chip on the Tmote and MicaZ supports 802.15.4 channels 11 - 26 802.15.4 uses 2.4 GHz spectrum This can lead to interference between motes and with 802.11, Bluetooth, and all sorts of other things 109 Sensorweb Research Laboratory -109- Georgia State University 802.15.4 Radio Channels If you’re seeing weird network behavior, set your CC2420 channel to something else: Command-line: CC2420_CHANNEL=xx make ... Makefile: PFLAGS = -DCC2420_DEF_CHANNEL=xx 110 Sensorweb Research Laboratory -110- Georgia State University LEDs The easiest way to display runtime information is to use the mote’s LEDs: interface Leds { async command void led0On(); async command void led0Off(); async command void led0Toggle(); async command void led1On(); async command void led1Off(); async command void led1Toggle(); async command void led2On(); async command void led2Off(); async command void led2Toggle(); async command uint8_t get(); async command void set(uint8_t val); } Provided by the components LedsC and NoLedsC 111 Sensorweb Research Laboratory -111- Georgia State University printf() You can use printf() to print debugging messages to the serial port The messages are sent in a printf_msg structure ($TOSROOT/tos/lib/printf/printf.h) Though printf() ships with TinyOS, its components are not automatically located by the included Makefile stubs To force make to locate the printf()-related components, add the following line to your Makefile: CFLAGS += -I$(TOSDIR)/lib/printf Note: adding this flag automatically turns on SerialActiveMessageC subsystem 112 Sensorweb Research Laboratory -112- Georgia State University BaseStation The BaseStation app in $TOSROOT/apps/BaseStation will sniff all wireless traffic and forward it to the serial port Extremely helpful for figuring out what data is being sent! 113 Sensorweb Research Laboratory -113- Georgia State University TOSSIM Special target: make micaz sim Compiles application to native C code for your own machine, which can be loaded into Python or C++ simulator (“TOSSIM”) Upshot: use your favorite Python or C++ debugger to trace through your app’s execution Unfortunately somewhat complex and beyond the scope of this talk; see TinyOS Tutorial 11 http://docs.tinyos.net/index.php/TOSSIM 115 Sensorweb Research Laboratory -115- Georgia State University Avrora + MSPsim Avrora: cycle-accurate Mica2 and MicaZ emulator http://compilers.cs.ucla.edu/avrora/ MSPsim: MSP430 (TelosB) emulator http://www.sics.se/project/mspsim/ Profile and benchmark apps, monitor packet transmissions, or interface with gdb Slower than TOSSIM, but highly accurate 116 Sensorweb Research Laboratory -116- Georgia State University Safe TinyOS New in TinyOS 2.1: make [platform] safe Augments code to enforce pointer and type safety at runtime (bad casts, out-of-bounds array accesses, NULL pointer dereferences, etc.) When safety violations detected, LEDs blink error code http://www.cs.utah.edu/~coop/safetinyos/ Nathan Cooprider, Will Archer, Eric Eide, David Gay, and John Regehr, “Efficient Memory Safety for TinyOS,” Proceedings of 5th ACM Conference on Embedded Networked Sensor Systems (SenSys 2007), 2007. 117 Sensorweb Research Laboratory -117- Georgia State University Advanced Programming Tips Sensorweb Research Laboratory -118- Georgia State University Three Most Useful Tips in TinyOS TinyOS core is a while loop of task execution, but interrupt can interrupt it. All *.nc files will be pre-processed to a single app.c file. async event comes from hardware interruption. atomic keyword presents interruption, good or bad If there is any keyword or nesC mechanism that you do not understand, look into app.c! Nothing can be hidden in app.c. If you are not sure which components or libraries you are using, or how the system architecture looks like, run “make [platform] docs” and then look at docs/[platform]/index.html. Here [platform] could be telosb, micaz, imote2, etc. Sensorweb Research Laboratory -119- Georgia State University Tips in TinyOS programming Refer: http://sensorweb.cs.gsu.edu/wiki/index.php/Tips Handle flow between layers Stack overflow traps Post task related traps Let task self-post itself, need a taskBusy flag Atomic efficiency concerns Sanity checks Check whether array index out of bound or not, before you use array[ind]. Check whether post task return SUCCESS or FAIL …… Sensorweb Research Laboratory -120- Georgia State University How to handle send flow between layers U – Up layer M – Middle layer U2 U1 B – Low layer sendDone send Busy = TRUE send Busy = FALSE Busy = TRUE sendDone Busy = FALSE time M send sendDone L Simple way in TinyOS programming Sensorweb Research Laboratory -121- Georgia State University How to handle send flow between layers The send request from U2 will fail U2 U1 send sendDone send Busy = TRUE time Busy = FALSE M send sendDone L But U1 and U2 are independent, they do not know each other. The later send by U2 will fail because M is busy. This is too bad if the data rate is high. Sensorweb Research Laboratory -122- Georgia State University How to handle send flow between layers Notice: sendDone order might be different than send order U – Up layer retry M – Middle layer B – Low layer U1 U2 U3 U1 U3 U1 U3 U3 U2 U1 U3 sDone - sendDone send send send send sDone sDone send Busy = TRUE sDone sDone sDone time Busy = FALSE M send sendDone L time Need a queue to buffer packets from up layers. However, make sure the queue size >= sum(queue size(Ui)) Sensorweb Research Laboratory -123- Georgia State University How to handle send flow between layers U – Up layer retry M – Middle layer B – Low layer sDone - sendDone U1 U2 U3 U1 U3 U1 U3 send send send send sDone sDone send Busy = TRUE U3 U1 U2 U3 sDone sDone sDone time Busy = FALSE M prioritization send (U1) send send (U3) (U2) send (U1) sDone sDone send (U1) (U3) (U3) L sDone sDone sDone (U3) (U1) (U2) U2 sent to UART which is slower Notice: sendDone order might be different than send order, either due to prioritization at M, or there are multiple L layer component (e.g., radio and UART) who has different speed Sensorweb Research Laboratory -124- Georgia State University How to handle receive flow between layers U – Up layer M – Middle layer U2 U1 B – Low layer receive receive time return original pointer as soon as possible M receive receive L If it takes some time to process it, copy it to buffer bool, and return original pointer immediately; otherwise, process it and return original pointer after that. If not, it will slow down the radio. Sensorweb Research Laboratory -125- Georgia State University Stack overflow traps Phenomenon: nodes software works at beginning, dies after run a while The tips to avoid: Do not define too many/big local variables inside a function. If big, define it as component variables Module test { // interfaces } Implementation { Module test { // interfaces } Implementation { void func() { int array[MAX_LEN]; TOSMsg msg; ……………. } } Sensorweb Research Laboratory int array[MAX_LEN]; TOSMsg msg; void func() { ……………. } } // if those variables are used by other functions as well, be careful with race conditions! -126- Georgia State University Stack Overflow Problem Do not use big data structure variables as a function parameter. Use pointer if necessary. void func (TOSMsgPtr msg, ……) void func (TOSMsg msg, ……) By the way, the following usage is wrong, not even mentioning the above subtle problems: typedef struct{ int data; int index; } FooData; FooData* foo(int ind){ FooData element; element.data = rand(); element.index = ind; return &element; } Sensorweb Research Laboratory -127- Georgia State University Make atomic section and async function routines as small as possible func(….) { } func(….) { } atomic { X = y; if(X > 100) { // Assume the following does not need to be atomic …… } } //End atomic atomic { X = y; temp = X; }//End atomic if(temp > 100) { // Assume the following does not need to be atomic …… } Sensorweb Research Laboratory -128- Georgia State University Potentially Nasty Bugs Sensorweb Research Laboratory -129- Georgia State University Sensorweb Research Laboratory -130- Georgia State University Sensorweb Research Laboratory -131- Georgia State University Sensorweb Research Laboratory -132- Georgia State University Sensorweb Research Laboratory -133- Georgia State University Sensorweb Research Laboratory -134- Georgia State University Sensorweb Research Laboratory -135- Georgia State University Three Most Useful Tips in TinyOS TinyOS core is a while loop of task execution, but interrupt can interrupt it. All *.nc files will be pre-processed to a single app.c file. async event comes from hardware interruption. atomic keyword presents interruption, good or bad If there is any keyword or nesC mechanism that you do not understand, look into app.c! Nothing can be hidden in app.c. If you are not sure which components or libraries you are using, or how the system architecture looks like, run “make [platform] docs” and then look at docs/[platform]/index.html. Here [platform] could be telosb, micaz, imote2, etc. Sensorweb Research Laboratory -136- Georgia State University