A Practical Introduction to Sensor Network Programming Wireless Communication and Networked Embedded Systems, VT 2011 Frederik Hermans, Communication Research, Uppsala Universitet
Overview ● Sensor node hardware: Zolertia Z1 ● TinyOS & nesC ● Components & Interfaces ● A first TinyOS program: Blink ● Networking in TinyOS: Active messages ● Contiki ● Protothreads ● A first Contiki program: Blink ● Networking in Contiki: The Rime stack ● Wrap-up
Zolertia Z1 ● General purpose sensor node for research ● Low power consumption ● Months to years on two AA batteries ● Specs ● 16 MHz, 8 kB RAM ● Radio: 250 kbps @ 2.4 GHz ● Three LEDs ● Accelerometer ● Temperature sensor
Some perspective on the specs Clock speed 16 MHz 4 MHz 1024 MHz RAM 8 kB 8 kB 589824 kB Program size 92 kB 8192 kB ~ 409600 kB Radio 250 kbps N/A 55296 kbps Lifetime Months to A few days A few days years
TinyOS ● OS designed for low-power wireless devices ● Large community ● Open source (BSD license) ● Event-based ● Split-phase operations instead of blocking ● TinyOS programs are written in nesC ● Allows to create really tiny programs ● Heavy optimization for size
nesC: Components ● A nesC program consists of components ● There are two types of components ● A module implements some program logic ● A configuration wires different modules together ● Components may use or provide interfaces Timer BlinkC TimerC
nesC: Interfaces ● An interface describes a behavior (cf. Java) ● It specifies commands and events ● Example: interface Timer { command void start(uint32_t dt); event void fired(); } ● If a module uses an interface, then it may call its commands and it must handle its events ● If a module provides an interface, then it must implement its command and it may signal its events
A first TinyOS program: Blink ● Increase a counter every second ● Make LEDs show last three bits of counter 5 0 4 3 2 1 4 4 4 4 4 4 1 1 1 1 1 1 2 2 2 2 2 2 ● Need to write two components ● A module to contain our program logic ● A configuration that wires our module to other modules in TinyOS
BlinkC: The module module BlinkC { Boot interface will tell Timer interface to generate uses interface Boot; uses interface Timer<TMilli>; us when the node booted an event every second uses interface Leds; For controlling LEDs } implementation { Gets called when Boot int counter = 0; signals boot() event Call command event void Boot.booted() { call Timer.startPeriodic(1024); startPeriodic(...) } Gets called when Timer event void Timer.fired() { signals fired() event counter++; Increase counter and call Leds.set(counter); call command set(...) } }
BlinkC: The configuration configuration BlinkAppC { } Our program will use these components implementation { components MainC, BlinkC, LedsC, new TimerMilliC(); MainC provides Boot TimerMilliC provides BlinkC.Boot -> MainC; BlinkC.Timer -> TimerMilliC; Timer<TMilli> BlinkC.Leds -> LedsC; LedsC provides Leds } Timer- Timer MilliC Boot BlinkC MainC Leds LedsC
Genericity configuration BlinkAppC { } implementation { components MainC, BlinkC, LedsC, new TimerMilliC(); BlinkC.Boot -> MainC; BlinkC.Timer -> TimerMilliC; BlinkC.Leds -> LedsC; } ● Components are singleton or generic ● Generic components need to be instantiated ● Generic components can take arguments ● Interfaces can also be generic ● E.g., the Timer<precision> interface
Blink: Programming nodes ● Next steps: Build and upload ● Need a compiler and linker suitable for target architecture ● Need a standard library for our target architecture ● Need TinyOS sources ● Virtual machine image with everything pre-installed ● Will be uploaded to the course page soon™ ● Use it!
Blink: Creating a binary ● Open a shell, change into the project directory, and run make Z1
Blink: Uploading the binary ● Connect the node using a USB cable ● In the project directory, run make z1 install
Overview ● Sensor node hardware: Zolertia Z1 ● TinyOS & nesC ● Components & Interfaces ● A first TinyOS program: Blink ● Networking in TinyOS: Active messages ● Contiki ● Protothreads ● A first Contiki program: Blink ● Networking in Contiki: The Rime stack ● Wrap-up
A first networked TinyOS program ● Clickers are real products ● Used for audience response ● How does our simple clicker work? ● Learn about TinyOS networking *click* ● Two types of nodes *click* – Clients, base station ● We need to – Turn on radio, send, and receive messages
TinyOS active messages ● Basic networking abstraction: Active message ● Single-hop, best-effort radio communication ● Each active message has (among other stuff) – Destination address – Type (similar to UDP port) – Payload ● Building block for more complex communication services ● Interfaces to turn on/off radio, manipulate, send and receive active messages
Active messages: Interfaces ● Relevant interfaces ● SplitControl – start/stop the radio ● Packet – manipulate a packet ● AMSend – send packets ● Receive – receive packets ● For details, see TEP 116
Active messages: Components ● Which components implement the interfaces? ● ActiveMessageC provides SplitControl ● AMSenderC provides AMSend and Packet ● AMReceiverC provides Receive ● AMSenderC and AMReceiverC are generic ● Need to be instantiated ● Constructor takes on argument: An active message type ● E.g., component new AMReceiveC(42)
Active messages: Starting the radio ● ActiveMessageC provides SplitControl to turn on/off radio ● Signals events startDone(err) and stopDone(err) module ClickerClientC { uses interface SplitControl; ... } implementation { event void Boot.booted() { call SplitControl.start(); Start the radio } event void SplitControl.startDone(error_t err) { if (err == SUCCESS) { /* We can use active messages now */ } else { call SplitControl.start(); } } event void SplitControl.stopDone(error_t err) { } }
Active messages: Packets ● A packet is stored in a variable of type message_t ● Contains type, destination, payload, ... Header Payload Footer ● Packets may look different on different platforms ● Therefore, a packet must never be modified by changing the fields of message_t directly ● Instead, use the functions provided by the Packet interfaces ● E.g., Packet.getPayload(msg, len)
Active messages: Type and payload ● Need to define active message type and structure of payload ● Type: Positive integer (cf. UDP port number), e.g. 42 ● Payload: Not really needed for our application – Let's send the string “Hej”, just for the sake of it Type that identifies enum { AM_CLICKER_MSG = 42 }; packets of our application nx_struct clickerMsg_s { Structure to hold nx_uint8_t string[4]; Assign name clickerMsg our payload }; to the struct typedef nx_struct clickerMsg_s clickerMsg; ● ( nx_ prefix to ensure correct endianness across platforms)
Active messages: Sending a packet ● AMSend provides command error_t send(...) ● Note: send(...) immediately returns whether initiating the sending was successful ● Split-phase operation, signals event void sendDone() on completion ● Need to make sure we're not sending another packet, while a packet is still in transmission
Active messages: Sending a packet, pt. 2 pkt Keep track of whether implementation { we're sending already bool radioBusy = FALSE; Packet to be sent message_t pkt; Payload Header Footer To store whether initiating void send() { send succeeded error_t result; clickPl ClickerMsg *clickPl; Set clickPl to point to Are we sending already? Pointer to packet payload the payload of pkt if (radioBusy) return; clickPl = (ClickerMsg *) (call Packet.getPayload(&pkt, sizeof(ClickerMsg))); memcpy(clickPl->string, “Hej”, 4); Broadcast the packet Copy “Hej” to the packet result = call AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(ClickerMsg)); if (result == SUCCESS) radioBusy = TRUE; } Update radio state ...
Active messages: Sending a packet, pt. 3 ● Still need to handle sendDone() event ... event void AMSend.sendDone(message_t *p, uint8_t len) { if (p == &pkt) { radioBusy = FALSE; } } }
User button ● Need to send a packet when button pressed ● Component UserButtonC provides interface Notify<button_state_t> ● command error_t enable() ● event void notify(button_state_t state) – state: BUTTON_PRESSED or BUTTON_RELEASED
Clicker: Client AMSend AMSenderC Boot Clicker- SplitControl Active- MainC ClientC MessageC Notify User- ButtonC ● Complete source code at course page
Active messages: Receiving packets ● Receiving is much simpler :) ● Use the Receive interface ● event message_t *receive(...) ● Note: receive event has a return value implementation { ... int numReceivedPkts; event message_t *Receive.receive(message_t *pkt, void *payload, uint8_t len) { if (len != sizeof(ClickerMsg)) return pkt; Set LEDs according to numReceivedMsgs++; Sanity check: Is this call Leds.set(numReceivedMsgs); number of packets a packet we expect? return pkt; Return pointer to } packet buffer }
Recommend
More recommend