1 // ************************************************************************************************
    2 //   Custom DPA Handler code example - Shows custom (un)bonding and factory settings using button *
    3 // ************************************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-Bonding.c,v $
    7 // Version: $Revision: 1.53 $
    8 // Date:    $Date: 2022/02/25 09:41:25 $
    9 //
   10 // Revision history:
   11 //   2022/02/24  Release for DPA 4.17
   12 //   2021/08/20  Release for DPA 4.16
   13 //   2019/12/11  Release for DPA 4.11
   14 //   2019/10/09  Release for DPA 4.10
   15 //   2019/06/03  Release for DPA 4.02
   16 //   2019/01/10  Release for DPA 4.00
   17 //   2018/10/25  Release for DPA 3.03
   18 //   2017/08/14  Release for DPA 3.01
   19 //   2017/03/13  Release for DPA 3.00
   20 //   2015/08/05  Release for DPA 2.20
   21 //
   22 // *********************************************************************
   23 
   24 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   25 
   26 // Default IQRF include (modify the path according to your setup)
   27 #include "IQRF.h"
   28 
   29 // Default DPA header (modify the path according to your setup)
   30 #include "DPA.h"
   31 // Default Custom DPA Handler header (modify the path according to your setup)
   32 #include "DPAcustomHandler.h"
   33 
   34 // This code illustrates bonding using a button connected to the custom pin.
   35 // Except going to sleep the code behaves the same way as the standard bonding procedure.
   36 // Please see https://www.iqrf.org/DpaTechGuide/index.html?page=device-startup.html and the following chapters.
   37 // ! Please note, that the overlapping networks support by SmartConnect DPA command https://doc.iqrf.org/DpaTechGuide/pages/smart-connect.html will not work with this example
   38 
   39 // Define to just reassign a bonding button with the button behavior implemented inside DPA
   40 // #define  USE_DpaEvent_BondingButton
   41 
   42 // Custom bonding button. Change to the pin and an active level of your choice.
   43 //#define   IsButton  ( !PORTA.5 )
   44 #define IsButton  buttonPressed
   45 
   46 // Returns TRUE when button is released shortly after LEDG goes out
   47 bit ButtonAfterGreen();
   48 // Same as above but with leading gap
   49 bit ButtonAfterGreenWithGap();
   50 // RedLED on for 1s
   51 void LEDR1s();
   52 
   53 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   54 //############################################################################################
   55 bit CustomDpaHandler()
   56 //############################################################################################
   57 {
   58   // Handler presence mark
   59   clrwdt();
   60 
   61   // Detect DPA event to handle
   62   switch ( GetDpaEvent() )
   63   {
   64     // -------------------------------------------------
   65     case DpaEvent_Interrupt:
   66       // Do an extra quick background interrupt work
   67       // ! 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.
   68       // ! 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.
   69       // ! 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.
   70       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   71       // ! Make sure race condition does not occur when accessing those variables at other places.
   72       // ! 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.
   73       // ! Do not call any OS functions except setINDFx().
   74       // ! Do not use any OS variables especially for writing access.
   75       // ! 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.
   76       return Carry;
   77 
   78 #ifdef USE_DpaEvent_BondingButton
   79       // -------------------------------------------------
   80     case DpaEvent_BondingButton:
   81       // Called to allow a bonding button customization
   82 
   83       userReg1.0 = 0;
   84       if ( IsButton )
   85         userReg1.0 = 1;
   86 
   87       return TRUE;
   88 #else
   89       // -------------------------------------------------
   90     case DpaEvent_Reset:
   91       // Called after module is reset
   92 
   93       // Handle (un)bonding
   94       if ( !amIBonded() )
   95       {
   96         // -------------------------------------------------
   97         // Not bonded
   98 
   99         // Very 1st bonding attempt and well released button after 2.5 s gap and LEDG?
  100         if ( ButtonAfterGreenWithGap() )
  101           // Factory settings!
  102           goto _factoryAndRemoveBond;
  103 
  104         // If and while node is not bonded
  105         for ( ;; )
  106         {
  107           // If button is not pressed, be ready to be SmartConnected
  108           while ( !IsButton )
  109           {
  110             // Do and wait for an indication
  111             pulseLEDR();
  112             waitMS( 6 );
  113             // Make sure to use LP mode
  114             setRFmode( _WPE | _RX_LP | _TX_LP );
  115 
  116             // Set sometimes a new service channel for SmartConnect, otherwise the last one
  117             static uns8 lastServiceChannel;
  118             static uns8 channelCnt;
  119             // Time to set a new service channel?
  120             if ( ( channelCnt & 0x03 ) == 0 )
  121               // Set a new service channel
  122               lastServiceChannel = 0;
  123             // Next "timer"
  124             channelCnt++;
  125 
  126             // Set the SmartConnect service channel
  127             setServiceChannel( lastServiceChannel );
  128             // Save last service channel used
  129             lastServiceChannel = param2 + 1;
  130 
  131             // Try SmartConnect
  132             _checkRFcfg_PQT = TRUE;
  133             toutRF = 6;
  134             // Use more strict RF RX filter for SmartConnect only
  135             checkRF( RxFilter + 4 );
  136             RFRXpacket();
  137             // Restore original RF RX filter
  138             checkRF( RxFilter );
  139             // Process SmartConnect packet, if received
  140             answerSystemPacket();
  141             // Restore RF settings (and make sure Spirit1 is not locked)
  142             wasRFICrestarted();
  143             DpaApiSetRfDefaults();
  144             // SmartConnect success?
  145             if ( amIBonded() )
  146               goto _NodeWasBonded;
  147           }
  148 
  149           // Bonding by button indication
  150           pulseLEDR();
  151           // Traditional bonding using 3 service channels
  152           _3CHTX = TRUE;
  153           // Bonded?
  154           if ( bondRequestAdvanced() )
  155           {
  156 _NodeWasBonded:
  157             // Indicate bonding for 0.5 s
  158             setLEDG();
  159             waitDelay( 50 );
  160             stopLEDG();
  161             // Exit the loop
  162             break;
  163           }
  164         }
  165       }
  166       else
  167       {
  168         // -------------------------------------------------
  169         // Bonded
  170 
  171         // If the button is pressed for 2s (Green LED is on during this time) and then released within 0.5s, then un-bond the node (indicated by 1s Red LED)
  172         if ( ButtonAfterGreen() )
  173         {
  174           // Red LED for 1s
  175           LEDR1s();
  176           // Remove bond + implicit restart
  177           _DpaDataLength = 0;
  178           _PNUM = PNUM_NODE;
  179           _PCMD = CMD_NODE_REMOVE_BOND;
  180           // Perform local DPA Request
  181           DpaApiLocalRequest();
  182           // Unreachable code because of restart
  183         }
  184 
  185         // Now wait if the button is still pressed for about 2.5 s and if again released after green LED do factory settings and remove bond
  186         if ( ButtonAfterGreenWithGap() )
  187         {
  188 _factoryAndRemoveBond:
  189           // Red LED for 1s
  190           LEDR1s();
  191           // Factory settings + remove bond + implicit restart
  192           _DpaDataLength = 0;
  193           _PNUM = PNUM_OS;
  194           _PCMD = CMD_OS_FACTORY_SETTINGS;
  195           // Perform local DPA Request
  196           DpaApiLocalRequest();
  197           // Unreachable code because of restart
  198         }
  199       }
  200 
  201       return TRUE;
  202 #endif
  203   }
  204 
  205   return FALSE;
  206 }
  207 
  208 //############################################################################################
  209 void LEDR1s()
  210 //############################################################################################
  211 {
  212   setLEDR();
  213   waitDelay( 100 );
  214   stopLEDR();
  215 }
  216 
  217 //############################################################################################
  218 bit ButtonAfterGreen()
  219 //############################################################################################
  220 {
  221   // Returns TRUE when button is released shortly after LEDG goes out
  222 
  223   // Time for Green LED on
  224   startDelay( 200 );
  225   // Green LED on
  226   setLEDG();
  227   do
  228   {
  229     // If button not pressed
  230     if ( !IsButton )
  231     {
  232       // Switch off Green LED
  233       stopLEDG();
  234       // And return FALSE
  235       return FALSE;
  236     }
  237     // Is button still pressed while Green LED is on?
  238   } while ( isDelay() );
  239 
  240   // Switch off Green LED
  241   stopLEDG();
  242   // Set testing delay
  243   startDelay( 50 );
  244   do
  245   {
  246     // If the button is released within the delay
  247     if ( !IsButton )
  248       // Return TRUE
  249       return TRUE;
  250   } while ( isDelay() );
  251   // Otherwise return FALSE
  252   return FALSE;
  253 }
  254 
  255 //############################################################################################
  256 bit ButtonAfterGreenWithGap()
  257 //############################################################################################
  258 {
  259   // Is button pressed for 2.55s ?
  260   startDelay( 255 );
  261   while ( IsButton && isDelay() );
  262   // If not the next call will return FALSE, otherwise will test for button release after Green LED goes off
  263   return ButtonAfterGreen();
  264 }
  265 
  266 //############################################################################################
  267 // 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)
  268 #include "DPAcustomHandler.h"
  269 //############################################################################################