1 // *********************************************************************
    2 //   Custom DPA Handler code example - Auto network example            *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-AutoNetwork.c,v $
    7 // Version: $Revision: 1.49 $
    8 // Date:    $Date: 2017/10/23 07:10:20 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2016/09/12  Release for DPA 2.28
   13 //   2016/02/10  Release for DPA 2.26
   14 //   2015/09/03  Release for DPA 2.21
   15 //   2015/08/05  Release for DPA 2.20
   16 //
   17 // *********************************************************************
   18 
   19 // On-line DPA documentation http://www.iqrf.org/DpaTechGuide/
   20 
   21 // This example implements automatic network construction. It works with corresponding AutoNetwork application build with IQRF SDK.
   22 // The same code is for [N] devices and [C] (use COORDINATOR_CUSTOM_HANDLER symbol to allow conditional compilation for [C] device)
   23 // The handler for [C] devices is used in case the autonetwork is controlled by external device connected to the [C]
   24 // Another example CustomDpaHandler-Coordinator-AutoNetwork-Embedded.c demonstrates self-containing [C] handler that controls 
   25 
   26 // Define the next symbol to let the [N] variant work at LP mode. 
   27 // Then TMR6 is not used for timing but less precise captureTicks() IQRF OS function.
   28 // The reason is the WDT sleeps during RF communication.
   29 //#define   DPA_LP
   30 
   31 #ifdef DPA_LP
   32 #message "CustomDpaHandler - AutoNetwork.c" compiled for LP mode
   33 #else
   34 #message "CustomDpaHandler - AutoNetwork.c" compiled for STD mode
   35 #endif
   36 
   37 // Default IQRF include (modify the path according to your setup)
   38 #include "IQRF.h"
   39 // Default DPA header (modify the path according to your setup)
   40 #include "DPA.h"
   41 // Default Custom DPA Handler header (modify the path according to your setup)
   42 #include "DPAcustomHandler.h"
   43 
   44 // Define to disable scanning the working channel, it saves the code size
   45 #define DO_NOT_SCAN_CHANNELS
   46 
   47 #if !defined( COORDINATOR_CUSTOM_HANDLER ) && !defined( DO_NOT_SCAN_CHANNELS )
   48 // 1st channel to scan
   49 #define FIRST_CHANNEL           0
   50 // Last channel to scan + 1
   51 #define LAST_CHANNEL            53
   52 // RSSI difference threshold to detect working channel
   53 #define PEAK_RSSI_THRESHOLD     25
   54 #endif
   55 
   56 // Define to enable example authorization of pre-bondig. See code for details.
   57 //#define   AUTHORIZE_PRE_BONDING
   58 
   59 #if defined( AUTHORIZE_PRE_BONDING ) && !defined( COORDINATOR_CUSTOM_HANDLER )
   60 // Initialize PIN for pre-bonding authorization example
   61 #pragma cdata[ __EESTART + PERIPHERAL_EEPROM_START ] = 0x78, 0x56, 0x34, 0x12
   62 #endif
   63 
   64 //############################################################################################
   65 
   66 // For all type of devices the code implements custom peripheral (PNUM=0x20, PCMD=0x00) with PDATA containing TStartBonding.
   67 // The command is executed at [N] only when it is broadcast and the device does not have a temporary address.
   68 // The command actually sends the peer2peer packet to all not yet bonded nodes to let them know that the bonding interval starts.
   69 // The packet is send from all nodes at isolated slots. The slots are identified by nodes' VRNs.
   70 
   71 // [N] device implements also custom bonding routine done inside Reset event.
   72 // First the node scans all RF channels from the specified range and tries to detect working channel. At TR-7x modules it tries
   73 // to detect RSSI peek of pattern up-peek-down. Whet such a peek is detected it tries to receive TStartBonding peer2peer
   74 // packet being sent by the previously described user peripheral command.
   75 // When the packet is received then the node uses LBT (listen before talk) to find the RF gap to request bonding. If node is then bonded,
   76 // it goes into main DPA loop. If node is not bonded it waits for double plus some random time and it tries bonding again.
   77 
   78 // Start bonding packet, it is sent both to the Peripheral and then as LP peer2peer to not yet bonded nodes
   79 // In real application the packet might store more useful information and it also should be somehow protected against potential misuse
   80 typedef struct
   81 {
   82   // Header, equals to TStartBondingHeader
   83   uns8  Header;
   84   // Total/remaining bonding interval time, 10 ms units
   85   uns16 BondingTime;
   86   // Total/remaining TStartBonding packet slots before bonding interval really starts
   87   uns8  Slots;
   88 } TStartBonding;
   89 
   90 /// TStartBondingHeader packet header
   91 #define TStartBondingHeader 'B'
   92 
   93 // Packet at DPA memory
   94 TStartBonding StartBondingDpa @ _DpaMessage.Request.PData;
   95 // Packet at bufferRF (BTW same as above]
   96 TStartBonding StartBondingRF @ bufferRF;
   97 
   98 // Length of the LP start bonding packet equals to the slot length (10 ms unit)
   99 #define SLOT_LENGTH MIN_LP_TIMESLOT
  100 
  101 #if !defined( COORDINATOR_CUSTOM_HANDLER )
  102 
  103 // Channel filter
  104 #define RXFLT DpaApiReadConfigByte( CFGIND_RXFILTER )
  105 
  106 // Random value
  107 static uns8 rand;
  108 // Generates next random value
  109 uns8 Rand();
  110 
  111 #endif // #if !defined( COORDINATOR_CUSTOM_HANDLER )
  112 
  113 // Sets RF mode for scanning channels
  114 void SetScanRfMode();
  115 // Disables used interrupts
  116 void DisableInterrupts();
  117 
  118 // Macros to atomically access Timeout variable, needed only at STD, because the variable is modified by interrupt then
  119 #ifndef DPA_LP
  120 #define GIEoff()  GIE = FALSE
  121 #define GIEon()   GIE = TRUE
  122 #else
  123 #define GIEoff()  do{}while(0)
  124 #define GIEon()   do{}while(0)
  125 #endif
  126 
  127 #ifdef DPA_LP
  128 // Ticks summed by calling myCaptureTicks()
  129 uns16 SumTicks;
  130 
  131 void myCaptureTicks();
  132 
  133 #ifdef __CC5XFREE__
  134 // Same as var1 -= var2 but even at free CC5X compiler version it is compiled nice way and sets the Carry correctly
  135 #define Minus16vars(var1,var2) \
  136   var1.low8 -= (uns8)( var2 & 0xFF ); \
  137   W = (uns8)( var2 >> 8 ); \
  138   var1.high8 = subWFB( var1.high8 );
  139 #else
  140 #define Minus16vars(var1,var2) var1 -= var2
  141 #endif
  142 
  143 #endif
  144 
  145 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  146 //############################################################################################
  147 bit CustomDpaHandler()
  148 //############################################################################################
  149 {
  150   // Handler presence mark
  151   clrwdt();
  152 
  153 #if !defined( COORDINATOR_CUSTOM_HANDLER )
  154   // Copy of start binding DPA packet later used to be sent as LP peer2peer
  155   static TStartBonding StartBondingValues;
  156   // To access use GIE=0, because it can be changed by ISR (valid for STD mode only)
  157   static uns16 Timeout;
  158 
  159 #ifndef DPA_LP
  160   // General timeout variable controlled by TMR6 interrupt, PreScallerInit variable used to count in 10 ms (1) or 100 ms (10)
  161   // Prescaller variables used to count Timeout variable in 10 ms (PreScallerInit value is 1) or in 100 ms (PreScallerInit value is 10)
  162   static uns8 PreScaller, PreScallerInit;
  163   // 10 ms tick used to indicate by LEDs
  164   static uns8 Tick10ms;
  165 #endif // #ifndef DPA_LP
  166 
  167 #endif // #if !defined( COORDINATOR_CUSTOM_HANDLER )
  168 
  169   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
  170   switch ( GetDpaEvent() )
  171   {
  172 #if !defined( COORDINATOR_CUSTOM_HANDLER )
  173     // -------------------------------------------------
  174     case DpaEvent_Interrupt:
  175       // Do an extra quick background interrupt work
  176       // ! 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.
  177       // ! 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.
  178       // ! 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.
  179       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  180       // ! Make sure race condition does not occur when accessing those variables at other places.
  181       // ! 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.
  182       // ! Do not call any OS functions except setINDFx().
  183       // ! Do not use any OS variables especially for writing access.
  184       // ! 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.
  185 
  186 #ifndef DPA_LP
  187       //  If 10 ms TMR6 interrupt occurred
  188       if ( TMR6IF )
  189       {
  190         // Unmask interrupt
  191         TMR6IF = 0;
  192         // Increase ticks
  193         Tick10ms++;
  194 
  195         // Prescaler is over?
  196         if ( --PreScaller == 0 )
  197         {
  198           // Yes, initialize it (PreScallerInit is 1 or 10)
  199           PreScaller = PreScallerInit;
  200           // Timeout is over?
  201           if ( Timeout != 0 )
  202             // No, decrement timeout counter (10 or 100 ms unit)
  203             Timeout--;
  204         }
  205       }
  206 #endif // #ifndef DPA_LP
  207 
  208       return TRUE;
  209 
  210       // -------------------------------------------------
  211     case DpaEvent_Idle:
  212       // Do a quick background work when RF packet is not received
  213 
  214       // Node did not get the real address for the long time?
  215       if ( ntwADDR == TEMPORARY_ADDRESS )
  216       {
  217 #ifndef DPA_LP
  218         GIEoff();
  219         if ( Timeout == 0 )
  220 #else
  221         captureTicks();
  222         // Decrease the timeout
  223         // To save the code we divide by 8 not by 10, so the time will go a little bit faster, but in this case it is not so important because sleeping inside RFRX is inaccurate anyway
  224         param4 += 4; // Rounding for /8
  225         param4 /= 8;
  226         Minus16vars( Timeout, param4 );
  227         // Overflow? Time is over!
  228         if ( !Carry )
  229 #endif  // #ifndef DPA_LP
  230         {
  231 #ifndef DPA_LP
  232           // Disable user interrupts
  233           DisableInterrupts();
  234 #endif
  235           // Unbond Node
  236           removeBond();
  237           // And restart device to bond node again
  238           _PNUM = PNUM_OS;
  239           _PCMD = CMD_OS_RESTART;
  240           _DpaDataLength = 0;
  241           DpaApiLocalRequest();
  242         }
  243         GIEon();
  244       }
  245 
  246       break;
  247 
  248       // -------------------------------------------------
  249     case DpaEvent_Reset:
  250       // Called after module is reset to allow (un)bonding
  251 
  252       // Optional: Allow LED indication e.g. during discovery
  253       _systemLEDindication = TRUE;
  254 
  255 #ifndef DPA_LP
  256       // When run fist time, do some global initializations
  257       static bit wasFirstReset;
  258       if ( !wasFirstReset )
  259       {
  260         // Already called once
  261         wasFirstReset = TRUE;
  262 
  263         // Set Timeout prescaler to 10 ms unit
  264         PreScaller = PreScallerInit = 1;
  265 
  266         // Setup TMR6 to ISR get ticks on the background (ticks every 10 ms)
  267 #if F_OSC == 16000000
  268         PR6 = 250 - 1;
  269         T6CON = 0b0.1001.1.10;  // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  270 #else
  271 #error Unsupported oscillator frequency
  272 #endif
  273       // Enable TMR6 ISR
  274         TMR6IE = TRUE;
  275       }
  276 #endif // #ifndef DPA_LP
  277 
  278       // Is node already bonded?
  279       if ( amIBonded() )
  280       {
  281 #ifndef DPA_LP
  282         // Force unbonding if temporary address was assigned
  283         startCapture();
  284 #endif
  285         // Do DPA default unbonding 
  286         goto DpaHandleReturnFALSE;
  287       }
  288       else
  289       {
  290         // Take care of custom bonding
  291 
  292         // Generate non-zero random number (seed) based on MID
  293         moduleInfo();
  294         // Seed is the lowest MID byte
  295         rand = bufferINFO[0];
  296         // Pseudo random seed must not be zero
  297         if ( rand == 0 )
  298           rand.4 = 1;
  299 
  300         // Set OS bonding count to the random value
  301         bondingCounter = rand;
  302 
  303         // Set RF scanning mode
  304         SetScanRfMode();
  305 
  306         // Loop until not bonded
  307         for ( ;; )
  308         {
  309 #if !defined( DO_NOT_SCAN_CHANNELS )
  310 
  311           // Variables to scan channels and identify RSSI peek (up-peek-down)
  312           // Set too high channel to force channel scanning initialization when the loop starts 1st time
  313           RFchannel = LAST_CHANNEL + 1;
  314           // RSSI of the previous channel
  315           int8 lastRSSI;
  316           // RSSI of the previous-previous channel
  317           int8 veryLastRSSI;
  318 
  319 #endif // #if !defined( DO_NOT_SCAN_CHANNELS )
  320 
  321           // Scan working channel
  322           for ( ;; )
  323           {
  324 #if !defined( DO_NOT_SCAN_CHANNELS )
  325             // Restart channel scanning from the 1st channel?
  326             if ( RFchannel == LAST_CHANNEL + 1 )
  327             {
  328               // Yes!
  329               // Current channel is 1st channel
  330               RFchannel = FIRST_CHANNEL;
  331 
  332               // RSSI of the previous channel, reset to very low value to force next fake RSSI increase
  333               lastRSSI = 0;
  334               veryLastRSSI = 0;
  335 
  336               // Check RFIC
  337               if ( wasRFICrestarted() )
  338                 DpaApiSetRfDefaults();
  339             }
  340 
  341             // Set current channel
  342             setRFchannel( RFchannel );
  343 
  344             // Measure RSSI
  345             checkRF( 90 );
  346             // Read stabilized RSSI
  347             int8 RSSI = getRSSI();
  348 
  349             // Is there a peak RSSI over the threshold?
  350             {
  351               int8 diffRSSI = lastRSSI - RSSI;
  352               if ( diffRSSI >= PEAK_RSSI_THRESHOLD )
  353               {
  354                 int8 diffRSSI = lastRSSI - veryLastRSSI;
  355                 if ( diffRSSI >= PEAK_RSSI_THRESHOLD )
  356                 {
  357                   // Set detected channel
  358                   setRFchannel( RFchannel - 1 );
  359                   // Set normal filter
  360                   checkRF( RXFLT );
  361 
  362 #else
  363             // Check RFIC
  364             if ( wasRFICrestarted() )
  365               DpaApiSetRfDefaults();
  366 
  367 #endif // #if !defined( DO_NOT_SCAN_CHANNELS )
  368             // Timeout to receive LP packet at _RX_STD
  369             toutRF = SLOT_LENGTH;
  370             // Try to receive start bonding packet
  371             if ( RFRXpacket() && DLEN == sizeof( StartBondingRF ) && StartBondingRF.Header == TStartBondingHeader )
  372               // If received then exit the channel scanning loop
  373               break;
  374 #if !defined( DO_NOT_SCAN_CHANNELS )
  375                 }
  376               }
  377             }
  378 
  379       // Shift buffers of (last-)last RSSIs
  380       veryLastRSSI = lastRSSI;
  381       lastRSSI = RSSI;
  382       // Go to the next channel
  383       RFchannel++;
  384 #endif // #if !defined( DO_NOT_SCAN_CHANNELS )
  385 
  386       // Indicate by LEDs
  387 #ifndef DPA_LP
  388       if ( Tick10ms.3 )
  389 #else
  390       captureTicks();
  391       if ( param3.3 )
  392 #endif
  393       {
  394         _LEDG = 1;
  395         _LEDR = 0;
  396       }
  397       else
  398       {
  399         _LEDG = 0;
  400         _LEDR = 1;
  401       }
  402           }
  403 
  404   // Start bonding LP packet was received
  405   _LEDG = _LEDR = 1;
  406 
  407   // Start with the shortest gap between bonding attempts
  408   uns8 BondingGapLength = 1;
  409 
  410   // Set timeout to the remaining length of the bonding interval
  411   GIEoff();
  412   Timeout = StartBondingRF.BondingTime;
  413   GIEon();
  414 
  415 #ifdef DPA_LP
  416   // Start measuring time at LP mode
  417   startCapture();
  418   SumTicks = 0;
  419 #endif // #ifdef DPA_LP
  420   // But first actively wait the remaining slots
  421   userReg0 = SLOT_LENGTH;
  422   do
  423   {
  424     clrwdt();
  425     waitDelay( StartBondingRF.Slots );
  426   } while ( --userReg0 != 0 );
  427 
  428   // And now wait some random time so nodes will not be synchronized when doing LBT
  429   waitDelay( Rand() / 2 );
  430 
  431   // Loop till the bonding interval is valid
  432   for ( ;; )
  433   {
  434     // Indicate
  435     _LEDG = 0;
  436     // Do LTB (listen before talk) for 400 x ( 1ms + checkRF )
  437     uns16 loop = 400;
  438     do
  439     {
  440       // Indicate
  441 #ifndef DPA_LP
  442       _LEDR = Tick10ms.3;
  443 #else
  444       myCaptureTicks();
  445       _LEDR = param3.3;
  446 #endif
  447       Rand();
  448       waitMS( 1 );
  449 
  450       // Bonding interval is over?
  451 #ifndef DPA_LP
  452       GIEoff();
  453       if ( Timeout == 0 )
  454       {
  455         GIEon();
  456         goto ExitBondingLoop;
  457       }
  458       GIEon();
  459 #else
  460       myCaptureTicks();
  461       // Decrease the timeout
  462       Minus16vars( Timeout, SumTicks );
  463       // Overflow? Time is over!
  464       if ( !Carry )
  465         goto ExitBondingLoop;
  466 
  467       SumTicks = 0;
  468 #endif // #ifndef DPA_LP
  469 
  470     } while ( !checkRF( RXFLT ) && --loop != 0 );
  471 
  472     // Indicate
  473     _LEDR = 0;
  474 
  475     // RF was quiet?
  476     if ( loop == 0 )
  477     {
  478       // Indicate
  479       pulseLEDR();
  480 
  481       // Set node mode and network filtering
  482       setNetworkFilteringOn();
  483       setNodeMode();
  484 
  485 #if defined( AUTHORIZE_PRE_BONDING )
  486       // In this example first 4 bytes of EEPROM peripheral must be pre-filled with 32bit PIN code 
  487       // The PIN code will be checked at the node or coordinator that provides pre-bonding
  488       eeReadData( PERIPHERAL_EEPROM_START, sizeof( UserBondingData ) );
  489       setFSR0( _FSR_INFO );
  490       copyMemoryBlock( FSR0, UserBondingData, sizeof( UserBondingData ) );
  491 #endif // #if defined( AUTHORIZE_PRE_BONDING )
  492 
  493       // Execute bonding
  494 #ifndef DPA_LP
  495               // To keep captureTicks() the most precise as possible
  496       waitNewTick();
  497 #endif // #ifndef DPA_LP
  498 
  499       // Are we bonded?
  500       if ( bondRequestAdvanced() )
  501       {
  502         // Bonded!
  503         // Set DPA API variable
  504         NodeWasBonded = TRUE;
  505 #ifndef DPA_LP
  506         // Now timeout will measure at 100 ms
  507         PreScallerInit = 10;
  508         GIEoff();
  509 #endif // #ifndef DPA_LP
  510         // Time to wait for getting real address
  511         Timeout.low8 = UserBondingData[0];
  512         Timeout.high8 = UserBondingData[1];
  513         GIEon();
  514 
  515 #if !defined( DO_NOT_SCAN_CHANNELS )
  516         // Write working channel to the configuration
  517         _PNUM = PNUM_OS;
  518         _PCMD = CMD_OS_WRITE_CFG_BYTE;
  519         _DpaDataLength = sizeof( TPerOSWriteCfgByteTriplet );
  520         _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Value = RFchannel;
  521         _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Address = CFGIND_CHANNEL_A;
  522         _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Mask = 0xFF;
  523         DpaApiLocalRequest();
  524 
  525 #endif // #if !defined( DO_NOT_SCAN_CHANNELS )
  526         // Go to the DPA main loop
  527         DpaApiSetRfDefaults();
  528         goto DpaHandleReturnTRUE;
  529       }
  530 
  531       // Decrease bonding counter back
  532       bondingCounter--;
  533 
  534       SetScanRfMode();
  535     }
  536 
  537     // Increase bonding count by incommensurable value with 256
  538     bondingCounter += 65;
  539 
  540     // Wait some random time proportional to the gap
  541     uns16 bondDelay = (uns16)BondingGapLength * 2;
  542     userReg0 = BondingGapLength | 0b11.1111;
  543     startLongDelay( bondDelay + ( Rand() & userReg0 ) );
  544     do
  545     {
  546       // Indicate
  547 #ifndef DPA_LP
  548       _LEDG = Tick10ms.3;
  549 #else
  550       myCaptureTicks();
  551       _LEDG = param3.3;
  552       clrwdt();
  553 #endif
  554     } while ( isDelay() );
  555 
  556     // Next gap size will be doubled (2^n-1)
  557     Carry = 1;
  558     BondingGapLength = rl( BondingGapLength );
  559   }
  560 ExitBondingLoop:
  561         }
  562       }
  563       break;
  564 
  565       // -------------------------------------------------
  566     case DpaEvent_AfterRouting:
  567       // Called after Notification and after routing of the DPA response was finished
  568 
  569       // Request to send peer2peer LP start bonding packet?
  570       if ( StartBondingValues.BondingTime != 0 )
  571       {
  572         // Wait number of slots equal to the VRN
  573         uns8 loop @ userReg0;
  574         loop = ntwVRN;
  575         do
  576         {
  577           // Decrease number of remaining slots
  578           StartBondingValues.Slots--;
  579           // Decrease remaining bonding interval time
  580           StartBondingValues.BondingTime -= SLOT_LENGTH;
  581           waitDelay( SLOT_LENGTH );
  582           clrwdt();
  583         } while ( --userReg0 != 0 );
  584 
  585         // Now send peer2peer packet
  586         // Set RF to peer2peer packet
  587         SetScanRfMode();
  588         // Prepare packet to bufferRF
  589         StartBondingRF.Header = TStartBondingHeader;
  590         StartBondingRF.BondingTime = StartBondingValues.BondingTime;
  591         StartBondingRF.Slots = StartBondingValues.Slots;
  592         // Send the packet
  593         DLEN = sizeof( StartBondingRF );
  594         PIN = 0;
  595         RFTXpacket();
  596         // Restore RF mode
  597         DpaApiSetRfDefaults();
  598         // and filtering
  599         setNetworkFilteringOn();
  600         // and node mode
  601         setNodeMode();
  602         // Peer2peer packet was already sent
  603         StartBondingValues.BondingTime = 0;
  604       }
  605 
  606       break;
  607 
  608 #ifndef DPA_LP
  609       // -------------------------------------------------
  610     case DpaEvent_AfterSleep:
  611       // Called after woken up after sleep
  612 
  613       // Use TMR6 again
  614       TMR6IE = TRUE;
  615       TMR6ON = TRUE;
  616       break;
  617 
  618       // -------------------------------------------------
  619     case DpaEvent_BeforeSleep:
  620       // Called before going to sleep
  621       // Fall through!
  622 
  623       // -------------------------------------------------
  624     case DpaEvent_DisableInterrupts:
  625       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM)
  626 
  627       // Do not need to use TMR6 any more
  628       DisableInterrupts();
  629       break;
  630 #endif // #ifndef DPA_LP
  631 
  632 #endif // #if !defined( COORDINATOR_CUSTOM_HANDLER )
  633 
  634 #if defined( AUTHORIZE_PRE_BONDING )
  635       // -------------------------------------------------
  636     case DpaEvent_AuthorizePreBonding:
  637       // Called when remote bonding is enabled and a node requests pre-bonding
  638 
  639       // 32 bit PIN that has to be matched is stored at first 4 bytes of peripheral RAM
  640       // PIN must be same as the PIN sent from the requesting NODE. Requesting node PIN is stored at its EEPROM peripheral.
  641       if ( UserBondingData[0] != PeripheralRam[0] || PeripheralRam[1] != UserBondingData[1] || UserBondingData[2] != PeripheralRam[2] || PeripheralRam[3] != UserBondingData[3] )
  642         // If PINs do not match, reject the pre-bonding request
  643         goto DpaHandleReturnTRUE;
  644 
  645       break;
  646 #endif // #if defined( AUTHORIZE_PRE_BONDING )
  647 
  648       // -------------------------------------------------
  649     case DpaEvent_DpaRequest:
  650       // Called to interpret DPA request for peripherals
  651       // -------------------------------------------------
  652       // Peripheral enumeration
  653       if ( IsDpaEnumPeripheralsRequest() )
  654       {
  655         // We implement one user peripheral
  656         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  657         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  658         // We implement a HW Profile
  659         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  660 
  661 DpaHandleReturnTRUE:
  662         return TRUE;
  663       }
  664       // -------------------------------------------------
  665       // Get information about peripheral
  666       else if ( IsDpaPeripheralInfoRequest() )
  667       {
  668         // We handle one user peripheral
  669         if ( _PNUM == PNUM_USER + 0 )
  670         {
  671           // It is user type peripheral
  672           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  673           // And write only style
  674           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_WRITE;
  675           goto DpaHandleReturnTRUE;
  676         }
  677 
  678         break;
  679       }
  680       // -------------------------------------------------
  681       else
  682       {
  683         // Handle broadcast only peripheral command
  684         if (
  685           // My peripheral
  686           _PNUM == PNUM_USER + 0 &&
  687           // My command
  688           _PCMD == 0 &&
  689           // Correct data length
  690           _DpaDataLength == sizeof( StartBondingDpa )
  691 #if !defined( DO_NOT_SCAN_CHANNELS ) && !defined( COORDINATOR_CUSTOM_HANDLER )
  692           // Broadcast
  693           && _NADR == BROADCAST_ADDRESS &&
  694           // And I do not have temporary address (i.e. no VRN)
  695           ntwADDR != TEMPORARY_ADDRESS
  696 #endif // #if !defined( DO_NOT_SCAN_CHANNELS ) && !defined( COORDINATOR_CUSTOM_HANDLER )
  697           )
  698         {
  699 #if !defined(  COORDINATOR_CUSTOM_HANDLER )
  700           // [N] version
  701 
  702           // Store peer2peer start bonding packet values
  703           StartBondingValues.BondingTime = StartBondingDpa.BondingTime;
  704           StartBondingValues.Slots = StartBondingDpa.Slots;
  705 #else // #if !defined(  COORDINATOR_CUSTOM_HANDLER )
  706           // [C] version
  707 
  708           // Now send peer2peer packet
  709           // Set RF to peer2peer packet
  710           SetScanRfMode();
  711           // Prepare packet to bufferRF
  712           // ! StartBondingRF == StartBondingDpa !
  713           StartBondingRF.Header = TStartBondingHeader;
  714           // Send the packet
  715           DLEN = sizeof( StartBondingRF );
  716           PIN = 0;
  717           RFTXpacket();
  718           // Restore RF mode
  719           DpaApiSetRfDefaults();
  720           // and filtering
  721           setNetworkFilteringOn();
  722           // and coordinator mode
  723           setCoordinatorMode();
  724 #endif // #if !defined(  COORDINATOR_CUSTOM_HANDLER )
  725 
  726           goto DpaHandleReturnTRUE;
  727         }
  728         else
  729           DpaApiReturnPeripheralError( ERROR_FAIL );
  730       }
  731   }
  732 
  733 DpaHandleReturnFALSE:
  734   return FALSE;
  735 }
  736 
  737 #if !defined( COORDINATOR_CUSTOM_HANDLER )
  738 //############################################################################################
  739 uns8 Rand()
  740 //############################################################################################
  741 {
  742   rand = lsr( rand );
  743 #pragma updateBank 0 /* OFF */  // Optimization (avoid duplicate bank setting for RandomValue access)
  744   W = 0b10111000;   // x^8 + x^6 + x^5 + x^4 + 1
  745   if ( Carry )
  746     rand ^= W;
  747 
  748   return rand;
  749 #pragma updateBank 1 /* ON */
  750 }
  751 
  752 #ifndef DPA_LP
  753 //############################################################################################
  754 void DisableInterrupts()
  755 //############################################################################################
  756 {
  757   TMR6ON = FALSE;
  758   TMR6IE = FALSE;
  759 }
  760 #endif // #ifndef DPA_LP
  761 #endif // #if !defined( COORDINATOR_CUSTOM_HANDLER )
  762 
  763 
  764 #ifdef DPA_LP
  765 //############################################################################################
  766 void myCaptureTicks()
  767 //############################################################################################
  768 {
  769   captureTicks();
  770   SumTicks += param4;
  771 }
  772 #endif
  773 
  774 //############################################################################################
  775 void SetScanRfMode()
  776 //############################################################################################
  777 {
  778   setNonetMode();
  779   setNetworkFilteringOff();
  780   setRFmode( _WPE | _RX_STD | _TX_LP );
  781 }
  782 
  783 //############################################################################################
  784 // 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) 
  785 #include "DPAcustomHandler.h"
  786 //############################################################################################