Title: Using Protothreads for Sensor Node Programming
1Using Protothreads for Sensor Node Programming
- Adam Dunkels, Oliver Schmidt, Thiemo Voigt
- REALWSN 2005
2What this talk is about
- Protothreads
- A novel programming abstraction andC-language
library that simplifies programming in
event-driven systems - ... such as sensor nodes
- Without increased memory requirements
- ... RAM overhead 2 bytes
- Heavily used in the Contiki OS
- Warning some low-level C-code ahead!
3Contributions
- Protothreads sequential programming for
event-driven systems, without increased memory
requirements - Shown that protothreads can beimplemented in
highly portable C code (100 ANSI C) - Only two bytes of memory overhead
4Background event-drivenprogramming
- Backwards to normal programming
- Code is executed only when events occur
- Incoming packets, sensor input, mouse clicks,
time-out - Nothing happens without events
- Programs have no idle loop
- Used in TinyOS, many GUIs
5Event-driven programs
- Programs are not threads
- Programs are not sequential
- Programs are event-handlers
- An event-handler is a C function
- An event-handler must always return
- An event-handler cannot block wait
forsomething to happen
6Example of event-driven code
- void handle_sensor_input(int data)
- if(check_sensor_data(data))
- send_radio_packet(data)
-
- return
-
- void handle_incoming_packet(char packet)
- if(packet_should_be_forwarded(packet))
- send_radio_packet(packet)
-
- return
7Why is TinyOS event-driven?
- In TinyOS, we have chosen an event model so that
high levels of concurrency can be handled in a
very small amount of space. A stack-based
threaded approach would require that stack space
be reserved for each execution context. - J. Hill, R. Szewczyk, A. Woo, S. Hollar, D.
Culler, and K. Pister. System architecture
directions for networked sensors. In Proc. ASPLOS
IX, November 2000.
8Event-driven, stacks
- With threads, every thread requires a stack
- Stacks contain dead state
- May use large amounts of the available memory
- With the event-driven model, only one stack is
needed - Stack rewound on each event
- Since event-handlers are normal functions that
explicitly return
9Problems with the event-driven model
- Backwards programming model
- Hard to program, understand, debug, maintain
- Debug stack not retained hard to know from
where a call came - Cannot use blocking waits
- Must turn programs into explicit state machines
- Cannot enclose state changes in C control
structures
10Problems with the event-driven model in TinyOS
- This approach is natural for reactive processing
and for interfacing with hardware, but
complicates sequencing high-level operations, as
a logically blocking sequence must be written in
a state-machine style. - P. Levis, S. Madden, D. Gay, J. Polastre, R.
Szewczyk, A. Woo, E. Brewer, and D. Culler. The
Emergence of Networking Abstractions and
Techniques in TinyOS. In Proc. NSDI'04, March
2004.
11Example radio sleep cycle
t_awake
ON
Turn on
Turn off
OFF
t_sleep
126-step informal specification
- Turn radio on.
- Wait for t_awake milliseconds.
- Turn radio off, but only if all communication has
completed. - If communication has not completed, wait until it
has completed. Then turn off the radio. - Wait for t_sleep milliseconds. If the radio could
not be turned off before t_sleep milliseconds
because of remaining communication, do not turn
the radio off at all. - Repeat from step 1.
Problem with events, we can't write this as a
6-step program!
13State-machine implementation
With events, we must use an explicit state
machine!
ON
OFF
t_sleep timerexpired
Communication active
Communication completed
WAITING
14State machine C implementation
void radio_wake_eventhandler() switch(state)
case OFF if(timer_expired(timer))
radio_on() state ON
timer_set(timer, T_AWAKE) break
case ON if(timer_expired(timer))
timer_set(timer, T_SLEEP)
if(!communication_complete()) state
WAITING else radio_off()
state OFF break
case WAITING if(communication_complete()
timer_expired(timer)) state ON
timer_set(timer, T_AWAKE) else
radio_off() state OFF
break
- enum
- ON,
- WAITING,
- OFF
- state
Quite complex!
15How do protothreads help?
16Protothreads
- With protothreads, we can write blocking waits,
inside an event-handler - PT_WAIT_UNTIL() conditional blocking
- Protothreads are a mixture of the event-driven
and the threaded model - Inherits most of the good sides from both, but
some of the bad sides too - A protothread can be driven by an event-handler
17Protothreads-basedimplementation
- PT_THREAD(radio_wake_thread(struct pt pt))
- PT_BEGIN(pt)
- while(1)
- radio_on()
- timer_set(timer, T_AWAKE)
- PT_WAIT_UNTIL(pt, timer_expired(timer))
- timer_set(timer, T_SLEEP)
- if(!communication_complete())
- PT_WAIT_UNTIL(pt, communication_complete()
- timer_expired(timer))
-
- if(!timer_expired(timer))
- radio_off()
- PT_WAIT_UNTIL(pt, timer_expired(timer))
-
-
- PT_END(pt)
-
18Protothreads
- Provides sequential code flow
- The 6-step informal specification visible in the
code - Possible to use C control structures
- if(), while(), for(), etc.
- Debug stack retained
- Possible to follow calls
- Implicit locking blocking calls evident
19Limitations
- Automatic variables (stack variables) not saved
across a blocking wait - Limitation inherited from the event-driven model
- Programmer must manually save automatic variables
- However, static local variables still work
asexpected - Standard C compiler issues a warning
20Implementation of protothreadsusing the C switch
statement
- (Low-level C code coming up!)
21Local continuations
- Protothreads based on local continuations
- A local continuation captures the state of the
program, but only within a singlefunction - Local continuations are similar tocontinuations,
but does not save the stack - Two operations set and resume
22Local continuations
- PT_THREAD(radio_wake_thread(struct pt pt))
- PT_BEGIN(pt)
- while(1)
- radio_on()
- timer_set(timer, T_AWAKE)
- PT_WAIT_UNTIL(pt, timer_expired(timer))
- timer_set(timer, T_SLEEP)
- if(!communication_complete())
- PT_WAIT_UNTIL(pt, communication_complete()
- timer_expired(timer))
-
- if(!timer_expired(timer))
- radio_off()
- PT_WAIT_UNTIL(pt, timer_expired(timer))
-
-
- PT_END(pt)
-
23Can be implemented using the C switch statement!
- Unconventional use of the C switch statement
- But fully standards compatible
- Similar to Duff's device
- 100 portable no assembly language or stack
fiddling - Very, very low memory overhead 2 bytes
24C switch statement example
- PT_THREAD(example(void))
- PT_BEGIN()
- while(1)
- PT_WAIT_UNTIL(timer_expired())
- call_a_function()
- PT_WAIT_UNTIL(timer_expired())
-
- PT_END()
25C switch statement example
- int example(void)
- switch(pt-gtlc)
- case 0
- while(1)
- case 4 pt-gtlc 4
- if(!timer_expired()) return 1
- call_a_function()
- case 6 pt-gtlc 6
- if(!timer_expired()) return 1
-
- return 0
26The relation between protothreads and the C
switch statement
- Dan Henry (Boulder, Co USA), in adiscussion
about protothreads on a 8052message board - Many of us use the switch/case construct to
explicitly implement concurrent statemachines in
our code. The protothread macros merely provide
a level ofabstraction above that so that the
code appears more linear and the overall logic
more visible.
27How well does it work?
- Quantitative results preliminary
- FTP client code went from 30 states to none
- Programming with protothreads more fun!
- Protothreads code already used at several sites
- Surprising uses streaming multimedia server,
MPEG decoding - The portability of protothreads seems to be a
killer feature
28Conclusions
- Protothreads makes implementations of high-level
logic on top of event-driven systems easier - Protothreads allow for sequential code, Ccontrol
structures - Mixture between events and threads
- 100 portable ANSI C code
- 2 bytes of RAM overhead
29Thank you!
http//www.sics.se/adam/pt/