[Tinyos-alliance] Agenda for 1/16 @10 am PST 415-692-0828 ext:
8604 pin: 3987#
Philip Levis
pal at cs.stanford.edu
Mon Jan 15 17:12:50 PST 2007
On Jan 15, 2007, at 3:35 PM, dculler wrote:
> 3. TEPs. We have progress updates.
TEP Updates:
101 is all but finalized (just needs authors to twiddle the headers).
108 is finalized.
106 is in author response.
TEPs 109, 112, and 114 passed last call in the core WG and so we need
to assign shepherds for them. I've attached them. Their subjects are
sensor boards (109), microcontroller power management (112), and data
acquisition interfaces (114).
net2 update:
A subset of the net2 WG met last Friday to finish the last bits of
CTP. The conclusion was that CTP shows good congestion behavior but
by dropping packets. Therefore the C bit's meaning was changed from a
forced action (can't send to a node with the C bit set) to a forced
notification (if you drop a packet you must set the C bit). I think
the next steps in the WG are going to be to review 118 and 119 for
presentation to the SC and to finish up 123. I've contacted the folks
at Sun to see if they'd like to try implementing CTP on Sun SPOT
nodes, to show interoperability.
Phil
-------------- next part --------------
=========================
Sensors and Sensor Boards
=========================
:TEP: 109
:Group: Core Working Group
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: David Gay, Phil Levis, Wei Hong, Joe Polastre, and Gilman Tolle
:Draft-Created: 10-Jun-2006
: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 how sensor drivers are organized in TinyOS and how
sets of sensor drivers are combined into sensor boards and sensor
platforms, along with general principles followed by the components
that provide access to sensors.
1. Principles
====================================================================
This section describes the basic organization principles for sensor
drivers in TinyOS.
For background, a sensor can be attached to the microcontroller on a
TinyOS platform through a few different types of connections:
* Included within the microcontroller itself
* Connected to general-purpose IO pins for level/edge detection
* Connected to an ADC in the microcontroller for voltage sampling
* Connected to general-purpose IO pins for digital communication
* Connected through a standard digital bus protocol (1-Wire, I2C, SPI)
Physically, these connections can also be decoupled by attaching the
sensors to a `sensor board`, which can be removed from the TinyOS
platform, and could attach to multiple different TinyOS platforms.
The capabilities of a physical sensor are made available to a TinyOS
application through a `sensor driver`.
According to the HAA [TEP2]_, TinyOS devices SHOULD provide both
simple hardware-independent interfaces for common-case use (HIL) and
rich hardware-dependent interfaces for special-case use (HAL). Sensor
drivers SHOULD follow this spirit as well.
TinyOS 2.x represents each sensor as an individual component. This
allows the compilation process to minimize the amount of code
included. A sensor board containing multiple sensors SHOULD be
represented as a collection of components, one for each sensor,
contained within a sensor board directory.
Sensors, being physical devices that can be shared, can benefit from
virtualization and arbitration. This document describes a design
pattern for sensor virtualization that SHOULD be followed by sensor
drivers.
The same physical sensor can be attached to multiple different TinyOS
platforms, through platform-dependent interconnections. The common
logic of sensor driver SHOULD be factored into chip-dependent,
platform-independent components, and those components SHOULD be bound
to the hardware resources on a platform by platform-dependent
components, and to the hardware resources on a sensor board by
sensorboard-dependent components.
A physical sensor has a general class and a specific set of
performance characteristics, captured by the make and model of the
sensor itself. The naming of the sensor driver components SHOULD
reflect the specifc name of the sensor, and MAY provide a component
with a generic name for application authors who only care about the
general class of the sensor.
This document takes no position on the meaning of the values returned
by sensor drivers. They MAY be raw uninterpreted values or they MAY
have some physical meaning. If a driver returns uninterpreted values,
the driver MAY provide additional interfaces that would allow
higher-level clients to obtain information needed to properly
interpret the value.
2. Sensor HIL Components
====================================================================
A sensor HIL component MUST provide:
- One or more SID interfaces [TEP114]_, for reading data.
A sensor HIL component MAY provide:
- One or more SID interfaces [TEP114]_, for reading or
writing calibration coefficients or control registers.
A sensor device driver SHOULD be a generic component that virtualizes
access to the sensor. A sensor device driver can provide such
virtualization for itself by defining a nesC generic client
component. When a client component is being used, a call to a
top-level SID interface SHOULD be delayed when the device is busy,
rather than failing. Using one of the system arbiters can make the
implementation of this requirement easier to accomplish.
For example::
generic configuration SensirionSht11C() {
provides interface Read<uint16_t> as Temperature;
provides interface ReadStream<uint16_t> as TemperatureStream;
provides interface DeviceMetadata as TemperatureDeviceMetadata;
provides interface Read<uint16_t> as Humidity;
provides interface ReadStream<uint16_t> as HumidityStream;
provides interface DeviceMetadata as HumidityDeviceMetadata;
}
implementation {
// connect to the ADC HIL, GPIO HAL, or sensor's HAL
}
When a HIL component is being used, the sensor MUST initialize itself,
either by including the `MainC` component and wiring to the
`SoftwareInit` interface, or by allowing a lower-level component (like
an ADC) to initialize itself.
In addition, the HIL sensor driver MUST start the physical sensor
automatically. For sensors without a constant power draw, the sensor
MAY be started once at boot time by wiring to the `MainC.Boot`
interface. Sensors that draw appreciable power MUST be started in
response to a call to one of the top-level SID interfaces, and stopped
some time after that call completes. Using one of the power-management
components described in [TEP115]_ can make this implementation easier.
Generally, simple types are made up of octets. However, sensor values
often have levels of precision besides a multiple of 8. To account for
such cases, each device MUST specify the precision of each one of its
interfaces by providing the DeviceMetadata interface::
interface DeviceMetadata {
command uint8_t getSignificantBits();
}
The name of the instance of DeviceMetadata MUST clearly indicate which
interface it corresponds to.
The getSignificantBits() call MUST return the number of significant
bits in the reading. For example, a sensor reading taken from a 12-bit
ADC MUST return the value "12".
Sensor driver components SHOULD be named according to the make and
model of the sensing device being presented. Using specific names
gives the developer the option to bind to a particular sensor, which
provides compile-time detection of missing sensors. However, wrapper
components using "common" names MAY also be provided by the driver
author, to support application developers who are only concerned with
the particular type of the sensor and not its make, model, or detailed
performance characteristics.
A "common" naming layer atop a HIL might look like this::
generic configuration TemperatureC() {
provides interface Read<uint16_t>;
provides interface ReadStream<uint16_t>;
provides interface DeviceMetadata;
}
implementation {
components new SensirionSht11C();
Read = SensirionSht11C.Temperature;
ReadStream = SensirionSht11C.TemperatureStream;
DeviceMetadata = SensirionSht11C.TemperatureDeviceMetadata;
}
generic configuration HumidityC() {
provides interface Read<uint16_t>;
provides interface ReadStream<uint16_t>;
provides interface DeviceMetadata;
}
implementation {
components new SensirionSht11C();
Read = SensirionSht11C.Humidity;
ReadStream = SensirionSht11C.HumidityStream;
DeviceMetadata = SensirionSht11C.HumidityDeviceMetadata;
}
3. Sensor HAL Components
====================================================================
Sensors with a richer interface than would be supported by the SID
interfaces MAY provide a HAL component in addition to a HIL
component.
A sensor HAL component MUST provide:
- A SID-based interface or a specific hardware-dependent interface
with commands for sampling and controlling the sensor device.
A sensor HAL component MAY need to provide:
- A `StdControl` or `SplitControl` interface for manual power
management by the user, following the conventions described in
[TEP115]_.
- A Resource[] interface for requesting access to the device and
possibly performing automated power management.
- Any other interfaces needed to control the device.
For example::
configuration SensirionSht11DeviceC {
provides interface Resource[ uint8_t client ];
provides interface SensirionSht11[ uint8_t client ];
}
implementation {
// connect to the sensor's platform-dependent HPL here
}
4. Directory Organization Guidelines
====================================================================
Because the same physical sensor can be attached to TinyOS platforms
in many different ways, the organization of sensor drivers SHOULD
reflect the distinction between sensor and sensor interconnect.
Sensor components commonly exist at three levels:
platform-independent, sensorboard-dependent, and
platform-dependent. Factoring a sensor driver into these three pieces
allows for greater code reuse when the same sensor is attached to
different sensorboards or platforms.
Platform-independent sensor driver components for a particular sensor,
like protocol logic, when in the core TinyOS 2.x source tree, SHOULD
be placed into "tos/chips/<sensor>", where <sensor> reflects the make
and model of the sensor device being supported. When not a part of the
core source tree, this directory can be placed anywhere as long as the
nesC compiler recieves a `-I` directive pointing to the sensor's
directory. However, not all sensors have a sufficiently large amount
of platform-independent logic to justify a separate "chips"
directory. Sensor chips are more likely to be digital sensors than
analog sensors, for example.
A sensor board is a collection of sensor components with a fixed name,
intended for attachment to multiple platforms. Each sensor board MUST
have its own directory named <sensorboard>. Default TinyOS 2.x sensor
boards are placed in "tos/sensorboards/<sensorboard>", but sensor
board directories can be placed anywhere as long as the nesC compiler
receives a `-I` directive pointing to the sensor board's directory.
Both sensors and sensor boards MUST have unique names. Case is
significant, but two sensor boards MUST differ in more than case. This
is necessary to support platforms where filename case differences are
not significant.
Each sensor board directory MUST contain a `.sensor` file. This file
is a perl script which gets executed as part of the `ncc` nesC
compiler frontend. It can add or modify any compile-time options
necessary for a particular sensor board. It MAY modify the following
perl variables, and MUST NOT modify any others:
- @new_args: This is the array of arguments which will be passed to
nescc. For instance, you might add an include directive to @new_args
with push @new_args, `-Isomedir`. This could be used to include
subdirectories.
- @commonboards: This can be set to a list of sensor board names which
will be added to the include path list. These sensor boards MUST be
in tinyos-2.x/tos/sensorboards.
If the sensor board wishes to define any C types or constants, it
SHOULD place these in a file named <sensorboard>.h in the sensor
board's directory.
A sensor board directory MAY contain a "chips" directory, with
subdirectories for each of the sensors connected to the sensor board.
If a "chips" subdirectory is used, sensorboard-dependent driver
components needed to connect platform-independent logic to a
particular attachment for that sensor SHOULD be placed in
"<sensorboard>/chips/<sensor>".
Components needed to connect the platform-independent sensor driver
components or sensorboard-dependent components to the hardware
resources available on a particular platform SHOULD be placed in
"tos/<platform>/chips/<sensor>". In addition, components for a sensor
that only exists on a particular platform SHOULD be placed in a such a
directory.
Sensors that exist as part of a larger chip, like a MCU internal
voltage sensor, SHOULD be placed in a subdirectory of the chip's
directory. "tos/<chip>/sensors/<sensor>".
The `.platform` and `.sensor` files need to include enough `-I`
directives to locate all of the necessary components needed to support
the sensors on a platform and/or sensorboard.
All of these directory organization guidelines are only intended for
code that will enter the core source tree. In general, sensor
components can be placed anywhere as long as the nesC compiler
receives enough `-I` directives to locate all of the necessary pieces.
5. Authors' Addresses
====================================================================
| David Gay
| 2150 Shattuck Ave, Suite 1300
| Intel Research
| Berkeley, CA 94704
|
| phone - +1 510 495 3055
|
| email - david.e.gay at intel.com
|
| Wei Hong
| Arch Rock
| 657 Mission St. Suite 600
| San Francisco, CA 94105
|
| email - wei.hong at gmail.com
|
| Philip Levis
| 358 Gates Hall
| Computer Science Department
| 353 Serra Mall
| Stanford, CA 94305
|
| phone - +1 650 725 9046
|
| email - pal at cs.stanford.edu
|
| Joe Polastre
| 467 Soda Hall
| UC Berkeley
| Berkeley, CA 94720
|
| email - polastre at cs.berkeley.edu
|
| Gilman Tolle
| Arch Rock
| 657 Mission St. Suite 600
| San Francisco, CA 94105
|
| email - gtolle at archrock.com
6. Citations
====================================================================
.. [TEP2] TEP 2: Hardware Abstraction Architecture
.. [TEP114] TEP 114: SIDs: Source and Sink Indepedent Drivers
.. [TEP115] TEP 115: Power Management of Non-Virtualized Devices
Appendix A: Sensor Driver Examples
====================================================================
1. Analog ADC-Connected Sensor
------------------------------
The Analog sensor requires two components
* a component to present the sensor itself (HamamatsuS1087ParC)
* a component to select the appropriate hardware resources, such as
ADC port 4, reference voltage 1.5V, and a slow sample and hold time
(HamamatsuS1087ParP).
The AdcReadClientC component and underlying machinery handles all of
the arbitration and access to the ADC.
::
tos/platforms/telosa/chips/s1087/HamamatsuS1087ParC.nc
generic configuration HamamatsuS1087ParC() {
provides interface Read<uint16_t>;
provides interface ReadStream<uint16_t>;
provides interface DeviceMetadata;
}
implementation {
components new AdcReadClientC();
Read = AdcReadClientC;
components new AdcReadStreamClientC();
ReadStream = AdcReadStreamClientC;
components HamamatsuS1087ParP;
DeviceMetadata = HamamatsuS1087ParP;
AdcReadClientC.AdcConfigure -> HamamatsuS1087ParP;
AdcReadStreamClientC.AdcConfigure -> HamamatsuS1087ParP;
}
::
tos/platforms/telosa/chips/s1087/HamamatsuS1087ParP.nc
#include "Msp430Adc12.h"
module HamamatsuS1087ParP {
provides interface AdcConfigure<const msp430adc12_channel_config_t*>;
provides interface DeviceMetadata;
}
implementation {
msp430adc12_channel_config_t config = {
inch: INPUT_CHANNEL_A4,
sref: REFERENCE_VREFplus_AVss,
ref2_5v: REFVOLT_LEVEL_1_5,
adc12ssel: SHT_SOURCE_ACLK,
adc12div: SHT_CLOCK_DIV_1,
sht: SAMPLE_HOLD_4_CYCLES,
sampcon_ssel: SAMPCON_SOURCE_SMCLK,
sampcon_id: SAMPCON_CLOCK_DIV_1
};
async command const msp430adc12_channel_config_t* AdcConfigure.getConfiguration() {
return &config;
}
command uint8_t DeviceMetadata.getSignificantBits() { return 12; }
}
2. Binary Pin-Connected Sensor
------------------------------
The Binary sensor gets a bit more complex, because it has three
components:
* one to present the sensor (UserButtonC)
* one to execute the driver logic (UserButtonLogicP)
* one to select the appropriate hardware resources, such as MSP430
Port 27 (HplUserButtonC).
Note that the presentation of this sensor is not arbitrated because
none of the operations are split-phase.
::
tos/platforms/telosa/UserButtonC.nc
configuration UserButtonC {
provides interface Get<bool>;
provides interface Notify<bool>;
provides interface DeviceMetadata;
}
implementation {
components UserButtonLogicP;
Get = UserButtonLogicP;
Notify = UserButtonLogicP;
DeviceMetadata = UserButtonLogicP;
components HplUserButtonC;
UserButtonLogicP.GpioInterrupt -> HplUserButtonC.GpioInterrupt;
UserButtonLogicP.GeneralIO -> HplUserButtonC.GeneralIO;
}
::
tos/platforms/telosa/UserButtonLogicP.nc
module UserButtonLogicP {
provides interface Get<bool>;
provides interface Notify<bool>;
provides interface DeviceMetadata;
uses interface GeneralIO;
uses interface GpioInterrupt;
}
implementation {
norace bool m_pinHigh;
task void sendEvent();
command bool Get.get() { return call GeneralIO.get(); }
command error_t Notify.enable() {
call GeneralIO.makeInput();
if ( call GeneralIO.get() ) {
m_pinHigh = TRUE;
return call GpioInterrupt.enableFallingEdge();
} else {
m_pinHigh = FALSE;
return call GpioInterrupt.enableRisingEdge();
}
}
command error_t Notify.disable() {
return call GpioInterrupt.disable();
}
async event void GpioInterrupt.fired() {
call GpioInterrupt.disable();
m_pinHigh = !m_pinHigh;
post sendEvent();
}
task void sendEvent() {
bool pinHigh;
pinHigh = m_pinHigh;
signal Notify.notify( pinHigh );
if ( pinHigh ) {
call GpioInterrupt.enableFallingEdge();
} else {
call GpioInterrupt.enableRisingEdge();
}
}
command uint8_t DeviceMetadata.getSignificantBits() { return 1; }
}
::
tos/platforms/telosa/HplUserButtonC.nc
configuration HplUserButtonC {
provides interface GeneralIO;
provides interface GpioInterrupt;
}
implementation {
components HplMsp430GeneralIOC as GeneralIOC;
components new Msp430GpioC() as UserButtonC;
UserButtonC -> GeneralIOC.Port27;
GeneralIO = UserButtonC;
components HplMsp430InterruptC as InterruptC;
components new Msp430InterruptC() as InterruptUserButtonC;
InterruptUserButtonC.HplInterrupt -> InterruptC.Port27;
GpioInterrupt = InterruptUserButtonC.Interrupt;
}
3. Digital Bus-Connected Sensor
-------------------------------
The Digital sensor is the most complex out of the set, and includes
six components:
* one to present the sensor (SensirionSht11C)
* one to request arbitrated access and to transform the sensor HAL
into the sensor HIL (SensirionSht11P)
* one to present the sensor HAL (HalSensirionSht11C)
* one to perform the driver logic needed to support the HAL, which
twiddles pins according to a sensor-specific protocol
(SensirionSht11LogicP).
* one to select the appropriate hardware resources, such as the clock,
data, and power pins, and to provide an arbiter for the sensor
(HplSensirionSht11C).
* one to perform the power control logic needed to support the power
manager associated with the arbiter (HplSensirionSht11P).
This bus-connected sensor is overly complex because it does not rely
on a shared framework of bus manipulation components. A sensor built
on top of the I2C or SPI bus would likely require fewer components.
::
tos/platforms/telosa/chips/sht11/SensirionSht11C.nc
generic configuration SensirionSht11C() {
provides interface Read<uint16_t> as Temperature;
provides interface DeviceMetadata as TemperatureDeviceMetadata;
provides interface Read<uint16_t> as Humidity;
provides interface DeviceMetadata as HumidityDeviceMetadata;
}
implementation {
components new SensirionSht11ReaderP();
Temperature = SensirionSht11ReaderP.Temperature;
TemperatureDeviceMetadata = SensirionSht11ReaderP.TemperatureDeviceMetadata;
Humidity = SensirionSht11ReaderP.Humidity;
HumidityDeviceMetadata = SensirionSht11ReaderP.HumidityDeviceMetadata;
components HalSensirionSht11C;
enum { TEMP_KEY = unique("Sht11.Resource") };
enum { HUM_KEY = unique("Sht11.Resource") };
SensirionSht11ReaderP.TempResource -> HalSensirionSht11C.Resource[ TEMP_KEY ];
SensirionSht11ReaderP.Sht11Temp -> HalSensirionSht11C.SensirionSht11[ TEMP_KEY ];
SensirionSht11ReaderP.HumResource -> HalSensirionSht11C.Resource[ HUM_KEY ];
SensirionSht11ReaderP.Sht11Hum -> HalSensirionSht11C.SensirionSht11[ HUM_KEY ];
}
::
tos/chips/sht11/SensirionSht11ReaderP.nc
generic module SensirionSht11ReaderP() {
provides interface Read<uint16_t> as Temperature;
provides interface DeviceMetadata as TemperatureDeviceMetadata;
provides interface Read<uint16_t> as Humidity;
provides interface DeviceMetadata as HumidityDeviceMetadata;
uses interface Resource as TempResource;
uses interface Resource as HumResource;
uses interface SensirionSht11 as Sht11Temp;
uses interface SensirionSht11 as Sht11Hum;
}
implementation {
command error_t Temperature.read() {
call TempResource.request();
return SUCCESS;
}
event void TempResource.granted() {
error_t result;
if ((result = call Sht11Temp.measureTemperature()) != SUCCESS) {
call TempResource.release();
signal Temperature.readDone( result, 0 );
}
}
event void Sht11Temp.measureTemperatureDone( error_t result, uint16_t val ) {
call TempResource.release();
signal Temperature.readDone( result, val );
}
command uint8_t TemperatureDeviceMetadata.getSignificantBits() { return 14; }
command error_t Humidity.read() {
call HumResource.request();
return SUCCESS;
}
event void HumResource.granted() {
error_t result;
if ((result = call Sht11Hum.measureHumidity()) != SUCCESS) {
call HumResource.release();
signal Humidity.readDone( result, 0 );
}
}
event void Sht11Hum.measureHumidityDone( error_t result, uint16_t val ) {
call HumResource.release();
signal Humidity.readDone( result, val );
}
command uint8_t HumidityDeviceMetadata.getSignificantBits() { return 12; }
event void Sht11Temp.resetDone( error_t result ) { }
event void Sht11Temp.measureHumidityDone( error_t result, uint16_t val ) { }
event void Sht11Temp.readStatusRegDone( error_t result, uint8_t val ) { }
event void Sht11Temp.writeStatusRegDone( error_t result ) { }
event void Sht11Hum.resetDone( error_t result ) { }
event void Sht11Hum.measureTemperatureDone( error_t result, uint16_t val ) { }
event void Sht11Hum.readStatusRegDone( error_t result, uint8_t val ) { }
event void Sht11Hum.writeStatusRegDone( error_t result ) { }
default event void Temperature.readDone( error_t result, uint16_t val ) { }
default event void Humidity.readDone( error_t result, uint16_t val ) { }
}
::
tos/platforms/telosa/chips/sht11/HalSensirionSht11C.nc
configuration HalSensirionSht11C {
provides interface Resource[ uint8_t client ];
provides interface SensirionSht11[ uint8_t client ];
}
implementation {
components new SensirionSht11LogicP();
SensirionSht11 = SensirionSht11LogicP;
components HplSensirionSht11C;
Resource = HplSensirionSht11C.Resource;
SensirionSht11LogicP.DATA -> HplSensirionSht11C.DATA;
SensirionSht11LogicP.CLOCK -> HplSensirionSht11C.SCK;
SensirionSht11LogicP.InterruptDATA -> HplSensirionSht11C.InterruptDATA;
components new TimerMilliC();
SensirionSht11LogicP.Timer -> TimerMilliC;
components LedsC;
SensirionSht11LogicP.Leds -> LedsC;
}
::
tos/chips/sht11/SensirionSht11LogicP.nc
generic module SensirionSht11LogicP() {
provides interface SensirionSht11[ uint8_t client ];
uses interface GeneralIO as DATA;
uses interface GeneralIO as CLOCK;
uses interface GpioInterrupt as InterruptDATA;
uses interface Timer<TMilli>;
uses interface Leds;
}
implementation {
... bus protocol details omitted for brevity ...
}
::
tos/platforms/telosa/chips/sht11/HplSensirionSht11C.nc
configuration HplSensirionSht11C {
provides interface Resource[ uint8_t id ];
provides interface GeneralIO as DATA;
provides interface GeneralIO as SCK;
provides interface GpioInterrupt as InterruptDATA;
}
implementation {
components HplMsp430GeneralIOC;
components new Msp430GpioC() as DATAM;
DATAM -> HplMsp430GeneralIOC.Port15;
DATA = DATAM;
components new Msp430GpioC() as SCKM;
SCKM -> HplMsp430GeneralIOC.Port16;
SCK = SCKM;
components new Msp430GpioC() as PWRM;
PWRM -> HplMsp430GeneralIOC.Port17;
components HplSensirionSht11P;
HplSensirionSht11P.PWR -> PWRM;
HplSensirionSht11P.DATA -> DATAM;
HplSensirionSht11P.SCK -> SCKM;
components new TimerMilliC();
HplSensirionSht11P.Timer -> TimerMilliC;
components HplMsp430InterruptC;
components new Msp430InterruptC() as InterruptDATAC;
InterruptDATAC.HplInterrupt -> HplMsp430InterruptC.Port15;
InterruptDATA = InterruptDATAC.Interrupt;
components new FcfsArbiterC( "Sht11.Resource" ) as Arbiter;
Resource = Arbiter;
components new SplitControlPowerManagerC();
SplitControlPowerManagerC.SplitControl -> HplSensirionSht11P;
SplitControlPowerManagerC.ArbiterInit -> Arbiter.Init;
SplitControlPowerManagerC.ArbiterInfo -> Arbiter.ArbiterInfo;
SplitControlPowerManagerC.ResourceController -> Arbiter.ResourceController;
}
::
tos/platforms/telosa/chips/sht11/HplSensirionSht11P.nc
module HplSensirionSht11P {
provides interface SplitControl;
uses interface Timer<TMilli>;
uses interface GeneralIO as PWR;
uses interface GeneralIO as DATA;
uses interface GeneralIO as SCK;
}
implementation {
task void stopTask();
command error_t SplitControl.start() {
call PWR.makeOutput();
call PWR.set();
call Timer.startOneShot( 11 );
return SUCCESS;
}
event void Timer.fired() {
signal SplitControl.startDone( SUCCESS );
}
command error_t SplitControl.stop() {
call SCK.makeInput();
call SCK.clr();
call DATA.makeInput();
call DATA.clr();
call PWR.clr();
post stopTask();
return SUCCESS;
}
task void stopTask() {
signal SplitControl.stopDone( SUCCESS );
}
}
-------------- next part --------------
====================================================================
Microcontroller Power Management
====================================================================
:TEP: 112
:Group: Core Working Group
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: Robert Szewczyk, Philip Levis, Martin Turon, Lama Nachman, Philip Buonadonna, Vlado Handziski
:Draft-Created: 19-Sep-2005
:Draft-Version: $Revision: 1.7 $
:Draft-Modified: $Date: 2007/01/10 18:33:38 $
: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 how TinyOS manages the lower power state of a
microcontroller.
1. Introduction
====================================================================
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 2.x 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. This memo documents these mechanisms
and how they work, as well as the basics of subsystem power
management.
2. Background
====================================================================
The TinyOS scheduler[2_] puts a processor into a sleep state when
the task queue is empty. However, processors can have a spectrum of
power states. For example, the MSP430 has one active mode (issuing
instructions) and five low-power modes. The low power modes range from
LPM0, which disables only the CPU and main system clock, to LPM4,
which disables the CPU, all clocks, and the oscillator, expecting to
be woken by an external interrupt source. The power draws of these low
power modes can differ by a factor of 350 or more (75 uA for LPM0 at
3V, 0.2 uA for LPM4). Correctly choosing the right microcontroller low
power state can greatly increase system lifetime.
TinyOS 1.x platforms manage MCU power in several different ways, but
there are commonalities in the approaches. The mica platforms, for
example, have a component named HPLPowerManagement, which has a
commands for enabling and disabling low power modes, as well as a
command (adjustPower()) to tell it to compute the low power state
based on the configuration of its various control and status
registers, storing the result in the Atmega128's MCU control
register. When TinyOS tells the microcontroller to go to sleep, it
uses the control register to decide exactly which power state to go
into. In contrast, MSP430 based platforms such as Telos and eyes
compute the low power state every time the scheduler tells the system
to go to sleep.
Each of the two approaches has benefits and drawbacks. The 1.x mica
approach is efficient, in that it only calculates the low power state
when told to. However, this leaves the decision of when to calculate
the low power state to other components, which is an easy way to
introduce bugs. The lack of a well-defined hardware abstraction
architecture in 1.x exacerbates this problem. In contrast, the MSP430
approach is simpler, in that the system will always enter the right
power state without any external prompting. However, it is
correspondingly costly, introducing 40-60 cycles of overhead to every
interrupt that wakes the system up, which can be a bottleneck on the rate
at which the system can handle interrupts.
Both of these approaches assume that TinyOS can determine the correct
low power state by examining control and status registers. For
example, the MSP430 defaults to low power mode 3 (LPM3) unless it
detects that Timer A, the USARTs, or the ADC is active, in which case
it uses low power mode 1 (LPM1). From the perspective of what
peripherals and subsystems might wake the node up or must continue
operating while the MCU sleeps, this is true. However, power modes
introduce wakeup latency, a factor which could be of interest to
higher-level components. While wakeup latency is not a significant
issue on very low power microcontrollers, such as the Atmega128 and
MSP430, more powerful processors, such as the Xscale family (the basis
of platforms such as the imote2) can have power states with wakeup
latencies as large as 5ms. For some application domains, this latency
could be a serious issue. Higher level components therefore need a way
to give the TinyOS microcontroller power manager information on their
requirements, which it considers when calculating the right low power
state.
3. Microcontroller Power Management
====================================================================
TinyOS 2.x uses three basic mechanisms to manage and control
microcontroller power states: a dirty bit, a chip-specific low power
state calculation function, and a power state override function. The
dirty bit tells TinyOS when it needs to calculate a new low power
state, the function performs the calculation, and the override allows
higher level components to introduce additional requirements, if
needed.
These three mechanisms all operate in the TinyOS core scheduling loop,
described in TEP 106: Schedulers and Tasks[2_]. This loop is
called from the boot sequence, which is described in TEP 107: Boot
Sequence[3_]. The command in question is
``Scheduler.taskLoop()``, when microcontroller sleeping is enabled.
If this command is called when the task queue is empty, the TinyOS
scheduler puts the microcontroller to sleep. It does so through
the ``McuSleep`` interface:
| ``interface McuSleep {``
| ``async command void sleep();``
| ``}``
``McuSleep.sleep()`` puts the microcontroller into a low power sleep
state, to be woken by an interrupt. This command deprecates the
``__nesc_atomic_sleep()`` call of TinyOS 1.x. Note that, as the 1.x
call suggests, putting the microcontroller to sleep MUST have certain
atomicity properties. The command is called from within an atomic
section, and MUST atomically re-enable interrupts and go to sleep. An
issue arises if the system handles an interrupt after it re-enables
interrupts but before it sleeps: the interrupt may post a task, but
the task will not be run until the microcontroller wakes up from sleep.
Microcontrollers generally have hardware mechanisms to support this
requirement. For example, on the Atmega128, the ``sei`` instruction
does not re-enable interrupts until two cycles after it is issued (so
the sequence ``sei sleep`` runs atomically).
A component named ``McuSleepC`` provides the McuSleep interface, and
``TinySchedulerC`` MUST automatically wire it to the scheduler
implementation. McuSleepC is a chip- or platform-specific component,
whose signature MUST include the following interfaces:
| ``component McuSleepC {``
| ``provides interface McuSleep;``
| ``provides interface PowerState;``
| ``uses interface PowerOverride;``
| ``}``
|
| ``interface McuPowerState {``
| ``async command void update();``
| ``}``
|
| ``interface McuPowerOverride {``
| ``async command mcu_power_t lowestState();``
| ``}``
McuSleepC MAY have additional interfaces.
3.1 The Dirty Bit
====================================================================
Whenever a Hardware Presentation Layer (HPL, see TEP 2: Hardware
Abstraction Architecture[1_]) component changes an aspect of
hardware configuration that might change the possible low power state
of the microcontroller, it MUST call McuPowerState.update(). This is
the first power management mechanism, a *dirty bit*. If
McuPowerState.update() is called, then McuSleepC MUST recompute the
low power state before the next time it goes to sleep as a result of
McuSleep.sleep() being called.
3.2 Low Power State Calculation
====================================================================
McuSleepC is responsible for calculating the lowest power state that
it can safely put the microcontroller into without disrupting the
operation of TinyOS subsystems. McuSleepC SHOULD minimize how often it
must perform this calculation: it is an inherently atomic calculation,
and so if performed very often (e.g., on every interrupt) can
introduce significant overhead and jitter.
MCU power states MUST be represented as an enum in the standard chip
implementation header file. This file MUST also define a type
``mcu_power_t`` and a combine function that given two power state
values returns one that provides the union of their functionality.
For example, consider a hypothetical microcontroller with three low
power states, (LPM0, LPM1, LPM2) and two hardware resources such as
clocks (HR0, HR1). In LPM0, both HR0 and HR1 are active. In LPM1, HR0
is inactive but HR1 is active. In LPM2, both HR0 and HR1 are inactive.
The following table describes the results of a proper combine function
(essentially a MAX):
+------+------+------+------+
| | LPM0 | LPM1 | LPM2 |
+------+------+------+------+
| LPM0 | LPM0 | LPM0 | LPM0 |
+------+------+------+------+
| LPM1 | LPM0 | LPM1 | LPM1 |
+------+------+------+------+
| LPM2 | LPM0 | LPM1 | LPM2 |
+------+------+------+------+
In contrast, if in LPM2, HR0 is active but HR1 is inactive, the
combine function would look like this:
+------+------+------+------+
| | LPM0 | LPM1 | LPM2 |
+------+------+------+------+
| LPM0 | LPM0 | LPM0 | LPM0 |
+------+------+------+------+
| LPM1 | LPM0 | LPM1 | LPM0 |
+------+------+------+------+
| LPM2 | LPM0 | LPM0 | LPM2 |
+------+------+------+------+
3.3 Power State Override
====================================================================
When McuSleepC computes the best low power state, it MUST call
``PowerOverride.lowestState().`` McuSleepC SHOULD have a default
implementation of this command, which returns the lowest power state
the MCU is capable of. The return value of this command is a
``mcu_power_t.`` McuSleepC MUST respect the requirements of the return
of this call and combine it properly with the low power state it
computes.
The PowerOverride functionality exists in case higher-level components
have some knowledge or requirements that cannot be captured in
hardware status and configuration registers, such as a maximum
tolerable wakeup latency. Because it can overrides all of the MCU
power conservation mechanisms, it SHOULD be used sparingly, if at
all. Because it is called in an atomic section during the core
scheduling loop, implementations of PowerOverride.lowestState() SHOULD
be an efficient function, and SHOULD NOT be longer than twenty or
thirty cycles; implementations SHOULD be a simple return of a cached
variable. Wiring arbitrarily to this command is an easy way to cause
TinyOS to behave badly. The presence of a combine function for
mcu_power_t means that this command can have fan-out calls.
Section 5 describes one example use of McuPowerOverride, in the
timer stack for the Atmega128 microcontroller family.
4. Peripherals and Subsystems
====================================================================
At the HIL level, TinyOS subsystems generally have a simple,
imperative power management interface. Depending on the latencies
involved, this interface is either ``StdControl``, ``SplitControl``,
or ``AsyncStdControl``.
These interfaces are imperative in that when any component calls
``stop`` on another component, it causes the subsystem that
component represents to enter an inactive, low-power state.
From the perspective of MCU power management, this transition causes a
change in status and control registers (e.g., a clock is
disabled). Following the requirements in 3.1, the MCU power management
subsystem will be notified of a significant change and act
appropriately when the system next goes to sleep. TEP 115[5_]
describes the power management of non-virtualized devices in
greater detail, and TEP 108[4_] describes how TinyOS can automatically
include power management into shared non-virtualized devices.
5. Implementation
====================================================================
An implementation of McuSleepC can be found in ``tinyos-2.x/tos/chips/atm128``,
``tinyos-2.x/tos/chips/msp430``, and ``tinyos-2.x/tos/chips/px27ax``.
An example of a use of McuPowerOverride can be found in the atmega128 timer
system. Because some low-power states have much longer wakeup latencies than
others, the timer system does not allow long latencies if it has a timer
that is going to fire soon. The implementation can be found in
``tinyos-2.x/tos/chips/atm128/timer/HplAtm128Timer0AsyncP.nc``, and
``tinyos-2.x/tos/chips/atm128/timer/HplAtm128Timer0AsyncC.nc`` automatically
wires it to McuSleepC if it is included.
6. Author's Address
====================================================================
| Robert Szewczyk
| Moteiv Corporation
| 2168 Shattuck Ave, Floor 2
| Berkeley, CA 94704
|
| email - rob at moteiv.com
|
|
| Philip Levis
| 358 Gates
| Computer Science Laboratory
| Stanford University
| Stanford, CA 94305
|
| phone - +1 650 725 9046
| email - pal at cs.stanford.edu
|
|
| Martin Turon
| PO Box 8525
| Berkeley, CA 94707
|
| phone - +1 408 965 3355
| email - mturon at xbow.com
|
|
| Lama Nachman
| 3600 Juliette Lane, SC12-319
| Intel Research
| Santa Clara, CA 95052
|
| email - lama.nachman at intel.com
|
|
| Phil Buonadonna
| Arched Rock Corp.
| 2168 Shattuck Ave. 2nd Floor
| Berkeley, CA 94704
|
| phone - +1 510 981 8714
| email - pbuonadonna at archedrock.com
|
|
| Vlado Handziski
| Sekr FT5
| Einsteinufer 25
| 10587 Berlin
| GERMANY
|
| email - handzisk at tkn.tu-berlin.de
|
6. Citations
====================================================================
.. [1] TEP 2: Hardware Abstraction Architecture
.. [2] TEP 106: Schedulers and Tasks.
.. [3] TEP 107: TinyOS 2.x Boot Sequence.
.. [4] TEP 108: Resource Arbitration
.. [5] TEP 115: Power Management of Non-Virtualised Devices
-------------- 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.7 $
:Draft-Modified: $Date: 2007/01/10 18:33:38 $
: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 [1]_, 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.
Finally, 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
|
6. Citations
============================================================================
.. [1] TEP 108: Resource Arbitration.
-------------- next part --------------
More information about the Tinyos-alliance
mailing list