1 // ********************************************************************* 2 // Custom DPA Handler code example - Scanning RSSI of neighbors * 3 // ********************************************************************* 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-ScanRSSI.c,v $ 7 // Version: $Revision: 1.48 $ 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 // Uncomment to compile Custom DPA Handler for Coordinator 25 //#define COORDINATOR_CUSTOM_HANDLER 26 27 // Default DPA header (modify the path according to your setup) 28 #include "DPA.h" 29 // Default Custom DPA Handler header (modify the path according to your setup) 30 #include "DPAcustomHandler.h" 31 32 // This example implements measurement of RSSI among all nodes of the IQRF network. This handler must be uploaded to all Nodes and with COORDINATOR_CUSTOM_HANDLER symbol defined to the coordinator too. 33 // To measure the RSSIs use FRC with command FRC_AcknowledgedBroadcastBits = 0x02 with DPA request for custom peripheral PNUM=0x20, PCMD=0, HWPID=0x001F and data corresponding to the TScanRSSI structure. 34 // When FRC is finished then the same peripheral at C must called. C then sends TScanPacket packet. Then come short 10 timeslots when all Ns send their TScanPacket packets. Timeslots are ordered according to the Ns' VRNs. 35 // When the measurement is finished then PNUM=0x20, PCMD=1 and PNUM=0x20, PCMD=2 are used to read RSSI levels from all nodes. 36 // The result is a bit array of bit pairs i.e. 2 bits for every node at the network. Bit pairs are ordered according the node address. Bits 0b00 indicated that the node of such address was not "listened" at all. 37 // 0b01 means low RSSI, 0b10 means middle RSSI and 0b11 specifies high RSSI 38 // ! Please note that this example stores RSSI levels in the bufferAUX memory buffer which is not generally recommended because bufferAUX is used internally by DPA for certain purposes (i.e. storing batches). 39 // ! Therefore it is important to read levels immediately after RSSI is measure otherwise levels might be overwritten. 40 // This example works only at STD mode, not at LP mode 41 42 // Structure holding data for custom peripheral PNUM=0x20, PCMD=0. This command initiates measurement of RSSI among nodes. It must be used only as a part of FRC_AcknowledgedBroadcastBits. 43 typedef struct 44 { 45 // Maximum VRN in the network (equals number of discovered nodes in the network) 46 uns8 MaxVrn; 47 // TX power used to measure RSSI 48 uns8 TxPower; 49 // If RSSI is equal of less than this number, it gets the level 1 50 uns8 RssiLow; 51 // If RSIS is equal of less than this number, it gets the level 2. If RSSI is greater then it gets code 3. 52 uns8 RssiMid; 53 } TScanRSSI; 54 55 // Structure of the packet used to measure RSSI. 56 typedef struct 57 { 58 // Packet header. equals 'N' 59 uns8 Header; 60 // Address of the node that sent the packet 61 uns8 Address; 62 } TScanPacket; 63 64 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 65 //############################################################################################ 66 bit CustomDpaHandler() 67 //############################################################################################ 68 { 69 // Handler presence mark 70 clrwdt(); 71 72 #ifndef COORDINATOR_CUSTOM_HANDLER 73 // Place for local static variables used only within CustomDpaHandler() among more events 74 static uns8 timer10msCounter; 75 #endif 76 77 // Detect DPA event to handle 78 switch ( GetDpaEvent() ) 79 { 80 #ifndef COORDINATOR_CUSTOM_HANDLER 81 // ------------------------------------------------- 82 case DpaEvent_Interrupt: 83 // Do an extra quick background interrupt work 84 // ! 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. 85 // ! 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. 86 // ! 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. 87 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 88 // ! Make sure race condition does not occur when accessing those variables at other places. 89 // ! 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. 90 // ! Do not call any OS functions except setINDFx(). 91 // ! Do not use any OS variables especially for writing access. 92 // ! 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. 93 94 // If TMR6 interrupt occurred 95 if ( TMR6IF ) 96 { 97 // Unmask interrupt 98 TMR6IF = 0; 99 // Increment count 100 timer10msCounter++; 101 } 102 103 return Carry; 104 105 // ------------------------------------------------- 106 case DpaEvent_Init: 107 // Do a one time initialization before main loop starts 108 { 109 // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 110 #if defined( TR7xG ) 111 TMR6MD = 0; 112 T6CON = 0b1.100.1001; 113 // Timer2/4/6 Clock Select bits = FOSC/4 114 T6CLKCON |= 0b0000.0001; 115 #else 116 T6CON = 0b0.1001.1.10; 117 #endif 118 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 119 _PR6 = 250 - 1; 120 121 TMR6IE = TRUE; 122 } 123 break; 124 125 // ------------------------------------------------- 126 case DpaEvent_AfterSleep: 127 // Called on wake-up from sleep 128 TMR6IE = TRUE; 129 _TMR6ON = TRUE; 130 break; 131 132 // ------------------------------------------------- 133 case DpaEvent_BeforeSleep: 134 // Called before going to sleep (the same handling as DpaEvent_DisableInterrupts event) 135 136 // ------------------------------------------------- 137 case DpaEvent_DisableInterrupts: 138 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 139 // Must not use TMR6 any more 140 _TMR6ON = FALSE; 141 TMR6IE = FALSE; 142 break; 143 144 #endif 145 146 // ------------------------------------------------- 147 case DpaEvent_DpaRequest: 148 // Called to interpret DPA request for peripherals 149 // ------------------------------------------------- 150 // Peripheral enumeration 151 if ( IsDpaEnumPeripheralsRequest() ) 152 { 153 // We implement 1 user peripheral 154 _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1; 155 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 156 _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x001F; 157 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000; 158 159 DpaHandleReturnTRUE: 160 return TRUE; 161 } 162 // ------------------------------------------------- 163 // Get information about peripheral 164 else if ( IsDpaPeripheralInfoRequest() ) 165 { 166 if ( _PNUM == PNUM_USER + 0 ) 167 { 168 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 169 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 170 goto DpaHandleReturnTRUE; 171 } 172 173 break; 174 } 175 // ------------------------------------------------- 176 else 177 { 178 // Handle peripheral 179 if ( _PNUM == PNUM_USER + 0 ) 180 { 181 switch ( _PCMD ) 182 { 183 // Initiate RSSI measurement 184 case 0: 185 { 186 // Check data length 187 if ( _DpaDataLength != sizeof( TScanRSSI ) ) 188 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 189 190 #ifndef COORDINATOR_CUSTOM_HANDLER 191 // At Node it can be called only from FRC acknowledged broadcast, otherwise it is not synchronized! 192 if ( _NADR != BROADCAST_ADDRESS ) 193 DpaApiReturnPeripheralError( ERROR_NADR ); 194 // NOde with temporary address does not measure 195 if ( ntwADDR == TEMPORARY_ADDRESS ) 196 DpaApiReturnPeripheralError( ERROR_FAIL ); 197 #endif 198 // Request data 199 TScanRSSI ScanRSSI @ _DpaMessage.Request.PData; 200 // Copy of request data 201 TScanRSSI ScanRSSICopy; 202 // Make a copy 203 copyMemoryBlock( &ScanRSSI, &ScanRSSICopy, sizeof( ScanRSSICopy ) ); 204 205 // Set non-network RF settings 206 setNonetMode(); 207 setNetworkFilteringOff(); 208 setRFmode( _WPE | _RX_STD | _TX_STD | _STDL ); 209 setRFpower( ScanRSSICopy.TxPower ); 210 211 // Scanning packet 212 TScanPacket ScanPacket @ bufferRF; 213 214 #ifndef COORDINATOR_CUSTOM_HANDLER 215 // Clear bufferAUX 216 clearBufferINFO(); 217 swapBufferINFO(); 218 219 // Give C some extra time to sent its ScanPacket packet 220 waitDelay( 3 ); 221 222 // We use the shortest RX timeout 223 toutRF = 1; 224 // Initialize timer, maximize gap before measurement starts 225 timer10msCounter = MAX_ADDRESS + 2; 226 // We did not send out packet yet 227 bit sent = FALSE; 228 // Loop all timeslots 229 for ( ;; ) 230 { 231 // If we receive ScanPacket from other node 232 if ( checkRF( 0 ) && RFRXpacket() && DLEN == sizeof( ScanPacket ) && ScanPacket.Header == 'N' && ScanPacket.Address <= MAX_ADDRESS ) 233 { 234 // bits variable stores the RSSI level from 1 to 3 according to the thresholds RssiLow and RssiMid 235 uns8 bits @ userReg0; 236 bits = 0b01; 237 if ( lastRSSI > ScanRSSICopy.RssiLow ) 238 { 239 bits++; 240 if ( lastRSSI > ScanRSSICopy.RssiMid ) 241 bits++; 242 } 243 244 // Shift level to the correct bit position according to the node address value 245 uns8 temp @ userReg1; 246 temp = ScanPacket.Address % 4; 247 while ( temp != 0 ) 248 { 249 temp--; 250 bits <<= 2; 251 } 252 253 // Compute byte address according to the node address value 254 FSR0 = ScanPacket.Address / 4; 255 FSR0 += bufferAUX; 256 257 // Combine bits with already stored ones 258 setINDF0( *FSR0 | bits ); 259 } 260 261 // Any timeslots pending? 262 if ( timer10msCounter != ScanRSSICopy.MaxVrn + 1 ) 263 { 264 // Is it my timeslot and the packet was not sent yet? 265 if ( timer10msCounter == ntwVRN && !sent ) 266 { 267 // Packet will be sent 268 sent = TRUE; 269 #endif 270 // Prepare the packet 271 ScanPacket.Header = 'N'; 272 #ifndef COORDINATOR_CUSTOM_HANDLER 273 ScanPacket.Address = ntwADDR; 274 #else 275 ScanPacket.Address = COORDINATOR_ADDRESS; 276 #endif 277 DLEN = sizeof( ScanPacket ); 278 PIN = 0; 279 // Send the packet 280 RFTXpacket(); 281 282 #ifndef COORDINATOR_CUSTOM_HANDLER 283 } 284 } 285 else 286 // Timeslots are over. Exit the loop. 287 break; 288 } 289 #endif 290 // Restore RF settings 291 DpaApiSetRfDefaults(); 292 setNetworkFilteringOn(); 293 // REstore network settings 294 #ifndef COORDINATOR_CUSTOM_HANDLER 295 setNodeMode(); 296 #else 297 setCoordinatorMode(); 298 #endif 299 300 goto DpaHandleReturnTRUE; 301 } 302 303 #ifndef COORDINATOR_CUSTOM_HANDLER 304 case 2: 305 // Return 2nd part of the RSSI bitmaps 306 memoryOffsetFrom = DPA_MAX_DATA_LENGTH; 307 308 case 1: 309 // Check data length 310 if ( _DpaDataLength != 0 ) 311 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 312 313 // Copy result from bufferAUX to the DPA response @ bufferRF 314 swapBufferINFO(); 315 copyBufferINFO2RF(); 316 swapBufferINFO(); 317 318 // Return correct data length 319 if ( _PCMD == 2 ) 320 _DpaDataLength = ( MAX_ADDRESS + 1 ) / 4 - DPA_MAX_DATA_LENGTH; 321 else 322 _DpaDataLength = DPA_MAX_DATA_LENGTH; 323 324 goto DpaHandleReturnTRUE; 325 #endif 326 } 327 } 328 } 329 } 330 331 return FALSE; 332 } 333 //############################################################################################ 334 // 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) 335 #include "DPAcustomHandler.h" 336 //############################################################################################