[Tinyos-2-commits] CVS: tinyos-2.x/doc/html/tutorial lesson7.html, 1.2, 1.3

Prabal Dutta prabal at users.sourceforge.net
Sat Apr 14 00:16:25 PDT 2007


Update of /cvsroot/tinyos/tinyos-2.x/doc/html/tutorial
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv1278

Modified Files:
	lesson7.html 
Log Message:
Update tutorials to bring into sync with code

Index: lesson7.html
===================================================================
RCS file: /cvsroot/tinyos/tinyos-2.x/doc/html/tutorial/lesson7.html,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** lesson7.html	6 Nov 2006 11:56:54 -0000	1.2
--- lesson7.html	14 Apr 2007 07:16:23 -0000	1.3
***************
*** 8,12 ****
  
  <div class="title">Lesson 7: Permanent Data Storage</div>
! <div class="subtitle">Last Modified: November 5, 2006</div>
  
  <p>This lesson introduces permanent (non-volatile) data storage in
--- 8,12 ----
  
  <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
***************
*** 45,50 ****
  
  Let's take a look at some of the interfaces that are in the
! <code>tos/interfaces</code> directory to familiarize ourselves with
! the general functionality of the storage system:
  
  
--- 45,51 ----
  
  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:
  
  
***************
*** 70,73 ****
--- 71,77 ----
  <a href="../../../tos/interfaces/LogWrite.nc">LogWrite</a></code>
  
+ <li><code>
+ <a href="../../../tos/types/Storage.h">Storage.h</a></code>
+ 
  
  </ul>
***************
*** 175,192 ****
  <h1>Storing Configuration Data</h1>
  
! <p>In <a name="fn2"><a href="lesson3.html">Lesson 3</a></a>, we
! implemented a simple application called BlinkToRadio that used a
! single timer, set to fire at a fixed rate, to toggle the LEDs.
  
! <p>See <a href="../../../apps/tutorials/BlinkConfig/">
  <code>tinyos-2.x/apps/tutorials/BlinkConfig/</code></a> for the
  accompanying code.
  
! <p>This lesson shows how parameters, like the timer period, can be
! configured at runtime and persisted across node power cycles.  The
! ability to store configuration data is useful in many applications.
! For example, it may be necessary to store a node's coordinates which
! are only known after the node is deployed.  Let's walk through the
! steps to use the <code>ConfigStorage</code> abstraction:
  
  <ol>
--- 179,307 ----
  <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>
***************
*** 223,228 ****
    uses {
      ...
-     interface Mount;
      interface ConfigStorage as Config;
      ...
    }
--- 338,343 ----
    uses {
      ...
      interface ConfigStorage as Config;
+     interface Mount;
      ...
    }
***************
*** 241,246 ****
    ...
  
-   App.Mount      -> ConfigStorageC.Mount;
    App.Config     -> ConfigStorageC.ConfigStorage;
    ...
  }
--- 356,361 ----
    ...
  
    App.Config     -> ConfigStorageC.ConfigStorage;
+   App.Mount      -> ConfigStorageC.Mount;
    ...
  }
***************
*** 248,263 ****
  
  <li>Before the flash chip can be used, it must be mounted using the
! two-phase mount/mountDone command.  Here we show chaining how this
! might be chained into the boot sequence:
  
  <pre>
    event void Boot.booted() {
!     call AMControl.start();
!   }
  
-   event void AMControl.startDone(error_t error) {
-     if (error != SUCCESS) {
-       call AMControl.start();
-     }
      if (call Mount.mount() != SUCCESS) {
        // Handle failure
--- 363,373 ----
  
  <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
***************
*** 267,358 ****
  
  <li>If the Mount.mount succeeds, then the <code>Mount.mountDone</code>
! event will be signaled.  In this case, we call the
! <code>ConfigStore.mount</code> command from with the event handler:
  
  <pre>
    event void Mount.mountDone(error_t error) {
!     if (error != SUCCESS) {
!       // Handle failure
      }
      else{
!       call Config.write(CONFIG_ADDR, &period, sizeof(period));
      }
    }
  </pre>
  
! <li>Once mounted, the flash can be written:
  
  <pre>
!   event void Mount.mountDone(error_t error) {
!     if (error != SUCCESS) {
!       // Handle failure
      }
!     else{
!       call Config.write(CONFIG_ADDR, &period, sizeof(period));
      }
    }
  </pre>
  
  <li>Data is not necessarily "written" to flash when
! <code>ConfigStore.write</code> is called.  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 result) {
      // Verify addr and len
  
!     if (result == SUCCESS) {
!       // Note success
      }
      else {
        // Handle failure
      }
-     if (call Config.commit() != SUCCESS) {
-       // Handle failure
-     }
    }
  </pre>
  
! <li>Finally, when the commit is complete, data can be read back from
! the flash:
  
  <pre>
!   event void Config.commitDone(error_t error) {
!     if (call Config.read(CONFIG_ADDR, &period2, sizeof(period2)) != SUCCESS) {
        // Handle failure
      }
    }
  
!   event void Config.readDone(storage_addr_t addr, void* buf, 
!     storage_len_t len, error_t result) __attribute__((noinline)) {
!     memcpy(&period2, buf, len);
  
!     if (period == period2) {
!       call Leds.led2On();
      }
  
!     if (len == 2 && addr == CONFIG_ADDR) {
        call Leds.led1On();
      }
    }
  </pre>
  
! </ol>
  
! <h1>Logging Data</h1>
  
! See <a href="../../../apps/tests/storage/Log/">
! <code>tinyos-2.x/apps/tests/storage/Log/</code></a> for an example of
! code that uses the Log storage abstraction. Log is generally used for
! append-based data streams consisting of relatively small data items.
! It provides atomicity guarantees for data writes.
  
  <h1>Storing Large Objects</h1>
  
  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. Block is generally
! used for storing large objects that cannot easily fit in RAM.
  
  <h1>Conclusions</h1>
--- 377,643 ----
  
  <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) {
!       error_t e;
!       do {
!         e = call LogRead.read(&m_entry, sizeof(logentry_t));
!       } while (e != SUCCESS);
      }
+     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 {
+       error_t e;
+       do {
+         e = call LogWrite.erase();
+       } while (e != SUCCESS);
+       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>



More information about the Tinyos-2-commits mailing list