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