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.21 $ 8 // Date: $Date: 2019/05/06 07:13:14 $ 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 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 239 53 54 //############################################################################################ 55 56 // PIR input 57 #define PIR buttonPressed 58 // Light output 59 #define LIGHT _LEDG 60 61 // Fixed PIR repetition delay [s] 62 #define PIRdelay 2 63 64 //############################################################################################ 65 66 // Length of the address list 67 #define ADDR_LIST_LEN 16 68 69 // 40 ms counter used to measure 1 s 70 static uns8 tmrCounter40ms; 71 72 // Light on counter 73 static uns8 lightTimeout; 74 75 // Initial light timeout counter value, read from EEPROM peripheral at address 0 76 static uns8 lightTimeoutSetting; 77 78 // Switches the light on, initializes timeout 79 void LightOn(); 80 // Starts new second 81 void StartSecond(); 82 83 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 84 //############################################################################################ 85 bit CustomDpaHandler() 86 //############################################################################################ 87 { 88 // Handler presence mark 89 clrwdt(); 90 91 // Place for local static variables used only within CustomDpaHandler() among more events 92 93 // If TRUE, PIR was activated and after timeout 94 static bit PIRactivated; 95 96 // Detect DPA event to handle 97 switch ( GetDpaEvent() ) 98 { 99 // ------------------------------------------------- 100 case DpaEvent_Interrupt: 101 // Do an extra quick background interrupt work 102 // ! 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. 103 // ! 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. 104 // ! 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. 105 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 106 // ! Make sure race condition does not occur when accessing those variables at other places. 107 // ! 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. 108 // ! Do not call any OS functions except setINDFx(). 109 // ! Do not use any OS variables especially for writing access. 110 // ! 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. 111 112 // PIR timeout counter 113 static uns8 PIRtimeout; 114 115 // If TMR6 interrupt occurred 116 if ( TMR6IF ) 117 { 118 // Unmask interrupt 119 TMR6IF = 0; 120 // Decrement count 121 if ( --tmrCounter40ms == 0 ) 122 { 123 // 1 s is over 124 StartSecond(); 125 126 // Decrement PIR counter 127 if ( PIRtimeout != 0 ) 128 PIRtimeout--; 129 130 // Decrement light counter 131 if ( lightTimeout != 0 ) 132 { 133 lightTimeout--; 134 if ( lightTimeout == 0 ) 135 // Switch off the light 136 LIGHT = 0; 137 } 138 } 139 } 140 141 // Last state of PIR input 142 static bit lastPIR; 143 144 // PIR is on? 145 if ( PIR ) 146 { 147 // Was PIR off and PIR timeout is over and not disabled? 148 if ( !lastPIR && PIRtimeout == 0 && !PeripheralRam[0].0 ) 149 { 150 // Initializes PIR timeout counter 151 PIRtimeout = PIRdelay; 152 // PIR is activated 153 PIRactivated = TRUE; 154 } 155 156 lastPIR = TRUE; 157 } 158 else 159 lastPIR = FALSE; 160 161 DpaHandleReturnTRUE: 162 return TRUE; 163 164 // ------------------------------------------------- 165 case DpaEvent_Idle: 166 // Do a quick background work when RF packet is not received 167 168 // PIR was activated, send the P2P packet 169 if ( PIRactivated ) 170 { 171 // Note: Here Listen before Talk technique should be implemented to avoid collisions 172 173 // PIR not activated any more 174 PIRactivated = FALSE; 175 176 // Save RF settings and set new ones 177 setNonetMode(); 178 setRFmode( _TX_STD | _STDL ); 179 180 // Prepare P2P packet 181 // Header 182 bufferRF[0] = 'P'; 183 // Address 184 bufferRF[1] = ntwADDR; 185 // Packet length 186 DLEN = 2; 187 PIN = 0; 188 // Transmit the prepared packet 189 RFTXpacket(); 190 191 // Restore RF settings 192 DpaApiSetRfDefaults(); 193 setNodeMode(); 194 195 LightOn(); 196 } 197 198 break; 199 200 // ------------------------------------------------- 201 case DpaEvent_PeerToPeer: 202 // Called when peer-to-peer (non-networking) packet is received 203 204 // Note: peer-to-peer packet should be better protected when not used at this example 205 206 // Is my peer-to-peer packet (check length and content)? 207 if ( DLEN == 2 && bufferRF[0] == 'P' ) 208 { 209 // Read list of condition bytes from EEPROM[1..x] 210 eeReadData( PERIPHERAL_EEPROM_START + 1, ADDR_LIST_LEN ); 211 // Force list end 212 bufferINFO[ADDR_LIST_LEN] = 0; 213 // Pointer before 1st address 214 FSR0 = bufferINFO - 1; 215 // Loop the list 216 while ( *++FSR0 != 0 ) 217 { 218 // Any address or address match? 219 if ( *FSR0 == 0xff || *FSR0 == bufferRF[1] ) 220 { 221 LightOn(); 222 break; 223 } 224 225 if ( *FSR0 > MAX_ADDRESS ) 226 { 227 // Compute absolute address difference 228 int8 addrDiff = bufferRF[1] - ntwADDR; 229 if ( addrDiff < 0 ) 230 addrDiff = -addrDiff; 231 232 // Get maximum difference 233 uns8 absDelta = *FSR0 - MAX_ADDRESS; 234 // Is max. difference met? 235 if ( (uns8)addrDiff <= absDelta ) 236 { 237 LightOn(); 238 break; 239 } 240 } 241 } 242 } 243 244 break; 245 246 // ------------------------------------------------- 247 case DpaEvent_Init: 248 // Do a one time initialization work before main loop starts 249 250 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 251 #if F_OSC == 16000000 252 PR6 = 250 - 1; 253 T6CON = 0b0.1001.1.11; // Prescaler 64, Postscaler 10, 64 * 10 * 250 = 160000 = 4MHz * 40ms 254 #else 255 #error Unsupported oscillator frequency 256 #endif 257 258 TMR6IE = TRUE; 259 260 // Initialize 1 s timer 261 StartSecond(); 262 263 RefreshLightDelay: 264 // Read light delay from EEPROM[0] 265 lightTimeoutSetting = eeReadByte( PERIPHERAL_EEPROM_START + 0 ); 266 break; 267 268 // ------------------------------------------------- 269 case DpaEvent_Notification: 270 // Called after DPA request was processed and after DPA response was sent 271 272 // Anything written to the EEPROM? 273 // (could be optimized by checking the EEPROM address that was written to) 274 if ( _PNUM == PNUM_EEPROM && _PCMD == CMD_EEPROM_WRITE ) 275 goto RefreshLightDelay; 276 277 break; 278 279 // ------------------------------------------------- 280 case DpaEvent_AfterSleep: 281 // Called on wake-up from sleep 282 TMR6IE = TRUE; 283 TMR6ON = TRUE; 284 break; 285 286 // ------------------------------------------------- 287 case DpaEvent_BeforeSleep: 288 // Called before going to sleep (the same handling as DpaEvent_DisableInterrupts event) 289 290 // ------------------------------------------------- 291 case DpaEvent_DisableInterrupts: 292 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 293 // Must not use TMR6 any more 294 TMR6ON = FALSE; 295 TMR6IE = FALSE; 296 break; 297 } 298 299 return FALSE; 300 } 301 302 //############################################################################################ 303 void LightOn() 304 //############################################################################################ 305 { 306 lightTimeout = lightTimeoutSetting; 307 LIGHT = 1; 308 StartSecond(); 309 } 310 311 //############################################################################################ 312 void StartSecond() 313 //############################################################################################ 314 { 315 tmrCounter40ms = 1000 / 40; 316 } 317 318 //############################################################################################ 319 // 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) 320 #include "DPAcustomHandler.h" 321 //############################################################################################