1 // ********************************************************************* 2 // Custom DPA Handler - Beaming aggregation example * 3 // ********************************************************************* 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-BeamingAggregation.c,v $ 7 // Version: $Revision: 1.18 $ 8 // Date: $Date: 2021/09/07 14:22:28 $ 9 // 10 // Revision history: 11 // 2021/08/20 Release for DPA 4.16 12 // 13 // ********************************************************************* 14 15 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 16 17 /* 18 * This is an example of the beaming aggregating repeater. 19 * It is recommended to start with CustomDpaHandler-FrcAggregation.c to fully understand FRC aggregation concept. 20 * 21 * This example stores received data from beaming sensors (see example CustomDpaHandler-SensorBeaming.c) into available RAM using linear addressing. 22 * When IQRF Standard Sensor FRC is requested, the code aggregates formerly received beaming sensor data into the returning FRC data. 23 * The code also shows, how to convert from native FRC into non-native FRC in case of quantities, that support more FRC commands (Temperature only in this example). 24 * The following quantities support non-native FRCs: Temperature, CO2, VOC, BinaryData7, BinaryData30. Normally conversion for all these quantities must be implemented. 25 */ 26 27 // Default IQRF include (modify the path according to your setup) 28 #include "IQRF.h" 29 30 // Default DPA header (modify the path according to your setup) 31 #include "DPA.h" 32 // Default Custom DPA Handler header (modify the path according to your setup) 33 #include "DPAcustomHandler.h" 34 // IQRF standards header (modify the path according to your setup) 35 #include "standard\IQRFstandard.h" 36 37 // One beaming DB record 38 typedef struct 39 { 40 // Node address 41 uns8 Addr; 42 // Data length (non-zero) 43 uns8 DataLength; 44 // Data 45 uns8 Data[0]; 46 } TDbRecord; 47 // This DB implementation limitations: 48 // Once the record for the certain node is allocated in the DB, it cannot be enlarged in case more data is received for the same node later. 49 // Once the record for the certain node is allocated in the DB, its place cannot be used for another node even though all record data are invalidated. 50 51 // The place from the end of the bank11 (UserBank_01) used as a linear DB 52 #define DB_BANK11_SIZE 46 53 // Total linear DB space is also the adjacent bank12 (UserBank_02) (i.e. PeripheralRam) 54 #define TOTAL_DB_SIZE ( DB_BANK11_SIZE + sizeof( PeripheralRam ) ) 55 // DB address 56 #define DB_ADDRESS ( 0x70 + UserBank_01 * 0x80 - DB_BANK11_SIZE ) 57 // Linear DB address 58 #define LINEAR_DB_ADDRESS ( (uns16)( 0x70 - 0x20 ) * ( UserBank_01 + 1 ) - DB_BANK11_SIZE + 0x2000 ) 59 // Reserve this space at bank11 against CC5X 60 uns8 _reserveDBatBank11[DB_BANK11_SIZE] @ ( 0x70 + UserBank_01 * 0x80 - DB_BANK11_SIZE ); 61 62 // Auxiliary functions 63 void FindNodeAtDb( uns8 node ); 64 void FSR1toDB(); 65 void CopyQuantity1B(); 66 67 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 68 //############################################################################################ 69 bit CustomDpaHandler() 70 //############################################################################################ 71 { 72 #pragma updateBank default = UserBank_01 73 74 // Handler presence mark 75 clrwdt(); 76 77 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 78 switch ( GetDpaEvent() ) 79 { 80 // ------------------------------------------------- 81 case DpaEvent_Interrupt: 82 // Do an extra quick background interrupt work 83 return Carry; 84 85 // ------------------------------------------------- 86 case DpaEvent_ReceiveDpaRequest: 87 // Called after DPA request was received 88 89 // Beaming packet received and beaming command we understand? 90 if ( !_ROUTEF && _PNUM == PNUM_STD_SENSORS && _PCMD == ( PCMD_STD_SENSORS_READ_TYPES_AND_FRC_VALUES | RESPONSE_FLAG ) ) 91 { 92 // Indicate beaming was received 93 pulseLEDG(); 94 // Find record for the TX we received the packet from 95 FindNodeAtDb( TX ); 96 // Found? 97 if ( FSR1[offsetof( TDbRecord, Addr )] != 0 ) 98 { 99 // Record found. Is the size same as before? 100 if ( FSR1[offsetof( TDbRecord, DataLength )] != _DpaDataLength ) 101 // No! Error! 102 goto _IndicateDbError; 103 104 // Save the quantity data into DB 105 _CopyRecordData: 106 FSR1 += offsetof( TDbRecord, Data ); 107 setFSR0( _FSR_DPA ); 108 copyMemoryBlock( FSR0, FSR1, _DpaDataLength ); 109 } 110 else 111 { 112 // No, record is missing, let's add a new record plus data to the DB 113 114 // Check if there is an enough space in the DB for a new record 115 FSR0 = FSR1 - LINEAR_DB_ADDRESS + sizeof( TDbRecord ); 116 // The DB length + maximum packet length cannot be more than 0xFF, so we can use just the lower byte 117 FSR0L += _DpaDataLength; 118 if ( FSR0L >= TOTAL_DB_SIZE ) 119 { 120 _IndicateDbError: 121 // No, error, record cannot be saved 122 pulseLEDR(); 123 } 124 else 125 { 126 // Store address of the N 127 FSR1 += offsetof( TDbRecord, Addr ); 128 setINDF1( TX ); 129 // Store data length 130 FSR1 += offsetof( TDbRecord, DataLength ) - offsetof( TDbRecord, Addr ); 131 setINDF1( _DpaDataLength ); 132 // Restore pointer 133 FSR1 -= offsetof( TDbRecord, DataLength ); 134 // And store the data 135 goto _CopyRecordData; 136 } 137 } 138 } 139 break; 140 141 // ------------------------------------------------- 142 case DpaEvent_FrcValue: 143 // Called to get FRC value 144 145 // IQRF Sensor FRC structure 146 shadowDef TPerStdSensorFrc PerStdSensorFrc @ ( &DataOutBeforeResponseFRC[0] ); 147 148 // Process only certain IQRF Std sensor FRC commands (FRC_STD_SENSORS_BIT for STD_SENSOR_TYPE_BINARYDATA7 is not implemented in this example) 149 if ( PerStdSensorFrc.Header == PNUM_STD_SENSORS ) 150 { 151 // What is the required FRC values size? 152 uns8 frcValueSize @ param4.low8; 153 frcValueSize = 2; 154 switch ( _PCMD ) 155 { 156 case FRC_STD_SENSORS_1B: 157 frcValueSize += 1 - 2 - ( 4 - 2 ); 158 // Fall through 159 160 case FRC_STD_SENSORS_4B: 161 frcValueSize += ( 4 - 2 ); 162 // Fall through 163 164 case FRC_STD_SENSORS_2B: 165 { 166 // Prepare for zero FRC values from myself (could be a real value to return) and other [Ns] 167 #if !defined( __CC5XFREE__ ) 168 responseFRCvalue4B = FRC_STD_FRC_NOT_IMPLEMENTED_VALUE; 169 #else 170 responseFRCvalue4B.low16 = FRC_STD_FRC_NOT_IMPLEMENTED_VALUE; 171 responseFRCvalue4B.high16 = 0; 172 #endif 173 clearBufferINFO(); 174 175 // FSR0 now points to the resulting INFO buffer for the 1st Node (this will skip useless FRC data slot for the coordinator) 176 FSR0L += frcValueSize; 177 // Node number to get data for 178 uns8 node @ param2; 179 node = 0; 180 do 181 { 182 // Is there enough space in the bufferINFO for more FRC values? If not, break the loop and responseFRC 183 if ( FSR0L >= ( &bufferINFO[sizeof( bufferINFO )] & 0xFF ) ) 184 break; 185 186 // Is the node selected for this FRC? 187 addressBitmap( ++node ); 188 #if &bufferRF[0] != &AddressedNodesBeforeResponseFRC[0] 189 #error Cannot optimize 190 #endif 191 setFSR1( _FSR_RF ); 192 FSR1L += bitmapByteIndex; // Note: FSR0L will not overflow 193 194 // IQRF OS bit indicating selective FRC (bit.0 at the 1st byte behind AddressedNodesBeforeResponseFRC) 195 bit isSelectiveFrc @ AddressedNodesBeforeResponseFRC[sizeof( AddressedNodesBeforeResponseFRC )].0; 196 // W = bitmap byte 197 W = *FSR1; 198 if ( !isSelectiveFrc ) // Note: must not modify W 199 W = 0xFF; // Not selective FRC => all nodes are selected 200 // Is Nodes selected? 201 if ( W & bitmapBitMask ) 202 { 203 // Try to find the node's record in the DB 204 FindNodeAtDb( node ); 205 if ( FSR1[offsetof( TDbRecord, Addr )] != 0 ) 206 { 207 // Prepare FRC Value for the "FRC command not supported" 208 setINDF0( FRC_STD_FRC_NOT_IMPLEMENTED_VALUE ); 209 // Now find the FRC value in the DB record for the (un)specified quantity and index 210 uns8 dataLength @ param3.low8; 211 dataLength = FSR1[offsetof( TDbRecord, DataLength )]; 212 // Sensor index to find 213 uns8 sensorIndex @ param3.high8; 214 sensorIndex = PerStdSensorFrc.SensorIndex + 1; 215 // FSR1 points to the 1st quantity 216 FSR1 += offsetof( TDbRecord, Data ); 217 do 218 { 219 // Decode quantity size 220 uns8 quantitySizePlus1 = 2 + 1; 221 // Not 2B quantity? 222 if ( ( FSR1[0] & 0b1000.0000 ) != 0b0000.0000 ) 223 { 224 // 1B quantity 225 quantitySizePlus1 += 1 + 1 - ( 2 + 1 ); 226 // 4B quantity? 227 if ( ( FSR1[0] & 0b1110.0000 ) == 0b1010.0000 ) 228 quantitySizePlus1 = 4 + 1; 229 } 230 231 // Does the quantity type and its index match? 232 if ( ( PerStdSensorFrc.SensorType == FSR1[0] || PerStdSensorFrc.SensorType == 0 ) && --sensorIndex == 0 ) 233 { 234 // Quantity found, now check the FRC data size required vs. Quantity size 235 if ( frcValueSize != quantitySizePlus1 - 1 ) 236 { 237 // Sizes do not equal 238 // Optionally: in case of temperature compute FRC 1B from provided FRC 2B 239 { // ---------------------------------------------------- 240 if ( _PCMD == FRC_STD_SENSORS_1B && FSR1[0] == STD_SENSOR_TYPE_TEMPERATURE ) 241 { 242 // Get 2B FRC temperature value and reset it in the DB 243 uns16 temperature @ param3; 244 temperature.low8 = *++FSR1; 245 setINDF1( 0 ); 246 temperature.high8 = *++FSR1; 247 setINDF1( 0 ); 248 // Now recompute from 2B FRC to 1B FRC 249 // Is there a temperature value to convert? If not keep 0 even for 1B FRC. 250 if ( temperature != 0 ) 251 { 252 // Convert from FRC value to raw value 253 temperature ^= 0x8000; 254 255 // Check the 1B FRC temperature limits 256 if ( (int16)temperature > (int16)( 105.5 * 16 ) || (int16)temperature < ( (int16)-20 * 16 ) ) 257 // Return sensor error 258 W = FRC_STD_FRC_ERROR_VALUE; 259 else 260 { 261 // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution 262 temperature += 8 / 2 + (uns16)44 * 8; // Note: do rounding for /8 263 // To avoid hidden compiler variable, do 3x /2 for /8 264 temperature /= 2; 265 temperature /= 2; 266 temperature /= 2; 267 W = temperature.low8; 268 } 269 270 // Store 1B FRC value 271 setINDF0( W ); 272 } 273 274 // And finish 275 goto _breakFindQuantity; 276 } 277 } // ---------------------------------------------------- 278 279 goto _breakFindQuantity; 280 } 281 282 // Copy FRC quantity data 283 switch ( _PCMD ) 284 { 285 default: // FRC_STD_SENSORS_4B: 286 CopyQuantity1B(); 287 CopyQuantity1B(); 288 // Fall through 289 290 case FRC_STD_SENSORS_2B: 291 CopyQuantity1B(); 292 // Fall through 293 294 case FRC_STD_SENSORS_1B: 295 CopyQuantity1B(); 296 break; 297 } 298 // Break the "find the quantity loop" 299 goto _breakFindQuantityNoAdvance; 300 } 301 302 // Decrease data length 303 dataLength -= quantitySizePlus1; 304 // Advance DB pointer to the next quantity 305 FSR1 += quantitySizePlus1; 306 // More quantities? 307 } while ( dataLength > 0 ); 308 _breakFindQuantity: 309 } 310 311 // Move pointer to the next FRCvalue in the buffer INFO 312 FSR0L += frcValueSize; 313 314 _breakFindQuantityNoAdvance: 315 } 316 } while ( node < MAX_ADDRESS ); 317 318 // Start FRC value aggregation. 319 // Important: 320 // This node must be discovered and the feature "FRC Aggregation" must be set from MICRORISC (i.e. the TR transceiver manufacturer) 321 // otherwise the aggregation will not work. 322 DpaApiAggregateFrc(); 323 break; 324 } 325 } 326 } 327 break; 328 329 // ------------------------------------------------- 330 case DpaEvent_FrcResponseTime: 331 // Called to get FRC response time 332 333 // In this example the FRC commands are fast 334 switch ( DataOutBeforeResponseFRC[0] ) 335 { 336 case FRC_STD_SENSORS_1B: 337 case FRC_STD_SENSORS_2B: 338 case FRC_STD_SENSORS_4B: 339 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 340 break; 341 } 342 break; 343 344 // ------------------------------------------------- 345 case DpaEvent_DpaRequest: 346 // Called to interpret DPA request for peripherals 347 if ( IsDpaEnumPeripheralsRequest() ) 348 { 349 // ------------------------------------------------- 350 // Peripheral enumeration 351 _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x345F; 352 return TRUE; 353 } 354 355 break; 356 } 357 358 return FALSE; 359 } 360 361 //############################################################################################ 362 void CopyQuantity1B() 363 //############################################################################################ 364 { 365 #pragma updateBank default = UserBank_01 366 #pragma updateBank exit = UserBank_01 367 // Advance quantity pointer and copy original quantity value to the FRC value 368 setINDF0( *++FSR1 ); 369 // Advance FRC value pointer 370 FSR0++; 371 // Reset (invalidate) quantity value 0 for the next "not updated value" FRC 372 setINDF1( 0 ); 373 } 374 375 //############################################################################################ 376 void FSR1toDB() 377 //############################################################################################ 378 { 379 FSR1 = LINEAR_DB_ADDRESS; 380 } 381 382 //############################################################################################ 383 void FindNodeAtDb( uns8 node ) 384 //############################################################################################ 385 { 386 #pragma updateBank default = UserBank_01 387 FSR1toDB(); 388 for ( ;; ) 389 { 390 // End of the DB or node found? 391 if ( FSR1[offsetof( TDbRecord, Addr )] == 0 || FSR1[offsetof( TDbRecord, Addr )] == node ) 392 return; 393 394 // Skip the current record 395 FSR1 += FSR1[offsetof( TDbRecord, DataLength )]; 396 FSR1 += sizeof( TDbRecord ); 397 } 398 } 399 400 //############################################################################################ 401 // 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) 402 #include "DPAcustomHandler.h" 403 //############################################################################################