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.74 $
    8 // Date:    $Date: 2018/10/25 09:51:28 $
    9 //
   10 // Revision history:
   11 //   2018/10/25  Release for DPA 3.03
   12 //   2017/11/16  Release for DPA 3.02
   13 //   2017/03/13  Release for DPA 3.00
   14 //   2016/09/12  Release for DPA 2.28
   15 //   2016/02/10  Release for DPA 2.26
   16 //   2015/09/03  Release for DPA 2.21
   17 //   2015/08/05  Release for DPA 2.20
   18 //
   19 // *********************************************************************
   20 
   21 //############################################################################################
   22 // Description
   23 
   24 // ! IMPORTANT - Please note the Autonetwork process is complex. Before using it in your production environment please understand 
   25 // the process in detail and do thorough test in the lab.
   26 // Please also read on-line DPA documentation http://www.iqrf.org/DpaTechGuide/ (chapter "8.6.2 AutoNetwork & Coordinator-AutoNetwork-Embedded").
   27 
   28 // This example implements automatic network construction and optional switching between autonetwork and user handler.
   29 // Define SWAP_HANDLERS to use automatic handler switching.
   30 // It allows to use the benefits of automatic network construction and save the program memory for user specific functions.
   31 // It works with corresponding AutoNetwork application build with IQRF SDK or CustomDpaHandler-Coordinator-AutoNetwork-Embedded.c.
   32 // The same code is for [N] devices and [C] (use COORDINATOR_CUSTOM_HANDLER symbol to allow conditional compilation for [C] device).
   33 // The handler for [C] devices is used in case the autonetwork is controlled by external device connected to the [C].
   34 // Another example CustomDpaHandler-Coordinator-AutoNetwork-Embedded.c demonstrates self-containing [C] handler that controls the whole Autonetwork process.
   35 
   36 // For all type of devices the code implements custom peripheral (PNUM=0x20, PCMD=0x00) with PDATA containing TStartBonding.
   37 // The command is executed at [N] only when it is broadcast and the device does not have a temporary address.
   38 // The command actually sends the peer2peer packet to all not yet bonded nodes to let them know that the bonding interval starts.
   39 // The packet is send from all nodes at isolated slots. The slots are identified by nodes' VRNs.
   40 
   41 // [N] device implements also custom bonding routine done inside Reset event.
   42 // First the node tries to receive 1st TStartBonding packet. Then it tries to receive more TStartBonding packets from other Nodes or Coordinator.
   43 // Node remembers senders of TStartBonding packets. Later Node will try to pre-bond to these senders only.
   44 // After all TStartBonding packets were sent then the Node uses LBT (listen before talk) to find the RF gap to request bonding. If node is then bonded,
   45 // it goes into main DPA loop. If node is not bonded it waits for double plus some random time and it tries bonding again.
   46 
   47 // Node LED Indication
   48 // * no LED => Node is bonded
   49 // * RED : before a bonding phase
   50 //   * RED => trying to receive 1st TStartBonding packet
   51 //   * RED + pulsing GREEN => receiving next TStartBonding packets, then bonding phase starts
   52 // * GREEN : bonding phase
   53 //   * GREEN + pulsing RED => ListenBeforeTalk (LBT) before trying to bond
   54 //   * GREEN => bonding and if not bonded random time delay before next LBT
   55 
   56 // When SWAP_HANDLERS is defined (applies only to the Node version, of course):
   57 //   * If the Node is bonded and assigned with real address, user handler is loaded.
   58 //   * If the Node is unbonded, autonetwork handler is loaded.
   59 // Some part (code and variables) of autonetwork handler must be implemented at user handler too.
   60 // All parts of user handler which are not used for autonetwork handler must be compiled conditionally.
   61 // Example - User handler requires DpaEvent_Init event implementation.
   62 // The DpaEvent_Init is compiled for user handler only, it is not used for autonetwork handler:
   63 // #ifdef USER_HANDLER
   64 //   case DpaEvent_Init:
   65      // Do a one time initialization work before main loop starts
   66      // User handler code
   67 //   break;
   68 // #endif // #ifndef USER_HANDLER
   69 // Use find function to quick find following marks:
   70 //  [User Handler] - Implement your code here.
   71 //  [Mandatory part] - Must be implemented at the user handler and/or the autonetwork handler in order to support handler swapping.
   72 //  [Customizable part] - The part could be customized (for example an error indication).
   73 // The example expects that both autonetwork and user handler of the example are preloaded at the external EEPROM in the way they can be loaded by CMD_OS_LOAD_CODE command.
   74 // Also, the example expects that there are parameters for CMD_OS_LOAD_CODE for loading the handlers stored at the external EEPROM too.
   75 // Parameters for the "autonetwork" handler must be stored at address 0 and for the "user" handler at the address 6 respectively.
   76 // Each set of parameters takes 6 bytes. Parameters have the following format (see https://www.iqrf.org/DpaTechGuide/#3.4.13%20LoadCode for details):
   77 // offset   name          value
   78 // 0        addrLow       lower byte of the corresponding driver .hex image in the external EEPROM
   79 // 1        addrHigh      same as above but higher byte
   80 // 2        lengthLow     lower byte of the corresponding driver .hex image length in the external EEPROM
   81 // 3        lengthHigh    same as above but higher byte
   82 // 4        checksumLow   lower byte of the corresponding driver .hex image checksum in the external EEPROM
   83 // 5        checksumHigh  same as above but higher byte
   84 // IQRF IDE Tools / IQRF Network Manager / Control / Upload can be used to upload both .hex images into the external EEPROM.
   85 // Then Terminal in DPA mode is used to write LoadCode parameters into external EEPROM too.
   86 
   87 //############################################################################################
   88 // Global Autonetwork declarations and definitions
   89 
   90 // Start bonding packet, it is sent both to the Peripheral and then as peer2peer to not yet bonded nodes
   91 // In real application the packet might store more useful information and it also should be somehow protected against potential misuse
   92 typedef struct
   93 {
   94   // Header, equals TStartBondingHeader
   95   uns8  Header;
   96   // Remaining invitation time, 10 ms units
   97   uns16 RemainingInvitationTime;
   98   // Total bonding interval time, 10 ms units
   99   uns16 BondingTime;
  100   // Temporary address timeout in 100 ms units
  101   uns16 TemporaryAddressTimeout;
  102   // Network channel
  103   uns8  Channel;
  104   // Sender address
  105   uns8  Sender;
  106 } TStartBonding;
  107 
  108 // TStartBondingHeader packet header
  109 #define TStartBondingHeader 'B'
  110 
  111 // Length of the start bonding packet TStartBonding equals the slot length (10 ms unit)
  112 #define SLOT_LENGTH MIN_STD_TIMESLOT
  113 
  114 //############################################################################################
  115 // Not used just as a header file?
  116 #ifndef CustomDpaHandler_AutoNetwork_HEADER
  117 
  118 #warning "This Autonetwork is obsolete. Use CustomDpaHandler-Coordinator-AutoNetworkV2-Embedded.c at Coordinator instead and no Custom DPA Handler at Nodes."
  119 
  120 //############################################################################################
  121 // Headers
  122 
  123 // Default IQRF include (modify the path according to your setup)
  124 #include "IQRF.h"
  125 // Default DPA header (modify the path according to your setup)
  126 #include "DPA.h"
  127 // Default Custom DPA Handler header (modify the path according to your setup)
  128 #include "DPAcustomHandler.h"
  129 
  130 //############################################################################################
  131 // Conditionals
  132 
  133 // Define the next symbol to let the [N] variant work at LP mode. 
  134 // Then TMR6 is not used for timing during bonding and when node has temporary address but less precise captureTicks() IQRF OS function.
  135 // The reason is the WDT sleeps during RF communication.
  136 //#define   DPA_LP
  137 #ifdef DPA_LP
  138 #message "CustomDpaHandler-AutoNetwork.c" compiled for LP mode
  139 #else
  140 #message "CustomDpaHandler-AutoNetwork.c" compiled for STD mode
  141 #endif
  142 
  143 // If compiled for coordinator, then no handler swapping is possible
  144 #ifndef COORDINATOR_CUSTOM_HANDLER
  145 
  146 // Define SWAP_HANDLERS to allow autonetwork and user handler switching
  147 //#define SWAP_HANDLERS
  148 
  149 #ifdef SWAP_HANDLERS
  150 
  151 // Define USER_HANDLER to compile swappable user handler. Otherwise swappable autonetwork handler is compiled.
  152 //#define USER_HANDLER
  153 
  154 #ifdef USER_HANDLER
  155 #message "Swap version - User handler for Node"
  156 #else
  157 #message "Swap version - AutoNetwork handler for Node"
  158 #endif
  159 
  160 #else // #ifdef SWAP_HANDLERS
  161 #message "AutoNetwork handler"
  162 #endif // #ifdef SWAP_HANDLERS
  163 
  164 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  165 
  166 // Define to enable example authorization of pre-bondig. See code for details.
  167 //#define   AUTHORIZE_PRE_BONDING
  168 
  169 //############################################################################################
  170 // Declarations, Definitions
  171 
  172 // Packet at DPA memory
  173 TStartBonding StartBondingDpa @ _DpaMessage.Request.PData;
  174 // Packet at bufferRF (BTW same as above]
  175 TStartBonding StartBondingRF @ bufferRF;
  176 
  177 #ifndef COORDINATOR_CUSTOM_HANDLER
  178 // Copy of start binding DPA packet later used to be sent as peer2peer
  179 TStartBonding StartBondingBackup @ PeripheralRam;
  180 // Bitmap of the inviting Nodes + Coordinator
  181 uns8 InvitingNodes[( MAX_ADDRESS + 1 + 7 ) / 8] @ PeripheralRam[sizeof( TStartBonding )];
  182 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  183 
  184 #if !defined( USER_HANDLER ) && !defined( COORDINATOR_CUSTOM_HANDLER )
  185 // Random value
  186 static uns8 rand;
  187 // Generates next random value and does clrwdt()
  188 uns8 Rand();
  189 #endif // #if !defined( USER_HANDLER ) && !defined( COORDINATOR_CUSTOM_HANDLER )
  190 
  191 // Sets RF mode for scanning channels
  192 void SetScanRfMode();
  193 // Disables used interrupts
  194 void DisableInterrupts();
  195 // Loads DPA handler from ext. EEPROM to Flash
  196 uns8 LoadHandler( uns16 address @ param3 );
  197 // Makes copy of bonding packet
  198 void BackupBondingPacket();
  199 // Compute FSR0 and bitmapBitMask at InvitingNodes
  200 void BitmapNode( uns8 address );
  201 // Runs local command at OS peripheral
  202 void DpaApiLocalRequest_OS( uns8 cmd @ W );
  203 
  204 // Macros to atomically access Timeout variable
  205 #define GIEoff()  GIE = FALSE
  206 #define GIEon()   GIE = TRUE
  207 
  208 #ifdef SWAP_HANDLERS
  209 // External EEPROM address of both swappable autonetwork versions description records (address, length, checksum), see structure TPerOSLoadCode_Request without Flags field
  210 #define EEE_ADDR_USER_HANDLER               0
  211 // 2nd record is adjacent to the 1st one
  212 #define EEE_ADDR_AUTONETWORK_HANDLER        ( EEE_ADDR_USER_HANDLER + sizeof( DpaRfMessage.PerOSLoadCode_Request ) - sizeof( DpaRfMessage.PerOSLoadCode_Request.Flags ) )
  213 #endif // #ifdef SWAP_HANDLERS
  214 
  215 #ifdef DPA_LP
  216 // Ticks summed after call captureTicks()
  217 uns16 SumTicks;
  218 
  219 #ifdef __CC5XFREE__
  220 // Same as var1 -= var2 but even at free CC5X compiler version it is compiled nice way and sets the Carry correctly
  221 #define Minus16vars(var1,var2) \
  222   var1.low8 -= (uns8)( var2 & 0xFF ); \
  223   W = (uns8)( var2 >> 8 ); \
  224   var1.high8 = subWFB( var1.high8 );
  225 #else // #ifdef __CC5XFREE__
  226 #define Minus16vars(var1,var2) var1 -= var2
  227 #endif // #ifdef __CC5XFREE__
  228 
  229 #endif // #ifdef DPA_LP
  230 
  231 #if defined( AUTHORIZE_PRE_BONDING ) && !defined( COORDINATOR_CUSTOM_HANDLER )
  232 // Initialize PIN for pre-bonding authorization example
  233 #pragma cdata[ __EESTART + PERIPHERAL_EEPROM_START ] = 0x78, 0x56, 0x34, 0x12
  234 #endif
  235 
  236 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  237 //############################################################################################
  238 bit CustomDpaHandler()
  239 //############################################################################################
  240 {
  241   // Handler presence mark
  242   clrwdt();
  243 
  244 #ifndef COORDINATOR_CUSTOM_HANDLER
  245 #ifndef USER_HANDLER
  246   // To access use GIE=0, because it can be changed by ISR (valid for STD mode only)
  247   // General timeout variable controlled by TMR6 interrupt, PreScallerInit variable used to count in 10 ms (1) or 100 ms (10)
  248   static uns16 Timeout;
  249   // Prescaler variables used to count Timeout variable in 10 ms (PreScallerInit value is 1) or in 100 ms (PreScallerInit value is 10)
  250   static uns8 PreScaller;
  251   static uns8 PreScallerInit;
  252 #ifdef DPA_LP
  253   // TMR6 is used at LP mode to measure time when TStartBonding packets are send by existing network Nodes and the Coordinator
  254   static bit UseTMRinLP;
  255 #endif // #ifdef DPA_LP
  256 #endif // #ifndef USER_HANDLER
  257 
  258 #if !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  259   // TRUE when an invitation to start bonding will be send later by already bonded Node
  260   static bit SendInvitation;
  261 #endif // !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  262 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  263 
  264 #ifdef SWAP_HANDLERS
  265   // [Mandatory part] - the variable HandlerNotPresent must be defined at both handlers if SWAP_HANDLERS is defined
  266   static bit HandlerNotPresent;
  267 #endif // #ifdef SWAP_HANDLERS
  268 
  269   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
  270   switch ( GetDpaEvent() )
  271   {
  272 #ifndef COORDINATOR_CUSTOM_HANDLER
  273     // -------------------------------------------------
  274     case DpaEvent_Interrupt:
  275       // Do an extra quick background interrupt work
  276       // ! 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.
  277       // ! 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.
  278       // ! 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.
  279       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  280       // ! Make sure race condition does not occur when accessing those variables at other places.
  281       // ! 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.
  282       // ! Do not call any OS functions except setINDFx().
  283       // ! Do not use any OS variables especially for writing access.
  284       // ! 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.
  285 
  286 #ifndef USER_HANDLER
  287       // Autonetwork handler code
  288       // If 10 ms TMR6 interrupt occurred
  289       if ( TMR6IF )
  290       {
  291         // Unmask interrupt
  292         TMR6IF = 0;
  293 
  294         // Prescaler is over?
  295         if ( --PreScaller == 0 )
  296         {
  297           // Yes, initialize it (PreScallerInit is 1 or 10)
  298           PreScaller = PreScallerInit;
  299           // Timeout is over?
  300           if ( Timeout != 0 )
  301 #ifdef DPA_LP
  302             if ( UseTMRinLP )
  303 #endif // #ifdef DPA_LP
  304               // No, decrement timeout counter (10 or 100 ms unit)
  305               Timeout--;
  306         }
  307       }
  308 #else // #ifndef USER_HANDLER
  309       // [User Handler] - User handler DpaEvent_Interrupt implementation
  310 
  311 #endif // #ifndef USER_HANDLER
  312       return Carry;
  313 
  314       // -------------------------------------------------
  315     case DpaEvent_Idle:
  316       // Do a quick background work when RF packet is not received
  317 #ifndef USER_HANDLER
  318       // Node did not get the real address (i.e. was not authorized) for the long time?
  319       if ( ntwADDR == TEMPORARY_ADDRESS )
  320       {
  321 #ifndef DPA_LP
  322         GIEoff();
  323         if ( Timeout == 0 )
  324 #else // #ifndef DPA_LP
  325         captureTicks();
  326         // Decrease the timeout
  327         // 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
  328         param4 += 4; // Rounding for /8
  329         param4 /= 8;
  330         Minus16vars( Timeout, param4 );
  331         // Overflow? Time is over!
  332         if ( !Carry )
  333 #endif  // #ifndef DPA_LP
  334         {
  335           // Disable user interrupts
  336           DisableInterrupts();
  337           // Unbond Node by IQRF OS call
  338           removeBond();
  339           // And restart device to bond node again
  340           _DpaDataLength = 0;
  341           DpaApiLocalRequest_OS( CMD_OS_RESTART );
  342         }
  343 #ifndef DPA_LP
  344         GIEon();
  345 #endif  // #ifndef DPA_LP
  346       }
  347       // [Mandatory part] - must be implemented at autonetwork handler if SWAP_HANDLERS is defined
  348 #ifdef SWAP_HANDLERS
  349       else
  350       {
  351         // Node is bonded and assigned with real address, switch to user handler, reset device
  352         if ( !HandlerNotPresent )
  353         {
  354           // Try to load user handler
  355           HandlerNotPresent = TRUE;
  356           // Try to load User handler (reset if OK)
  357           LoadHandler( EEE_ADDR_USER_HANDLER );
  358         }
  359         else
  360         {
  361           // [Customizable part]
  362           // Error - User handler not preloaded at external EEPROM
  363           // LEDR is still on at STD mode, flashing at LP mode
  364           setLEDR();
  365 #ifdef DPA_LP
  366           waitMS( 20 );
  367 #endif // #ifndef DPA_LP
  368         }
  369       }
  370 #endif // #ifdef SWAP_HANDLERS
  371 #else // #ifndef USER_HANDLER
  372       // [User Handler] - User handler DpaEvent_Idle implementation
  373       {
  374 #ifndef DPA_LP
  375         // User handler STD mode example - LEDG is flashing
  376         static uns16 tmr;
  377         tmr++;
  378         if ( tmr.9 )
  379         {
  380           tmr.9 = 0;
  381           _LEDG ^= 1;
  382         }
  383 #else // #ifndef DPA_LP
  384         // User handler LP mode example - LEDG is flashing
  385         setLEDG();
  386         waitMS( 20 );
  387 #endif // #ifndef DPA_LP
  388       }
  389 #endif // #ifndef USER_HANDLER
  390       break;
  391 
  392       // [User Handler] - User handler DpaEvent_Init implementation   
  393 #if !defined ( SWAP_HANDLERS ) || defined( USER_HANDLER )
  394       // -------------------------------------------------
  395     case DpaEvent_Init:
  396       // Do a one time initialization work before main loop starts
  397 
  398       break;
  399 #endif // #if !defined ( SWAP_HANDLERS ) || defined( USER_HANDLER   )
  400 
  401       // -------------------------------------------------
  402     case DpaEvent_Reset:
  403       // Called after module is reset to allow (un)bonding
  404       // When run fist time, do some global initializations
  405       static bit wasFirstReset;
  406       if ( !wasFirstReset )
  407       {
  408         // Already called once
  409         wasFirstReset = TRUE;
  410         // [Mandatory part] - must be implemented at both handlers if SWAP_HANDLERS is defined
  411 #ifdef SWAP_HANDLERS
  412         HandlerNotPresent = FALSE;
  413 #endif // #ifdef SWAP_HANDLERS
  414 
  415 #ifndef USER_HANDLER
  416         // Set Timeout prescaler to 10 ms unit
  417         PreScaller = PreScallerInit = 1;
  418 
  419         // Setup TMR6 to ISR get ticks on the background (ticks every 10 ms)
  420 #if F_OSC == 16000000
  421         PR6 = 250 - 1;
  422         T6CON = 0b0.1001.1.10;  // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  423 #else // #if F_OSC == 16000000
  424 #error Unsupported oscillator frequency
  425 #endif // #if F_OSC == 16000000
  426       // Enable TMR6 ISR
  427         TMR6IE = TRUE;
  428 #endif // #ifndef USER_HANDLER
  429       }
  430 
  431 #ifndef USER_HANDLER
  432       // Optional: Allow LED indication e.g. during discovery
  433       _systemLEDindication = TRUE;
  434 
  435       // Is node already bonded?
  436       if ( amIBonded() )
  437       {
  438 #ifdef DPA_LP
  439         // Force unbonding if temporary address was assigned
  440         startCapture();
  441 #endif  // #ifdef DPA_LP
  442         // Do DPA default unbonding 
  443         goto DpaHandleReturnFALSE;
  444       }
  445       else
  446       {
  447         // Take care of custom bonding
  448 
  449         // Generate non-zero random number (seed) based on MID
  450         moduleInfo();
  451         // Seed is the lowest MID byte (pseudo random seed must not be zero)
  452         rand = bufferINFO[0] | 0x10;
  453 
  454         // Remember current DPA RF mode
  455         uns8 saveRFmode = RFmodeByte;
  456 
  457         // Loop until not bonded
  458         for ( ;; )
  459         {
  460           // Reset inviting network items bitmap
  461           clearBufferINFO();
  462           // setFSR0( _FSR_INFO ); // Already done by clearBufferINFO
  463           copyMemoryBlock( FSR0, InvitingNodes, sizeof( InvitingNodes ) );
  464 
  465           // Set RF mode
  466           SetScanRfMode();
  467           // Work until invitation is received
  468           bit invitationRecevied = FALSE;
  469           for ( ;;)
  470           {
  471             Rand();
  472             setLEDR();
  473 
  474             // Check RFIC
  475             if ( wasRFICrestarted() )
  476               DpaApiSetRfDefaults();
  477 
  478             // Set the best bonding channel
  479             if ( !invitationRecevied )
  480               setServiceChannel( 0 );
  481             else
  482             {
  483               GIEoff();
  484               if ( Timeout == 0 )
  485                 break;
  486               GIEon();
  487             }
  488 
  489             toutRF = 100;
  490             if ( RFRXpacket() && DLEN == sizeof( StartBondingRF ) && StartBondingRF.Header == TStartBondingHeader )
  491             {
  492               BitmapNode( StartBondingRF.Sender );
  493               setINDF0( INDF0 | bitmapBitMask );
  494 
  495               if ( !invitationRecevied )
  496               {
  497                 invitationRecevied = TRUE;
  498 #ifdef DPA_LP
  499                 UseTMRinLP = TRUE;
  500 #endif // #ifdef DPA_LP
  501                 // Copy the packet
  502                 BackupBondingPacket();
  503 
  504                 pulsingLEDG();
  505 
  506                 GIEoff();
  507                 Timeout = StartBondingBackup.RemainingInvitationTime;
  508                 GIEon();
  509               }
  510             }
  511           }
  512 
  513           // Start bonding packet(s) was received
  514           // Set timeout to the length of the bonding invitation
  515 #ifdef DPA_LP
  516           UseTMRinLP = FALSE;
  517           startCapture();
  518           SumTicks = 0;
  519 #endif // #ifdef DPA_LP
  520           Timeout = StartBondingBackup.BondingTime;
  521           GIEon();
  522 
  523           // And now wait some random time so nodes will not be synchronized when doing LBT
  524           waitDelay( Rand() / 2 );
  525 
  526           // Start with the shortest gap between bonding attempts
  527           uns8 BondingGapLength = 1;
  528 
  529           // Set network working channel
  530           setRFchannel( StartBondingBackup.Channel );
  531           // Indicate bonding phase
  532           stopLEDG();
  533           // Loop till the bonding interval is valid
  534           for ( ;; )
  535           {
  536             // Set bonding counter to a random already bonded node (or coordinator) address
  537             for ( ;; )
  538             {
  539               // Get (including zero) random address
  540               bondingCounter = Rand() - 1;
  541               if ( bondingCounter <= MAX_ADDRESS )
  542               {
  543                 BitmapNode( bondingCounter );
  544                 if ( ( *FSR0 & bitmapBitMask ) != 0 )
  545                 {
  546                   // Adjust address for later bondRequestAdvanced() that pre-increments bondingCounter
  547                   bondingCounter--;
  548                   break;
  549                 }
  550               }
  551             }
  552 
  553             // Indicate
  554             setLEDG();
  555             pulsingLEDR();
  556             // Do LBT (listen before talk) for 400 x ( 1ms + checkRF )
  557             uns16 loop = 400;
  558             do
  559             {
  560               Rand();
  561               waitMS( 1 );
  562 
  563               // Bonding interval is over?
  564 #ifndef DPA_LP
  565               GIEoff();
  566               if ( Timeout == 0 )
  567               {
  568                 GIEon();
  569                 goto ExitBondingLoop;
  570               }
  571               GIEon();
  572 #else // #ifndef DPA_LP
  573               captureTicks();
  574               SumTicks += param4;
  575               // Decrease the timeout
  576               Minus16vars( Timeout, SumTicks );
  577               // Overflow? Time is over!
  578               if ( !Carry )
  579                 goto ExitBondingLoop;
  580 
  581               SumTicks = 0;
  582 #endif // #ifndef DPA_LP
  583             } while ( !checkRF( DpaApiReadConfigByte( CFGIND_RXFILTER ) ) && --loop != 0 );
  584 
  585             // Indicate
  586             stopLEDR();
  587 
  588             // RF was quiet?
  589             if ( loop == 0 )
  590             {
  591               // Set original RF settings
  592               setRFmode( saveRFmode );
  593 
  594               // Set node mode and network filtering
  595               setNetworkFilteringOn();
  596               setNodeMode();
  597 
  598 #if defined( AUTHORIZE_PRE_BONDING )
  599               // In this example first 4 bytes of EEPROM peripheral must be pre-filled with 32bit PIN code 
  600               // The PIN code will be checked at the node or coordinator that provides pre-bonding
  601               eeReadData( PERIPHERAL_EEPROM_START, sizeof( nodeUserDataToSend ) );
  602               setFSR0( _FSR_INFO );
  603               copyMemoryBlock( FSR0, nodeUserDataToSend, sizeof( nodeUserDataToSend ) );
  604 #endif // #if defined( AUTHORIZE_PRE_BONDING )
  605 
  606               // Execute bonding
  607 #ifdef DPA_LP
  608               // To keep captureTicks() the most precise as possible
  609               waitNewTick();
  610 #endif // #ifdef DPA_LP
  611               // Are we bonded?
  612               if ( bondRequestAdvanced() )
  613               {
  614                 stopLEDG();
  615                 // Bonded!
  616                 // Set DPA API variable
  617                 NodeWasBonded = TRUE;
  618 #ifndef DPA_LP
  619                 // Now timeout will measure at 100 ms
  620                 PreScallerInit = 10;
  621                 GIEoff();
  622 #else // #ifndef DPA_LP
  623                 startCapture();
  624 #endif // #ifndef DPA_LP
  625                 // Time to wait for getting real address
  626                 Timeout = StartBondingBackup.TemporaryAddressTimeout;
  627 #ifndef DPA_LP
  628                 GIEon();
  629 #endif // #ifndef DPA_LP
  630 
  631                 // Write working channel to the configuration
  632                 _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Value = RFchannel;
  633                 _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Address = CFGIND_CHANNEL_A;
  634                 _DpaMessage.PerOSWriteCfgByte_Request.Triplets[0].Mask = 0xFF;
  635                 _DpaDataLength = sizeof( TPerOSWriteCfgByteTriplet );
  636                 DpaApiLocalRequest_OS( CMD_OS_WRITE_CFG_BYTE );
  637 
  638                 // Go to the DPA main loop
  639                 DpaApiSetRfDefaults();
  640                 goto DpaHandleReturnTRUE;
  641               }
  642 
  643               // Setup RF
  644               SetScanRfMode();
  645             }
  646 
  647             // Delay for a random time proportional to the gap
  648             // Fixed part
  649             startLongDelay( (uns16)BondingGapLength * 2 );
  650             do
  651             {
  652               Rand();
  653             } while ( isDelay() );
  654 
  655             // Random part
  656             waitDelay( ( Rand() & BondingGapLength ) | 0b11.1111 );
  657 
  658             // Next gap size will be doubled (2^n-1)
  659             Carry = TRUE;
  660             BondingGapLength = rl( BondingGapLength );
  661           }
  662 
  663 ExitBondingLoop:
  664           stopLEDR();
  665           stopLEDG();
  666         }
  667       }
  668 #else  // #ifndef USER_HANDLER
  669       // [Mandatory part] - must be implemented at user handler is defined
  670       // Check the node is bonded
  671       if ( !amIBonded() )
  672       {
  673         // No, switch to Autonetwork handler, reset device
  674         if ( !HandlerNotPresent )
  675         {
  676           // Try to load autonetwork handler (reset if OK)       
  677           HandlerNotPresent = TRUE;
  678           LoadHandler( EEE_ADDR_AUTONETWORK_HANDLER );
  679         }
  680         else
  681         {
  682           // [Customizable part] - If the Autonetwork handler not preloaded at external EEPROM, implement your error indication.
  683           // In this example standard DPA bonding is used when Autonetwork handler not preloaded.
  684         }
  685       }
  686       // [User Handler] - User handler DpaEvent_Reset implementation
  687 
  688       break;
  689 #endif // #ifndef USER_HANDLER
  690 
  691       // -------------------------------------------------
  692 #if !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  693     case DpaEvent_AfterRouting:
  694       // Called after Notification and after routing of the DPA response was finished
  695       // Request to send peer2peer start bonding packet?
  696       if ( SendInvitation )
  697       {
  698         SendInvitation = FALSE;
  699         // Wait number of slots equal the VRN
  700         uns8 loop @ userReg0;
  701         loop = ntwVRN;
  702         do
  703         {
  704           clrwdt();
  705           // Decrease number of remaining invitation time
  706           StartBondingBackup.RemainingInvitationTime -= SLOT_LENGTH;
  707           waitDelay( SLOT_LENGTH );
  708         } while ( --userReg0 != 0 );
  709 
  710         // Now send peer2peer packet
  711         SetScanRfMode();
  712 
  713         // Prepare packet to bufferRF
  714         setFSR1( _FSR_RF );
  715         copyMemoryBlock( &StartBondingBackup, FSR1, DLEN = sizeof( StartBondingBackup ) );
  716         StartBondingRF.Sender = ntwADDR;
  717         StartBondingRF.Channel = RFchannel;
  718 
  719         // Send the packet at all 3 service channels
  720         uns8 channelIndex = SERVICE_CHANNELS_COUNT;
  721         do
  722         {
  723           setServiceChannel( channelIndex );
  724           // Send the packet
  725           PIN = 0;
  726           RFTXpacket();
  727           waitMS( 5 );
  728         } while ( --channelIndex != 0 );
  729 
  730         // Restore RF mode
  731         DpaApiSetRfDefaults();
  732         // and filtering
  733         setNetworkFilteringOn();
  734         // and node mode
  735         setNodeMode();
  736       }
  737 
  738       break;
  739 #endif // #if !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  740 
  741       // -------------------------------------------------
  742     case DpaEvent_AfterSleep:
  743       // Called after woken up after sleep
  744 #ifndef USER_HANDLER
  745       // Use TMR6 again
  746       TMR6IE = TRUE;
  747       TMR6ON = TRUE;
  748 #else // #ifndef USER_HANDLER
  749       // [User Handler] - User handler DpaEvent_AfterSleep implementation
  750 
  751 #endif // #ifndef USER_HANDLER
  752       break;
  753 
  754       // -------------------------------------------------
  755     case DpaEvent_BeforeSleep:
  756       // Called before going to sleep
  757 #ifndef USER_HANDLER
  758       // Fall through!
  759 #else // #ifndef USER_HANDLER
  760 // [User Handler] - User handler DpaEvent_BeforeSleep implementation
  761 
  762       break;
  763 #endif // #ifndef USER_HANDLER
  764 
  765       // -------------------------------------------------
  766     case DpaEvent_DisableInterrupts:
  767       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM)
  768 #ifndef USER_HANDLER
  769       // Do not need to use TMR6 any more
  770       DisableInterrupts();
  771 #else // #ifndef USER_HANDLER
  772 // [User Handler] - User handler DpaEvent_DisableInterrupts implementation
  773 
  774 #endif // #ifndef USER_HANDLER
  775       break;
  776 
  777 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  778 
  779 #if defined( AUTHORIZE_PRE_BONDING ) && ( !defined( SWAP_HANDLERS ) || defined( USER_HANDLER ) )
  780       // -------------------------------------------------
  781     case DpaEvent_AuthorizePreBonding:
  782       // Called when remote bonding is enabled and a node requests pre-bonding
  783 
  784       // 32 bit PIN that has to be matched is stored at last 4 bytes of peripheral RAM
  785       // PIN must be same as the PIN sent from the requesting NODE. Requesting node PIN is stored at its EEPROM peripheral.
  786       if ( nodeUserDataReceived[0] != PeripheralRam[PERIPHERAL_RAM_LENGTH - 4] ||
  787            PeripheralRam[PERIPHERAL_RAM_LENGTH - 3] != nodeUserDataReceived[1] ||
  788            nodeUserDataReceived[2] != PeripheralRam[PERIPHERAL_RAM_LENGTH - 2] ||
  789            PeripheralRam[PERIPHERAL_RAM_LENGTH - 1] != nodeUserDataReceived[3] )
  790         // If PINs do not match, reject the pre-bonding request
  791         goto DpaHandleReturnTRUE;
  792 
  793       break;
  794 #endif // #if defined( AUTHORIZE_PRE_BONDING ) && ( !defined( SWAP_HANDLERS ) || defined( USER_HANDLER ) )
  795 
  796       // -------------------------------------------------
  797     case DpaEvent_DpaRequest:
  798       // Called to interpret DPA request for peripherals
  799       // -------------------------------------------------
  800       // Peripheral enumeration
  801       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  802       {
  803 #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  804         // Autonetwork handler implements user peripheral PNUM_USER + 0
  805         _DpaMessage.EnumPeripheralsAnswer.UserPerNr++; // = 1;
  806         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  807 #endif // #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  808         // [User Handler] - implement user Peripheral enumeration
  809 
  810         // We implement a HW Profile
  811 #ifndef USER_HANDLER
  812         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  813 #else // #ifndef USER_HANDLER
  814         // [User Handler] - define user HW Profile
  815         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  816 #endif // #ifndef USER_HANDLER
  817 
  818 DpaHandleReturnTRUE:
  819         return TRUE;
  820       }
  821       // -------------------------------------------------
  822       // Get information about peripheral
  823       else
  824       {
  825 #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  826         // User peripheral PNUM_USER + 0 is reserved for autonetwork
  827         if ( _PNUM == PNUM_USER + 0 )
  828         {
  829           // It is user type peripheral
  830           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  831           // And write only style
  832           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_WRITE;
  833           goto DpaHandleReturnTRUE;
  834         }
  835 #endif // #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  836 
  837         // [User Handler] - implement user peripheral information
  838 
  839         break;
  840       }
  841       // -------------------------------------------------
  842       {
  843 #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  844         // Handle broadcast only peripheral command
  845         if (
  846           // My peripheral
  847           _PNUM == PNUM_USER + 0 &&
  848           // My command
  849           _PCMD == 0 &&
  850           // Correct data length
  851           _DpaDataLength == sizeof( StartBondingDpa )
  852 #ifndef COORDINATOR_CUSTOM_HANDLER
  853           // Broadcast
  854           && _NADR == BROADCAST_ADDRESS &&
  855           // And I do not have temporary address (i.e. no VRN)
  856           ntwADDR != TEMPORARY_ADDRESS
  857 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  858           )
  859         {
  860 #ifndef COORDINATOR_CUSTOM_HANDLER
  861           // [N] version
  862 
  863 #if !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  864           // Store peer2peer start bonding packet values
  865           BackupBondingPacket();
  866           // Send invitation later
  867           SendInvitation = TRUE;
  868 #endif  // !defined( SWAP_HANDLERS ) || defined( USER_HANDLER ) )
  869 
  870 #else // #ifndef COORDINATOR_CUSTOM_HANDLER
  871           // [C] version
  872 
  873           // Now send peer2peer packet
  874           // Set RF to peer2peer packet
  875           SetScanRfMode();
  876           // Prepare packet to bufferRF
  877           // ! StartBondingRF == StartBondingDpa !
  878           StartBondingRF.Channel = RFchannel;
  879 
  880           // Send the packet at all 3 service channels
  881           uns8 channelIndex = SERVICE_CHANNELS_COUNT;
  882           do
  883           {
  884             setServiceChannel( channelIndex );
  885             // Send the packet
  886             DLEN = sizeof( StartBondingRF );
  887             PIN = 0;
  888             RFTXpacket();
  889             waitMS( 5 );
  890           } while ( --channelIndex != 0 );
  891 
  892           // Restore RF mode
  893           DpaApiSetRfDefaults();
  894           // and filtering
  895           setNetworkFilteringOn();
  896           // and coordinator mode
  897           setCoordinatorMode();
  898 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  899 
  900           goto DpaHandleReturnTRUE;
  901         }
  902         else
  903         {
  904           // [User Handler] - implement user peripheral request
  905 
  906           DpaApiReturnPeripheralError( ERROR_FAIL );
  907         }
  908 #endif // #if defined( COORDINATOR_CUSTOM_HANDLER ) || !defined( SWAP_HANDLERS ) || defined( USER_HANDLER )
  909       }
  910   }
  911 
  912 DpaHandleReturnFALSE:
  913   return FALSE;
  914 }
  915 
  916 //############################################################################################
  917 
  918 #ifndef COORDINATOR_CUSTOM_HANDLER
  919 #ifndef USER_HANDLER
  920 
  921 //############################################################################################
  922 uns8 Rand()
  923 //############################################################################################
  924 {
  925   clrwdt();
  926   rand = lsr( rand );
  927 #pragma updateBank 0 /* OFF */  // Optimization (avoid duplicate bank setting for RandomValue access)
  928   W = 0b10111000;   // x^8 + x^6 + x^5 + x^4 + 1
  929   if ( Carry )
  930     rand ^= W;
  931 
  932   return rand;
  933 #pragma updateBank 1 /* ON */
  934 }
  935 
  936 //############################################################################################
  937 void BitmapNode( uns8 address )
  938 //############################################################################################
  939 {
  940   addressBitmap( address );
  941   FSR0 = InvitingNodes;
  942   FSR0L += bitmapByteIndex; // Note: FSR0H will not overflow
  943 }
  944 
  945 //############################################################################################
  946 void DisableInterrupts()
  947 //############################################################################################
  948 {
  949   TMR6ON = FALSE;
  950   TMR6IE = FALSE;
  951 }
  952 
  953 #endif // #ifndef COORDINATOR_CUSTOM_HANDLER
  954 
  955 //############################################################################################
  956 void BackupBondingPacket()
  957 //############################################################################################
  958 {
  959   setFSR0( _FSR_RF );
  960   copyMemoryBlock( FSR0, &StartBondingBackup, sizeof( StartBondingBackup ) );
  961 }
  962 
  963 #endif // #ifndef USER_HANDLER
  964 
  965 //############################################################################################
  966 // [Mandatory part] - SetScanRfMode function must be implemented at both handlers
  967 void SetScanRfMode()
  968 //############################################################################################
  969 {
  970   setNonetMode();
  971   setNetworkFilteringOff();
  972   setRFmode( _WPE | _RX_STD | _TX_STD );
  973 }
  974 
  975 //############################################################################################
  976 void DpaApiLocalRequest_OS( uns8 cmd @ W )
  977 //############################################################################################
  978 {
  979   _PCMD = cmd;
  980   _PNUM = PNUM_OS;
  981   DpaApiLocalRequest();
  982 }
  983 
  984 #ifdef SWAP_HANDLERS
  985 //############################################################################################
  986 // [Mandatory part] - must be implemented at both handlers
  987 uns8 LoadHandler( uns16 address @ param3 )
  988 //############################################################################################
  989 {
  990   // Read CMD_OS_LOAD_CODE parameters (address, length, checksum) into bufferINFO for loading the other driver
  991   eeeReadData( address );
  992   // Copy CMD_OS_LOAD_CODE parameters to the correct offset at the DPA request
  993   memoryOffsetTo = offsetof( TPerOSLoadCode_Request, Address );
  994   // Copy loaded parameters from bufferINFO into bufferRF where actually _DpaMessage is
  995   copyBufferINFO2RF();
  996   // We load custom DPA handler stored at .hex format and want to really load it
  997   _DpaMessage.PerOSLoadCode_Request.Flags = 0x01;
  998   // Length of the DPA request
  999   _DpaDataLength = sizeof( _DpaMessage.PerOSLoadCode_Request );
 1000   // Perform LoadCode, if it succeeds the device resets
 1001   DpaApiLocalRequest_OS( CMD_OS_LOAD_CODE );
 1002   // If the LoadCode failed (reason: .hex not loaded at the external EEPROM memory or the LoadCode parameters stored at external EEPROM are incorrect) ...
 1003   return _DpaMessage.Response.PData[0];
 1004 }
 1005 #endif // #ifdef SWAP_HANDLERS
 1006 
 1007 //############################################################################################
 1008 // 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) 
 1009 #include "DPAcustomHandler.h"
 1010 //############################################################################################
 1011 #endif // #ifndef CustomDpaHandler_AutoNetwork_HEADER
 1012 //############################################################################################