1 // ********************************************************************* 2 // IQD-RC4-01 Example 3 // ********************************************************************* 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-IQD-RC4-01.c,v $ 7 // Version: $Revision: 1.9 $ 8 // Date: $Date: 2025/06/27 15:47:48 $ 9 // 10 // Revision history: 11 // 2025/05/30 Release for DPA 4.33 12 // 2024/04/17 Release for DPA 4.31 13 // 14 // ********************************************************************* 15 16 // Online DPA documentation https://doc.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 example is a template for the IQD-RC4-01 remote control. ** 27 // Please note that the LED indications described below are for development purposes and may not be retained in the final product. 28 // In any device state, the DPA Menu can be opened by simultaneously pressing SW1 & SW4 (see AllowOpenDpaMenu). Then release one of the SW1 or SW4 buttons and use the other to control the DPA Menu. 29 // * ReadyToBond state: 30 // Default behavior and content of the DPA Menu. 31 // * Online state: 32 // The 1st item of the OnLine DPA Menu switches from the OnLine to the Beaming state. 33 // After 2 minutes of RF inactivity, the device switches from the OnLine to the Beaming state. 34 // * Beaming state: 35 // Entering and leaving the Beaming state are signaled by the LEDR & LEDG pulse. 36 // The 3rd (user) DPA Beaming Menu item exits Beaming to OnLine state. 37 // This template allows you to record single and multiple button presses as well as their combination with an optional final long button press. 38 // Each button press is indicated by a very short LEDG flash. 39 // Pressing and releasing SW1 to SW4 pulses the LED(s) 1 to 4 times. 40 // Pressing and releasing SW2 & SE3 simultaneously flashes the LED(s) 5 times. 41 // If the press is long no more presses are recorded and then both LEDR & LEDG pulse, otherwise only LEDG pulses. 42 // The number of presses greater than 1 is finally indicated by the corresponding number of LEDR pulses. 43 // 44 // Examples of button presses and corresponding LED indication: 45 // [a] short SW1 => one LEDG pulse 46 // [b] short SW1, gap, short SW1 => one LEDG pulse and then two LEDR pulses 47 // [c] long SW4 => four LEDG & LEDR pulses 48 // [d] short SW2, gap, short SW2, gap, long SW2 => two LEDG & LEDR pulses and then three LEDR pulses 49 // 50 // * StandBy state: 51 // Default behavior and content of the DPA Menu. 52 53 //############################################################################################ 54 55 #if defined( TR7xD ) 56 #error This example is intended for TR7xG 57 #endif 58 59 #define _HWPID_ 0x321F 60 #define _HWPIDver_ 0xBabe 61 62 // Timeout in seconds to start beaming in case of network inactivity (655 seconds is maximum) 63 #define BEAMING_TIMEOUT 120 64 65 // SW* GPIOs. Pressing any SW* also "presses" IQRF button at PORTB.4 66 #define SW1 PORTC.7 67 #define SW2 PORTC.6 68 #define SW3 PORTA.0 69 #define SW4 PORTC.2 70 71 // Switch coding flags 72 #define SW1flag 0b0.0001 73 #define SW2flag 0b0.0010 74 #define SW3flag 0b0.0100 75 #define SW4flag 0b0.1000 76 #define SWlongFlag 0b1.0000 77 78 // Executes beaming mode 79 void Beaming(); 80 // Checks the condition for DPA Menu to open 81 bit AllowOpenDpaMenu(); 82 83 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 84 //############################################################################################ 85 // https://doc.iqrf.org/DpaTechGuide/pages/custom-dpa-handler.html 86 bit CustomDpaHandler() 87 //############################################################################################ 88 { 89 // TRUE to start beaming at Idle event 90 static bit startBeamingAtIdle; 91 92 // Handler presence mark 93 clrwdt(); 94 95 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 96 switch ( GetDpaEvent() ) 97 { 98 // ------------------------------------------------- 99 case DpaEvent_Interrupt: 100 // Do an extra quick background interrupt work 101 // ! 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. 102 // ! 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. 103 // ! 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. 104 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 105 // ! Make sure race condition does not occur when accessing those variables at other places. 106 // ! 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. 107 // ! Do not call any OS functions except setINDFx(). 108 // ! Do not use any OS variables especially for writing access. 109 // ! 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. 110 // https://doc.iqrf.org/DpaTechGuide/pages/EventInterrupt.html 111 112 // Note: IQRF OS does IOCBF = 0; 113 return Carry; 114 115 // ------------------------------------------------- 116 case DpaEvent_Idle: 117 // Do a quick background work when RF packet is not received 118 // https://doc.iqrf.org/DpaTechGuide/pages/idle.html 119 120 if ( startBeamingAtIdle ) 121 { 122 startBeamingAtIdle = FALSE; 123 // Go to Beaming mode 124 Beaming(); 125 goto _startCapture; 126 } 127 128 // Start beaming after a defined inactivity time 129 captureTicks(); 130 if ( param3 > (uns16)BEAMING_TIMEOUT * 100 ) 131 { 132 if ( ntwADDR != TEMPORARY_ADDRESS ) 133 Beaming(); 134 goto _startCapture; 135 } 136 break; 137 138 // ------------------------------------------------- 139 case DpaEvent_Reset: 140 // Called after module is reset 141 // https://doc.iqrf.org/DpaTechGuide/pages/ResetEvent.html 142 143 // Unused SPI pins set as output in low level 144 // SS 145 LATA &= 0b1101.1111; 146 TRISA &= 0b1101.1111; 147 // SCK+SDI+SDO 148 LATC &= 0b1100.0111; 149 TRISC &= 0b1100.0111; 150 151 break; 152 153 // ------------------------------------------------- 154 case DpaEvent_Init: 155 // Do a one time initialization before main loop starts 156 // https://doc.iqrf.org/DpaTechGuide/pages/init.html 157 158 goto _startCapture; 159 160 // ------------------------------------------------- 161 case DpaEvent_ReceiveDpaRequest: 162 // Called after DPA request was received 163 // https://doc.iqrf.org/DpaTechGuide/pages/receivedparequest.html 164 165 if ( _ROUTEF ) 166 { 167 // Start inactivity timer 168 _startCapture: 169 startCapture(); 170 } 171 break; 172 173 // ------------------------------------------------- 174 case DpaEvent_MenuActivated: 175 // Called to customize DPA menu 176 // https://doc.iqrf.org/DpaTechGuide/pages/menuactivated.html 177 178 // Do not open DPA Menu? 179 if ( !AllowOpenDpaMenu() ) 180 { 181 // Yes, do not open event the IQRF button at RB4 is low 182 userReg1 = DMENU_Ext_DoNotOpen; 183 goto DpaHandleReturnFALSE; 184 } 185 186 // Only OnLine menu is customized, exit the event at others 187 if ( userReg1 != DMENU_Online ) 188 goto DpaHandleReturnFALSE; 189 190 // Customize OnLine DPA Menu 191 userReg1 = DMENU_Item_Implemented_Beaming; 192 goto DpaHandleReturnTRUE; 193 194 // ------------------------------------------------- 195 case DpaEvent_MenuItemSelected: 196 // Called to indicate "OK" or "Error" for selected menu item 197 // https://doc.iqrf.org/DpaTechGuide/pages/menuitemselected.html 198 199 // Which menu item? 200 switch ( userReg1 ) 201 { 202 // Start beaming? 203 case MakeDMenuAndItem( DMENU_Online, DMENU_Item_Beaming ): 204 if ( amIBonded() ) 205 { 206 startBeamingAtIdle = TRUE; 207 goto DpaHandleReturnTRUE; // return TRUE to indicate "OK" for menu item specified by userReg1, otherwise to indicate Error 208 } 209 break; 210 } 211 break; 212 213 // ------------------------------------------------- 214 case DpaEvent_DpaRequest: 215 // Called to interpret DPA request for peripherals 216 // https://doc.iqrf.org/DpaTechGuide/pages/EventDpaRequest.html 217 if ( IsDpaEnumPeripheralsRequest() ) 218 { 219 // ------------------------------------------------- 220 // Peripheral enumeration 221 // https://doc.iqrf.org/DpaTechGuide/pages/enumerate-peripherals.html 222 223 _DpaMessage.EnumPeripheralsAnswer.HWPID |= _HWPID_; 224 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= _HWPIDver_; 225 226 DpaHandleReturnTRUE: 227 return TRUE; 228 } 229 break; 230 } 231 232 DpaHandleReturnFALSE: 233 return FALSE; 234 } 235 236 //############################################################################################ 237 uns8 GetButtons() 238 //############################################################################################ 239 { 240 uns8 btns @ W; // Note: optimization using 241 btns = 0; 242 if ( SW1 ) // Note: must not modify W 243 btns |= SW1flag; 244 if ( SW2 ) // Note: must not modify W 245 btns |= SW2flag; 246 if ( SW3 ) // Note: must not modify W 247 btns |= SW3flag; 248 if ( SW4 ) // Note: must not modify W 249 btns |= SW4flag; 250 251 return btns; 252 } 253 254 //############################################################################################ 255 void ReleaseButtons() 256 //############################################################################################ 257 { 258 // Works only at beaming mode because of using DpaApiSleep 259 do 260 { 261 _DpaApiDeepSleep( WDTCON_2ms ); 262 } while ( GetButtons() ); 263 } 264 265 //############################################################################################ 266 void Beaming() 267 //############################################################################################ 268 { 269 // Indicate start of beaming 270 setLEDG(); 271 setLEDR(); 272 // Longer LEDs 273 waitMS( 200 ); 274 275 FirstDpaApiSleep = TRUE; 276 for ( ;; ) 277 { 278 // Check for the rare lock of the Spirit1 RF chip 279 if ( wasRFICrestarted() ) 280 _DpaApiSetRfDefaults(); 281 282 // Setup IOC for waking up from the next sleep by a "IQRF button" at RB4 283 IOCBN.4 = TRUE; 284 // Note: is cleared by OS at iqrf[Deep]Sleep 285 IOCIE = TRUE; 286 // Deep sleep until button is pressed 287 _DpaApiDeepSleep( DpaApiSleep_WdtOff ); 288 // Disables port on change settings 289 IOCIE = FALSE; 290 // Clears port on change 291 IOCBF = 0; 292 293 // No SW* pressed, so we just woke up after 256 s? 294 if ( GetButtons() == 0 ) 295 // Then keep sleeping... 296 continue; 297 298 // A gap for the other optional buttons to be pressed by user 299 _DpaApiDeepSleep( WDTCON_64ms ); 300 301 // Condition to open DPA Beaming Menu is met? 302 if ( AllowOpenDpaMenu() ) 303 { 304 // Open Beaming DPA Menu 305 uns8 menuAndItem = DpaApiMenu( DMENU_Beaming, DMENU_Item_Implemented_User1 ); 306 // Which menu item was selected? 307 switch ( menuAndItem ) 308 { 309 // ConnectivityCheck - 1st item 310 case MakeDMenuAndItem( DMENU_Beaming, DMENU_Item_ConnectivityCheck ): 311 // Stop beaming sleep as we will be doing some RF stuff 312 _DpaApiAfterSleep(); 313 setRFready(); 314 // Voluntary indication of the TestRange execution 315 pulsingLEDR(); 316 // Bitmap of repeaters to test at bufferINFO 317 clearBufferINFO(); 318 bufferINFO[0 / 8] = 0b1111.1110; // Nodes 1...7 319 // Do the test and result indication 320 DpaApiMenuIndicateResult( DpaApiLocalFrc( FRC_Ping, TX_POWER_MAX ) ); 321 // Continue with the regular beaming sleep 322 FirstDpaApiSleep = TRUE; 323 break; 324 325 // Abort beaming - 3rd user item 326 case MakeDMenuAndItem( DMENU_Beaming, DMENU_Item_User1 ): 327 // Abort beaming indication 328 setLEDG(); 329 setLEDR(); 330 // Longer LEDs 331 _DpaApiDeepSleep( WDTCON_256ms ); 332 // No more sleep 333 _DpaApiAfterSleep(); 334 // Restore RF settings 335 setRFready(); 336 _DpaApiSetRfDefaults(); 337 // Abort beaming (start inactivity timer at caller) 338 return; 339 340 // Other (default) menu items 341 default: 342 // Stop beaming sleep 343 _DpaApiAfterSleep(); 344 // Execute the menu item 345 DpaApiMenuExecute( menuAndItem ); 346 // Continue with the regular beaming sleep 347 FirstDpaApiSleep = TRUE; 348 break; 349 350 // No menu item was selected 351 case MakeDMenuAndItem( DMENU_Beaming, DMENU_Item_None ): 352 break; 353 } 354 } 355 else 356 { 357 // Beaming DPA Menu will not open, we might do the real beaming based on pressed buttons 358 359 // Button press count 360 uns8 pressCnt = 0; 361 // Current active pressed button(s) 362 uns8 btns = GetButtons(); 363 364 _Press: 365 // Voluntary very short pulse button(s) press indication 366 setLEDG(); 367 _DpaApiDeepSleep( WDTCON_2ms ); 368 stopLEDG(); 369 370 // Count the press 371 pressCnt++; 372 // Long 500 ms button timeout variable 373 uns8 longPressTimeout = 500 / 8/*see delay below*/ + 0.5; 374 do 375 { 376 _DpaApiDeepSleep( WDTCON_8ms ); 377 // Same button(s)? 378 if ( GetButtons() != btns ) 379 { 380 // No, test for 200 ms whether the same button(s) combination repeats 381 uns8 againCnt = 200 / 8/*see delay below*/ + 0.5; 382 do 383 { 384 _DpaApiDeepSleep( WDTCON_8ms ); 385 // Same press? 386 if ( GetButtons() == btns ) 387 // Yes, repeated press, start the main button loop over 388 goto _Press; 389 } while ( --againCnt != 0 ); 390 391 // No more repeats, it is the last short press 392 goto _ShortPress; 393 } 394 } while ( --longPressTimeout != 0 ); 395 // The long press 396 btns |= SWlongFlag; 397 398 _ShortPress: 399 // Release button(s) 400 ReleaseButtons(); 401 // Test the supported button(s) combination 402 uns8 ledPulses; 403 switch ( btns ) 404 { 405 case SW1flag: 406 case SW1flag | SWlongFlag: 407 ledPulses = 1; 408 break; 409 410 case SW2flag: 411 case SW2flag | SWlongFlag: 412 ledPulses = 2; 413 break; 414 415 case SW3flag: 416 case SW3flag | SWlongFlag: 417 ledPulses = 3; 418 break; 419 420 case SW4flag: 421 case SW4flag | SWlongFlag: 422 ledPulses = 4; 423 break; 424 425 case SW2flag | SW3flag: 426 case SW2flag | SW3flag | SWlongFlag: 427 ledPulses = 5; 428 break; 429 430 default: 431 ledPulses = 0; 432 break; 433 } 434 435 // Do the LED action if any supported SW? combination was pressed 436 if ( ledPulses != 0 ) 437 { 438 // Indicate SW? by LEDG (& LEDR) pulse(s) 439 do 440 { 441 setLEDG(); 442 // Was long press? 443 if ( btns & SWlongFlag ) 444 setLEDR(); 445 446 _DpaApiDeepSleep( WDTCON_128ms ); 447 stopLEDG(); 448 stopLEDR(); 449 _DpaApiDeepSleep( WDTCON_256ms ); 450 } while ( --ledPulses != 0 ); 451 452 // Indicate number of presses from 2 up by LEDR pulses 453 if ( pressCnt > 1 ) 454 do 455 { 456 _DpaApiDeepSleep( WDTCON_256ms ); 457 setLEDR(); 458 _DpaApiDeepSleep( WDTCON_128ms ); 459 stopLEDR(); 460 } while ( --pressCnt != 0 ); 461 } 462 463 // Wait for button(s) to be released 464 ReleaseButtons(); 465 } 466 } 467 } 468 469 //############################################################################################ 470 bit AllowOpenDpaMenu() 471 //############################################################################################ 472 { 473 return GetButtons() == ( SW1flag | SW4flag ); 474 } 475 476 //############################################################################################ 477 // 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) 478 #include "DPAcustomHandler.h" 479 //############################################################################################