1 // *********************************************************************
    2 //   Custom DPA Handler code example - PIR controlled lighting demo    *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-PIRlighting.c,v $
    7 // Version: $Revision: 1.38 $
    8 // Date:    $Date: 2023/03/07 08:03:13 $
    9 //
   10 // Revision history:
   11 //   2023/03/07  Release for DPA 4.30
   12 //   2022/10/05  Release for DPA 4.18
   13 //   2022/02/24  Release for DPA 4.17
   14 //   2017/03/13  Release for DPA 3.00
   15 //   2015/08/05  Release for DPA 2.20
   16 //
   17 // *********************************************************************
   18 
   19 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   20 
   21 // Default IQRF include (modify the path according to your setup)
   22 #include "IQRF.h"
   23 
   24 // Default DPA header (modify the path according to your setup)
   25 #include "DPA.h"
   26 // Default Custom DPA Handler header (modify the path according to your setup)
   27 #include "DPAcustomHandler.h"
   28 
   29 // This is an example of PIR controlled lighting. The application can be configured and controlled by EEPROM and RAM DPA peripherals respectively.
   30 //
   31 // When a PIR input is active the device sends non-network Peer2Peer packet that contains the address of the device.
   32 // PIR input is then inactive for next PIRdelay seconds in order not to jam with many same packets.
   33 // Other nodes that receive the Peer2Peer packet will set the LIGHT output if the address of the sender matches any of the condition bytes
   34 // from the 16 byte long list stored at EEPROM peripheral from address 1. The light will stay on for number of second stored at EEPROM peripheral at address 0.
   35 // Condition bytes:
   36 //  0         End of the condition bytes list
   37 //  255       Any node will switch the light on. This condition byte overrides condition bytes described below.
   38 //  1 - 239   Only node having address equal this value switches the light on.
   39 //  240 - 254 Only node having address that differs ( ConditionByte - 239 ) from my address maximum switches the light on.
   40 //            E.g. when my address is 10 and condition byte is 241, then only nodes with address 8-12 would switch my light on.
   41 //
   42 // If bit PeripheralRam[0].0 is set, PIR input is inactive. This allows to disable PIR detectors e.g. during the day.
   43 //
   44 // ! Please note that User peer-to-peer packets must be enabled at the HWP configuration
   45 // This example works only at STD mode, not at LP mode
   46 
   47 // Initialize the EEPROM peripheral area
   48 #pragma cdata[ __EESTART + PERIPHERAL_EEPROM_START ] = \
   49   /* Set light timeout to 5 seconds. */ \
   50   5, \
   51   /* Allow switching the light on only by nodes with address 1 and address 2 and with address that differs 2 maximum. */ \
   52   1, 2, MAX_ADDRESS + 2, \
   53   /* End of the list. */ \
   54   0
   55 // Note: symbol MAX_ADDRESS equals 239
   56 
   57 //############################################################################################
   58 
   59 // PIR input
   60 #define PIR   buttonPressed
   61 // Light output
   62 #define LIGHT _LEDG
   63 
   64 // Fixed PIR repetition delay [s]
   65 #define PIRdelay    2
   66 
   67 //############################################################################################
   68 
   69 // Length of the address list
   70 #define ADDR_LIST_LEN 16
   71 
   72 // 40 ms counter used to measure 1 s
   73 static uns8 tmrCounter40ms;
   74 
   75 // Light on counter
   76 static uns8 lightTimeout;
   77 
   78 // Initial light timeout counter value, read from EEPROM peripheral at address 0
   79 static uns8 lightTimeoutSetting;
   80 
   81 // Switches the light on, initializes timeout
   82 void LightOn();
   83 // Starts new second
   84 void StartSecond();
   85 
   86 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   87 //############################################################################################
   88 bit CustomDpaHandler()
   89 //############################################################################################
   90 {
   91   // Handler presence mark
   92   clrwdt();
   93 
   94   // Place for local static variables used only within CustomDpaHandler() among more events
   95 
   96   // If TRUE, PIR was activated and after timeout
   97   static bit PIRactivated;
   98 
   99   // Detect DPA event to handle
  100   switch ( GetDpaEvent() )
  101   {
  102     // -------------------------------------------------
  103     case DpaEvent_Interrupt:
  104       // Do an extra quick background interrupt work
  105       // ! 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.
  106       // ! 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.
  107       // ! 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.
  108       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  109       // ! Make sure race condition does not occur when accessing those variables at other places.
  110       // ! 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.
  111       // ! Do not call any OS functions except setINDFx().
  112       // ! Do not use any OS variables especially for writing access.
  113       // ! 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.
  114 
  115       // PIR timeout counter
  116       static uns8 PIRtimeout;
  117 
  118       //  If TMR6 interrupt occurred
  119       if ( TMR6IF )
  120       {
  121         // Unmask interrupt
  122         TMR6IF = 0;
  123         // Decrement count
  124         if ( --tmrCounter40ms == 0 )
  125         {
  126           // 1 s is over
  127           StartSecond();
  128 
  129           // Decrement PIR counter
  130           if ( PIRtimeout != 0 )
  131             PIRtimeout--;
  132 
  133           // Decrement light counter
  134           if ( lightTimeout != 0 )
  135           {
  136             lightTimeout--;
  137             if ( lightTimeout == 0 )
  138               // Switch off the light
  139               LIGHT = 0;
  140           }
  141         }
  142       }
  143 
  144       // Last state of PIR input
  145       static bit lastPIR;
  146 
  147       // PIR is on?
  148       if ( PIR )
  149       {
  150         // Was PIR off and PIR timeout is over and not disabled?
  151         if ( !lastPIR && PIRtimeout == 0 && !PeripheralRam[0].0 )
  152         {
  153           // Initializes PIR timeout counter
  154           PIRtimeout = PIRdelay;
  155           // PIR is activated
  156           PIRactivated = TRUE;
  157         }
  158 
  159         lastPIR = TRUE;
  160       }
  161       else
  162         lastPIR = FALSE;
  163 
  164 DpaHandleReturnTRUE:
  165       return TRUE;
  166 
  167       // -------------------------------------------------
  168     case DpaEvent_Idle:
  169       // Do a quick background work when RF packet is not received
  170 
  171       // PIR was activated, send the P2P packet
  172       if ( PIRactivated )
  173       {
  174         // Note: Here Listen before Talk technique should be implemented to avoid collisions
  175 
  176         // PIR not activated any more
  177         PIRactivated = FALSE;
  178 
  179         // Save RF settings and set new ones
  180         setNonetMode();
  181         setRFmode( _TX_STD | _STDL );
  182 
  183         // Prepare P2P packet
  184         // Header
  185         bufferRF[0] = 'P';
  186         // Address
  187         bufferRF[1] = ntwADDR;
  188         // Packet length
  189         DLEN = 2;
  190         PIN = 0;
  191         // Transmit the prepared packet
  192         RFTXpacket();
  193 
  194         // Restore RF settings
  195         DpaApiSetRfDefaults();
  196         setNodeMode();
  197 
  198         LightOn();
  199       }
  200 
  201       break;
  202 
  203       // -------------------------------------------------
  204     case DpaEvent_PeerToPeer:
  205       // Called when peer-to-peer (non-networking) packet is received
  206 
  207       // Note: peer-to-peer packet should be better protected when not used at this example
  208 
  209       // Is my peer-to-peer packet (check length and content)?
  210       if ( DLEN == 2 && bufferRF[0] == 'P' )
  211       {
  212         // Read list of condition bytes from EEPROM[1..x]
  213         eeReadData( PERIPHERAL_EEPROM_START + 1, ADDR_LIST_LEN );
  214         // Force list end
  215         bufferINFO[ADDR_LIST_LEN] = 0;
  216         // Pointer before 1st address
  217         FSR0 = bufferINFO - 1;
  218         // Loop the list
  219         while ( *++FSR0 != 0 )
  220         {
  221           // Any address or address match?
  222           if ( *FSR0 == 0xff || *FSR0 == bufferRF[1] )
  223           {
  224             LightOn();
  225             break;
  226           }
  227 
  228           if ( *FSR0 > MAX_ADDRESS )
  229           {
  230             // Compute absolute address difference
  231             int8 addrDiff = bufferRF[1] - ntwADDR;
  232             if ( addrDiff < 0 )
  233               addrDiff = -addrDiff;
  234 
  235             // Get maximum difference
  236             uns8 absDelta = *FSR0 - MAX_ADDRESS;
  237             // Is max. difference met?
  238             if ( (uns8)addrDiff <= absDelta )
  239             {
  240               LightOn();
  241               break;
  242             }
  243           }
  244         }
  245       }
  246 
  247       break;
  248 
  249       // -------------------------------------------------
  250     case DpaEvent_Init:
  251       // Do a one time initialization before main loop starts
  252 
  253       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  254 #if defined( TR7xG )
  255       TMR6MD = 0;
  256       T6CON = 0b1.100.1001;
  257       //  Timer2/4/6 Clock Select bits = FOSC/4
  258       T6CLKCON |= 0b0000.0001;
  259 #else
  260       T6CON = 0b0.1001.1.10;
  261 #endif
  262       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  263       _PR6 = 250 - 1;
  264 
  265       TMR6IE = TRUE;
  266 
  267       // Initialize 1 s timer
  268       StartSecond();
  269 
  270 RefreshLightDelay:
  271       // Read light delay from EEPROM[0]
  272       lightTimeoutSetting = eeReadByte( PERIPHERAL_EEPROM_START + 0 );
  273       break;
  274 
  275       // -------------------------------------------------
  276     case DpaEvent_Notification:
  277       // Called after DPA request was processed and after DPA response was sent
  278 
  279       // Anything written to the EEPROM?
  280       // (could be optimized by checking the EEPROM address that was written to)
  281       if ( _PNUM == PNUM_EEPROM && _PCMD == CMD_EEPROM_WRITE )
  282         goto RefreshLightDelay;
  283 
  284       break;
  285 
  286       // -------------------------------------------------
  287     case DpaEvent_AfterSleep:
  288       // Called on wake-up from sleep
  289       TMR6IE = TRUE;
  290       _TMR6ON = TRUE;
  291       break;
  292 
  293       // -------------------------------------------------
  294     case DpaEvent_BeforeSleep:
  295       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  296 
  297       // -------------------------------------------------
  298     case DpaEvent_DisableInterrupts:
  299       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  300       // Must not use TMR6 any more
  301       _TMR6ON = FALSE;
  302       TMR6IE = FALSE;
  303       break;
  304   }
  305 
  306   return FALSE;
  307 }
  308 
  309 //############################################################################################
  310 void LightOn()
  311 //############################################################################################
  312 {
  313   lightTimeout = lightTimeoutSetting;
  314   LIGHT = 1;
  315   StartSecond();
  316 }
  317 
  318 //############################################################################################
  319 void StartSecond()
  320 //############################################################################################
  321 {
  322   tmrCounter40ms = 1000 / 40;
  323 }
  324 
  325 //############################################################################################
  326 // 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)
  327 #include "DPAcustomHandler.h"
  328 //############################################################################################