+++ /dev/null
-/*\r
- * Copyright (c) 2005-2006 Rincon Research Corporation\r
- * All rights reserved.\r
- *\r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions\r
- * are met:\r
- * - Redistributions of source code must retain the above copyright\r
- * notice, this list of conditions and the following disclaimer.\r
- * - Redistributions in binary form must reproduce the above copyright\r
- * notice, this list of conditions and the following disclaimer in the\r
- * documentation and/or other materials provided with the\r
- * distribution.\r
- * - Neither the name of the Arch Rock Corporation nor the names of\r
- * its contributors may be used to endorse or promote products derived\r
- * from this software without specific prior written permission.\r
- *\r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\r
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\r
- * ARCHED ROCK OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\r
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\r
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\r
- * OF THE POSSIBILITY OF SUCH DAMAGE\r
- */\r
-\r
-/**\r
- * Low Power Listening for the CC2420\r
- *\r
- * @author David Moss\r
- */\r
-\r
-#include "CC2420LowPowerListening.h"\r
-\r
-module CC2420LowPowerListeningP {\r
- provides {\r
- interface Init;\r
- interface LowPowerListening;\r
- interface Send;\r
- interface Receive;\r
- }\r
- \r
- uses {\r
- interface Leds;\r
- interface Send as SubSend;\r
- interface CC2420Transmit as Resend;\r
- interface Receive as SubReceive;\r
- interface AMPacket;\r
- interface SplitControl as SubControl;\r
- interface CC2420DutyCycle;\r
- interface PacketAcknowledgements;\r
- interface State as SendState;\r
- interface State as RadioState;\r
- interface State as SplitControlState;\r
- interface Random;\r
- interface Timer<TMilli> as OffTimer;\r
- interface Timer<TMilli> as SendDoneTimer;\r
- }\r
-}\r
-\r
-implementation {\r
- \r
- /** The message currently being sent */\r
- message_t *currentSendMsg;\r
- \r
- /** The length of the current send message */\r
- uint8_t currentSendLen;\r
- \r
- /** TRUE if the radio is duty cycling and not always on */\r
- bool dutyCycling;\r
- \r
- /** Tx DSN to ensure multiple transmitted messages get across only once */ \r
- uint8_t txDsn;\r
- \r
- /** The last received broadcast DSN. TODO is this the best way? */\r
- uint8_t lastRxDsn;\r
-\r
- /**\r
- * Radio State\r
- */\r
- enum {\r
- S_OFF,\r
- S_ON,\r
- };\r
- \r
- /**\r
- * Send States\r
- */\r
- enum {\r
- S_IDLE,\r
- S_SENDING,\r
- };\r
- \r
- \r
- /***************** Prototypes ***************/\r
- task void send();\r
- task void resend();\r
- task void startRadio();\r
- task void stopRadio();\r
- \r
- void startOffTimer();\r
- cc2420_header_t *getHeader(message_t *msg);\r
- cc2420_metadata_t *getMetadata(message_t* msg);\r
- uint16_t getActualDutyCycle(uint16_t dutyCycle);\r
- void signalDone(error_t error);\r
- \r
- /***************** Init Commands ***************/\r
- command error_t Init.init() {\r
- txDsn = call Random.rand16();\r
- dutyCycling = FALSE;\r
- return SUCCESS;\r
- }\r
- \r
- /***************** LowPowerListening Commands ***************/\r
- /**\r
- * Set this this node's radio sleep interval, in milliseconds.\r
- * Once every interval, the node will sleep and perform an Rx check \r
- * on the radio. Setting the sleep interval to 0 will keep the radio\r
- * always on.\r
- *\r
- * This is the equivalent of setting the local duty cycle rate.\r
- *\r
- * @param sleepIntervalMs the length of this node's Rx check interval, in [ms]\r
- */\r
- command void LowPowerListening.setLocalSleepInterval(\r
- uint16_t sleepIntervalMs) {\r
- call CC2420DutyCycle.setSleepInterval(sleepIntervalMs);\r
- }\r
- \r
- /**\r
- * @return the local node's sleep interval, in [ms]\r
- */\r
- command uint16_t LowPowerListening.getLocalSleepInterval() {\r
- return call CC2420DutyCycle.getSleepInterval();\r
- }\r
- \r
- /**\r
- * Set this node's radio duty cycle rate, in units of [percentage*100].\r
- * For example, to get a 0.05% duty cycle,\r
- * <code>\r
- * call LowPowerListening.setDutyCycle(5); // or equivalently...\r
- * call LowPowerListening.setDutyCycle(00005); // for better readability?\r
- * </code>\r
- *\r
- * For a 100% duty cycle (always on),\r
- * <code>\r
- * call LowPowerListening.setDutyCycle(10000);\r
- * </code>\r
- *\r
- * This is the equivalent of setting the local sleep interval explicitly.\r
- * \r
- * @param dutyCycle The duty cycle percentage, in units of [percentage*100]\r
- */\r
- command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) {\r
- call CC2420DutyCycle.setSleepInterval(\r
- call LowPowerListening.dutyCycleToSleepInterval(dutyCycle));\r
- }\r
- \r
- /**\r
- * @return this node's radio duty cycle rate, in units of [percentage*100]\r
- */\r
- command uint16_t LowPowerListening.getLocalDutyCycle() {\r
- return call LowPowerListening.sleepIntervalToDutyCycle(\r
- call CC2420DutyCycle.getSleepInterval());\r
- }\r
- \r
- \r
- /**\r
- * Configure this outgoing message so it can be transmitted to a neighbor mote\r
- * with the specified Rx sleep interval.\r
- * @param msg Pointer to the message that will be sent\r
- * @param sleepInterval The receiving node's sleep interval, in [ms]\r
- */\r
- command void LowPowerListening.setRxSleepInterval(message_t *msg, \r
- uint16_t sleepIntervalMs) {\r
- getMetadata(msg)->rxInterval = sleepIntervalMs;\r
- }\r
- \r
- /**\r
- * @return the destination node's sleep interval configured in this message\r
- */\r
- command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg) {\r
- return getMetadata(msg)->rxInterval;\r
- }\r
- \r
- /**\r
- * Configure this outgoing message so it can be transmitted to a neighbor mote\r
- * with the specified Rx duty cycle rate.\r
- * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25.\r
- * \r
- * @param msg Pointer to the message that will be sent\r
- * @param dutyCycle The duty cycle of the receiving mote, in units of \r
- * [percentage*100]\r
- */\r
- command void LowPowerListening.setRxDutyCycle(message_t *msg, \r
- uint16_t dutyCycle) {\r
- getMetadata(msg)->rxInterval =\r
- call LowPowerListening.dutyCycleToSleepInterval(dutyCycle);\r
- }\r
- \r
- \r
- /**\r
- * @return the destination node's duty cycle configured in this message\r
- * in units of [percentage*100]\r
- */\r
- command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) {\r
- return call LowPowerListening.sleepIntervalToDutyCycle(\r
- getMetadata(msg)->rxInterval);\r
- }\r
- \r
- /**\r
- * Convert a duty cycle, in units of [percentage*100], to\r
- * the sleep interval of the mote in milliseconds\r
- * @param dutyCycle The duty cycle in units of [percentage*100]\r
- * @return The equivalent sleep interval, in units of [ms]\r
- */\r
- command uint16_t LowPowerListening.dutyCycleToSleepInterval(\r
- uint16_t dutyCycle) {\r
- dutyCycle = getActualDutyCycle(dutyCycle);\r
- \r
- if(dutyCycle == 10000) {\r
- return 0;\r
- }\r
- \r
- return (DUTY_ON_TIME * (10000 - dutyCycle)) / dutyCycle;\r
- }\r
- \r
- /**\r
- * Convert a sleep interval, in units of [ms], to a duty cycle\r
- * in units of [percentage*100]\r
- * @param sleepInterval The sleep interval in units of [ms]\r
- * @return The duty cycle in units of [percentage*100]\r
- */\r
- command uint16_t LowPowerListening.sleepIntervalToDutyCycle(\r
- uint16_t sleepInterval) {\r
- if(sleepInterval == 0) {\r
- return 10000;\r
- }\r
- \r
- return getActualDutyCycle((DUTY_ON_TIME * 10000) \r
- / (sleepInterval + DUTY_ON_TIME));\r
- }\r
-\r
- \r
- /***************** Send Commands ***************/\r
- /**\r
- * Each call to this send command gives the message a single\r
- * DSN that does not change for every copy of the message\r
- * sent out. For messages that are not acknowledged, such as\r
- * a broadcast address message, the receiving end does not\r
- * signal receive() more than once for that message.\r
- */\r
- command error_t Send.send(message_t *msg, uint8_t len) {\r
- if(call SplitControlState.getState() == S_OFF) {\r
- // Everything is off right now, start SplitControl and try again\r
- return EOFF;\r
- }\r
- \r
- if(call SendState.requestState(S_SENDING) == SUCCESS) {\r
- currentSendMsg = msg;\r
- currentSendLen = len;\r
- (getHeader(msg))->dsn = ++txDsn;\r
- \r
- // In case our off timer is running...\r
- call OffTimer.stop();\r
- \r
- if(call RadioState.getState() == S_ON) {\r
- if(call LowPowerListening.getRxSleepInterval(currentSendMsg) \r
- > ONE_MESSAGE) {\r
- // Send it repetitively within our transmit window\r
- call PacketAcknowledgements.requestAck(currentSendMsg);\r
- call SendDoneTimer.startOneShot(\r
- call LowPowerListening.getRxSleepInterval(currentSendMsg) * 2);\r
- }\r
- \r
- post send();\r
- \r
- } else {\r
- post startRadio();\r
- }\r
- \r
- return SUCCESS;\r
- }\r
- \r
- return FAIL;\r
- }\r
-\r
- command error_t Send.cancel(message_t *msg) {\r
- if(currentSendMsg == msg) {\r
- call SendState.toIdle();\r
- return SUCCESS;\r
- }\r
- \r
- return FAIL;\r
- }\r
- \r
- \r
- command uint8_t Send.maxPayloadLength() {\r
- return call SubSend.maxPayloadLength();\r
- }\r
-\r
- command void *Send.getPayload(message_t* msg) {\r
- return call SubSend.getPayload(msg);\r
- }\r
- \r
- /***************** Receive Commands ***************/\r
- command void *Receive.getPayload(message_t* msg, uint8_t* len) {\r
- return call SubReceive.getPayload(msg, len);\r
- }\r
-\r
- command uint8_t Receive.payloadLength(message_t* msg) {\r
- return call SubReceive.payloadLength(msg);\r
- }\r
-\r
-\r
- /***************** DutyCycle Events ***************/\r
- /**\r
- * A transmitter was detected. You must now take action to\r
- * turn the radio off when the transaction is complete.\r
- */\r
- event void CC2420DutyCycle.detected() {\r
- // At this point, the duty cycling has been disabled temporary\r
- // and it will be this component's job to turn the radio back off\r
- \r
- startOffTimer();\r
- }\r
- \r
- \r
- /***************** SubControl Events ***************/\r
- event void SubControl.startDone(error_t error) {\r
- if(!error) {\r
- call RadioState.forceState(S_ON);\r
- \r
- if(call SendState.getState() == S_SENDING) {\r
- if(call LowPowerListening.getRxSleepInterval(currentSendMsg) \r
- > ONE_MESSAGE) {\r
- // Send it repetitively within our transmit window\r
- call PacketAcknowledgements.requestAck(currentSendMsg);\r
- call SendDoneTimer.startOneShot(\r
- call LowPowerListening.getRxSleepInterval(currentSendMsg) * 2);\r
- }\r
- \r
- post send();\r
- }\r
- }\r
- }\r
- \r
- event void SubControl.stopDone(error_t error) {\r
- if(!error) {\r
- call RadioState.forceState(S_OFF);\r
-\r
- if(call SendState.getState() == S_SENDING) {\r
- // We're in the middle of sending a message; start the radio back up\r
- post startRadio();\r
- }\r
- }\r
- }\r
- \r
- /***************** SubSend Events ***************/\r
- event void SubSend.sendDone(message_t* msg, error_t error) {\r
- if(call SendState.getState() == S_SENDING \r
- && call SendDoneTimer.isRunning()) {\r
- if(call PacketAcknowledgements.wasAcked(msg)) {\r
- signalDone(error);\r
- \r
- } else {\r
- post resend();\r
- }\r
- \r
- return;\r
- }\r
- \r
- signalDone(error);\r
- }\r
- \r
- /***************** SubReceive Events ***************/\r
- /**\r
- * If the received message is new, we signal the receive event and\r
- * start the off timer. If the last message we received had the same\r
- * DSN as this message, then the chances are pretty good\r
- * that this message should be ignored, especially if the destination address\r
- * as the broadcast address\r
- *\r
- * TODO\r
- * What happens if a unicast Tx doesn't get Rx's ack, and resends that\r
- * message?\r
- */\r
- event message_t *SubReceive.receive(message_t* msg, void* payload, \r
- uint8_t len) {\r
- \r
- if((getHeader(msg))->dsn == lastRxDsn \r
- && call AMPacket.destination(msg) == AM_BROADCAST_ADDR) {\r
- // Already got this broadcast message.\r
- // TODO should we do something similar with unicast messages?\r
- return msg;\r
-\r
- } else {\r
- lastRxDsn = (getHeader(msg))->dsn;\r
- startOffTimer();\r
- return signal Receive.receive(msg, payload, len);\r
- }\r
- }\r
- \r
- /***************** Timer Events ****************/\r
- event void OffTimer.fired() {\r
- /*\r
- * Only stop the radio if the radio is supposed to be off permanently\r
- * or if the duty cycle is on and our sleep interval is not 0\r
- */\r
- if(call SplitControlState.getState() == S_OFF\r
- || (call CC2420DutyCycle.getSleepInterval() > 0\r
- && call SplitControlState.getState() == S_ON)) { \r
- post stopRadio();\r
- }\r
- }\r
- \r
- /**\r
- * When this timer is running, that means we're sending repeating messages\r
- * to a node that is receive check duty cycling.\r
- */\r
- event void SendDoneTimer.fired() {\r
- if(call SendState.getState() == S_SENDING) {\r
- // The next time SubSend.sendDone is signaled, send is complete.\r
- call SendState.toIdle();\r
- }\r
- }\r
- \r
- /***************** Resend Events ****************/\r
- /**\r
- * Signal that a message has been sent\r
- *\r
- * @param p_msg message to send.\r
- * @param error notifaction of how the operation went.\r
- */\r
- async event void Resend.sendDone( message_t* p_msg, error_t error ) {\r
- // This is actually caught by SubSend.sendDone\r
- }\r
- \r
- \r
- /***************** Tasks ***************/\r
- task void send() {\r
- if(call SubSend.send(currentSendMsg, currentSendLen) != SUCCESS) {\r
- post send();\r
- }\r
- }\r
- \r
- task void resend() {\r
- // Resend the last message without CCA checks.\r
- if(call Resend.resend() != SUCCESS) {\r
- post resend();\r
- }\r
- }\r
- \r
- task void startRadio() {\r
- if(call SubControl.start() != SUCCESS) {\r
- post startRadio();\r
- }\r
- }\r
- \r
- task void stopRadio() {\r
- if(call SubControl.stop() != SUCCESS) {\r
- post stopRadio();\r
- }\r
- }\r
- \r
- /***************** Functions ***************/\r
- void startOffTimer() {\r
- call OffTimer.startOneShot(DELAY_AFTER_RECEIVE);\r
- }\r
- \r
- /**\r
- * Check the bounds on a given duty cycle\r
- * We're never over 100%, and we're never at 0%\r
- */\r
- uint16_t getActualDutyCycle(uint16_t dutyCycle) {\r
- if(dutyCycle > 10000) {\r
- return 10000;\r
- } else if(dutyCycle == 0) {\r
- return 1;\r
- }\r
- \r
- return dutyCycle;\r
- }\r
- \r
- cc2420_header_t *getHeader(message_t *msg) {\r
- return (cc2420_header_t *)(msg->data - sizeof( cc2420_header_t ));\r
- }\r
- \r
- cc2420_metadata_t *getMetadata(message_t* msg) {\r
- return (cc2420_metadata_t*)msg->metadata;\r
- }\r
- \r
- void signalDone(error_t error) {\r
- call SendState.toIdle();\r
- startOffTimer();\r
- signal Send.sendDone(currentSendMsg, error);\r
- currentSendMsg = NULL;\r
- }\r
-}\r
-\r