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