1 // *********************************************************************
    2 //   Custom DPA Handler code example - Scanning RSSI of neighbors      *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-ScanRSSI.c,v $
    7 // Version: $Revision: 1.31 $
    8 // Date:    $Date: 2019/05/06 07:13:14 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2015/08/05  Release for DPA 2.20
   13 //
   14 // *********************************************************************
   15 
   16 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   17 
   18 // Default IQRF include (modify the path according to your setup)
   19 #include "IQRF.h"
   20 
   21 // Uncomment to compile Custom DPA Handler for Coordinator
   22 //#define COORDINATOR_CUSTOM_HANDLER
   23 
   24 // Default DPA header (modify the path according to your setup)
   25 #include "DPA.h"
   26 // Default Custom DPA Handler header (modify the path according to your setup)
   27 #include "DPAcustomHandler.h"
   28 
   29 // This example implements measurement of RSSI among all nodes of the IQRF network. This handler must be uploaded to all Nodes and with COORDINATOR_CUSTOM_HANDLER symbol defined to the coordinator too.
   30 // To measure the RSSIs use FRC with command FRC_AcknowledgedBroadcastBits = 0x02 with DPA request for custom peripheral PNUM=0x20, PCMD=0, HWPID=0x001F and data corresponding to the TScanRSSI structure.
   31 // When FRC is finished then the same peripheral at C must called. C then sends TScanPacket packet. Then come short 10 timeslots when all Ns send their TScanPacket packets. Timeslots are ordered according to the Ns' VRNs. 
   32 // When the measurement is finished then PNUM=0x20, PCMD=1 and PNUM=0x20, PCMD=2 are used to read RSSI levels from all nodes. 
   33 // The result is a bit array of bit pairs i.e. 2 bits for every node at the network. Bit pairs are ordered according the node address. Bits 0b00 indicated that the node of such address was not "listened" at all.
   34 // 0b01 means low RSSI, 0b10 means middle RSSI and 0b11 specifies high RSSI
   35 // ! Please note that this example stores RSSI levels in the bufferAUX memory buffer which is not generally recommended because bufferAUX is used internally by DPA for certain purposes (i.e. storing batches). 
   36 // ! Therefore it is important to read levels immediately after RSSI is measure otherwise levels might be overwritten.
   37 // This example works only at STD mode, not at LP mode
   38 
   39 // Structure holding data for custom peripheral PNUM=0x20, PCMD=0. This command initiates measurement of RSSI among nodes. It must be used only as a part of FRC_AcknowledgedBroadcastBits.
   40 typedef struct
   41 {
   42   // Maximum VRN in the network (equals number of discovered nodes in the network)
   43   uns8 MaxVrn;
   44   // TX power used to measure RSSI
   45   uns8 TxPower;
   46   // If RSSI is equal of less than this number, it gets the level 1
   47   uns8 RssiLow;
   48   // If RSIS is equal of less than this number, it gets the level 2. If RSSI is greater then it gets code 3.
   49   uns8 RssiMid;
   50 } TScanRSSI;
   51 
   52 // Structure of the packet used to measure RSSI.
   53 typedef struct
   54 {
   55   // Packet header. equals 'N'
   56   uns8 Header;
   57   // Address of the node that sent the packet
   58   uns8 Address;
   59 } TScanPacket;
   60 
   61 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   62 //############################################################################################
   63 bit CustomDpaHandler()
   64 //############################################################################################
   65 {
   66   // Handler presence mark
   67   clrwdt();
   68 
   69 #ifndef COORDINATOR_CUSTOM_HANDLER
   70   // Place for local static variables used only within CustomDpaHandler() among more events
   71   static uns8 timer10msCounter;
   72 #endif
   73 
   74   // Detect DPA event to handle
   75   switch ( GetDpaEvent() )
   76   {
   77 #ifndef COORDINATOR_CUSTOM_HANDLER
   78     // -------------------------------------------------
   79     case DpaEvent_Interrupt:
   80       // Do an extra quick background interrupt work
   81       // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible.
   82       // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one.
   83       // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time.
   84       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   85       // ! Make sure race condition does not occur when accessing those variables at other places.
   86       // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt.
   87       // ! Do not call any OS functions except setINDFx().
   88       // ! Do not use any OS variables especially for writing access.
   89       // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage.
   90 
   91       //  If TMR6 interrupt occurred
   92       if ( TMR6IF )
   93       {
   94         // Unmask interrupt
   95         TMR6IF = 0;
   96         // Increment count
   97         timer10msCounter++;
   98       }
   99 
  100       return Carry;
  101 
  102       // -------------------------------------------------
  103     case DpaEvent_Init:
  104       // Do a one time initialization work before main loop starts
  105     {
  106       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  107 #if F_OSC == 16000000
  108       PR6 = 250 - 1;
  109       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  110 #else
  111 #error Unsupported oscillator frequency
  112 #endif
  113 
  114       TMR6IE = TRUE;
  115     }
  116     break;
  117 
  118     // -------------------------------------------------
  119     case DpaEvent_AfterSleep:
  120       // Called on wake-up from sleep
  121       TMR6IE = TRUE;
  122       TMR6ON = TRUE;
  123       break;
  124 
  125       // -------------------------------------------------
  126     case DpaEvent_BeforeSleep:
  127       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  128 
  129       // -------------------------------------------------
  130     case DpaEvent_DisableInterrupts:
  131       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  132       // Must not use TMR6 any more
  133       TMR6ON = FALSE;
  134       TMR6IE = FALSE;
  135       break;
  136 
  137 #endif
  138 
  139       // -------------------------------------------------
  140     case DpaEvent_DpaRequest:
  141       // Called to interpret DPA request for peripherals
  142       // -------------------------------------------------
  143       // Peripheral enumeration
  144       if ( IsDpaEnumPeripheralsRequest() )
  145       {
  146         // We implement 1 user peripheral
  147         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  148         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  149         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x001F;
  150         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0000;
  151 
  152 DpaHandleReturnTRUE:
  153         return TRUE;
  154       }
  155       // -------------------------------------------------
  156       // Get information about peripheral
  157       else if ( IsDpaPeripheralInfoRequest() )
  158       {
  159         if ( _PNUM == PNUM_USER + 0 )
  160         {
  161           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  162           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  163           goto DpaHandleReturnTRUE;
  164         }
  165 
  166         break;
  167       }
  168       // -------------------------------------------------
  169       else
  170       {
  171         // Handle peripheral
  172         if ( _PNUM == PNUM_USER + 0 )
  173         {
  174           switch ( _PCMD )
  175           {
  176             // Initiate RSSI measurement
  177             case 0:
  178             {
  179               // Check data length
  180               if ( _DpaDataLength != sizeof( TScanRSSI ) )
  181                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  182 
  183 #ifndef COORDINATOR_CUSTOM_HANDLER
  184               // At Node it can be called only from FRC acknowledged broadcast, otherwise it is not synchronized!
  185               if ( _NADR != BROADCAST_ADDRESS )
  186                 DpaApiReturnPeripheralError( ERROR_NADR );
  187               // NOde with temporary address does not measure
  188               if ( ntwADDR == TEMPORARY_ADDRESS )
  189                 DpaApiReturnPeripheralError( ERROR_FAIL );
  190 #endif
  191               // Request data
  192               TScanRSSI ScanRSSI @ _DpaMessage.Request.PData;
  193               // Copy of request data
  194               TScanRSSI ScanRSSICopy;
  195               // Make a copy
  196               copyMemoryBlock( &ScanRSSI, &ScanRSSICopy, sizeof( ScanRSSICopy ) );
  197 
  198               // Set non-network RF settings
  199               setNonetMode();
  200               setNetworkFilteringOff();
  201               setRFmode( _WPE | _RX_STD | _TX_STD | _STDL );
  202               setRFpower( ScanRSSICopy.TxPower );
  203 
  204               // Scanning packet
  205               TScanPacket ScanPacket @ bufferRF;
  206 
  207 #ifndef COORDINATOR_CUSTOM_HANDLER
  208               // Clear bufferAUX
  209               clearBufferINFO();
  210               swapBufferINFO();
  211 
  212               // Give C some extra time to sent its ScanPacket packet
  213               waitDelay( 3 );
  214 
  215               // We use the shortest RX timeout
  216               toutRF = 1;
  217               // Initialize timer, maximize gap before measurement starts
  218               timer10msCounter = MAX_ADDRESS + 2;
  219               // We did not send out packet yet
  220               bit sent = FALSE;
  221               // Loop all timeslots
  222               for ( ;; )
  223               {
  224                 // If we receive ScanPacket from other node
  225                 if ( checkRF( 0 ) && RFRXpacket() && DLEN == sizeof( ScanPacket ) && ScanPacket.Header == 'N' && ScanPacket.Address <= MAX_ADDRESS )
  226                 {
  227                   // bits variable stores the RSSI level from 1 to 3 according to the thresholds RssiLow and RssiMid
  228                   uns8 bits @ userReg0;
  229                   bits = 0b01;
  230                   if ( lastRSSI > ScanRSSICopy.RssiLow )
  231                   {
  232                     bits++;
  233                     if ( lastRSSI > ScanRSSICopy.RssiMid )
  234                       bits++;
  235                   }
  236 
  237                   // Shift level to the correct bit position according to the node address value
  238                   uns8 temp @ userReg1;
  239                   temp = ScanPacket.Address % 4;
  240                   while ( temp != 0 )
  241                   {
  242                     temp--;
  243                     bits <<= 2;
  244                   }
  245 
  246                   // Compute byte address according to the node address value
  247                   FSR0 = ScanPacket.Address / 4;
  248                   FSR0 += bufferAUX;
  249 
  250                   // Combine bits with already stored ones
  251                   setINDF0( *FSR0 | bits );
  252                 }
  253 
  254                 // Any timeslots pending?
  255                 if ( timer10msCounter != ScanRSSICopy.MaxVrn + 1 )
  256                 {
  257                   // Is it my timeslot and the packet was not sent yet?
  258                   if ( timer10msCounter == ntwVRN && !sent )
  259                   {
  260                     // Packet will be sent
  261                     sent = TRUE;
  262 #endif
  263                     // Prepare the packet
  264                     ScanPacket.Header = 'N';
  265 #ifndef COORDINATOR_CUSTOM_HANDLER
  266                     ScanPacket.Address = ntwADDR;
  267 #else
  268                     ScanPacket.Address = COORDINATOR_ADDRESS;
  269 #endif
  270                     DLEN = sizeof( ScanPacket );
  271                     PIN = 0;
  272                     // Send the packet
  273                     RFTXpacket();
  274 
  275 #ifndef COORDINATOR_CUSTOM_HANDLER
  276                   }
  277                 }
  278                 else
  279                   // Timeslots are over. Exit the loop.
  280                   break;
  281               }
  282 #endif
  283               // Restore RF settings
  284               DpaApiSetRfDefaults();
  285               setNetworkFilteringOn();
  286               // REstore network settings
  287 #ifndef COORDINATOR_CUSTOM_HANDLER
  288               setNodeMode();
  289 #else
  290               setCoordinatorMode();
  291 #endif
  292 
  293               goto DpaHandleReturnTRUE;
  294             }
  295 
  296 #ifndef COORDINATOR_CUSTOM_HANDLER
  297             case 2:
  298               // Return 2nd part of the RSSI bitmaps
  299               memoryOffsetFrom = DPA_MAX_DATA_LENGTH;
  300 
  301             case 1:
  302               // Check data length
  303               if ( _DpaDataLength != 0 )
  304                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  305 
  306               // Copy result from bufferAUX to the DPA response @ bufferRF
  307               swapBufferINFO();
  308               copyBufferINFO2RF();
  309               swapBufferINFO();
  310 
  311               // Return correct data length
  312               if ( _PCMD == 2 )
  313                 _DpaDataLength = ( MAX_ADDRESS + 1 ) / 4 - DPA_MAX_DATA_LENGTH;
  314               else
  315                 _DpaDataLength = DPA_MAX_DATA_LENGTH;
  316 
  317               goto DpaHandleReturnTRUE;
  318 #endif
  319           }
  320         }
  321       }
  322   }
  323 
  324   return FALSE;
  325 }
  326 //############################################################################################
  327 // Default Custom DPA Handler header; 2nd include to implement Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup) 
  328 #include "DPAcustomHandler.h"
  329 //############################################################################################