Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Current »

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

 

If you are going to work on IEEE802.15.4e, we stronly recommend you print out the chronogram attached to this page:

  File Modified
You are not logged in. Any changes you make will be marked as anonymous. You may want to Log In if you already have an account.
No files shared here yet.
  • Drag and drop to upload or browse for files
  • Some keys for understanding this file:

    • the file represents the activity of a transmitter (TX) and receiver (RX) mote during a single slot.
    • for both the TX and RX mote, the file shows;
      • a representation of the activity of a mote. When the red line is above/below the dotted line, the mote is transmitting/receiving, respectively;
      • the value of the global variables;
      • the events and timers;
      • the state of four of the debug pins as you should see them on your scope once you are done implementing.
    • the central part shows the naming for the different times.
    • each timer, task and isr activity is given a name:
      • the first letter identifies whether it refers to the transmitter (t) or receiver (r);
      • the second letter refers to the type: timer (t), task (a), ISR (i);
      • the number is used for unique naming of each activity;
      • isr activities labeled with an e are exception activities.

    Source code

    This page is merely an HTML version of the reference source code at  IEEE802154E.c. Each function in that file is commented using Doxygen syntax.

    Events

    The following events happen:

    • the timer expires. As discussed in the Timer Setup  step, IEEE802.15.4e can be implemented with a single compare register which is set to different values as the state machine progresses through the slot;
    • the radio detects a start of a frame, which corresponds to the SFD byte of the packet being sent/received. Note that this event happens each time a packet is transmitted or received;
    • the radio detects an end of a frame; Note that this event happens each time a packet is transmitted or received;

    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

    // 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:

    • for isr and task, what computation should be performed;
    • for timer, what this timer does.

    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 to jump to a particular event.

    ti1

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

    In C-code:

    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.

    ti2

    Triggered by #tt1 expiring, i.e. timer fires while state==S_TXDATAOFFSET.

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

    In C-code:

    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.

    tie1

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

    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:

    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:

    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.

    tie2

    Triggered by #tt3 expiring, i.e. timer fires while state==TXDATADELAY.

    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:

    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:

    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.

    tie3

    Triggered by #tt4 expiring, i.e. timer fires while state==S_TXDATA.

    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:

    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:

    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:

    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.

    tie4

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

    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:

    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.

    Sets the radio to actively listen and arms #tt7.

    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:

    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.

    tie5

    Triggered by #tt7 expiring, i.e. timer fires while state==S_RXACKLISTEN.

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

    In C-code:

    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:

    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.

    tie6

    Triggered by #tt8 expiring, i.e. timer fires while state==S_RXACK.

    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:

    inline void activity_tie6() {
       // abort
       endSlot();
    }

    ti9

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

    In C-code:

    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

    Triggered by #rt1 expiring, e.g. timer fires while state==S_RXDATAOFFSET.

    In C-code:

    inline void activity_ri2() {
       uint8_t frequency;
       
       // change state
       changeState(S_RXDATAPREPARE);
    
       // 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 does not actively listen yet.
       radio_rxEnable();
    
       // arm rt2
       ieee154etimer_schedule(DURATION_rt2);
    
       // change state
       changeState(S_RXDATAREADY);
    }

    rt2

    Times when to give the 'go' signal to the radio to actively listen for packets.

    rie1

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

    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:

    #!c
    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 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.

    rie2

    Triggered by #rt3 expiring, i.e. timer fires while state==S_RXDATALISTEN.

    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:

    #!c
    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 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:

    #!c
    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 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:

    #!c
    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.

    rie4

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

    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:

    #!c
    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:

    #!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:

    #!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:

    #!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

    Triggered by #rt8 expiring, i.e. timer fires while state==S_TXACK.

    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:

    #!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:

    #!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:

    #!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.

    It is called whenever there is nothing left to do in the slot. This can be:

    • when the transmitter sent the data, received the ack
    • when a receiver listens for a packet, but the transmitter does not transmit

    Note that communication with the upper layer needs to be done before calling this function.

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

    In C-code:

    #!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);
    }
    
    • No labels