Versions Compared

Key

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

...

These events trigger the execution, in interrupt mode, of one of the #Activities, depending of the current state of the IEEE802.15.4e FSM. Each interrupt handler is a switch statement which performs some computation depending on the current state. In each state, the CPU starts by executing some code, then waits for an event; this event causes the state machine to switch to a different state.

Defines

No Formatcode
// 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

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

...

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

In C-code:

No Formatcode
#!c
inline void activity_ti1ORri1() {
   uint8_t cellType;
   open_addr_t neighbor;
   
   //stop outputting serial data
   openserial_stop();
   
   // increment ASN (do this first so debug pins are in sync)
   ieee154e_vars.asn++;
   
   // wiggle debug pins
   DEBUG_PIN_SLOT_TOGGLE();
   if (ieee154e_vars.asn%SCHEDULELENGTH==0) {
      DEBUG_PIN_FRAME_TOGGLE();
   }
   
   // desynchronize if needed
   if (idmanager_getMyID(ADDR_16B)->addr_16b[1]==DEBUG_MOTEID_SLAVE) {
      ieee154e_vars.syncTimeout--;
      if (ieee154e_vars.syncTimeout==0) {
         changeIsSync(FALSE);
         endSlot();
         return;
      }
   }

   // if the previous slot took too long, we will not be in the right state
   if (ieee154e_vars.state!=S_SLEEP) {
      // log the error
      openserial_printError(COMPONENT_IEEE802154E,
                            ERR_WRONG_STATE_IN_STARTSLOT,
                            ieee154e_vars.state,
                            ieee154e_vars.asn%SCHEDULELENGTH);
      // abort
      endSlot();
      return;
   }

   // check the schedule to see what type of slot this is
   cellType = schedule_getType(ieee154e_vars.asn);
   switch (cellType) {
      case CELLTYPE_OFF:
         // I have nothing to do
         // abort
         endSlot();
         //start outputing serial
         openserial_startOutput();
         break;
      case CELLTYPE_ADV:
         ieee154e_vars.dataToSend = openqueue_getAdvPacket();
         if (ieee154e_vars.dataToSend==NULL) {
            // I will be listening for an ADV
            // change state
            changeState(S_RXDATAOFFSET);
            // arm rt1
            ieee154etimer_schedule(DURATION_rt1);
         } else {
            // I will be sending an ADV
            // change state
            changeState(S_TXDATAOFFSET);
            // fill in the ASN field of the ADV
            asnWrite(ieee154e_vars.dataToSend);
            // arm tt1
            ieee154etimer_schedule(DURATION_tt1);
         }
         break;
      case CELLTYPE_TX:
         schedule_getNeighbor(ieee154e_vars.asn,&neighbor);
         ieee154e_vars.dataToSend = openqueue_getDataPacket(&neighbor);
         if (ieee154e_vars.dataToSend!=NULL) {
            // I have a packet to send
            // change state
            changeState(S_TXDATAOFFSET);
            // arm tt1
            ieee154etimer_schedule(DURATION_tt1);
         } else {
            // abort
            endSlot();
         }
         break;
      case CELLTYPE_RX:
         // I need to listen for packet
         // change state
         changeState(S_RXDATAOFFSET);
         // arm rt1
         ieee154etimer_schedule(DURATION_rt1);
         break;
      case CELLTYPE_SERIALRX:
         //todo implement
         break;
      default:
         // log the error
         openserial_printError(COMPONENT_IEEE802154E,
                               ERR_WRONG_CELLTYPE,
                               cellType,
                               ieee154e_vars.asn%SCHEDULELENGTH);
         // abort
         endSlot();
         break;
   }
}
tt1

Used to delay preparing to transmit/receive. The idea is to avoid to reach the *READY state too early, because in that state, the radio is on and drawing a lot of current, but it is not actively sending or receiving.

...

It prepares the radio for transmitting the packet held in the dataToSend variable.

In C-code:

No Formatcode
#!c
inline void activity_ti2() {
   uint8_t frequency;
   
   // change state
   changeState(S_TXDATAPREPARE);

   // 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.dataToSend);

   // enable the radio in Tx mode. This does not send the packet.
   radio_txEnable();

   // arm tt2
   ieee154etimer_schedule(DURATION_tt2);

   // change state
   changeState(S_TXDATAREADY);
}
tt2

Times when to give the 'go' signal to the radio for the packet to be sent.

...

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

In C-code:

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

   // abort
   endSlot();
}
ti3

Triggered by #tt2 expiring, i.e. timer fires while state==S_TXDATAREADY.

The packet 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_ti3() {
   // change state
   changeState(S_TXDATADELAY);
   
   // arm tt3
   ieee154etimer_schedule(DURATION_tt3);
   
   // give the 'go' to transmit
   radio_txNow();
}
tt3

Makes sure the radio is working properly. In theory, giving the radio a 'go' signal should have the radio transmit. But to make absolutely sure, we arm this timer. When the radio signals the start of frame, we cancel the timer; everything is OK. If the timer elapses, it indicates something went wrong in the radio. This timer is used to gracefully recover from that situation.

...

This is an exception state. It happens when the radio fails to transmit. This could be because of an timing issue (you set maxTxDataPrepare 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_tie2() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_WDRADIO_OVERFLOW,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);

   // abort
   endSlot();
}
ti4

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

Cancels #tt3 and captures the transmit time.

In C-code:

No Formatcode
#!c
inline void activity_ti4(uint16_t capturedTime) {
   // change state
   changeState(S_TXDATA);

   // cancel tt3
   ieee154etimer_cancel();

   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;
   
   if (ieee154e_vars.capturedTime!=62) {
      __no_operation();
   }
   
   // arm tt4
   ieee154etimer_schedule(DURATION_tt4);
}
tt4

Failsafe timer against a radio glitch. A packet is at most 128 bytes long from the SFD to the last byte. At 250kbps, it takes 4.096ms to transmit 128 bytes. Transmission should always be over after that, so if we haven't received the end of frame event by then, something is going on.

...

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_tie3() {
   // log the error
   openserial_printError(COMPONENT_IEEE802154E,
                         ERR_WDDATADURATION_OVERFLOWS,
                         ieee154e_vars.state,
                         ieee154e_vars.asn%SCHEDULELENGTH);

   // abort
   endSlot();
}
ti5

Triggered by an end of frame event while state==S_TXDATA.

Decides whether to listen for an ACK and arms #tt5.

In C-code:

No Formatcode
#!c
inline void activity_ti5(uint16_t capturedTime) {
   bool listenForAck;
   
   // change state
   changeState(S_RXACKOFFSET);
   
   // cancel tt4
   ieee154etimer_cancel();
   
   // turn off the radio
   radio_rfOff();

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

   // decides whether to listen for an ACK
   if (packetfunctions_isBroadcastMulticast(&ieee154e_vars.dataToSend->l2_nextORpreviousHop)==TRUE) {
      listenForAck = FALSE;
   } else {
      listenForAck = TRUE;
   }

   if (listenForAck==TRUE) {
      // arm tt5
      ieee154etimer_schedule(DURATION_tt5);
   } else {
      // indicate that the packet was sent successfully
      res_sendDone(ieee154e_vars.dataToSend,E_SUCCESS);
      // reset local variable
      ieee154e_vars.dataToSend = NULL;
      // abort
      endSlot();
   }
}
tt5

See #tt1.

ti6

Triggered by #tt5 expiring, i.e. timer fires while state==S_RXACKOFFSET.

Prepares the radio to receive the ACK.

In C-code:

No Formatcode
#!c
inline void activity_ti6() {
   uint8_t frequency;
   
   // change state
   changeState(S_RXACKPREPARE);

   // 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);

   // enable the radio in Rx mode. The radio is not actively listening yet.
   radio_rxEnable();

   // arm tt6
   ieee154etimer_schedule(DURATION_tt6);
   
   // change state
   changeState(S_RXACKREADY);
}
tt6

Sets a delay between preparing the radio receive, and actively receiving.

...

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

In C-code:

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

   // abort
   endSlot();
}t();
}
ti7

Triggered by #tt6 expiring, i.e. timer fires while state==S_RXACKREADY.

...

Note that this state machine allows some delay (called delayRx) between the moment you instruct the radio to receive, and the moment is actually start listening. This delay is normally written down in your radio's datasheet.

In C-code:

No Formatcode
#!c
inline void activity_ti7() {
   // change state
   changeState(S_RXACKLISTEN);

   // start listening
   radio_rxNow();

   // arm tt7
   ieee154etimer_schedule(DURATION_tt7);
}
tt7

Times the receives window for the ACK packet. This timer is canceled whenever the radio starts receiving a packet. If it hasn't received a packet by the time this timer fires, it means it will never receive one, and so the #tie5 turns the radio off.

...

This is an exception state, where the transmitter did not receive an ACK from the receiver.

In C-code:

No Formatcode
#!c
inline void activity_tie5() {
   // 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;

   // abort
   endSlot();
}
ti8

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

Capture the time, cancel #tt7 and arm #tt8.

In C-code:

No Formatcode
#!c
inline void activity_ti8(uint16_t capturedTime) {
   // change state
   changeState(S_RXACK);
   
   // cancel tt7
   ieee154etimer_cancel();

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

   // arm tt8
   ieee154etimer_schedule(DURATION_tt8);
}
tt8

This is a failsafe timer against the radio taking too long to receive an ACK. An ACK contains a maximum number of bytes, a it is therefore possible to know how long it will take for the radio to receive it, in the worst case. This time is called wdAckDuration. If it hasn't received the packet (i.e. the end of frame event hasn't happened), something is wrong and #tt8 will elapse. #ti9 cancels #tt8 when it received the end of frame event from the radio.

...

This happens when the ack packet is received for longer than expected. This can happen when, while you listen for an ACK, the radio picks-up a completely different packet which can be longer than the ACK. This is therefore, not per se, an error.

In C-code:

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

Triggered by an end of frame event while state==S_RXACK.

In C-code:

No Formatcode
#!c
inline void activity_ti9(uint16_t capturedTime) {
   ieee802154_header_iht ieee802514_header;
   
   // change state
   changeState(S_TXPROC);
   
   // cancel tt8
   ieee154etimer_cancel();
   
   // turn off the radio
   radio_rfOff();

   // record the captured time
   ieee154e_vars.capturedTime = capturedTime;
   
   // get a buffer to put the (received) ACK in
   ieee154e_vars.ackReceived = openqueue_getFreePacketBuffer();
   if (ieee154e_vars.ackReceived==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.ackReceived->creator = COMPONENT_IEEE802154E;
   ieee154e_vars.ackReceived->owner   = COMPONENT_IEEE802154E;
   
   // retrieve the received frame from the radio's Rx buffer
   radio_getReceivedFrame(ieee154e_vars.ackReceived);
   
   // parse the IEEE802.15.4 header
   ieee802154_retrieveHeader(ieee154e_vars.ackReceived,&ieee802514_header);
   
   // store header details in packet buffer
   memcpy(&(ieee154e_vars.ackReceived->l2_nextORpreviousHop),&(ieee802514_header.src),sizeof(open_addr_t));
   ieee154e_vars.ackReceived->l2_frameType = ieee802514_header.frameType;
   
   // toss the IEEE802.15.4 header
   packetfunctions_tossHeader(ieee154e_vars.ackReceived,ieee802514_header.headerLength);
   
   // if frame is a valid ACK, handle
   if (isValidAck(&ieee802514_header)==TRUE) {
      // if packet sent successfully, inform upper layer
      res_sendDone(ieee154e_vars.dataToSend,E_SUCCESS);
      ieee154e_vars.dataToSend = NULL;
   }

   // free the received ack so corresponding RAM memory can be recycled
   openqueue_freePacketBuffer(ieee154e_vars.ackReceived);
   
   // clear local variable
   ieee154e_vars.ackReceived = NULL;

   // official end of Tx slot
   endSlot();
}
ri1

See #ti1.

rt1

See #tt1.

ri2

...