]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - doc/html/tutorial/lesson7.html
Update to tutorials to redirect them to the wiki now instead of directly being modifi...
[tinyos-2.x.git] / doc / html / tutorial / lesson7.html
index c23d97df57fb3134f60e1f40e09cc640b22ace4d..74ce0211a9600d1578821c861a8a02d8c1c2007f 100644 (file)
-<!DOCTYPE doctype PUBLIC "-//w3c//dtd html 4.0 transitional//en">
-<html>
+<!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>Lesson 7: Permanent Data Storage</title>
-  <link href="../../stylesheets/tutorial.css" rel="stylesheet" type="text/css">
+<title>klueska.com</title>
 </head>
+<frameset>
+<frame src="http://docs.tinyos.net/index.php/Storage" name="redir_frame" />
+<noframes>
 <body>
-
-<div class="title">Lesson 7: Permanent Data Storage</div>
-<div class="subtitle">Last Modified: April 13, 2007</div>
-
-<p>This lesson introduces permanent (non-volatile) data storage in
-TinyOS.  Permanent storage allows a node to persist data even if power
-is disconnected or the node is reprogrammed with a new image.  You
-will become familar with different kinds of data storage including
-small objects, logs, and large objects.  You will be exposed to the
-TinyOS interfaces and components that support permanent data storage
-on motes and you will learn how to:
-
-<p>
-
-<ul>
-
-<li>Divide the flash chip into volumes, which allows multiple and/or
-different type of data to be stored.
-
-<li>Store configuration data that survives a power cycle.
-
-<li>Store packets using the logging abstraction and retransmit the
-overheard packets after a power cycle.
-
-
-</ul>
-
-
-<h1>Introduction</h1>
-
-TinyOS 2.x provides three basic storage abstractions: small objects,
-circular logs, and large objects.  TinyOS 2.x also provides
-<i>interfaces</i> to abstract the underlying storage services and
-<i>components</i> that <i>provide</i> (implement) these interfaces.
-
-<h2>Interfaces</h2>
-
-Let's take a look at some of the interfaces that are in the
-<code>tos/interfaces</code> directory and the types defined in the
-<code>tos/types</code> to familiarize ourselves with the general
-functionality of the storage system:
-
-
-<p>
-<ul>
-
-<li><code>
-<a href="../../../tos/interfaces/BlockRead.nc">BlockRead</a></code> 
-
-<li><code>
-<a href="../../../tos/interfaces/BlockWrite.nc">BlockWrite</a></code>
-
-<li><code>
-<a href="../../../tos/interfaces/Mount.nc">Mount</a></code>
-
-<li><code>
-<a href="../../../tos/interfaces/ConfigStorage.nc">ConfigStorage</a></code>
-
-<li><code>
-<a href="../../../tos/interfaces/LogRead.nc">LogRead</a></code>
-
-<li><code>
-<a href="../../../tos/interfaces/LogWrite.nc">LogWrite</a></code>
-
-<li><code>
-<a href="../../../tos/types/Storage.h">Storage.h</a></code>
-
-
-</ul>
-
-<h2>Components</h2>
-
-<p>Components provide concrete implementations of the interfaces.  You
-should be familiar with these components because your code needs to
-specify both the <i>interfaces</i> your application
-<i>uses</i> as well as the <i>components</i> which <i>provide</i>
-(implement) these interfaces:
-
-<p>
-<ul>
-
-<li><code><a href="../../../tos/chips/stm25p/ConfigStorageC.nc">ConfigStorageC</a></code>
-
-<li><code><a href="../../../tos/chips/stm25p/LogStorageC.nc">LogStorageC</a></code>
-
-<li><code><a href="../../../tos/chips/stm25p/BlockStorageC.nc">BlockStorageC</a></code>
-
-</ul>
-
-<p>
-
-<h2>Implementations</h2>
-
-<p>The preceding components are actually <em>chip-specific
-implementations</em> of these abstractions.  Since TinyOS supports
-multiple platforms, each of which might have its own implementation of
-the storage drivers, you may need to be aware of platform-specific
-constants or other details (e.g. erase size) that can couple a storage
-client to the underlying chip-specific implementation.
-
-<p>For example, the preceding links are all specific to the ST
-Microelectronics M25Pxx family of flash memories used in the Telos and
-Tmote Sky motes.  You <em>do not</em> need to worry about the details
-of where these files reside because TinyOS's make system includes the
-correct drivers automatically.  However, you <em>do</em> need to know
-what these components are called because your code must list them in a
-<code>components</code> declaration.
-
-<p>If you are curious, the following links will let you browse the
-implementations of the Atmel AT45DB family of flash memories used in
-the Mica2/MicaZ motes:
-
-<p>
-<ul>
-
-<li><code><a href="../../../tos/chips/at45db/ConfigStorageC.nc">ConfigStorageC</a></code>
-
-<li><code><a href="../../../tos/chips/at45db/LogStorageC.nc">LogStorageC</a></code>
-
-<li><code><a href="../../../tos/chips/at45db/BlockStorageC.nc">BlockStorageC</a></code>
-
-</ul>
-
-<p>Finally, the following links will let you browse the implementation
-for the Intel imote2 flash memory:
-
-<p>
-<ul>
-
-<li><code><a href="../../../tos/platforms/intelmote2/ConfigStorageC.nc">ConfigStorageC</a></code>
-
-<li><code><a href="../../../tos/platforms/intelmote2/LogStorageC.nc">LogStorageC</a></code>
-
-<li><code><a href="../../../tos/platforms/intelmote2/BlockStorageC.nc">BlockStorageC</a></code>
-
-</ul>
-
-
-<h1>Volumes</h1>
-
-<p>TinyOS 2.x divides a flash chip into one or more fixed-sized
-<em>volume</em>s that are specified at compile-time using an XML file.
-This file, called the volume table, allows the application developer
-to specify the name, size, and (optionally) the base address of each
-volume in the flash.  Each volume provides a single type of storage
-abstraction (e.g. configuration, log, or block storage).  The
-abstraction type defines the physical layout of data on the flash
-memory.  A volume table might look like:
-
-<pre>
-&lt;volume_table&gt;
-  &lt;volume name="CONFIGLOG" size="65536"/&gt;
-  &lt;volume name="PACKETLOG" size="65536"/&gt;
-  &lt;volume name="SENSORLOG" size="131072"/&gt;
-  &lt;volume name="CAMERALOG" size="524288"/&gt;
-&lt;/volume_table&gt;
-</pre>
-
-<p>The volume table for a particular application must be placed in the
-application's directory (where one types 'make') and must be named
-<code>volumes-CHIPNAME.xml</code> where CHIPNAME is replaced with the
-platform-specific flash chip's name.  For example, the Telos mote uses
-the ST Microelectronics M25P family of flash memories.  The drivers
-for these chips can be found in the <code>tos/chips/stm25p</code>
-directory.  Therefore, a Telos-based application that uses the storage
-abstractions needs a file named <code>volumes-stm25p.xml</code>.
-
-<p>Note that the size parameter is a multiple of the erase unit for a
-particular flash chip.  See Section 4.1 in <a href="#fn1">TEP 103</a>
-for more details.
-
-<h1>Storing Configuration Data</h1>
-
-<p>This lesson shows how configuration data can be written to and read
-from non-volatile storage.  Configuration data typically exhibit some
-subset of the following properties.  They are <b>limited in size</b>,
-ranging from a fews tens to a couple hundred bytes.  Their values may
-be <b>non-uniform</b> across nodes.  Sometimes, their values are
-<b>unknown</b> prior to deployment in the field and sometimes their
-values are <b>hardware-specific</b>, rather than being tied to the
-software running on a node.
-
-<p>Because configuration data can be non-uniform across nodes or
-unknown <em>a priori</em>, their values may be difficult to specify at
-compile-time and since the data are sometimes hardware-specific, their
-values must survive reprogramming, suggesting that encoding these
-values in the program image is not the simplest approach.  Storing
-configuration data in volatile memory is also problematic since this
-data would not survive a reset or power cycle.
-
-<p>In summary, configuration data must persist through node resets,
-power cycles, or reprogramming, and then be restored afterward.  The
-ability to persist and restore configuration data in this manner is
-useful in many scenarios.
-
-<p><ul>
-
-<li><b>Calibration.</b> Calibration coefficients for sensors might be
-factory-configured and persisted, so they are not lost when power is
-removed for shipping or the node is reprogrammed post-calibration.
-For example, a hypothetical temperature sensor might have an offset
-and gain that must be calibrated, because these parameters are
-hardware-specific, and stored because they are needed to convert the
-output voltage into the more useful units of degrees Celcius.  The
-calibration data for such a sensor might look like:
-
-<pre>
-typedef struct calibration_config_t {
-  int16_t temp_offset;
-  int16_t temp_gain;
-} calibration_config_t;
-</pre>
-
-<li><b>Identification.</b> Device identification information, like
-IEEE-compliant MAC addresses or the TinyOS TOS_NODE_ID parameters are
-non-uniform across nodes although they are not hardware-specific, once
-they are assigned to a node, these values should be <em>sticky</em> in
-that they are persisted across reset, power cycle, and reprogramming
-operations (and not lost or reassigned to another node).
-
-<pre>
-typedef struct radio_config_t {
-  ieee_mac_addr_t mac;
-  uint16_t tos_node_id;
-} radio_config_t;
-</pre>
-
-<li><b>Location.</b> Node location data may be unknown at compile-time
-and only become available during deployment.  An application might,
-for example, store node coordinates as follows and update these values
-in the field:
-
-<pre>
-typedef struct coord_config_t {
-  uint16_t x;
-  uint16_t y;
-  uint16_t z;
-} coord_config_t;
-</pre>
-
-<li><b>Sensing.</b> Sensing and signal processing parameters like
-sample period, filter coefficients, and detection thresholds might be
-adjusted in the field.  The configuration data for such an application
-might look like:
-
-<pre>
-typedef struct sense_config_t {
-  uint16_t temp_sample_period_milli;
-  uint16_t temp_ema_alpha_numerator;
-  uint16_t temp_ema_alpha_denominator;
-  uint16_t temp_high_threshold;
-  uint16_t temp_low_threshold;
-} sense_config_t;
-</pre>
-
-</ul>
-
-<p>
-
-
-Now that we have discussed <i>why</i> one might use this type of
-storage, let's see <i>how</i> to use it.  We will implement a simple
-demo application that illustrates how to use the <code>Mount</code>
-and <code>ConfigStorage</code> abstractions.  A timer period will be
-read from flash, divided by two, and written back to flash.  An LED is
-toggled each time the timer fires.  But, before diving into code,
-let's discuss some high-level design considerations.
-
-<p>
-See <a href="../../../apps/tutorials/BlinkConfig/">
-<code>tinyos-2.x/apps/tutorials/BlinkConfig/</code></a> for the
-accompanying code.
-
-<p>
-
-Prior to its first usage, a volume does not contain any valid data.
-So, our code should detect the first usage of a volume and take any
-appropriate actions (e.g. preload it with default values).  Similarly,
-when the data layout of the volume changes (for example, if the
-application requires new or different configuration variables), then
-application code should detect this and take appropriate actions
-(e.g. migrate the old data to the new layout or erase the volume and
-reload the defaults).  These requirements suggest that we should have
-a way of keeping track of the volume version.  We will use a version
-number for this purpose (and will need to maintain a discipline of
-updating the version number when the data layout changes
-incompatibly).  Our configuration struct might have the following
-fields for the version number and blink period:
-
-<pre>
-typedef struct config_t {
-  uint16_t version;
-  uint16_t period;
-} config_t;
-</pre>
-
-<p>
-
-
-<ol>
-
-<li>Create a <code>volumes-CHIPNAME.xml</code> file, enter the volume
-table in this file, and place the file in the application directory.
-Note that <code>CHIPNAME</code> is the flash chip used on your target
-plaform.  For example, <code>CHIPNAME</code> will be
-<code>stm25p</code> for the Telos platform and <code>at45db</code> for
-the MicaZ platform.  Our file will have the following contents:
-
-<pre>
-&lt;volume_table&gt;
-  &lt;volume name="LOGTEST" size="262144"/&gt;
-  &lt;volume name="CONFIGTEST" size="131072"/&gt;
-&lt;/volume_table&gt;
-</pre>
-
-This volume information is used by the toolchain to create an include
-file.  The auto-generated file, however, has to be included manually.
-Place the following line in the configuration file which declares the
-ConfigStorageC component (e.g. <code>BlinkConfigAppC.nc</code>):
-
-<pre>
-#include "StorageVolumes.h"
-</pre>
-
-<li>BlinkConfigC, the application code for this simple demo,
-<em>uses</em> the <code>Mount</code> and <code>ConfigStorage</code>
-interfaces (note that we rename <code>ConfigStorage</code> to
-<code>Config</code>).
-
-<pre>
-module BlinkConfigC {
-  uses {
-    ...
-    interface ConfigStorage as Config;
-    interface Mount;
-    ...
-  }
-}
-</pre>
-
-<li>Each interface must be wired to an <em>implementation</em> that
-will provide it:
-
-<pre>
-configuration BlinkConfigAppC {
-}
-implementation {
-  components BlinkConfigC as App;
-  components new ConfigStorageC(VOLUME_CONFIGTEST); 
-  ...
-
-  App.Config     -> ConfigStorageC.ConfigStorage;
-  App.Mount      -> ConfigStorageC.Mount;
-  ...
-}
-</pre>
-
-<li>Before the flash chip can be used, it must be mounted using the
-two-phase mount/mountDone command.  Here we show how this might be
-chained into the boot sequence:
-
-<pre>
-  event void Boot.booted() {
-    conf.period = DEFAULT_PERIOD;
-
-    if (call Mount.mount() != SUCCESS) {
-      // Handle failure
-    }
-  }
-</pre>
-
-<li>If the Mount.mount succeeds, then the <code>Mount.mountDone</code>
-event will be signaled.  The following code shows how to check if the
-volume is valid, and if it is, how to initiate a read from the volume
-using the <code>ConfigStore.read</code> command.  If the volume is
-invalid, calling <code>Config.commit</code> will make it valid (this
-call is also used to flush buffered data to flash much like the UNIX
-fsync system call is supposed to flush buffered writes to disk):
-
-<pre>
-  event void Mount.mountDone(error_t error) {
-    if (error == SUCCESS) {
-      if (call Config.valid() == TRUE) {
-        if (call Config.read(CONFIG_ADDR, &conf, sizeof(conf)) != SUCCESS) {
-          // Handle failure
-        }
-      }
-      else {
-        // Invalid volume.  Commit to make valid.
-        call Leds.led1On();
-        if (call Config.commit() == SUCCESS) {
-          call Leds.led0On();
-        }
-        else {
-          // Handle failure
-        }
-      }
-    }
-    else{
-      // Handle failure
-    }
-  }
-</pre>
-
-
-<li>If the read is successful, then a <code>Config.readDone</code>
-event will occur.  In this case, we first check for a successful read,
-and if successful, we then check the version number.  If the version
-number matches what we expected, we copy of the configuration data to
-a local variable, and adjust its values.  If there is a version
-mismatch, we set the value of the configuration information to a
-default value.  Finally, we call the the <code>Config.write</code>
-function:
-
-<pre>
-  event void Config.readDone(storage_addr_t addr, void* buf, 
-    storage_len_t len, error_t err) __attribute__((noinline)) {
-
-    if (err == SUCCESS) {
-      memcpy(&conf, buf, len);
-      if (conf.version == CONFIG_VERSION) {
-        conf.period = conf.period/2;
-        conf.period = conf.period > MAX_PERIOD ? MAX_PERIOD : conf.period;
-        conf.period = conf.period < MIN_PERIOD ? MAX_PERIOD : conf.period;
-      }
-      else {
-        // Version mismatch. Restore default.
-        call Leds.led1On();
-        conf.version = CONFIG_VERSION;
-        conf.period = DEFAULT_PERIOD;
-      }
-      call Leds.led0On();
-      call Config.write(CONFIG_ADDR, &conf, sizeof(conf));
-    }
-    else {
-      // Handle failure.
-    }
-  }
-
-</pre>
-
-<li>Data is not necessarily "written" to flash when
-<code>ConfigStore.write</code> is called and
-<code>Config.writeDone</code> is signaled.  To ensure data is
-persisted to flash, a <code>ConfigStore.commit</code> call is
-required:
-
-<pre>
-  event void Config.writeDone(storage_addr_t addr, void *buf, 
-    storage_len_t len, error_t err) {
-    // Verify addr and len
-
-    if (err == SUCCESS) {
-      if (call Config.commit() != SUCCESS) {
-        // Handle failure
-      }
-    }
-    else {
-      // Handle failure
-    }
-  }
-</pre>
-
-<li>Finally, when the <code>Config.commitDone</code> event is
-signaled, data has been durably written to flash and will survive a
-node power cycle:
-
-<pre>
-  event void Config.commitDone(error_t err) {
-    call Leds.led0Off();
-    call Timer0.startPeriodic(conf.period);
-    if (err == SUCCESS) {
-      // Handle failure
-    }
-  }
-</pre>
-
-</ol>
-
-<h1>Logging Data</h1>
-
-Reliable (atomic) logging of events and small data items is a common
-application requirement.  Logged data should not be lost if a system
-crashes.  Logs can be either linear (stop logging when the volume is
-full) or circular (overwrite the least recently written data when the
-volume is full).
-
-<p>
-
-The TinyOS LogStorage abstraction supports these requirements. The log
-is record based: each call to LogWrite.append (see below) creates a
-new record.  On failure (a crash or power cycle), the log only loses
-whole records from the end of the log.  Additionally, once a circular
-log wraps around, log writes only lose whole records from the
-beginning of the log.
-
-<p>
-
-A demo application called <code>PacketParrot</code> shows how to use
-the <code>LogWrite</code> and <code>LogRead</code> abstractions.  A
-node writes received packets to a circular log and retransmits the
-logged packets (or at least the parts of the packets above the AM
-layer) when power is cycled.
-
-<p>
-
-See <a href="../../../apps/tutorials/PacketParrot/">
-<code>tinyos-2.x/apps/tutorials/PacketParrot/</code></a> for the
-accompanying code.
-
-<p>
-
-The application logs packets it receives from the radio to flash.  On
-a subsequent power cycle, the application transmits any logged
-packets, erases the log, and then continues to log packets again.  The
-red LED is on when the log is being erased.  The blue (yellow) LED
-turns on when a packet is received and turns off when a packet has
-been logged successfully.  The blue (yellow) LED remains on when
-packets are being received but are not logged (because the log is
-being erased).  The green LED flickers rapidly after a power cycle
-when logged packets are transmitted.
-
-
-<ol>
-
-<li>The first step when using the log is to decide what kind of data
-you want to store in the log.  In this case, we will declare a struct
-of the type:
-
-<pre>
-  typedef nx_struct logentry_t {
-    nx_uint8_t len;
-    message_t msg;
-  } logentry_t;
-</pre>
-
-<li>Unlike Config storage, Log storage does not require the volume to
-be explicitly mounted by the application.  Instead, a simple read
-suffices in which a buffer and the number of bytes to read are passed
-to <code>LogRead.read</code>:
-
-<pre>
-  event void AMControl.startDone(error_t err) {
-    if (err == SUCCESS) {
-      if (call LogRead.read(&m_entry, sizeof(logentry_t)) != SUCCESS) {
-        // Handle error
-      }
-    }
-    else {
-      call AMControl.start();
-    }
-  }
-</pre>
-
-<li>If the call to <code>LogRead.read</code> returns SUCCESS, then a
-<code>LogRead.readDone</code> event will be signaled shortly
-thereafter.  When that happens, we check if the data that was returned
-is the same length as what we expected.  If it is, we use the data but
-if not, we assume that either the log is empty or that we have lost
-synchronization, so the log is erased:
-
-<pre>
-
-  event void LogRead.readDone(void* buf, storage_len_t len, error_t err) {
-    if ( (len == sizeof(logentry_t)) && (buf == &m_entry) ) {
-      call Send.send(&m_entry.msg, m_entry.len);
-      call Leds.led1On();
-    }
-    else {
-      if (call LogWrite.erase() != SUCCESS) {
-        // Handle error.
-      }
-      call Leds.led0On();
-    }
-  }
-
-</pre>
-
-<li>The <code>PacketParrot</code> application stores packets received
-over the radio to flash by first saving the <code>message_t</code> and
-its length to a <code>log_entry_t</code> struct and then calling
-<code>LogWrite.append</code>:
-
-<pre>
-  event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
-    call Leds.led2On();
-    if (!m_busy) {
-      m_busy = TRUE;
-      m_entry.len = len;
-      m_entry.msg = *msg;
-      if (call LogWrite.append(&m_entry, sizeof(logentry_t)) != SUCCESS) {
-        m_busy = FALSE;
-      }
-    }
-    return msg;
-  }
-</pre>
-
-<li>If the <code>LogWrite.write</code> returned SUCCESS, then a short
-time later, a <code>LogWrite.appendDone</code> will be signaled.  This
-event returns the details of the write including the source buffer,
-length of data written, whether any records were lost (if this is a
-circular buffer) and any error code.  If no errors occurred, then the
-data was written to flash with atomicity, consistency, and durability
-guarantees (and will survive node crashes and reboots):
-
-<pre>
-  event void LogWrite.appendDone(void* buf, storage_len_t len, 
-                                 bool recordsLost, error_t err) {
-    m_busy = FALSE;
-    call Leds.led2Off();
-  }
-
-</pre>
-
-</ol>
-
-<h1>Storing Large Objects</h1>
-
-Block storage is generally used for storing large objects that cannot
-easily fit in RAM.  Block is a low-level system interface that
-requires care when using since it is essentially a write-once model of
-storage.  Rewriting requires an erase which is time-consuming, occurs
-at large granularity (e.g. 256 B to 64 KB), and can only happen a
-limited number of times (e.g. 10,000 to 100,000 times is typical).
-The TinyOS network reprogramming system uses Block storage to store
-program images.
-
-<p>
-
-See <a href="../../../apps/tests/storage/Block/">
-<code>tinyos-2.x/apps/tests/storage/Block/</code></a> for an example
-of code that uses the Block storage abstraction.
-
-<h1>Conclusions</h1>
-
-This lesson introduced the basic storage abstractions in Tiny 2.x.
-
-<a name=#related_docs>
-<h1>Related Documentation</h1>
-
-<ul>
-<li> (1) <a name="fn1"><a href="../tep103.html">TEP 103: Permanent Data Storage</a></a>
-<li> (2) <a name="fn2"><a href="lesson1.html">Lesson 1: Getting Started with TinyOS and nesC</a></a>
-<i>TinyOS Programming</i></a>
-</ul>
-
-<!-- Begin footer -->
-<br>
-<hr>
-<center>
-<p>&lt;&nbsp;<b><a href="lesson6.html">Previous Lesson</a></b> |&nbsp; <b><a
- href="index.html">Top</a></b> &nbsp;|&nbsp; <b><a href="lesson8.html">Next Lesson </a>&nbsp;&gt;</b>
-</center>
-
+<p>Sorry, your browser does not support frames. Please <a href="http://docs.tinyos.net/index.php/Storage" target="_top">go here</a>.</p>
 </body>
-</html>
+</noframes>
+</frameset>
+</html>
\ No newline at end of file