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
TinyOS Tutorial
Mo Sha
CSE 520S Fall 2011
TinyOS Installation
TinyOS Installation (cont.)
TinyOS Community
http://www.tinyos.net/
Various installation options listed under Getting started section
TinyOS Installation (cont.)
TinyOS Installation (cont.)
Pre-compiled .rpm and .deb packages for Fedora and Ubuntu
Linux users
Ubuntu users: be sure to remove brltty package
All necessary drivers already included with Linux kernel
OS X unofficially supported but works well
Windows installation uses Cygwin to emulate Linux software
layer
Works well under XP, refuses to work on some Vista/7 machines
(updating Cygwin after the installation may help)
Step 5b (optional): Post-install fixes in Windows/Cygwin environment
Running a XubunTOS Virtual Machine Image in VMware Player
5
TinyOS Directory Structure
make System
/opt/tinyos-2.1.1 ($TOSROOT)
$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
apps
support
make
sdk
tools
tos
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, micaz, telosb)
make System
Useful commands
make [re]install.[node ID] [platform]
[programming options]
motelist
make telosb
make telosb reinstall,1
mica2/micaz: mib510,/dev/ttyXYZ
telosb: bsl,/dev/ttyXYZ
make clean
make docs [platform]
See the list of motes connecting with pc
node ID: 0 - 255
programming options:
Compile your code
Program the mote
make telosb install,1
Generates HTML documentation in
$TOSROOT/doc/nesdoc/[platform]
Compile your code
Program the mote
make docs telosb
Generate docs
10
11
Example under Windows
Build Stages
C:\cygwin\bin
http://students.cec.wustl.edu/~ms31/520S/
Preprocess .nc to .c, then compile .c
to binary
Set AM address and node ID in
binary
Program mote
12
13
Homework
How to Get Help
Install TinyOS 2.1 and build Blink
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/tinywiki/index.php/TinyOS_Tutorials
(Not graded, but a good idea to make sure you have
everything up and running)
14
15
How to Get Help
Outline
nesdoc: annotated API for all interfaces and components in
TinyOS:
http://docs.tinyos.net/tinywiki/index.php/Source_Code_D
ocumentation
TinyOS Enhancement Protocols (TEP): formal
documentation for TinyOS features:
http://docs.tinyos.net/tinywiki/index.php/TEPs
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
16
Tmote Sky (aka TelosB)
17
MICA2 Mote (MPR400CB)
IEEE 802.15.4 Radio
128KB
Instruction
EEPROM
250kbps
TI MSP430 microcontroller
16MHz, 16 MIPS, 10kB RAM
Integrated antenna & USB interface
Low power utilization
2 AA
4KB Data
EEPROM
Chipcon
CC1000 radio,
38K or 19K baud,
Manchester,
315, 433, or
900MHz
1.8mA/5.1A vs. Mica 2s 8mA/15A
Quantity w/o on-board sensors: 6
Quantity w/temperature, light, and humidity sensors: 20
Quantity w/SBT80 sensor board: 4
Quantity:
56
19
Atmel
ATmega128L P
7.3827MHz (8 MIPS)
SPI bus
3 LEDs
UART 2
512KB
External
Flash
Memory
ADC 0-7
UART 1
I2C Bus
51 pin I/O Connector
To Sensors, JTAG, and/or
Programming Board
20
MPR2400 MICAz
Programming Board (MIB510)
Mote JTAG
Same as Mica2 except with IEEE 802.15.4 radio
MICA2Dot interface
Serial interface
to laptop
2.4GHz
250kbps
MICA2 interface
ISPJTAG
Quantity: 7
Block data
to laptop
Quantity: 3
5V Power
Reset
21
MTS310CA Sensor Board
4.6KHz
Speaker
51 pin MICA2
Interface
2 Axis
Accelerometer
22
MTS420 Sensor Board
Tone Detector
Light and
Temperature
Microphone
GPS
Accelerometer
Light
Temperature
Humidity
Barometric Pressure
2KB EEPROM Conf.
Magnetometer
Quantity: 1
Quantity: 3
23
24
MDA300 Sensor Board
NSLU2 Network Storage Link (Slug)
General measurement platform for motes
266MHz Xscale CPU, 32MB SDRAM, 8MB
flash, 1x Ethernet port
Wired power
No built-in radio, but 2x USB 2.0 ports for
add-on 802.11/Bluetooth/mote interface
Temperature/Humidity sensor
7 single-ended or 3 differential ADC
channels
6 digital I/O channels
Quantity: 1
Can be easily converted to an embedded Linux box with
third-party firmware
Our testbed uses the OpenWrt distribution (http://openwrt.org)
Quantity: 15
25
26
Outline
TinyOS Execution Model
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
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
28
TinyOS Component Model
29
Components != Objects
event command
startDone()
start()
provides
SplitControl
NetworkHandlerP
provides
SplitControl
AppLogicP
NetworkHandlerP
AnotherHandlerP
ActiveMessageC
ActiveMessageC
ActiveMessageC
uses Receive
provides
Receive
ActiveMessageC
NetworkHandlerC
30
31
Interfaces
Modules
List of exposed events and commands
Like ordinary C function declarations, except with event
or command in front
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 {
...
}
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);
}
Rename interfaces with the as keyword -- required if you
are using/providing more than one of the same interface!
32
33
Modules
Modules: Variables and Functions
implementation block may contain:
Variable declarations
Helper functions
Tasks
Event handlers
Command implementations
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;
}
}
34
35
Modules: Tasks
Modules: Task Scheduling
Look a lot like functions, except:
Tasks are scheduled using the post keyword
Prefixed with task
Cant return anything or accept any parameters
error_t retval;
retval = post handlePacket();
// retval == SUCCESS if task was scheduled, or FAIL if not
implementation {
...
task void legalTask() {
// OK
}
task bool illegalTask() {
// Error: cant have a return value!
}
task void anotherIllegalTask(bool param1) {
// Error: cant have parameters!
}
}
TinyOS guarantees that task will eventually run
Default scheduling policy: FIFO
task1
task2
task1
task3
task1
36
Modules: Commands and Events
37
Commands are invoked using the call keyword:
they start with the keyword command or event
the function name is in the form
call Leds.led0Toggle();
// Invoke the led0Toggle command on the Leds interface
InterfaceName.CommandOrEventName
Event handlers are invoked using the signal keyword:
e.g.
implementation {
command error_t SplitControl.start() {
// Implements SplitControls start() command
}
Modules: Commands and Events
Commands and events also look like C functions, except:
...
signal SplitControl.startDone();
// Invoke the startDone event handler on the SplitControl interface
event message_t * Receive.receive(message_t * msg, void * payload,
uint8_t len) {
// Handles Receives receive() event
}
38
39
Modules: Commands and Events
Synchronous vs. Asynchronous
A command, event handler, or function can call or signal any other
command or event from any interface wired into the module:
Commands and event handlers normally run in
synchronous context
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;
}
...
}
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
}
}
40
41
Reminder: Race Conditions
Reminder: Race Conditions
Use atomic blocks to avoid race conditions
Tasks are always synchronous
If timing isnt crucial, defer code to tasks to avoid race conditions
implementation {
uint8_t sharedCounter;
implementation {
uint8_t sharedCounter;
async event void Alarm.fired() {
atomic {
Interrupts are disabled here -- use sparingly
sharedCounter++;
and make as short as practical
}
}
task void incrementCounter() { sharedCounter++; }
async event void Alarm.fired() {
post incrementCounter();
}
event void Receive.receive(...) {
...
sharedCounter++;
}
Task is scheduled
immediately, but
executes later
event void Receive.receive(...) {
...
sharedCounter++;
}
}
42
43
nesC and Race Conditions
TOSThreads
nesC can catch some, but not all, potential race conditions
If youre absolutely sure that theres no race condition (or
dont care if there is), use the norace keyword:
New in TinyOS 2.1: the TOSThreads threading library
Threads add a third execution context to TinyOSs
concurrency layer
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);
}
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 nonblocking I/O
Described in TOSThreads Tutorial (http://docs.tinyos.net/
index.php/TOSThreads_Tutorial) or TEP 134
Race condition is
impossible; events
are mutually
exclusive
}
44
45
Configurations
Example-BLink
configuration NetworkHandlerC {
provides interface SplitControl;
}
implementation {
components NetworkHandlerP as NH,
ActiveMessageP as AM;
//NH.Receive -> AM.Receive;
//SplitControl = NH.SplitControl;
NH.Receive -> AM;
SplitControl = NH;
}
Three files:
1.Makefile
2.BlinkAppC.nc
3.BlinkC.nc
46
47
Makefile
BlinkAppC.nc
COMPONENT=BlinkAppC
include $(MAKERULES)
configuration BlinkAppC
{
}
implementation
{
components MainC, BlinkC, LedsC;
components new TimerMilliC() as Timer0;
components new TimerMilliC() as Timer1;
components new TimerMilliC() as Timer2;
BlinkC -> MainC.Boot;
BlinkC.Timer0 -> Timer0;
BlinkC.Timer1 -> Timer1;
BlinkC.Timer2 -> Timer2;
BlinkC.Leds -> LedsC;
}
48
BlinkC.nc
#include "Timer.h"
module BlinkC {
uses interface Timer<TMilli> as Timer0;
uses interface Timer<TMilli> as Timer1;
uses interface Timer<TMilli> as Timer2;
uses interface Leds;
uses interface Boot;
}
49
Outline
implementation
{
event void Boot.booted()
{
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}
event void Timer0.fired()
{
dbg("BlinkC", "Timer 0 fired @
%s.\n", sim_time_string());
call Leds.led0Toggle();
}
event void Timer1.fired() {..}
event void Timer2.fired() { }
}
50
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
51
High-Level Summary
Interfaces with Arguments
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: youre almost certain to use code that
incorporates these features
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);
}
52
Interfaces with Arguments
53
Fan-In: No Big Deal
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>;
}
AppLogicP
NetworkHandlerP
AnotherHandlerP
uses Receive
uses Receive
uses Receive
Many-to-one calls work
like youd expect ...
provides Receive
module BooleanSensorP {
provides interface Read<bool>;
}
RadioP
54
Fan-Out: Bad Things Happen
return &buffer1;
return &buffer2;
return &buffer3;
AppLogicP
NetworkHandlerP
AnotherHandlerP
uses Receive
uses Receive
uses Receive
55
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 cant avoid it, see TinyOS Programming Guide 5.2 for
more info on combining return values
but what about oneto-many calls?
provides Receive
RadioP
56
57
Parameterized Wiring
Parameterized Wiring
Consider the following way to avoid fan-out:
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(...);
...
}
...
}
AppLogicP
Network
HandlerP
Another
HandlerP
uses Receive
uses Receive
uses Receive
The idea works in concept, but isnt 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(...);
RadioP
}
...
}
58
59
Using Parameterized Wiring
Unique Parameters
You can wire parameterized interfaces like so:
In most cases, its 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:
AppLogicP -> RadioP.Receive[0];
NetworkHandlerP -> RadioP.Receive[1];
AnotherHandlerP -> RadioP.Receive[2];
AppLogicP -> RadioP.Receive[unique(RadioP)];
// unique(RadioP) expands to 0
If each component is wired in with a unique parameter,
then fan-out goes away
NetworkHandlerP -> RadioP.Receive[unique(RadioP)];
// unique(RadioP) expands to 1
AnotherHandlerP -> RadioP.Receive[unique(RaadioP)];
// unique(RaadioP) expands to 0 (oops)
...
60
61
uniqueCount()
Defaults
What if your component needs to store different state for
each unique parameter?
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(...);
}
uniqueCount(X)
module RadioP {
expands to # of times
...
unique(X) appears in
}
the application
implementation {
int16_t state[uniqueCount(RadioP) ];
default event void Receive.received[uint8_t id](...) {
// e.g., do nothing
}
...
...
}
}
62
63
Outline
Slight Diversion: App Bootstrapping
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
Each app has a main configuration which wires together
the apps constituent components
But how do these components start running?
TinyOS includes a MainC component which provides the
Boot interface:
interface Boot {
event void booted();
}
64
Slight Diversion #2: error_t Data
Type
Slight Diversion: App Bootstrapping
Create one module which initializes your application, then
wire MainCs Boot interface into it:
configuration MyAppC {
}
implementation {
components MyAppP;
components MainC;
...
MyAppP.Boot -> MainC;
}
65
module MyAppP {
uses interface Boot;
}
implementation {
event void Boot.booted() {
// Initialize app here
}
...
}
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 (everythings 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
66
Message Addressing
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)
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 nodes own address
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
Each message also has an 8-bit Active Message ID
(am_id_t) analogous to TCP ports
67
Determines how host should handle received packets, not which
host receives it
68
69
Split-Phase Operation
Active Messaging Interfaces
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
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);
}
Application issues start...()
command
that returns immediately
Error code
here indicates
Error
whether
TinyOS could
startcode here indicates
An event is signaled when
its actually
done
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);
interface Receive {
event message_t* receive(message_t * msg, void *
payload, uint8_t len);
}
}
70
71
Other Networking Interfaces
Other Networking Interfaces
interface Packet {
command void clear(message_t * msg);
interface AMPacket {
command am_addr_t address();
command am_group_t localGroup();
command void* getPayload(message_t * msg, uint8_t
len);
command
command
command
command
command uint8_t payloadLength(message_t * msg);
command void setPayLoadLength(message_t * msg, uint8_t
len);
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);
command uint8_t maxPayloadLength();
72
73
Other Networking Interfaces
Message Buffer Ownership
interface PacketAcknowledgements {
async command error_t requestAck(message_t* msg);
async command error_t noAck(message_t* msg);
async command bool wasAcked(message_t* msg);
}
Transmission: Radio driver gains ownership of the buffer
until sendDone(...) is signaled
Reception: Applications event handler gains ownership of
the buffer, but it must return a free buffer for the next
message
Application
Default behavior: no ACKs
Even with ACKs enabled, no automatic retransmissions
Optional packet link layer can handle retransmissions;
#define PACKET_LINK and see TEP 127
Active Messaging
74
msg
75
Network Types
Sending a Message
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
First create a .h file with an nx_struct defining the
message data format, and a unique active message ID
(127255)
typedef struct {
uint16_t field1;
bool field2;
} bad_message_t;
// Can have endianness problems
// if sent to a host with a
// different architecture
enum {
AM_SENSORREADING = 240,
};
typedef nx_struct {
nx_uint16_t field1;
nx_bool field2;
} good_message_t;
typedef nx_struct sensor_reading {
nx_int16_t temperature;
nx_uint8_t humidity;
} sensor_reading_t;
// nesC will resolve endian
// issues for you
76
77
Sending a Message
Sending a Message
Declare a message_t variable in your module to store the
packets contents
Get the packets payload using the Packet interface; cast it
to your message type; and store your data
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)
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;
...
}
}
78
79
Sending a Message
Receiving a Message
The AM subsystem will signal AMSend.sendDone() when
the packet has been completely processed, successfully or
not
When messages with the correct AM ID are received, the
Receive interface fires the receive() event
event void AMSend.sendDone(message_t * msg, error_t err) {
if(err == SUCCESS) {
// Prepare next packet if needed
}
else {
post sendTask();
// Resend on failure
}
}
80
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;
}
}
81
Networking Components
Networking Components
Note that we didnt mention the packets AM ID anywhere in the code
Thats because TinyOS includes generic components to manage the
AM ID for you when you send/receive:
Before you can send/receive, you need to turn the radio on
ActiveMessageC component provides a SplitControl
interface to control the radios power state
components new AMSenderC(AM_SENSORREADING);
components new AMReceiverC(AM_SENSORREADING);
components ActiveMessageC;
MyAppP.RadioPowerControl -> ActiveMessageC;
MyAppP.AMSend -> AMSenderC;
// AMSenderC provides AMSend interface
MyAppP.Receive -> AMReceiverC;
// AMReceiverC provides Receive interface
MyAppP.Packet -> AMSenderC;
MyAppP.AMPacket -> AMSenderC;
// AMSenderC and AMReceiverC provide Packet and AMPacket
// interfaces (pick one or the other)
82
83
What About Multi-Hop?
Until recently, TinyOS did not include a general-purpose,
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: IPv6 stack added to TinyOS 2.1.1
(http://docs.tinyos.net/index.php/BLIP_Tutorial)
Collection Tree Protocol (CTP)
Basic Operation
84
configuration MyCtpAppC {
}
implementation {
components AppLogicP;
components CollectionC;
...
MyAppP.RoutingControl -> CollectionC;
MyAppP.RootControl -> CollectionC;
...
module AppLogicP {
uses interface StdControl as
RoutingControl;
uses interface RootControl;
...
}
implementation {
...
configuration MyCtpAppC {
}
implementation {
components AppLogicP;
components CollectionC;
...
event void RadioControl.startDone(
error_t err) {
...
if(TOS_NODE_ID == 100)
call RootControl.setRoot();
call RoutingControl.start();
}
}
85
MyAppP.Send -> CollectionC.
Send[MY_MSG_ID];
MyAppP.Receive -> CollectionC.
Receive[MY_MSG_ID];
MyAppP.Packet -> CollectionC;
...
module AppLogicP
...
uses interface
uses interface
uses interface
...
}
implementation {
...
{
Send;
Receive;
Packet;
task void sendPacket() {
result_t err = call Send.send(
&msg, sizeof(MyMsg));
...
}
event message_t * Receive.receive(
message_t * msg, void * payload,
uint8_t len) {
// Only signaled on root node
...
}
...
Collection Tree Protocol (CTP)
Collection Tree Protocol (CTP)
Initializing CTP
86
Sending/Receiving Packets
87
Collection Tree Protocol (CTP)
To link into your app, include these lines in your Makefile:
CFLAGS += -I$(TOSDIR)/lib/net
CFLAGS += -I$(TOSDIR)/lib/net/4bitle
CFLAGS += -I$(TOSDIR)/lib/net/ctp
CTP automatically turns on packet ACKs, retransmits up to
30 times at each hop
But no end-to-end acknowledgments;
PacketAcknowledgments.wasAcked() only tells you if the packet
made it to the first hop
Dissemination
Basic Operation
88
89
For More Information
Sending Data to a PC
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)
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;
90
91
Interfacing With Motes
Interfacing With Motes
TinyOS includes a Java-based SerialForwarder utility that
implements PC side of TEP 113
Java SDK connects to SerialForwarder and converts TinyOS
messages to/from native Java objects
mig application auto-generates these classes from your
apps header files
java net.tinyos.sf.SerialForwarder -comm
serial@[port]:[speed]
[speed] may be a specific baud rate or a platform name (e.g., telosb)
Listens on TCP port and sends/receives TinyOS messages
from local or remote applications
mig java -java-classname=[classname]
[header.h][message-name] o [classname].java
SomeMessage
OtherMessage
MoteIF
92
.
.
.
93
SDK Support for Other Languages
SDK Support for Other Languages
C/C++
Python
C reimplementation of SerialForwarder (sf) and a few test apps
found in $TOSROOT/support/sdk/c/sf
Building sf also builds libmote.a for accessing the motes in your
own code
See sfsource.h and serialsource.h to get started
Python classes in $TOSROOT/support/sdk/python closely
mirror Java SDK
Not completely stand-alone; Python MoteIF implementation talks
to Java or C SerialForwarder
See tinyos/message/MoteIF.py to get started
C#
mig can generate C# classes to parse/generate raw TinyOS
packets
But its up to the user to actually get those packets from the serial
port or SerialForwarder
94
Outline
95
Obtaining Sensor Data
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
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
97
Sensor Reading Example
98
Sensor Components
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
}
...
}
99
Sensor components are stored in:
$TOSROOT/tos/platform/[platform] (for standard sensors)
Note that telosb extends telosa, so look in both directories if
youre 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
100
External Sensors
External Sensors
Digital I/O: wire directly into HplMsp430GeneralIOC component
interface HplMsp430GeneralIO {
command void makeInput();
command void makeOutput();
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
...
}
command bool get();
command void clr();
command void set();
command void toggle();
}
as
as
as
as
as
as
as
as
as
as
ADC0;
ADC1;
ADC2;
ADC3;
ADC4;
ADC5;
ADC6;
ADC7;
DAC0;
DAC1;
I2C: read TEP 117 (Low-Level I/O)
Analog I/O: read TEP 101 (Analog-to-Digital Converters)
101
Outline
102
Hard-Learned Lessons
Installing TinyOS and Building Your First App
Hardware Primer
Basic nesC Syntax
Advanced nesC Syntax
Network Communication
Sensor Data Acquisition
Debugging Tricks and Techniques
Be sure to check return values -- dont assume SUCCESS!
At the very least, set an LED when something goes wrong
The TinyOS toolchain doesnt 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
103
802.15.4 Radio Channels
104
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
106
107
802.15.4 Radio Channels
Active Message Groups
If youre seeing weird network behavior, set your CC2420
channel to something else:
To avoid address collision with other applications or
networks, you can also change the AM group:
Defaults to 26
Command-line: CC2420_CHANNEL=xx make ...
Makefile: PFLAGS = -DCC2420_DEF_CHANNEL=xx
Defaults to 0x22
Makefile: DEFAULT_LOCAL_GROUP=xx (any 16-bit value)
On 802.15.4 compliant chips, maps to PAN ID
Does not prevent physical interference of packets: only
instructs radio chip/driver to filter out packets addressed
to other groups
(Small request: please do not use channels 12, 14, 22, or 25!)
108
109
LEDs
printf()
The easiest way to display runtime information is to use
the motes LEDs:
You can use printf() to print debugging messages to the
serial port
Messages are buffered and sent to serial port in bulk;
printfflush() asks TinyOS to flush buffer
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);
}
DONT USE printf() FOR CRITICAL MESSAGES
When its buffer fills up, printf() starts throwing away
data
Provided by the components LedsC and NoLedsC
110
111
printf()
BaseStation
To enable the printf library, add the following line to your
Makefile:
The BaseStation app in
$TOSROOT/apps/BaseStation will sniff all wireless
traffic and forward it to the serial port
Listen tool prints hex-dump of packets to console:
CFLAGS += -I$(TOSDIR)/lib/printf
Note: this automatically turns on SerialActiveMessageC
subsystem
java net.tinyos.tools.Listen
[-comm serial@[port]:[speed]]
Included PrintfClient utility displays printed messages to
console
Extremely helpful for figuring out what data is being sent!
java net.tinyos.tools.PrintfClient
[-comm serial@[port]:[speed]]
112
113
TOSSIM
TOSSIM Configuration: Topology
make micaz sim compiles application to native C code for
your own machine, which can be loaded into Python or
C++ simulator (TOSSIM)
Good way to rapidly test application logic, at the cost of
some realism
List of links in the network and
associated gain (signal strength in dBm)
Several sources:
e.g., does not emulate sensing and does not reproduce timing of
real microcontrollers
Besides app code, need two configuration details:
Topology of simulated network
Noise trace from simulated environment
0 1 -90.80
1 0 -95.95
0 2 -97.48
2 0 -102.10
0 3 -111.33
3 0 -115.49
0 4 -104.82
40 -110.09
...
Real measurements
Samples included in TinyOS
($TOSDIR/lib/tossim/topologies)
Generate one based on various parameters
(http://www.tinyos.net/
tinyos-2.x/doc/html/tutorial/usctopologies.html)
(from 15-15-sparse-mica2-grid.txt)
115
116
TOSSIM Configuration: Noise Trace
Other TOSSIM Features
Trace of ambient noise readings in dBm
Must contain at least 100 entries; more
is better, but RAM consumption
increases with larger traces
Two sources:
Log debug messages to console or to a file
Inject packets into network
Debugging support
Real measurements
Samples included in TinyOS
($TOSDIR/lib/tossim/noise)
-39
-98
-98
-98
-99
-98
-94
-98
...
Python TOSSIM: read variablescontents
C++ TOSSIM: use gdb
TOSSIM Live fork: TOSSIM acts as SerialForwarder,
send/receive serial packets to simulated motes
http://docs.tinyos.net/index.php/TOSSIM_Live
See TinyOS Tutorial 11 for more details
(from meyer-heavy.txt)
117
118
Avrora + MSPsim
Safe TinyOS
Avrora: cycle-accurate Mica2 and MicaZ emulator
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://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
119
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.
120
Demo: Putting it All Together
Demo Example
Three files:
1.Makefile
2.DemoMessage.h
3.DemoAppC.nc
4.DemoP.nc
121
122
Makefile
DemoMessage.h
COMPONENT=DemoAppC
include $(MAKERULES)
#ifndef __DEMOMESSAGE_H
#define __DEMOMESSAGE_H
enum
{
AM_DEMO_MSG = 231,
};
typedef nx_struct demo_msg
{
nx_uint16_t lastReading;
} demo_msg_t;
#endif
123
124
DemoAppC.nc
#include "DemoMessage.h"
configuration DemoAppC{}
implementation{
components DemoP, MainC;
DemoP.Boot -> MainC.Boot;
components LedsC;
DemoP.Leds -> LedsC;
components new HamamatsuS10871TsrC() as PhotoSensor;
DemoP.Read -> PhotoSensor;
components ActiveMessageC;
DemoP.RadioControl -> ActiveMessageC;
components new AMSenderC(AM_DEMO_MSG),
new AMReceiverC(AM_DEMO_MSG);
DemoP.AMSend -> AMSenderC;
DemoP.Receive -> AMReceiverC;
DemoP.Packet -> AMSenderC;
components new TimerMilliC();
DemoP.Timer -> TimerMilliC;
}
DemoP.nc
module DemoP
{
uses interface Boot;
uses interface Leds;
uses interface Read<uint16_t>;
uses interface SplitControl as RadioControl;
uses interface AMSend;
uses interface Receive;
uses interface Packet;
uses interface Timer<TMilli>;
}
125
126
DemoP.nc
DemoP.nc
implementation
{
message_t buf;
task void readSensor();
task void sendBuffer();
event void Boot.booted()
{
if(call RadioControl.start() != SUCCESS)
call Leds.led0On();
}
event void Timer.fired()
{
post readSensor();
}
task void readSensor()
{
if(call Read.read() != SUCCESS)
post readSensor();
}
event void RadioControl.startDone(error_t err)
{
if(err != SUCCESS)
call Leds.led0On();
if(TOS_NODE_ID == 0)
call Timer.startPeriodic(64);
event void Read.readDone(error_t err, uint16_t val)
{
demo_msg_t * payload = (demo_msg_t *)call Packet.getPayload(&buf, sizeof(demo_msg_t));
payload->lastReading = val;
post sendBuffer();
127
DemoP.nc
task void sendBuffer()
{
if(call AMSend.send(AM_BROADCAST_ADDR,
&buf, sizeof(demo_msg_t)) != SUCCESS)
post sendBuffer();
}
event void AMSend.sendDone(message_t * jkdsakljads, error_t err)
{
if(err != SUCCESS)
post sendBuffer();
}
event message_t * Receive.receive(message_t * m,void * payload,uint8_t size)
{
demo_msg_t * dpayload = (demo_msg_t *)payload;
call Leds.set(dpayload->lastReading / 200);
return m;
}
event void RadioControl.stopDone(error_t err) {}
129
128