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