]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/rf230/LowPowerListeningLayerP.nc
low power listening
[tinyos-2.x.git] / tos / chips / rf230 / LowPowerListeningLayerP.nc
diff --git a/tos/chips/rf230/LowPowerListeningLayerP.nc b/tos/chips/rf230/LowPowerListeningLayerP.nc
new file mode 100644 (file)
index 0000000..5325284
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2007, Vanderbilt University
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
+ * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Author: Miklos Maroti
+ */
+
+#include <RadioAssert.h>
+
+module LowPowerListeningLayerP
+{
+       provides
+       {
+               interface SplitControl;
+               interface Send;
+               interface Receive;
+
+               interface LowPowerListening;
+       }
+
+       uses
+       {
+               interface SplitControl as SubControl;
+               interface Send as SubSend;
+               interface Receive as SubReceive;
+
+               interface PacketField<uint16_t> as PacketSleepInterval;
+               interface Timer<TMilli>;
+       }
+}
+
+implementation
+{
+       enum
+       {
+               // minimum wakeup time to catch a transmission in milliseconds
+               LISTEN_WAKEUP = 6,      // use xxxL if LISTEN_WAKEUP * 10000 > 65535
+
+               // extra wakeup time after receiving a message in milliseconds
+               AFTER_RECEIVE = 10,
+
+               // extra wakeup time after transmitting a message in milliseconds
+               AFTER_TRANSMIT = 10,
+
+               MIN_SLEEP = 2,          // the minimum sleep interval in milliseconds
+               MAX_SLEEP = 30000,      // the maximum sleep interval in milliseconds
+               MIN_DUTY = 2,           // the minimum duty cycle
+       };
+
+       uint16_t rxSleepInterval;
+
+/*----------------- state machine -----------------*/
+
+       enum
+       {
+               STATE_OFF = 0,          // timer off, radio off
+               STATE_SLEEP = 1,        // timer on, radio off
+               STATE_LISTEN = 2,       // timer on/off, radio on
+               STATE_SEND = 3,         // timer on/off, radio on
+
+               STATE_OFF_TO_LISTEN = 10,
+               STATE_SLEEP_TO_LISTEN = 11,
+               STATE_SLEEP_TO_SEND = 12,
+               STATE_SLEEP_TO_OFF = 13,
+               STATE_LISTEN_TO_SLEEP_1 = 14,   // we go back to listen if a message arrives in this state
+               STATE_LISTEN_TO_SLEEP_2 = 15,
+               STATE_LISTEN_TO_OFF = 16,
+               STATE_SEND_DONE = 17,
+       };
+
+       uint8_t state;
+
+       message_t* txMsg;
+       uint8_t txLen;
+
+       task void transition()
+       {
+               error_t error;
+
+               if( state == STATE_OFF_TO_LISTEN || state == STATE_SLEEP_TO_LISTEN || state == STATE_SLEEP_TO_SEND )
+               {
+                       error = call SubControl.start();
+                       ASSERT( error == SUCCESS || error == EBUSY );
+
+                       if( error != SUCCESS )
+                               post transition();
+               }
+               else if( state == STATE_LISTEN_TO_OFF || state == STATE_LISTEN_TO_SLEEP_1 )
+               {
+                       error = call SubControl.stop();
+                       ASSERT( error == SUCCESS || error == EBUSY );
+
+                       if( error != SUCCESS )
+                               post transition();
+                       else if( state == STATE_LISTEN_TO_SLEEP_1 )
+                               state = STATE_LISTEN_TO_SLEEP_2;
+               }
+               else if( state == STATE_SLEEP_TO_OFF )
+               {
+                       state = STATE_OFF;
+                       signal SplitControl.stopDone(SUCCESS);
+               }
+               else if( state == STATE_SEND )
+               {
+                       error = call SubSend.send(txMsg, txLen);
+                       if( error == SUCCESS )
+                               state = STATE_SEND_DONE;
+                       else
+                       {
+                               state = STATE_LISTEN;
+                               if( rxSleepInterval > 0 )
+                                       call Timer.startOneShot(AFTER_TRANSMIT);
+
+                               signal Send.sendDone(txMsg, error);
+                       }
+               }
+               else if( state == STATE_LISTEN )
+               {
+                       if( rxSleepInterval > 0 )
+                               call Timer.startOneShot(LISTEN_WAKEUP);
+               }
+               else if( state == STATE_SLEEP )
+               {
+                       if( rxSleepInterval > 0 )
+                               call Timer.startOneShot(rxSleepInterval);
+                       else
+                       {
+                               state = STATE_SLEEP_TO_LISTEN;
+                               post transition();
+                       }
+               }
+       }
+
+       command error_t SplitControl.start()
+       {
+               if( state != STATE_OFF )
+                       return EALREADY;
+
+               state = STATE_OFF_TO_LISTEN;
+               post transition();
+
+               return SUCCESS;
+       }
+
+       event void SubControl.startDone(error_t error)
+       {
+               ASSERT( error == SUCCESS || error == EBUSY );
+               ASSERT( state == STATE_OFF_TO_LISTEN || state == STATE_SLEEP_TO_LISTEN || state == STATE_SLEEP_TO_SEND );
+
+               if( error == SUCCESS )
+               {
+                       if( state == STATE_OFF_TO_LISTEN )
+                               signal SplitControl.startDone(SUCCESS);
+                       else if( state == STATE_SLEEP_TO_SEND )
+                               state = STATE_SEND;
+                       else
+                               state = STATE_LISTEN;
+               }
+
+               post transition();
+       }
+
+       command error_t SplitControl.stop()
+       {
+               if( state == STATE_OFF )
+                       return EALREADY;
+               else if( state != STATE_LISTEN || state != STATE_SLEEP )
+                       return EBUSY;
+
+               call Timer.stop();
+               if( state == STATE_SLEEP )
+                       state = STATE_SLEEP_TO_OFF;
+               else
+                       state = STATE_LISTEN_TO_OFF;
+
+               post transition();
+
+               return SUCCESS;
+       }
+
+       event void SubControl.stopDone(error_t error)
+       {
+               ASSERT( error == SUCCESS || error == EBUSY );
+               ASSERT( state == STATE_LISTEN_TO_SLEEP_2 || state == STATE_LISTEN_TO_OFF );
+
+               if( error == SUCCESS )
+               {
+                       if( state == STATE_LISTEN_TO_OFF )
+                               state = STATE_SLEEP_TO_OFF;
+                       else
+                               state = STATE_SLEEP;
+               }
+
+               post transition();
+       }
+
+       event void Timer.fired()
+       {
+               ASSERT( state == STATE_LISTEN || state == STATE_SLEEP );
+
+               if( state == STATE_LISTEN )
+                       state = STATE_LISTEN_TO_SLEEP_1;
+               else
+                       state = STATE_SLEEP_TO_LISTEN;
+
+               post transition();
+       }
+
+       void rxSleepIntervalChanged()
+       {
+               if( rxSleepInterval == 0 )
+               {
+                       call Timer.stop();
+                       if( state == STATE_SLEEP )
+                               state = STATE_SLEEP_TO_LISTEN;
+               }
+
+               post transition();
+       }
+
+       event message_t* SubReceive.receive(message_t* msg, void* payload, uint8_t len)
+       {
+               if( state == STATE_LISTEN_TO_SLEEP_1 )
+                       state = STATE_LISTEN;
+
+               if( state == STATE_LISTEN && rxSleepInterval > 0 )
+                       call Timer.startOneShot(AFTER_RECEIVE);
+
+               return signal Receive.receive(msg, payload, len);
+       }
+
+       command error_t Send.send(message_t* msg, uint8_t len)
+       {
+               if( state == STATE_LISTEN || state == STATE_SLEEP )
+                       call Timer.stop();
+
+               if( state == STATE_LISTEN || state == STATE_LISTEN_TO_SLEEP_1 )
+               {
+                       state = STATE_SEND;
+                       post transition();
+               }
+               else if( state == STATE_SLEEP )
+               {
+                       state = STATE_SLEEP_TO_SEND;
+                       post transition();
+               }
+               else if( state == STATE_SLEEP_TO_LISTEN )
+                       state = STATE_SLEEP_TO_SEND;
+               else
+                       return EBUSY;
+
+               txMsg = msg;
+               txLen = len;
+       }
+
+       command error_t Send.cancel(message_t* msg)
+       {
+               return call SubSend.cancel(msg);
+       }
+
+       event void SubSend.sendDone(message_t* msg, error_t error)
+       {
+               ASSERT( state == STATE_SEND_DONE );
+
+               state = STATE_LISTEN;
+               if( rxSleepInterval > 0 )
+                       call Timer.startOneShot(AFTER_TRANSMIT);
+
+               signal Send.sendDone(msg, error);
+       }
+
+       command uint8_t Send.maxPayloadLength()
+       {
+               return call SubSend.maxPayloadLength();
+       }
+
+       command void* Send.getPayload(message_t* msg, uint8_t len)
+       {
+               return call SubSend.getPayload(msg, len);
+       }
+
+/*----------------- LowPowerListening -----------------*/
+
+       command uint16_t LowPowerListening.dutyCycleToSleepInterval(uint16_t dutyCycle)
+       {
+               if( dutyCycle >= 10000 )
+                       return 0;
+               else if( dutyCycle <= MIN_DUTY  )
+                       return MAX_SLEEP;
+
+               return ((10000 * LISTEN_WAKEUP) / dutyCycle) - LISTEN_WAKEUP;
+       }
+
+       command uint16_t LowPowerListening.sleepIntervalToDutyCycle(uint16_t sleepInterval)
+       {
+               if( sleepInterval < MIN_SLEEP )
+                       return 10000;
+               else if( sleepInterval >= MAX_SLEEP )
+                       return MIN_DUTY;
+
+               return (10000 * LISTEN_WAKEUP) / (LISTEN_WAKEUP + sleepInterval);
+       }
+
+       command void LowPowerListening.setLocalSleepInterval(uint16_t sleepInterval)
+    {
+               if( sleepInterval < MIN_SLEEP )
+                       sleepInterval = 0;
+               else if( sleepInterval > MAX_SLEEP )
+                       sleepInterval = MAX_SLEEP;
+
+               rxSleepInterval = sleepInterval;
+               rxSleepIntervalChanged();
+       }
+
+       command uint16_t LowPowerListening.getLocalSleepInterval()
+    {  
+               return rxSleepInterval;
+       }
+
+       command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle)
+       {
+               call LowPowerListening.setLocalSleepInterval(
+                       call LowPowerListening.dutyCycleToSleepInterval(dutyCycle));
+       }
+
+       command uint16_t LowPowerListening.getLocalDutyCycle()
+       {
+               return call LowPowerListening.sleepIntervalToDutyCycle(rxSleepInterval);
+       }
+
+       command void LowPowerListening.setRxSleepInterval(message_t *msg, uint16_t sleepInterval)
+       {
+               if( sleepInterval < MIN_SLEEP )
+                       sleepInterval = 0;
+               else if( sleepInterval > MAX_SLEEP )
+                       sleepInterval = MAX_SLEEP;
+
+               call PacketSleepInterval.set(msg, sleepInterval);
+       }
+
+       command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg)
+    {
+               if( ! call PacketSleepInterval.isSet(msg) )
+                       return 0;
+
+               return call PacketSleepInterval.get(msg);
+       }
+
+       command void LowPowerListening.setRxDutyCycle(message_t *msg, uint16_t dutyCycle)
+    {
+               call PacketSleepInterval.set(msg, 
+                       call LowPowerListening.dutyCycleToSleepInterval(dutyCycle));
+       }
+
+       command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg)
+    {
+               if( ! call PacketSleepInterval.isSet(msg) )
+                       return 10000;
+
+               return call LowPowerListening.sleepIntervalToDutyCycle(
+                       call PacketSleepInterval.get(msg));
+       }
+}