[Tinyos Core WG] Meeting: 12/13

Philip Levis pal at cs.stanford.edu
Tue Dec 12 17:28:33 PST 2006


Wednesday, November 29, 2006 9:30 AM US Pacific Time
Bridge: 2, Passcode: 0604666

Numbers:
US: 1-916-356-2663 or 1-888-875-9370 (non-Intel)
UK: +44 1793 402663
Denmark: +45 4527 5090

Agenda:

Transition to HEAD
WG formation/guidance requests
TEP 113 (attached), serial initialization, and atm128 UART
TEP 114 (attached) and GetDataNow
Bug report policy

-------------- next part --------------
============================
Serial Communication
============================

:TEP: 113
:Group: Core Working Group 
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: Ben Greenstein and Philip Levis

:Draft-Created: 11-Jul-2005
:Draft-Version: $Revision: 1.1.2.6 $
:Draft-Modified: $Date: 2006/11/07 23:14:55 $
:Draft-Discuss: TinyOS Developer List <tinyos-devel at mail.millennium.berkeley.edu>

.. Note::

   This memo documents a part of TinyOS for the TinyOS Community, and
   requests discussion and suggestions for improvements.  Distribution
   of this memo is unlimited. This memo is in full compliance with
   TEP 1.

Abstract
====================================================================

This memo describes the structure and standard implementation of the
TinyOS 2.x serial communication system for mote-to-PC data
exchange. The system is broken into three levels (encoding, framing,
and dispatch) to allow easy experimentation and replacement. It can
also handle multiple packet formats: unlike 1.x, 2.x serial packets
are not bound to the mote's radio packet format. Additionally, one of
the supported packet formats is platform independent, so PC-side
applications can communicate with arbitrary motes.


1. Introduction
====================================================================

Users need to read data out of a TinyOS network. The most common
approach is to attach a mote to a PC or latop with a wired
connection. While the interface on the PC side can vary from a serial
cable to a USB device to IP, the mote generally talks to a serial port
(UART). In TinyOS 1.x, the UART packet format is platform-specific,
pushing a good deal of complexity into the protocol and PC-side tools
in order to discover and properly handle platform diversity. TinyOS
2.0 introduces the notion of packet format dispatch, so a mote can
support multiple UART packet formats simultaneously.  This allows
transparent bridging (e.g., an 802.15.4 base station) to exist in
parallel with platform-independent communication, which allows
simplifies the PC toolchain. This memo documents the protocols and
structure of the TinyOS 2.x serial communication stack.

2. Serial Stack Structure
====================================================================

The TinyOS 2.x serial communication stack is broken up into four
functional components. From bottom to top, they are

  o the raw UART,

  o the encoder/framer,

  o the protocol,

  o and the dispatcher.

Structurally, they look like this:

::

         _____________________     
        |                     |    
        |     Dispatcher      |       Packet formatting.
        |_____________________|    
         _____________________     
        |                     |    
        |      Protocol       |       Acknowledgements, CRC computation,
        |_____________________|       windowing.
         _____________________     
        |                     |    
        |    Encoder/Framer   |       Translating raw bytes into frame
        |_____________________|       delimiters, escape bytes.
         _____________________     
        |                     |    
        |      Raw UART       |       Platform code for reading/writing
        |_____________________|       bytes over the serial connection.
                                        

The bottom three provide a byte-level interface: only the Dispatcher
provides a packet-level interface. The top three are all
platform-independent: only the UART is platform-specific code.

The lowest level of the stack is the raw UART. This HIL component
provides functionality for configuring the UART (speed, stop bytes,
etc.) as well as sending/receiving bytes.

The Encoder/Framer sits above the raw UART. This component translates
raw data bytes into packet bytes using a serial protocol's
encoding. The Encoder/Framer assumes that a protocol's encoding has
two kinds of bytes: delimiters and data bytes, and signals each in
separate events to the component above.

The Protocol component handles data and delimiter byte events. It is
responsible for reading in and sending all protocol control
packets. If the Protocol component starts receiving a data packet, it
signals to the Dispatcher that a packet has started and signals the
data bytes. When the data packet completes, the Protocol signals to
the Dispatcher that the packet is complete and whether it passed the
protocol-level CRC.

The Dispatcher component handles data packet bytes and delimiters. It
is responsible for reading data bytes into a message_t and signaling
packet reception to components above it. The dispatcher can support
multiple packet formats. Based on how message_t works (see TEP
111[tep111_]), this boils down to knowing where in a message_t a
particular packet format begins (based on its header size). Section
3.4 describes how the default TinyOS 2.x implementation,
``SerialDispatcherC`` does this.


3. The 2.x Serial Stack Implementation
====================================================================

Section 2 describes the basic structure of the TinyOS 2.x serial
stack structure. This section describes its actual implementation,
including SerialActiveMessageC, which sits on top of the Dispatcher.
All of the components except for UartC are part of the serial
library that lives in ``tos/lib/serial``.

3.1 Raw UART: UartC
--------------------------------------------------------------------

The UART HIL[TEP2_] is ``UartC``, which provides a byte-level
interface to the underlying serial communication. It provides the
``SerialByteComm`` interface:

::

  interface SerialByteComm {
    async command error_t put(uint8_t data);
    async event void get(uint8_t data);
    async event void putDone();
  }

It also provides interfaces for configuring the serial port. *NOTE:
These are not codified yet, and so working out the UART HIL seems like
a good idea.*


3.2 Encoder/Framer: HdlcTranslateC
--------------------------------------------------------------------

HdlcTranslateC is the serial encoder/framer. It uses the
``SerialByteComm`` interface and provides the ``SerialFrameComm``
interface:

::

  interface SerialFrameComm {
    async command error_t putDelimiter();
    async command error_t putData(uint8_t data);
    async command void resetSend();
    async command void resetReceive();
    async event void delimiterReceived();
    async event void dataReceived(uint8_t data);
    async event void putDone();
  }

As its name suggests, it uses the same encoding as the HDLC[HDLC_]
protocol. ``0x7e`` is reserved as a frame delimiter byte, and ``0x7d``
is reserved as an escape byte. HdlcTranslateC maintains ten bits of
state. The receive and send paths each have one bit to store whether
they are using an escape byte, and the transmit path has a byte for
when it sends an escaped byte.

When HdlcTranslateC receives a delimiter byte, it signals
delimiterReceived(). When HdlcTranslateC receives an escape byte, it
sets the receiveEscape flag to true. When it receives any other byte,
it tests to see if the receiveEscape flag is set; if so, it XORs the
data byte with ``0x20`` and clears the flag. It signals dataReceived()
with the byte. The most common use of escape byte is to transmit data
bytes corresponding to the delimiter byte or escape byte. For example,
``0x7e`` becomes ``0x7d 0x5e``.

HdlcTranslateC performs similar actions on the transmit side. When
told to transmit the delimiter or escape byte as a data byte, it sets
the transmitEscape flag to true, stores the data byte XOR ``0x20``,
and sends an escape byte. When the escape byte is sent, it sends the
stored data byte.

3.3 Protocol: SerialP
--------------------------------------------------------------------

The SerialP component implements the serial protocol using PPP/HDLC-
like framing (See RFC 1662[RFC1662_]). Type dispatch and buffer
management are left to higher layers in the serial stack. The protocol
is currently stop-and-wait in the host-to-mote direction and best
effort in the mote-to-host direction. The first performance upgrade of
this module will be to implement sliding window reliability in both
directions.

SerialP provides two byte-level interfaces to the upper layer for
sending and receiving packets, respectively called SendBytePacket and
ReceiveBytePacket.

On the sending side, SerialP is responsible for encapsulation of upper
layer packets. An upper layer component such as SerialDispatcherC
initiates the sending of a packet by calling startSend, passing the
first byte to send. SerialP collects subsequent bytes by signalling
nextByte. Within the nextByte handler or between calls to nextByte,
the upper layer should indicate the end-of-packet by calling
completeSend. If completeSend is called from within a nextByte
handler, SerialP will ignore the return of the call to nextByte.

::

  interface SendBytePacket {
    async command error_t startSend(uint8_t first_byte);
    async command error_t completeSend();
    async event uint8_t nextByte();
    async event void sendCompleted(error_t error);
  }

SerialP maintains a small window of bytes that have been received by
the upper layer and not yet sent to the UART. Depending on the timing
requirements of the underlying UART, the size of this window can be
changed.  SerialP uses repeated calls to nextByte to keep this window
filled.

SerialP uses SerialFrameComm to send a delimiter between frames, 
a serial-level type field, the bytes of the packet, and a two-byte
frame CRC. For mote-to-host gap detection and link reliability, a
sequence number may also be sent (not currently activated).

After sending an entire frame and receiving the last putDone event
from below, SerialP signals sendCompleted to indicate the success or
failure of a requested transmission.

Packet reception is also managed by SerialP and the interface
provided to the upper layer is ReceiveBytePacket:

::

  interface ReceiveBytePacket {
    async event error_t startPacket();
    async event void byteReceived(uint8_t b);
    async event void endPacket(error_t result);
  }

Upon receiving an interframe delimiter and a new frame's header,
SerialP signals the upper layer indicating that a packet is
arriving. For each byte received, SerialP signals byteReceived. (Note:
SerialP signals on byte k-2 when byte k arrives, because the
implementation precludes it from knowing when it has encountered the
2-byte CRC in the frame footer until after it has received it. Lagging
behind by two bytes makes it possible to hide all frame details from
the upper layer.) Once SerialP receives the complete frame it signals
endPacket with a value of SUCCESS. If instead it loses sync during
reception it signals endPacket with FAIL.

SerialP acknowledges frames it receives. Acknowledgements have a
higher priority than data transmissions and consequently, data frames
may be slightly delayed. However, acknowledgement information is
stored in a queue separate from the data buffer, so a data packet to
be transmitted may begin spooling into SerialP while SerialP is
actively sending an acknowledgement.


3.4 Dispatcher: SerialDispatcherC
--------------------------------------------------------------------

SerialDispatcherC handles the data packets that the Protocol component
receives. It uses the SendBytePacket and ReceiveBytePacket interfaces,
and provides parameterized Send and Receive interfaces. The parameter
in the Send and Receive interfaces (``uart_id_t``) determines the
packet format contained in the message_t.

SerialDispatcherC places a one-byte header, the packet format
identifier, on the packets sent and received through SerialP.
SerialDispatcherC uses a parameterized SerialPacketInfo interface to
be able to handle various packet formats:

::

  interface SerialPacketInfo {
    async command uint8_t offset();
    async command uint8_t dataLinkLength(message_t* msg, uint8_t upperLen);
    async command uint8_t upperLength(message_t* msg, uint8_t dataLinkLen);
  }

When SerialDispatcherC receives the first data byte of a packet from
SerialP, it stores it as the packet type and calls
SerialPacketInfo.offset() to determine where in a message_t that
packet format begins. It then spools data bytes in, filling them into
its message_t buffer. Similarly, on the send side, it first sends the
type byte and spools out data bytes starting from the index denoted by
the call to offset(). SerialDispatcherC uses the two length commands,
dataLinkLength and upperLength, to translate between the two notions
of packet length: above, length refers to the payload excluding
header, while below it refers to the payload plus header.

A component that provides communication over the serial port with
uart_id_t *U* MUST wire a component implementing SerialPacketInfo to
SerialDispatcherC with uart_id_t *U*. The file ``Serial.h`` contains
reserved uart_id_t's for supported packet formats. Currently, only
platform independent active messages
(``TOS_SERIAL_ACTIVE_MESSAGE_ID``, described in Section 3.5), 802.15.4
active messages (``TOS_SERIAL_802_15_4_ID``), mica2 CC1000 packets
(``TOS_SERIAL_CC1000_ID``) and the error code
``TOS_SERIAL_UNKNOWN_ID`` are reserved. New packet formats MUST NOT
reuse any reserved identifiers.

3.5 SerialActiveMessageC
--------------------------------------------------------------------

SerialActiveMessageC is a platform-independent active message layer
that operates on top of the serial communication
stack. SerialActiveMessageC is a configuration that wires
SerialActiveMessageP to SerialDispatcherC with uart_id_t
TOS_SERIAL_ACTIVE_MESSAGE_ID and wires SerialPacketInfoActiveMessageP
to SerialDispatcherC with uart_id_t TOS_SERIAL_ACTIVE_MESSAGE_ID:

::

  includes Serial;``
  configuration SerialActiveMessageC {
    provides {
      interface Init;
      interface AMSend[am_id_t id];
      interface Receive[am_id_t id];
      interface Packet;
      interface AMPacket;
    }
    uses interface Leds;
  }
  implementation {
    components new SerialActiveMessageP() as AM, SerialDispatcherC;
    components SerialPacketInfoActiveMessageP as Info;
   
    Init = SerialDispatcherC;
    Leds = SerialDispatcherC;
   
    AMSend = AM;
    Receive = AM;
    Packet = AM;
    AMPacket = AM;
    
    AM.SubSend -> SerialDispatcherC.Send[TOS_SERIAL_ACTIVE_MESSAGE_ID];
    AM.SubReceive -> SerialDispatcherC.Receive[TOS_SERIAL_ACTIVE_MESSAGE_ID];
    
    SerialDispatcherC.SerialPacketInfo[TOS_SERIAL_ACTIVE_MESSAGE_ID] -> Info;
  }


SerialActiveMessageP is a generic component so that it can be used to
sit on top of any packet-level communication layer. It does not filter
packets based on destination address or group. It assumes that if the
packet was received over the serial port, it was destined to the
node. This saves PC-side tools from having to discover or consider the
ID and group of a mote.

Platform-independent active messages do not have a CRC (they assumes
the serial stack CRC is sufficient), and have the following header:

::

  typedef nx_struct SerialAMHeader {
    nx_am_addr_t addr;
    nx_uint8_t length;
    nx_am_group_t group;
    nx_am_id_t type;
  } SerialAMHeader;



3.6 Packet Format
--------------------------------------------------------------------

A data packet in the TinyOS 2.x serial stack has the following format
over the wire. Each protocol field is associated with a specific component:

::

     ____________________________________________
    | | | | |                               |  | |
    | | | | |                               |  | | 
    |_|_|_|_|_______________________________|__|_|
     F P S D         Payload                 CR F
   
  F       = Framing byte, denoting start of packet: HdlcTranslateC
  P       = Protocol byte: SerialP
  S       = Sequence number byte: SerialP
  D       = Packet format dispatch byte: SerialDispatcherC
  Payload = Data payload (stored in SerialDispatcherC): SerialDispatcherC
  CR      = Two-byte CRC over S to end of Payload: SerialP
  F       = Framing byte denoting end of packet: HdlcTranslateC

Payload is a contiguous packet that SerialDispatcherC reads in.  Note
that any data bytes (P - CR) equal to 0x7e or 0x7d will be escaped to
0x7d 0x5e or 0x7d 0x5d accordingly. For example, a platform
independent AM packet of type 6, group 0x7d, and length 5 to
destination 0xbeef with a payload of 1 2 3 4 5 would look like this:

``7e 40 09 00 be ef 05 7d 5d 06 01 02 03 04 05 7e``

Note that the group 0x7d is escaped to 0x7d 0x5d. The protocol field
(P) is 0x40 (64), corresponding to ``SERIAL_PROTO_ACK`` (in Serial.h).


4. Access Abstractions
====================================================================

Two generic components: SerialAMSenderC and SerialAMReceiverC connect
to SerialActiveMessageC to provide virtualized access to the serial
stack. Each instantiation of SerialAMSenderC has its own queue of
depth one. Therefore, it does not have to contend with other
SerialAMSender instantiations for queue space. The underlying
implementation schedulers the packets in these queues using some form
of fair-share queueing. SerialAMReceiverC provides the virtualized
abstraction for reception. These abstraction are very similar to
TinyOS's radio abstractions, namely, AMSenderC and AMReceiverC. See
Section 4 of TEP 116[TEP116_] for more information. Unlike the
services in the TEP 116, the serial component virtualizations provide
no snooping capabilities.


5. Author's Address
====================================================================

| Philip Levis
| 358 Gates
| Computer Science Laboratory
| Stanford University
| Stanford, CA 94305
|
| phone - +1 650 725 9046
| email - pal at cs.stanford.edu
|
|
| Ben Greenstein
| Center for Embedded Networked Sensing
| UCLA 3563 Boelter Hall
| Los Angeles, CA 90095-1596
|
| phone -  +1 310 206 3925
| email - ben at cs.ucla.edu

6. Citations
====================================================================

.. [TEP2] TEP 2: Hardware Abstraction Architecture. tinyos-2.x/doc/txt/tep2.txt

.. [TEP111] TEP 111: message_t. tinyos-2.x/doc/txt/tep111.txt

.. [TEP116] TEP 116: Packet Protocols. tinyos-2.x/doc/txt/tep116.txt
 
.. [HDLC] International Organization For Standardization, ISO Standard 3309-1979, "Data communication - High-level data link control procedures - Frame structure", 1979.

.. [RFC1662] PPP in HDLC-like Framing, Internet Engineering Task Force (IETF), 1994
-------------- next part --------------
=====================================================
SIDs: Source and Sink Independent Drivers
===================================================== 

:TEP: 114
:Group: Core Working Group
:Type: Documentary 
:Status: Draft
:TinyOS-Version: 2.x
:Author: Gilman Tolle, Philip Levis, and David Gay

:Draft-Created: 30-Oct-2005
:Draft-Version: $Revision: 1.1.2.6 $
:Draft-Modified: $Date: 2006/11/15 18:40:55 $
:Draft-Discuss: TinyOS Developer List <tinyos-devel at mail.millennium.berkeley.edu>

.. Note::

   This memo documents a part of TinyOS for the TinyOS Community, and
   requests discussion and suggestions for improvements.  Distribution
   of this memo is unlimited. This memo is in full compliance with
   TEP 1.

Abstract
====================================================================

This memo documents a set of hardware- and sensor-independent interfaces 
for data sources and sinks in TinyOS 2.x.

1. Introduction
====================================================================

Sensing is an integral part of any sensor network application.  Having
a wide variety of sensor interfaces usually does not impose a large
burden on an application developer, as any given application uses a
small and static set. However, applications often build on top of more
general systems, such as management or database layers, which may need
to sample sensors. Since these are general and sensor-independent
systems, they require a sensor-independent interface. TinyOS 2.0
therefore has telescoping sensor abstractions, providing both simple
and sensor-independent as well as sensor-specific interfaces.

2. Sensors in TinyOS 1.x
====================================================================

Early TinyOS sensors were generally analog. To sample one of these
sensors, an application makes an analog-to-digital conversion using
the MCU ADC.  Because all early sensors required ADC conversions, the
ADC interface has become the de-facto 1.x sensor interface. However,
the ADC interface was originally designed for inexpensive,
interrupt-driven sampling.  All of its commands and events are async
and sensor values are always 16 bits, although only some subset of the
bits may be significant (e.g., a 12-bit value).

Because sensing can be a part of high-level application logic,
the asynchronicity of these events means that high-level components
must deal with atomic sections and possible race conditions, even
if the sampling rate is very low (e.g., every five minutes)
and so could be easily placed in a task.

Additionally, not all sensors require ADC conversions from the MCU.
Many sensors today are digital. To sample these sensors, the MCU sends
a sample command and receives the corresponding data over a bus (e.g.,
SPI, I2C). The latency involved, combined with possible Resource
arbitration [_tep108], means that these bus operations are often
synchronous code. In the command direction, this can force a task
allocation to convert async to sync; in the event direction, the
application has to deal with async code even though the event is, in
practice, in a task.

Finallly, the simplicity of the ADC interface has led many sensors to
introduce several new ones for calibration and control, such as
``Mic`` and ``MagSetting``. Because ADCs generally do not have error
conditions, the ADC interface has no way to signal that a sample
failed. This turns out to be important for sensors where the sampling
request is split-phase, such as sensors over a bus. In these cases, it
is possible that the driver accepts the request to sample, but once
acquiring the bus discovers something is wrong with the sensor. This
property has led bus-based sensors to also have a separate
``ADCError`` interface; this interface breaks the basic TinyOS pattern
of a tight coupling between split-phase commands and their completion
events, as the command is in ADC but the completion event is in
ADCError.

All of these complications can make it difficult to write high-level
code that is sensor independent, unless the sensor is a simple ADC
reading. Sensors, when possible, should follow an approach similar to
the HAA[_haa], where they have sensor- or sensor-class-specific
interfaces for high performance or special case use, but also simple
and common interfaces for basic and portable use. Providing a
telescoping sensor abstraction allows both classes of use.

3. Sensors in TinyOS 2.x
====================================================================

TinyOS 2.x has several sensor-independent interfaces, which cover a
range of common use cases. These interfaces can be used to write a
Source- or Sink-Independent Driver (SID). A SID is source/sink
independent because its interfaces do not themselves contain
information on the sort of sensor or device they sit on top of.  A SID
SHOULD provide one or more of the interfaces described in this
section, depending on its expected uses and underlying data model.

3.1 Split-Phase Small Scalar I/O
--------------------------------------------------------------------

The first set of interfaces can be used for low-rate scalar I/O::

  interface Read<val_t> {
    command error_t read();
    event void readDone( error_t result, val_t val );
  }

  interface Write<val_t> {
    command error_t write( val_t val );
    event void writeDone( error_t result );
  }

A component that provides both read and write functionality might want
to use a combined version of the interface, to reduce the amount of
wiring required by the client application::

  interface ReadWrite<val_t> {
    command error_t read();
    event void readDone( error_t result, val_t val );

    command error_t write( val_t val );
    event void writeDone( error_t result );
  }

A component that can support concurrent reads and writes SHOULD
provide separate Read/Write interfaces. A component whose internal
logic will cause a read to fail while a write is pending or a write to
fail while a read is pending MUST provide a ReadWrite interface.

If the ``result`` parameter of the ``Read.readDone`` and
``ReadWrite.readDone`` events is not SUCCESS, then the memory of the
``val`` parameter MUST be filled with zeroes.

If the call to ``Read.read`` has returned SUCCESS, but the
``Read.readDone`` event has not yet been signalled, then a subsequent
call to ``Read.read`` MUST not return SUCCESS. This simple locking
technique, as opposed to a more complex system in which multiple
read/readDone pairs may be outstanding, is intended to reduce the
complexity of code that is a client of a SID interface.

Examples of sensors that would be suited to this class of interface
include many basic sensors, such as photo, temp, voltage, and ADC
readings.

3.2 Split-Phase Large Scalar I/O 
--------------------------------------------------------------------

If the SID's data object is too big to be passed efficienctly on the
stack, it can provide read/write interfaces that pass parameters by
pointer rather than value::

  interface ReadRef<val_t> {
    command error_t read( val_t* val );
    event void readDone( error_t result, val_t* val );
  }

  interface WriteRef<val_t> {
    command error_t write( val_t* val );
    event void writeDone( error_t result, val_t* val );
  }

  interface ReadWriteRef<val_t> {
    command error_t read( val_t* val );
    event void readDone( error_t result, val_t* val );

    command error_t write( val_t* val );
    event void writeDone( error_t result, val_t* val );
  }

The caller is responsible for allocating storage pointed to by the val
pointer which is passed to read() or write(). The SID MUST then
take ownership of the storage, store a new value into it or copy a
value out of it, and then relinquish it before signaling readDone() or
writeDone(). If read or write() returns SUCCESS then the caller MUST
NOT access or modify the storage pointed to by the val pointer until
it handles the readDone() or writeDone() event.

As is the case with the parameters by value, whether a component
provides separate ReadRef and WriteRef or ReadWriteRef affects the
concurrency it allows. If a component can allow the two to execute
concurrently, then it SHOULD provide separate ReadRef and WriteRef
interfaces. If the two cannot occur concurrently, then it MUST provide
ReadWriteRef.

If the ``result`` parameter of the ``ReadRef.readDone`` and
``ReadWriteRef.readDone`` events is not SUCCESS, then the memory the
``val`` parameter points to MUST be filled with zeroes. 

Examples of sensors that are suited to this set of interfaces include
those that generate multiple simultaneous readings for which
passing by value is inefficient, such as a two-axis digital 
accelerometer.

3.4 Single-Phase Scalar I/O
--------------------------------------------------------------------

Some devices may have their state cached or readily available. In
these cases, the device can provide a single-phase instead of
split-phase operation.  Examples include a node's MAC address (which
the radio stack caches in memory), profiling information (e.g.,
packets received), or a GPIO pin. These devices MAY use these
single-phase interfaces::

  interface Get<val_t> {
    command val_t get();
  }

  interface Set<val_t> {
    command void set( val_t val );
  }

  interface GetSet<val_t> {
    command val_t get();
    command void set( val_t val );
  }

If a device's data object is readily available but still too large to
be passed on the stack, then the device MAY use these interfaces::

  interface GetRef<val_t> {
    command error_t get( val_t* val );
  }

  interface SetRef<val_t> {
    command error_t set( val_t* val );
  }

  interface GetSetRef<val_t> {
    command error_t get( val_t* val );
    command error_t set( val_t* val );
  }

3.5 Notification-Based Scalar I/O
--------------------------------------------------------------------

Some sensor devices represent triggers, rather than request-driven
data acquisition. Examples of such sensors include switches,
passive-IR (PIR) motion sensors, tone detectors, and smoke
detectors. This class of event-driven sensors can be presented with
the Notify interface::

  interface Notify<val_t> {
    command error_t enable();
    command error_t disable();
    event void notify( val_t val );
  }

The Notify interface is intended for relatively low-rate events (e.g.,
that can easily tolerate task latencies). High-rate events may require
more platform- or hardware-specific async interfaces.

The enable() and disable() command enable and disable notification
events for the interface instance used by a single particular
client. They are distinct from the sensor's power state. For example,
if an enabled sensor is powered down, then when powered up it MUST
remain enabled.

The val parameter is used as defined in the Read interface.

3.7 Split-Phase Streaming I/O
--------------------------------------------------------------------

Some sensors can provide a continuous stream of readings, and some
actuators can accept a continuous stream of new data. Depending on the
rate needed and jitter bounds that higher level components can
tolerate, it can be useful to be able to read or write readings in
blocks instead of singly.  For example, a microphone or accelerometer
may provide data at a high rate that cannot be processed quickly
enough when each new reading must be transferred from asynchronous to
synchronous context through the task queue.

The ReadStreaming interface MAY be provided by a device that can
provide a continuous stream of readings::

  interface ReadStream<val_t> {

    command error_t postBuffer( val_t* buf, uint16_t count );

    command error_t read( uint32_t usPeriod );

    event void bufferDone( error_t result, 
                           val_t* buf, uint16_t count );

    event void readDone( error_t result );
  }    

The postBuffer command takes an array parameterized by the sample
type, and the number of entries in that buffer. A driver can then
enqueue the buffer for filling. The client can call postBuffer() more
than once, to "pre-fill" the queue with any number of buffers. The
size of the memory region pointed to by the buf parameter MUST be at
least as large as the size of a pointer on the node architecture plus
the size of the uint16_t count argument. This requirement supports
drivers that may store the queue of buffers and count sizes by
building a linked list.

After posting at least one buffer, the client can call read() with a
specified sample period in terms of microseconds. The driver then
begins to fill the buffers in the queue, signalling the bufferDone()
event when a buffer has been filled. The client MAY call postBuffer()
after read() in order to provide the device with new storage for
future reads.

If the device ever takes a sample that it cannot store (e.g., no
buffers are available), it MUST signal readDone(). If an error occurs
during a read, then the device MUST signal readDone() with an
appropriate failure code. Before a device signals readDone(), it MUST
signal bufferDone() for all outstanding buffers. If a readDone() is
pending, calls to postBuffer MUST return FAIL.

The following interface can be used for bulk writes::

  interface WriteStream<val_t> {

    command error_t postBuffer( val_t* buf, uint16_t count );

    command error_t write( uint32_t period );

    event void bufferDone( error_t result, 
                           val_t* buf, uint16_t count );

    event void writeDone( error_t result );
  }

postBuffer() and bufferDone() are matched in the same way described
for the ReadStream interface, as are write() and writeDone().

4. Summary
====================================================================

According to the design principles described in the HAA[_haa], authors
should write device drivers that provide rich, device-specific
interfaces that expose the full capabilities of each device. In
addition, authors can use the interfaces described in this memo to
provide a higher-level device-independent abstractions: SIDs. By
providing such an abstraction, driver authors can support developers
who only need simple interfaces, and can reduce the effort needed to
connect a sensor into a more general system.

5. Author's Address
====================================================================

| Gilman Tolle
| 2168 Shattuck Ave.
| Arched Rock Corporation
| Berkeley, CA 94704
|
| phone - +1 510 981 8714
| email - gtolle at archedrock.com
|
|
| Philip Levis
| 358 Gates
| Computer Science Laboratory
| Stanford University
| Stanford, CA 94305
|
| phone - +1 650 725 9046
| email - pal at cs.stanford.edu
|
|
| David Gay
| 2150 Shattuck Ave, Suite 1300
| Intel Research
| Berkeley, CA 94704
|
| phone - +1 510 495 3055
| email - david.e.gay at intel.com
|

-------------- next part --------------

Phil


More information about the Tinyos-2.0wg mailing list