Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

The following image is a one-page chronogram of the IEEE802.15.4e state machine.

...

Code Block
// TX
#define DURATION_tt1 ieee154e_vars.capturedTime+TsTxOffset-delayTx-maxTxDataPrepare
#define DURATION_tt2 ieee154e_vars.capturedTime+TsTxOffset-delayTx
#define DURATION_tt3 ieee154e_vars.capturedTime+TsTxOffset-delayTx+wdRadioTx
#define DURATION_tt4 ieee154e_vars.capturedTime+wdDataDuration
#define DURATION_tt5 ieee154e_vars.capturedTime+TsTxAckDelay-TsShortGT-delayRx-maxRxAckPrepare
#define DURATION_tt6 ieee154e_vars.capturedTime+TsTxAckDelay-TsShortGT-delayRx
#define DURATION_tt7 ieee154e_vars.capturedTime+TsTxAckDelay+TsShortGT
#define DURATION_tt8 ieee154e_vars.capturedTime+wdAckDuration
// RX
#define DURATION_rt1 ieee154e_vars.capturedTime+TsTxOffset-TsLongGT-delayRx-maxRxDataPrepare
#define DURATION_rt2 ieee154e_vars.capturedTime+TsTxOffset-TsLongGT-delayRx
#define DURATION_rt3 ieee154e_vars.capturedTime+TsTxOffset+TsLongGT
#define DURATION_rt4 ieee154e_vars.capturedTime+wdDataDuration
#define DURATION_rt5 ieee154e_vars.capturedTime+TsTxAckDelay-delayTx-maxTxAckPrepare
#define DURATION_rt6 ieee154e_vars.capturedTime+TsTxAckDelay-delayTx
#define DURATION_rt7 ieee154e_vars.capturedTime+TsTxAckDelay-delayTx+wdRadioTx
#define DURATION_rt8 ieee154e_vars.capturedTime+wdAckDuration

Activities

Expand
titlejump to activity...

Table of Contents
maxLevel3
minLevel2

This section is meant to be read together with the chronogram. It details:

...

If this is the first time you see this page, we recommend you read through the following sections in order, while looking at the corresponding event in the chronogram. During implementation, use the links on the right abovejump to jump to a particular event.

ti1

Triggered by the IEEE802.15.4e timer rolling over, indicating the beginning of a new slot.

...

This is an error state which indicates that #ri2 didn't have enough time to execute. Chances are you set maxRxDataPrepare too small. The recommended behavior is to log the error and move on.

In C-code:

No Formatcode
#!c inline inline void activity_rie1() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_MAXRXDATAPREPARE_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);

   // abort
   endSlot();
}

ri3

Triggered by #rt2 expiring, i.e. timer fires while state==S_RXDATAREADY.

The radio is configured to the right channel. This interrupt gives it the 'go' signal to start listening. Note that this FSM takes into account the delay between this 'go' and the radio actually listening, which is called #DelayRx. You need to calibrate #DelayRx from your radio's datasheet. If you're unsure, the value 0 is conservative.

In C-code:

#!c inline
No Format
Code Block
 inline void activity_ri3() {
   // change state
   changeState(S_RXDATALISTEN);

   // give the 'go' to receive
   radio_rxNow();

   // arm rt3
   ieee154etimer_schedule(DURATION_rt3);
}

rt3

Times the maximum duration the mote should be listening for a packet. If no packet is received by the time this timer expires, then none will be received ever and it is safe to switch of the radio (see #rie1). This timer is set such that the radio will be on during two consecutive windows of duration #TsLongGT.

...

This is not an error, it just indicates that the transmitter has no packet to send (or that the packet got lost).

In C-code:

No Formatcode
#!c inline inline void activity_rie2() {
   // abort
   endSlot();
}

ri4

Triggered by a start of frame event while state==S_RXDATALISTEN.

Capture the time, cancel #rt3 and arm #rt4.

In C-code:

#!c inline
No Format
Code Block
 inline void activity_ri4(uint16_t capturedTime) {
   // change state
   changeState(S_RXDATA);
   
   // cancel rt3
   ieee154etimer_cancel();

   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;

   // arm rt4
   ieee154etimer_schedule(DURATION_rt4);
}

rt4

See #tt4.

rie3

Triggered by #rt4 expiring, i.e. timer fires while state==S_RXDATA.

This is an error state which indicates the radio took too long to transmit the data packet. The recommended behavior is to log the error and move on.

In C-code:

No Formatcode
#!c inline inline void activity_rie3() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_WDDATADURATION_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);
   
   // abort
   endSlot();
}

ri5

Triggered by a end of frame event while state==S_RXDATA.

In C-code:

#!c inline
No Format
Code Block
 inline void activity_ri5(uint16_t capturedTime) {
   ieee802154_header_iht ieee802514_header;
   
   // change state
   changeState(S_TXACKOFFSET);
   
   // cancel rt4
   ieee154etimer_cancel();
   
   // turn off the radio
   radio_rfOff();
   
   // get a buffer to put the (received) data in
   ieee154e_vars.dataReceived = openqueue_getFreePacketBuffer();
   if (ieee154e_vars.dataReceived==NULL) {
      // log the error
      openserial_printError(COMPONENT_IEEE802154E,
                            ERR_NO_FREE_PACKET_BUFFER,
                            0,
                            0);
      // abort
      endSlot();
      return;
   }
   
   // declare ownership over that packet
   ieee154e_vars.dataReceived->creator = COMPONENT_IEEE802154E;
   ieee154e_vars.dataReceived->owner   = COMPONENT_IEEE802154E;
   
   // retrieve the received frame from the radio's Rx buffer
   radio_getReceivedFrame(ieee154e_vars.dataReceived);
   
   // parse the IEEE802.15.4 header
   ieee802154_retrieveHeader(ieee154e_vars.dataReceived,&ieee802514_header);
   
   // store header details in packet buffer
   memcpy(&(ieee154e_vars.dataReceived->l2_nextORpreviousHop),&(ieee802514_header.src),sizeof(open_addr_t));
   ieee154e_vars.dataReceived->l2_frameType = ieee802514_header.frameType;
   
   // toss the IEEE802.15.4 header
   packetfunctions_tossHeader(ieee154e_vars.dataReceived,ieee802514_header.headerLength);
   
   // if I just received a valid ADV, handle and stop
   if (isValidAdv(&ieee802514_header)==TRUE) {
      
      if (idmanager_getIsDAGroot()==FALSE) {
         // record the ASN
         //ieee154e_vars.asn = asnRead(ieee154e_vars.dataReceived);
         if (ieee154e_vars.asn != asnRead(ieee154e_vars.dataReceived)) {
            __no_operation();
         };
         
         // synchronize the slots to the sender's
         synchronize(ieee154e_vars.capturedTime,&ieee802514_header.src);
         
         // declare synchronized
         changeIsSync(TRUE);
      }
      
      // free the received data so corresponding RAM memory can be recycled
      openqueue_freePacketBuffer(ieee154e_vars.dataReceived);
      
      // clear local variable
      ieee154e_vars.dataReceived = NULL;
      
      // abort
      endSlot();
      return;
   }
   
   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;
   
   // if I juts received an invalid data frame, stop
   if (isValidData(&ieee802514_header)==FALSE) {
      // free the received data so corresponding RAM memory can be recycled
      openqueue_freePacketBuffer(ieee154e_vars.dataReceived);
      
      // clear local variable
      ieee154e_vars.dataReceived = NULL;
   
      // abort
      endSlot();
      return;
   }
   
   // check if ack requested
   if (ieee802514_header.ackRequested==1) {
      // arm rt5
      ieee154etimer_schedule(DURATION_rt5);
   } else {
      // indicate reception to upper layer
      res_receive(ieee154e_vars.dataReceived);
      // reset local variable
      ieee154e_vars.dataReceived = NULL;
      // abort
      endSlot();
   }
}

rt5

See #tt1.

ri6

Triggered by #rt5 expiring, i.e. timer fires while state==S_TXACKOFFSET.

Prepares the radio to transmit an ACK.

In C-code:

No Formatcode
#!c inline inline void activity_ri6() {
   uint8_t frequency;
   
   // change state
   changeState(S_TXACKPREPARE);
   
   // get a buffer to put the ack to send in
   ieee154e_vars.ackToSend = openqueue_getFreePacketBuffer();
   if (ieee154e_vars.ackToSend==NULL) {
      // log the error
      openserial_printError(COMPONENT_IEEE802154E,
                            ERR_NO_FREE_PACKET_BUFFER,
                            0,
                            0);
      // indicate we received a packet anyway (we don't want to loose any)
      res_receive(ieee154e_vars.dataReceived);
      // free local variable
      ieee154e_vars.dataReceived = NULL;
      // abort
      endSlot();
      return;
   }
   
   // declare ownership over that packet
   ieee154e_vars.ackToSend->creator = COMPONENT_IEEE802154E;
   ieee154e_vars.ackToSend->owner   = COMPONENT_IEEE802154E;
   
   // add the payload to the ACK (i.e. the timeCorrection)
   packetfunctions_reserveHeaderSize(ieee154e_vars.ackToSend,sizeof(IEEE802154E_ACK_ht));
   ((IEEE802154E_ACK_ht*)(ieee154e_vars.ackToSend->payload))->timeCorrection[0] = 0x00;//todo
   ((IEEE802154E_ACK_ht*)(ieee154e_vars.ackToSend->payload))->timeCorrection[1] = 0x00;//todo
   
   // prepend the IEEE802.15.4 header to the ACK
   ieee154e_vars.ackToSend->l2_frameType = IEEE154_TYPE_ACK;
   ieee802154_prependHeader(ieee154e_vars.ackToSend,
                            ieee154e_vars.ackToSend->l2_frameType,
                            IEEE154_SEC_NO_SECURITY,
                            0x00,
                            &(ieee154e_vars.dataReceived->l2_nextORpreviousHop)
                            );
   // TODO: change the dsn
   
   // space for 2-byte CRC
   packetfunctions_reserveFooterSize(ieee154e_vars.ackToSend,2);
   
   // calculate the frequency to transmit on
   frequency = calculateFrequency(ieee154e_vars.asn, schedule_getChannelOffset(ieee154e_vars.asn) );
   
   // configure the radio for that frequency
   radio_setFrequency(frequency);
   
   // load the packet in the radio's Tx buffer
   radio_loadPacket(ieee154e_vars.ackToSend);
   
   // enable the radio in Tx mode. This does not send that packet.
   radio_txEnable();
   
   // arm rt6
   ieee154etimer_schedule(DURATION_rt6);
   
   // change state
   changeState(S_TXACKREADY);
}

rt6

Times when to give the 'go' signal for the radio to send the already loaded ACK packet.

...

This is an error state which indicates that #ri6 didn't have enough time to execute. Chances are you set maxTxAckPrepare too small. The recommended behavior is to log the error and move on.

In C-code:

No Formatcode
#!c inline inline void activity_rie4() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_MAXTXACKPREPARE_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);
   
   // abort
   endSlot();
}

ri7

Triggered by #rt6 expiring, i.e. timer fires while state==S_TXACKREADY.

The ACK is ready for transmission in the radio. This interrupt is used just to give the 'go' to transmit. Note that this FSM takes into account the delay between this 'go' and the SFD actually leaving the radio, which is called delayTx. You need to calibrate delayTx by measuring that delay in your radio.

In C-code:

No Formatcode
#!c
inline void activity_ri7() {
   // change state
   changeState(S_TXACKDELAY);
   
   // arm rt7
   ieee154etimer_schedule(DURATION_rt7);
   
   // give the 'go' to transmit
   radio_txNow();
}

rt7

See #tt3.

rie5

Triggered by #rt7 expiring, i.e. timer fires while state==TXACKDELAY.

This is an exception state. It happens when the radio fails to transmit. This could be because of an timing issue (you set maxTxAckPrepare too short), or a glitch in the radio. The recommended behavior is to log the error and move on.

In C-code:

No Formatcode
#!c
inline void activity_rie5() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_WDRADIOTX_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);
   
   // abort
   endSlot();
}

ri8

Triggered by a start of frame event while state==S_TXACKDELAY.

Cancels #rt7 and captures timer value.

In C-code:

No Formatcode
#!c
inline void activity_ri8(uint16_t capturedTime) {
   // change state
   changeState(S_TXACK);
   
   // cancel rt7
   ieee154etimer_cancel();
   
   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;
   
   // arm rt8
   ieee154etimer_schedule(DURATION_rt8);
}

rt8

See wdAckDuration.

rie6

...

This is an error state, indicating the radio took too long to transmit the packet. Probably a radio glitch. The recommended behavior is to log the error and move on.

In C-code:

No Formatcode
#!c
inline void activity_rie6() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_WDACKDURATION_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);
   
   // abort
   endSlot();
}

ri9

Triggered when end of frame event while state==S_TXACK.

In C-code:

No Formatcode
#!c
inline void activity_ri9(uint16_t capturedTime) {
   // change state
   changeState(S_RXPROC);
   
   // cancel rt8
   ieee154etimer_cancel();
   
   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;
   
   // free the ack we just sent so corresponding RAM memory can be recycled
   openqueue_freePacketBuffer(ieee154e_vars.ackToSend);
   
   // clear local variable
   ieee154e_vars.ackToSend = NULL;
   
   // inform upper layer of reception
   res_receive(ieee154e_vars.dataReceived);
   
   // clear local variable
   ieee154e_vars.dataReceived = NULL;
   
   // official end of Rx slot
   endSlot();
}

changeState

This function is simply used to updated the state variable. On top of that, we use that function to wiggle the fsm debug pin.

In C-code:

No Formatcode
#!c
void changeState(uint8_t newstate) {
   // update the state
   ieee154e_vars.state = newstate;
   // wiggle the FSM debug pin
   switch (ieee154e_vars.state) {
      case S_SYNCHRONIZING:
      case S_TXDATAOFFSET:
         DEBUG_PIN_FSM_SET();
         break;
      case S_SLEEP:
      case S_RXDATAOFFSET:
         DEBUG_PIN_FSM_CLR();
         break;
      case S_TXDATAPREPARE:
      case S_TXDATAREADY:
      case S_TXDATADELAY:
      case S_TXDATA:
      case S_RXACKOFFSET:
      case S_RXACKPREPARE:
      case S_RXACKREADY:
      case S_RXACKLISTEN:
      case S_RXACK:
      case S_TXPROC:
      case S_RXDATAPREPARE:
      case S_RXDATAREADY:
      case S_RXDATALISTEN:
      case S_RXDATA:
      case S_TXACKOFFSET:
      case S_TXACKPREPARE:
      case S_TXACKREADY:
      case S_TXACKDELAY:
      case S_TXACK:
      case S_RXPROC:
         DEBUG_PIN_FSM_TOGGLE();
         break;
   }
}

endSlot

An "endSlot" exception event is part of normal operation, and should not be treated as an error. It is called exactly once in each slot.

...

You can safely implement it as a single function, called from different locations in the code.

In C-code:

No Formatcode
#!c
void endSlot() {
   // turn off the radio
   radio_rfOff();
   
   // clear any pending timer
   ieee154etimer_cancel();
   
   // reset capturedTime
   ieee154e_vars.capturedTime = 0;
   
   // clean up dataToSend
   if (ieee154e_vars.dataToSend!=NULL) {
      // if everything went well, dataToSend was set to NULL in ti9
      // transmit failed, decrement transmits left counter
      ieee154e_vars.dataToSend->l2_retriesLeft--;
      // indicate tx fail if no more retries left
      if (ieee154e_vars.dataToSend->l2_retriesLeft==0) {
         res_sendDone(ieee154e_vars.dataToSend,E_FAIL);
      }
      // reset local variable
      ieee154e_vars.dataToSend = NULL;
   }
   
   // clean up dataReceived
   if (ieee154e_vars.dataReceived!=NULL) {
      // assume something went wrong. If everything went well, dataReceived
      // would have been set to NULL in ri9.
      // indicate  "received packet" to upper layer since we don't want to loose packets
      res_receive(ieee154e_vars.dataReceived);
      // reset local variable
      ieee154e_vars.dataReceived = NULL;
   }
   
   // clean up ackToSend
   if (ieee154e_vars.ackToSend!=NULL) {
      // free ackToSend so corresponding RAM memory can be recycled
      openqueue_freePacketBuffer(ieee154e_vars.ackToSend);
      // reset local variable
      ieee154e_vars.ackToSend = NULL;
   }
   
   // clean up ackReceived
   if (ieee154e_vars.ackReceived!=NULL) {
      // free ackReceived so corresponding RAM memory can be recycled
      openqueue_freePacketBuffer(ieee154e_vars.ackReceived);
      // reset local variable
      ieee154e_vars.ackReceived = NULL;
   }
   
   // change state
   changeState(S_SLEEP);
}