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