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