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