-<html>
- <head>
- <title>TinyOS Tutorial Lesson 16: Writing Low Power Sensing Applications</title>
- <link href="../../stylesheets/tutorial.css" rel="stylesheet" Type="text/css">
- </head>
- <body>
-
- <div class="title">Lesson 16: Writing Low Power Sensing Applications</div>
- <div class="subtitle">Last updated August 31st, 2007</div>
-
- <p>
- This lesson demonstrates how to write low power sensing applications in TinyOS. At
- any given moment, the power consumption of a wireless sensor node is a function of its
- microcontroller power state, whether the radio, flash, and sensor peripherals are on,
- and what operations active peripherals are performing. This tutorial shows you
- how to best utilize the features provided by TinyOS to keep the power consumption
- of applications that use these devices to a minumum.
- </p>
-
- <h1>Overview</h1>
- <p>
- Energy management is a critical concern in wireless sensor networks. Without it,
- the lifetime of a wireless sensor node is limited to just a few short weeks or even
- days, depending on the type of application it is running and the size of batteries
- it is using. With proper energy management, the same node, running the
- same application, can be made to last for months or years on the same set of batteries.
- </p>
- <p>
- Ideally, one would want all energy management to be handled transparently by the
- operating system, relieving application developers from having to deal with this
- burden themselves. While attempts have been made in the past to provide such
- capabilities, most operating systems today still just provide the necessary primitives for
- performing energy management -- the logic of when and how to do so is left completely up
- to the application.
- </p>
- <p>
- TinyOS provides a novel method of performing energy management directly
- within the OS. The method it uses is as efficient as it is elegant. Developers
- no longer have to struggle with code that explicitly manages the power state for any of the
- peripheral devices that it uses. To get a better idea of the complexity involved in
- doing something as simple as periodically taking a set of sensor readings
- and logging them to flash in the most energy efficient manner possible, take a look
- at the pseudo code found below.
- </p>
- <pre>
-Every Sample Period:
- Turn on SPI bus
- Turn on flash chip
- Turn on voltage reference
- Turn on I2C bus
- Log prior readings
- Start humidity sample
- Wait 5ms for log
- Turn off flash chip
- Turn off SPI bus
- Wait 12ms for vref
- Turn on ADC
- Start total solar sample
- Wait 2ms for total solar
- Start photo active sample
- Wait 2ms for photo active
- Turn off ADC
- Turn off vref
- Wait 34ms for humidity
- Start temperature sample
- Wait 220ms for temperature
- Turn off I2C bus
- </pre>
- <p>
- With the methods provided by TinyOS, hand-tuned application code that looks like that
- can be transformed into this:
- </p>
- <pre>
-Every Sample Period:
- Log prior readings
- Sample photo active
- Sample total solar
- Sample temperature
- Sample humidity
- </pre>
- <p>
- The pseudo code shown above is for concurrently sampling all of the
- the onboard sensors found on the latest revision of the tmote sky sensor nodes.
- Experiments have shown that using the methods provided by TinyOS, this application
- comes within 1.6% of the energy efficiency of the hand-tuned implementation --
- even at sampling rates as fast as 1 sample per second. For more information
- on these experiments and the method TinyOS uses to actually manage energy
- so efficiently, please refer to the paper found
- <a href="http://sing.stanford.edu/klueska/Black_Site/Publications_files/klues07icem.pdf">
- here.</a>
- </p>
- <!---
- <center>
- <img src="img/tmote_current.png" width=66% ></img>
- </center>
- -->
- <p>
- The rest of this tutorial is dedicated to teaching you how to write applications
- that allow TinyOS to manage energy for you in the most efficient manner possible.
- As a rule, TinyOS can manage energy more efficiently when I/O requests are submitted
- by an application in parallel. Submitting them serially may result in energy
- wasted by unnecessarily turning devices on and off more frequently than required.
- </p>
- <h1>
- Peripheral Energy Management
- </h1>
- <p>
- Compare the following two code snippets:
- </p>
-<table>
- <tr><td><b>Parallel</b></td><td width=10></td><td><b>Serial</b></td></tr>
- <tr><td valign=top>
-<pre>
-event void Boot.booted() {
- call Timer.startPeriodic(SAMPLE_PERIOD);
-}
-
-event void Timer.fired() {
- call LogWrite.append(current_entry, sizeof(log_entry_t));
-
- current_entry_num = !current_entry_num;
- current_entry = &(entry[current_entry_num]);
- current_entry->entry_num = ++log_entry_num;
-
- call Humidity.read();
- call Temperature.read();
- call Photo.read();
- call Radiation.read();
-}
-
-event void Humidity.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- current_entry->hum = val;
- }
- else current_entry->hum = 0xFFFF;
-}
-
-event void Temperature.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- current_entry->temp = val;
- }
- else current_entry->temp = 0xFFFF;
-}
-
-event void Photo.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- current_entry->photo = val;
- }
- else current_entry->photo = 0xFFFF;
- }
-
-event void Radiation.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- current_entry->rad = val;
- }
- else current_entry->rad = 0xFFFF;
-}
-
-event void LogWrite.appendDone(void* buf,
- storage_len_t len,
- bool recordsLost,
- error_t error) {
- if (error == SUCCESS)
- call Leds.led2Toggle();
-}
-</pre>
-</td>
-<td></td>
-<td valign=top>
-<pre>
-event void Boot.booted() {
- call Timer.startPeriodic(SAMPLE_PERIOD);
-}
-
-event void Timer.fired() {
- call Humidity.read();
-}
-
-event void Humidity.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- entry->rad = val;
- }
- else current_entry->rad = 0xFFFF;
- call Temperature.read();
-}
-
-event void Temperature.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- entry->rad = val;
- }
- else current_entry->rad = 0xFFFF;
- call Photo.read();
-}
-
-event void Photo.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- entry->rad = val;
- }
- else current_entry->rad = 0xFFFF;
- call Radiation.read();
-}
-
-event void Radiation.readDone(error_t result, uint16_t val) {
- if(result == SUCCESS) {
- entry->rad = val;
- }
- else current_entry->rad = 0xFFFF;
- call LogWrite.append(entry, sizeof(log_entry_t));
-}
-
-event void LogWrite.appendDone(void* buf,
- storage_len_t len,
- bool recordsLost,
- error_t error) {
- if (error == SUCCESS)
- call Leds.led2Toggle();
-}
-
-
-
-
-
- </pre>
- </td>
- </tr>
- </table>
- <p>
- In the parallel case, logging to flash and requesting samples from each sensor is
- all done within the body of the <code>Timer.fired()</code> event. In the serial case,
- a chain of events is triggered by first calling <code>Humidity.read()</code> in
- <code>Timer.fired()</code>, sampling each subsequent sensor in the body of the previous
- <code>readDone()</code> event, and ending with all sensor readings
- being logged to flash.
- </p>
- <p>
- By logging to flash and sampling all sensors within the body of a single event, the OS
- has the opportunity to schedule each operation as it sees fit. Performing each operation
- after the completion of the previous one gives the OS no such opportunity. The only
- downside of the parallel approach is that the application must manually manage a double
- buffer so that the values written to flash are not overwritten before they are logged. To save
- the most energy, however, the parallel version should always be used.
- Keep in mind that in both cases, the developer must also make sure that the sampling
- interval is longer than the time it takes to gather all sensor readings. If it is not,
- data corruption will inevitably occur.
- </p>
- <h1>
- Radio Power Management
- </h1>
- <p>
- By default, TinyOS provides low power radio operation through a technique known as
- <i>Low-Power Listening</i>. In low-power listening, a node turns on
- its radio just long enough to detect a carrier on the channel.
- If it detects a carrier, then it keeps the radio on long enough to detect a packet.
- Because the LPL check period is much longer than a packet, a transmitter must send
- its first packet enough times for a receiver to have a chance to hear it. The
- transmitter stops sending once it receives a link-layer acknowledgment or a timeout
- of twice the check period. When a node receives a packet, it stays awake long
- enough to receive a second packet. Therefore, a packet burst amortizes the wakeup
- cost of the first packet over the follow-up packets. It is therefore more energy
- efficient to send packets in bursts when using low-power listening than sending individual
- packets at some fixed constant rate. Keep this in mind when developing applications
- that require low power operation.
- </p>
- <p>
- Controlling the operation of low-power listening in TinyOS is provided through the
- use of the <code>LowPowerListening</code> interface. Currently, this interface
- is only supported for the cc1000 and cc2420 radios. In the future we hope to
- support other radio platforms as well.
- </p>
- <pre>
-interface LowPowerListening {
- /**
- * Set this this node's radio sleep interval, in milliseconds.
- * Once every interval, the node will sleep and perform an Rx check
- * on the radio. Setting the sleep interval to 0 will keep the radio
- * always on.
- *
- * This is the equivalent of setting the local duty cycle rate.
- *
- * @param sleepIntervalMs the length of this node's Rx check interval, in [ms]
- */
- command void setLocalSleepInterval(uint16_t sleepIntervalMs);
-
- /**
- * @return the local node's sleep interval, in [ms]
- */
- command uint16_t getLocalSleepInterval();
-
- /**
- * Set this node's radio duty cycle rate, in units of [percentage*100].
- * For example, to get a 0.05% duty cycle,
- * call LowPowerListening.setDutyCycle(5);
- *
- * For a 100% duty cycle (always on),
- * call LowPowerListening.setDutyCycle(10000);
- *
- * This is the equivalent of setting the local sleep interval explicitly.
- *
- * @param dutyCycle The duty cycle percentage, in units of [percentage*100]
- */
- command void setLocalDutyCycle(uint16_t dutyCycle);
-
- /**
- * @return this node's radio duty cycle rate, in units of [percentage*100]
- */
- command uint16_t getLocalDutyCycle();
-
- /**
- * Configure this outgoing message so it can be transmitted to a neighbor mote
- * with the specified Rx sleep interval.
- * @param msg Pointer to the message that will be sent
- * @param sleepInterval The receiving node's sleep interval, in [ms]
- */
- command void setRxSleepInterval(message_t *msg, uint16_t sleepIntervalMs);
-
- /**
- * @return the destination node's sleep interval configured in this message
- */
- command uint16_t getRxSleepInterval(message_t *msg);
-
- /**
- * Configure this outgoing message so it can be transmitted to a neighbor mote
- * with the specified Rx duty cycle rate.
- * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25.
- *
- * @param msg Pointer to the message that will be sent
- * @param dutyCycle The duty cycle of the receiving mote, in units of
- * [percentage*100]
- */
- command void setRxDutyCycle(message_t *msg, uint16_t dutyCycle);
-
- /**
- * @return the destination node's duty cycle configured in this message
- * in units of [percentage*100]
- */
- command uint16_t getRxDutyCycle(message_t *msg);
-
- /**
- * Convert a duty cycle, in units of [percentage*100], to
- * the sleep interval of the mote in milliseconds
- * @param dutyCycle The duty cycle in units of [percentage*100]
- * @return The equivalent sleep interval, in units of [ms]
- */
- command uint16_t dutyCycleToSleepInterval(uint16_t dutyCycle);
-
- /**
- * Convert a sleep interval, in units of [ms], to a duty cycle
- * in units of [percentage*100]
- * @param sleepInterval The sleep interval in units of [ms]
- * @return The duty cycle in units of [percentage*100]
- */
- command uint16_t sleepIntervalToDutyCycle(uint16_t sleepInterval);
-
-}</pre>
-<p>
-This interface is located in <code>tos/interfaces</code> in the standard TinyOS tree. Take a
-look at the comments for each command to familiarize yourself with how this interface can be used.
-</p>
-<p>
-Using this interface typically involves first setting a nodes local duty cycle within the
-<code>Boot.booted()</code> event of the top level application. For each packet the application
-wishes to send, the duty cycle of its destination is then specified as metadata so that the
-correct number of preambles can be prepended to it. The code snippet found below demonstrates
-this usage pattern:
-</p>
-<pre>
-event void Boot.booted() {
- call LPL.setLocalSleepInterval(LPL_INTERVAL);
- call AMControl.start();
-}
-
-event void AMControl.startDone(error_t e) {
- if(e != SUCCESS)
- call AMControl.start();
-}
-
-...
-
-void sendMsg() {
- call LPL.setRxSleepInterval(&msg, LPL_INTERVAL);
- if(call Send.send(dest_addr, &msg, sizeof(my_msg_t)) != SUCCESS)
- post retrySendTask();
-}</pre>
-
-The <code>AMControl</code> interface is provided by <code>ActiveMessageC</code>, and is
-used, among other things, to enable the operation of low-power listening for the radio.
-Once <code>AMControl.start()</code> has completed successfully, the radio begins to duty cycle
-itself as specified by the parameter to the <code>setLocalSleepInterval()</code> command. Calling
-<code>setRxSleepInterval()</code> with a specific sleep interval then allows the correct number of preambles to be sent
-for the message specified in its parameter list.
-
-<h1>
-Microcontroller Power Management
-</h1>
-<p>
-Microcontrollers often have several power states, with varying power draws,
-wakeup latencies, and peripheral support. The microcontroller should always be
-in the lowest possible power state that can satisfy application requirements.
-Determining this state accurately requires knowing a great deal about the power
-state of many subsystems and their peripherals. Additionally, state transitions
-are common. Every time a microcontroller handles an interrupt, it moves from a low
-power state to an active state, and whenever the TinyOS scheduler finds the task
-queue empty it returns the microcontroller to a low power state. TinyOS uses
-three mechanisms to decide what low power state it puts a microcontroller into:
-status and control registers, a dirty bit, and a power state override.
-Please refer to <a href=../tep112.html>TEP 112</a> for more information.
-</p>
-</p>
-As a developer, you will not have to worry about MCU power managment at all in
-most situations. TinyOS handles everything for you automatically. At times,
-however, you may want to use the provided power state override functionality.
-Take a look at <code>tos/chips/atm128/timer/HplAtm128Timer0AsyncP.nc</code> if
-you are interested in seeing an example of where this override functionality is used.
-</p>
-
-<h1>
-Low Power Sensing Application
-</h1>
-<p>
-A fully functional low-power sensing application has been created that combines each
-of the techniques found in this tutorial. At present, this application is not included
-in the official TinyOS distribution (<= 2.0.2). If you are using TinyOS from a cvs
-checkout, you will find it located under <code>apps/tutorials/LowPowerSensing</code>.
-Otherwise, you can obtain it from
-cvs by running the following set of commands from a terminal window:
-</p>
-
- <pre>
-cd $TOSROOT/apps/tutorials
-cvs -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos login
-cvs -z3 -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos co -P -d LowPowerSensing tinyos-2.x/apps/tutorials/LowPowerSensing</pre>
- <p>
- Just hit enter when prompted for a CVS password. You do not need to enter one.
- </p>
-
-This
-application has been tested on telosb and mica2 platforms, but should be usable
-on others without modification. Take a look at the README file found in the top level directory
-for more information.
-</p>
-<a name=#related_docs>
-<h1>Related Documentation</h1>
-</a>
-<ul>
-<li> <a href="../tep103.html">TEP 103: Permanent Data Storage (Flash)</a>
-<li> <a href="../tep105.html">TEP 105: Low Power Listening</a>
-<li> <a href="../tep108.html">TEP 108: Resource Arbitration</a>
-<li> <a href="../tep109.html">TEP 109: Sensors and Sensor Boards</a>
-<li> <a href="../tep112.html">TEP 112: Microcontroller Power Management</a>
-<li> <a href="../tep114.html">TEP 114: SIDs: Source and Sink Independent Drivers</a>
-<li> <a href="../tep115.html">TEP 115: Power Management of Non-Virtualised Devices</a>
-<li> <a href="http://sing.stanford.edu/klueska/Black_Site/Publications_files/klues07icem.pdf">
-Integrating Concurrency Control and Energy Management in Device Drivers</a>
-</ul>
-
-<!-- Begin footer -->
-<br>
-<hr>
-<center>
-<p>< <b><a href="lesson15.html">Previous Lesson</a></b> | <b><a
- href="index.html">Top</a></b> |
-</center>
-
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" ^M "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>klueska.com</title>
+</head>
+<frameset>
+<frame src="http://docs.tinyos.net/index.php/Writing_Low-Power_Applications" name="redir_frame" />
+<noframes>
+<body>
+<p>Sorry, your browser does not support frames. Please <a href="http://docs.tinyos.net/index.php/Writing_Low-Power_Applications" target="_top">go here</a>.</p>
</body>
-</html>
+</noframes>
+</frameset>
+</html>
\ No newline at end of file