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 //############################################################################################