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.32 $ 8 // Date: $Date: 2022/02/25 09:41:25 $ 9 // 10 // Revision history: 11 // 2022/02/24 Release for DPA 4.17 12 // 2021/08/20 Release for DPA 4.16 13 // 14 // ********************************************************************* 15 16 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 17 18 /* 19 * This is an example of the beaming aggregating repeater. 20 * It is recommended to start with CustomDpaHandler-FrcAggregation.c to fully understand FRC aggregation concept. 21 * 22 * This example stores received data from beaming sensors (see example CustomDpaHandler-SensorBeaming.c) into all available RAM using linear addressing. 23 * When IQRF Standard Sensor FRC is requested, the code aggregates formerly received beaming sensor data into the returning FRC data. 24 * 25 * The code also shows, how to convert from native FRC into non-native FRC in case of quantities, that support more FRC commands. 26 * The following quantities support non-native FRCs: Temperature (2B->1B), CO2 (2B->1B), VOC (2B->1B), BinaryData7 (1B->2b), BinaryData30 (4B->2B). 27 * 28 * Important: The feature "FRC Aggregation" must be set from MICRORISC (i.e. the TR transceiver manufacturer) otherwise the aggregation will not work. 29 */ 30 31 // Default IQRF include (modify the path according to your setup) 32 #include "IQRF.h" 33 34 // Default DPA header (modify the path according to your setup) 35 #include "DPA.h" 36 // Default Custom DPA Handler header (modify the path according to your setup) 37 #include "DPAcustomHandler.h" 38 // IQRF standards header (modify the path according to your setup) 39 #include "standard\IQRFstandard.h" 40 41 // One beaming DB record 42 typedef struct 43 { 44 // Node address 45 uns8 Addr; 46 // Data length (non-zero) 47 uns8 DataLength; 48 // Data 49 uns8 Data[0]; 50 } TDbRecord; 51 // The DB implementation limitations: 52 // Once the record for the certain node is allocated in the DB, the same amount of data must be also received later, otherwise the new data is discarded. 53 // 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. 54 55 // The place from the end of the bank11 (UserBank_01) used as a linear DB 56 #define DB_BANK11_SIZE 46 57 // Total linear DB space is also the adjacent bank12 (UserBank_02) (i.e. PeripheralRam) 58 #define TOTAL_DB_SIZE ( DB_BANK11_SIZE + sizeof( PeripheralRam ) ) 59 // Linear DB address 60 #define LINEAR_DB_ADDRESS ( (uns16)( 0x70 /* End of bank0 address + 1 */ - 0x20 /* Bank0 linear address */ ) /* Length of the bank in the linear address space */ * ( UserBank_01 + 1 ) - DB_BANK11_SIZE + 0x2000 /* Linear address start */ ) 61 // Reserve this space at bank11 against CC5X 62 uns8 _reserveDBatBank11[DB_BANK11_SIZE] @ ( 0x70 /* End of bank0 address + 1 */ + UserBank_01 * 0x80 /* Bank size */ - DB_BANK11_SIZE ); 63 64 // Auxiliary functions 65 void FindNodeAtDb( uns8 node ); 66 void FSR1toDB(); 67 void CopyQuantity1B(); 68 69 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 70 //############################################################################################ 71 bit CustomDpaHandler() 72 //############################################################################################ 73 { 74 #pragma updateBank default = UserBank_01 75 76 // Handler presence mark 77 clrwdt(); 78 79 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 80 switch ( GetDpaEvent() ) 81 { 82 // ------------------------------------------------- 83 case DpaEvent_Interrupt: 84 // Do an extra quick background interrupt work 85 return Carry; 86 87 // ------------------------------------------------- 88 case DpaEvent_ReceiveDpaRequest: 89 // Called after DPA request was received 90 91 // Beaming packet received and beaming command we understand? 92 if ( !_ROUTEF && _PNUM == PNUM_STD_SENSORS && _PCMD == ( PCMD_STD_SENSORS_READ_TYPES_AND_FRC_VALUES | RESPONSE_FLAG ) ) 93 { 94 // Indicate beaming was received 95 pulseLEDG(); 96 // Find record for the TX we received the packet from 97 FindNodeAtDb( TX ); 98 // Found? 99 if ( FSR1[offsetof( TDbRecord, Addr )] != 0 ) 100 { 101 // Record found. Is the size same as before? 102 if ( FSR1[offsetof( TDbRecord, DataLength )] != _DpaDataLength ) 103 // No! Error! 104 goto _IndicateDbError; 105 106 // Save the quantity data into DB 107 _CopyRecordData: 108 FSR1 += offsetof( TDbRecord, Data ); 109 setFSR0( _FSR_DPA ); 110 copyMemoryBlock( FSR0, FSR1, _DpaDataLength ); 111 } 112 else 113 { 114 // No, record is missing, let's add a new record plus data to the DB 115 116 // Check if there is an enough space in the DB for a new record 117 FSR0 = FSR1 - LINEAR_DB_ADDRESS + sizeof( TDbRecord ); 118 // The DB length + maximum packet length cannot be more than 0xFF, so we can use just the lower byte 119 FSR0L += _DpaDataLength; 120 if ( FSR0L >= TOTAL_DB_SIZE ) 121 { 122 _IndicateDbError: 123 // No, error, record cannot be saved 124 pulseLEDR(); 125 } 126 else 127 { 128 // Store address of the N 129 FSR1 += offsetof( TDbRecord, Addr ); 130 setINDF1( TX ); 131 // Store data length 132 FSR1 += offsetof( TDbRecord, DataLength ) - offsetof( TDbRecord, Addr ); 133 setINDF1( _DpaDataLength ); 134 // Restore pointer 135 FSR1 -= offsetof( TDbRecord, DataLength ); 136 // And store the data 137 goto _CopyRecordData; 138 } 139 } 140 } 141 break; 142 143 // ------------------------------------------------- 144 case DpaEvent_FrcValue: 145 // Called to get FRC value 146 147 // IQRF Sensor FRC structure 148 shadowDef TPerStdSensorFrc PerStdSensorFrc @ ( &DataOutBeforeResponseFRC[0] ); 149 150 // Process only certain IQRF Std sensor FRC commands (FRC_STD_SENSORS_BIT for STD_SENSOR_TYPE_BINARYDATA7 is not implemented in this example) 151 if ( PerStdSensorFrc.Header == PNUM_STD_SENSORS ) 152 { 153 switch ( _PCMD ) 154 { 155 case FRC_STD_SENSORS_BIT: 156 W = 0; 157 goto _IsStdSensFrc; 158 159 case FRC_STD_SENSORS_1B: 160 W = 1; 161 goto _IsStdSensFrc; 162 163 case FRC_STD_SENSORS_2B: 164 W = 2; 165 goto _IsStdSensFrc; 166 167 case FRC_STD_SENSORS_4B: 168 { 169 W = 4; 170 171 _IsStdSensFrc: 172 uns8 frcValueSize @ param4.low8; 173 frcValueSize = W; 174 175 // Prepare for zero FRC values from myself (could be a real value to return) and other [Ns] 176 #if !defined( __CC5XFREE__ ) 177 responseFRCvalue4B = FRC_STD_FRC_NOT_IMPLEMENTED_VALUE; 178 #else 179 responseFRCvalue4B.low16 = FRC_STD_FRC_NOT_IMPLEMENTED_VALUE; 180 responseFRCvalue4B.high16 = 0; 181 #endif 182 // Start with no FRC values returned 183 clearBufferINFO(); 184 185 // Mask for FRC_STD_SENSORS_BIT && STD_SENSOR_TYPE_BINARYDATA7 186 uns8 bitFrcMask = 0x02; // for N#1 187 188 // FSR0 now points to the resulting INFO buffer for the 1st Node (this will skip useless FRC data slot for the coordinator) 189 FSR0L += frcValueSize; 190 // Node number to get data for 191 uns8 node @ param2; 192 node = 0; 193 do 194 { 195 // Is there enough space in the bufferINFO for more FRC values? If not, break the loop and responseFRC 196 if ( FSR0L >= ( &bufferINFO[sizeof( bufferINFO )] & 0xFF ) ) 197 break; 198 199 // Is the node selected for this FRC? 200 addressBitmap( ++node ); 201 #if &bufferRF[0] != &AddressedNodesBeforeResponseFRC[0] 202 #error Cannot optimize 203 #endif 204 setFSR1( _FSR_RF ); 205 FSR1L += bitmapByteIndex; // Note: FSR0L will not overflow 206 207 // IQRF OS bit indicating selective FRC (bit.0 at the 1st byte behind AddressedNodesBeforeResponseFRC) 208 bit isSelectiveFrc @ AddressedNodesBeforeResponseFRC[sizeof( AddressedNodesBeforeResponseFRC )].0; 209 // W = bitmap byte 210 W = *FSR1; 211 if ( !isSelectiveFrc ) // Note: must not modify W 212 W = 0xFF; // Not selective FRC => all nodes are selected 213 // Is Nodes selected? 214 if ( W & bitmapBitMask ) 215 { 216 // Try to find the node's record in the DB 217 FindNodeAtDb( node ); 218 if ( FSR1[offsetof( TDbRecord, Addr )] != 0 ) 219 { 220 // Prepare FRC Value for the "FRC command not supported" 221 if ( _PCMD != FRC_STD_SENSORS_BIT ) 222 // 1B, 2B, 4B FRCs 223 W = FRC_STD_FRC_NOT_IMPLEMENTED_VALUE; 224 else 225 // 2b FRC 226 W = *FSR0 | bitFrcMask; 227 setINDF0( W ); 228 229 // Now find the FRC value in the DB record for the (un)specified quantity and index 230 uns8 dataLength @ param3.low8; 231 dataLength = FSR1[offsetof( TDbRecord, DataLength )]; 232 // Sensor index to find 233 uns8 sensorIndex @ param3.high8; 234 sensorIndex = ( PerStdSensorFrc.SensorIndex & 0x1F ) + 1; 235 // FSR1 points to the 1st quantity 236 FSR1 += offsetof( TDbRecord, Data ); 237 do 238 { 239 // Decode quantity size 240 uns8 quantitySizePlus1 = 2 + 1; 241 // Not 2B quantity? 242 if ( ( FSR1[0] & 0b1000.0000 ) != 0b0000.0000 ) 243 { 244 // 1B quantity 245 quantitySizePlus1 += 1 + 1 - ( 2 + 1 ); 246 // 4B quantity? 247 if ( ( FSR1[0] & 0b1110.0000 ) == 0b1010.0000 ) 248 quantitySizePlus1 = 4 + 1; 249 } 250 251 // Does the quantity type and its index match? 252 if ( ( PerStdSensorFrc.SensorType == FSR1[0] || PerStdSensorFrc.SensorType == 0 ) && --sensorIndex == 0 ) 253 { 254 // Quantity found, now check the FRC data size required vs. Quantity size 255 if ( frcValueSize != quantitySizePlus1 - 1 ) 256 { 257 // Now handle conversion to the non-native FRC size 258 // Sizes do not equal, what is the requested FRC size? 259 switch ( _PCMD ) 260 { 261 case FRC_STD_SENSORS_BIT: 262 if ( FSR1[0] == STD_SENSOR_TYPE_BINARYDATA7 ) 263 { 264 // Valid value? 265 if ( FSR1[1] > 3 ) 266 { 267 // Get the bit index to return (bits.5-7 from frcSensorIndex) 268 W = swap( PerStdSensorFrc.SensorIndex ); 269 // Now get mask of the bit 270 addressBitmap( rr( W ) ); 271 // If value bit is set, set the lower FRC bit 272 if ( ( ( FSR1[1] - 4 ) & bitmapBitMask ) != 0 ) 273 W = *FSR0 | bitFrcMask; 274 else 275 W = *FSR0 & ~bitFrcMask; 276 setINDF0( W ); 277 278 // Always set upper FRC bit, that is +32 bytes further 279 FSR0 += 16; FSR0 += 32 - 16; // Note: instruction ADDFSR supports maximum +31 280 setINDF0( *FSR0 | bitFrcMask ); 281 FSR0 -= 32; 282 } 283 284 goto _ZeroQuantityValue; 285 } 286 break; 287 288 case FRC_STD_SENSORS_1B: 289 // What quantity? 290 switch ( FSR1[0] ) 291 { 292 case STD_SENSOR_TYPE_TEMPERATURE: 293 case STD_SENSOR_TYPE_CO2: 294 case STD_SENSOR_TYPE_VOC: 295 { 296 // Get 2B FRC quantity value 297 uns16 quantity @ param3; 298 quantity.low8 = FSR1[1]; 299 quantity.high8 = FSR1[2]; 300 // Now recompute from 2B FRC to 1B FRC 301 // Not valid value or zero? 302 if ( quantity <= 3 ) 303 { 304 // Return it 305 W = quantity.low8; 306 } 307 else 308 { 309 // Converting Temperature? 310 if ( FSR1[0] == STD_SENSOR_TYPE_TEMPERATURE ) 311 { 312 // Convert from FRC value to raw value 313 quantity ^= 0x8000; 314 // Check the 1B FRC temperature limits 315 if ( (int16)quantity > (int16)( 105.5 * 16 ) || (int16)quantity < ( (int16)-20 * 16 ) ) 316 // Return sensor error 317 W = FRC_STD_FRC_ERROR_VALUE; 318 else 319 { 320 // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution 321 quantity += 8 / 2 + (uns16)44 * 8; // Note: do rounding for /8 322 // To avoid hidden compiler variable, do 3x /2 for /8 323 quantity /= 2; 324 quantity /= 2; 325 quantity /= 2; 326 W = quantity.low8; 327 } 328 } 329 else 330 { 331 // Check the CO2 or VOC limits or error 332 if ( quantity > ( 4016 + 4 ) ) 333 W = FRC_STD_FRC_ERROR_VALUE; 334 else 335 { 336 quantity += /* rounding */ 16 / 2 - /* from 2B FRC to raw */ 4 + /* from raw to 1B FRC being divided by 16 */ 4 * 16; 337 // To avoid hidden compiler variable, do 4x /2 for /16 338 // same as quantity /= 16 339 quantity.high8 = lsr( quantity.high8 ); quantity.low8 = rr( quantity.low8 ); 340 quantity.high8 = lsr( quantity.high8 ); quantity.low8 = rr( quantity.low8 ); 341 quantity.high8 = lsr( quantity.high8 ); quantity.low8 = rr( quantity.low8 ); 342 quantity.high8 = lsr( quantity.high8 ); W = rr( quantity.low8 ); 343 } 344 } 345 } 346 347 // Store 1B FRC value 348 setINDF0( W ); 349 _ZeroQuantityValue: 350 uns8 zeroLoop @ param3.low8; 351 zeroLoop = quantitySizePlus1 - 1; 352 do 353 { 354 FSR1++; 355 setINDF1( 0 ); 356 } while ( --zeroLoop != 0 ); 357 break; 358 } 359 } 360 break; 361 362 case FRC_STD_SENSORS_2B: 363 if ( FSR1[0] == STD_SENSOR_TYPE_BINARYDATA30 ) 364 { 365 shadowDef uns32 quantity @ ( ¶m3 /*+param4*/ ); 366 // Read 4B FRC data from RAM 367 quantity.low8 = FSR1[1]; 368 quantity.midL8 = FSR1[2]; 369 quantity.midH8 = FSR1[3]; 370 quantity.high8 = FSR1[4]; 371 // Is there a quantityValue value to convert? 372 #if !defined( __CC5XFREE__ ) 373 if ( quantity > 0x00000003 ) 374 #else 375 if ( ( quantity.midL8 | quantity.midH8 | quantity.high8 ) != 0 || quantity.low8 > 0x03 ) 376 #endif 377 { 378 // Get raw value from FRC value 379 #if !defined( __CC5XFREE__ ) 380 quantity -= 4; 381 #else 382 W = 4; 383 quantity.low8 -= W; 384 W = 0; 385 quantity.midL8 = subWFB( quantity.midL8 ); 386 quantity.midH8 = subWFB( quantity.midH8 ); 387 quantity.high8 = subWFB( quantity.high8 ); 388 #endif 389 390 // Get bits.0-14? 391 if ( PerStdSensorFrc.SensorIndex.5 ) 392 { 393 // No, get bits.15-29 394 quantity.midL8 = rl( quantity.midL8 ); 395 quantity.midH8 = rl( quantity.midH8 ); 396 quantity.high8 = rl( quantity.high8 ); 397 quantity.low16 = quantity.high16; 398 } 399 // Make FRC value from raw value 400 quantity.15 = 0; 401 quantity.low16 += 4; 402 } 403 404 setINDF0( quantity.low8 ); 405 FSR0++; 406 setINDF0( quantity.midL8 ); 407 FSR0--; 408 goto _ZeroQuantityValue; 409 } 410 break; 411 } 412 goto _breakFindQuantity; 413 } 414 415 // Copy FRC quantity data 416 switch ( _PCMD ) 417 { 418 default: // FRC_STD_SENSORS_4B: 419 CopyQuantity1B(); 420 CopyQuantity1B(); 421 // Fall through 422 423 case FRC_STD_SENSORS_2B: 424 CopyQuantity1B(); 425 // Fall through 426 427 case FRC_STD_SENSORS_1B: 428 CopyQuantity1B(); 429 break; 430 } 431 // Break the "find the quantity loop" 432 goto _breakFindQuantityNoAdvance; 433 } 434 435 // Decrease data length 436 dataLength -= quantitySizePlus1; 437 // Advance DB pointer to the next quantity 438 FSR1 += quantitySizePlus1; 439 // More quantities? 440 } while ( dataLength > 0 ); 441 _breakFindQuantity: 442 } 443 444 // Move in the output buffer 445 if ( frcValueSize != 0 ) 446 // Move pointer to the next FRCvalue in the buffer INFO 447 FSR0L += frcValueSize; 448 else 449 { 450 // Advance next 2bit FRC mask and index 451 W = lsl( bitFrcMask ); 452 if ( Carry ) 453 FSR0++; // Note: must not modify Carry 454 bitFrcMask = rl( bitFrcMask ); 455 } 456 457 _breakFindQuantityNoAdvance: 458 } 459 } while ( node < MAX_ADDRESS ); 460 461 // Start FRC value aggregation. 462 // Important: 463 // This node must be discovered and the feature "FRC Aggregation" must be set from MICRORISC (i.e. the TR transceiver manufacturer) 464 // otherwise the aggregation will not work. 465 DpaApiAggregateFrc(); 466 // And indicate 467 pulseLEDR(); 468 pulseLEDG(); 469 break; 470 } 471 } 472 } 473 break; 474 475 // ------------------------------------------------- 476 case DpaEvent_FrcResponseTime: 477 // Called to get FRC response time 478 479 // In this example the FRC commands are fast 480 switch ( DataOutBeforeResponseFRC[0] ) 481 { 482 case FRC_STD_SENSORS_BIT: 483 case FRC_STD_SENSORS_1B: 484 case FRC_STD_SENSORS_2B: 485 case FRC_STD_SENSORS_4B: 486 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 487 break; 488 } 489 break; 490 491 // ------------------------------------------------- 492 case DpaEvent_DpaRequest: 493 // Called to interpret DPA request for peripherals 494 if ( IsDpaEnumPeripheralsRequest() ) 495 { 496 // ------------------------------------------------- 497 // Peripheral enumeration 498 _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x345F; 499 return TRUE; 500 } 501 502 break; 503 } 504 505 return FALSE; 506 } 507 508 //############################################################################################ 509 void CopyQuantity1B() 510 //############################################################################################ 511 { 512 #pragma updateBank default = UserBank_01 513 #pragma updateBank exit = UserBank_01 514 // Advance quantity pointer and copy original quantity value to the FRC value 515 setINDF0( *++FSR1 ); 516 // Advance FRC value pointer 517 FSR0++; 518 // Reset (invalidate) quantity value 0 for the next "not updated value" FRC 519 setINDF1( 0 ); 520 } 521 522 //############################################################################################ 523 void FSR1toDB() 524 //############################################################################################ 525 { 526 FSR1 = LINEAR_DB_ADDRESS; 527 } 528 529 //############################################################################################ 530 void FindNodeAtDb( uns8 node ) 531 //############################################################################################ 532 { 533 #pragma updateBank default = UserBank_01 534 FSR1toDB(); 535 for ( ;; ) 536 { 537 // End of the DB or node found? 538 if ( FSR1[offsetof( TDbRecord, Addr )] == 0 || FSR1[offsetof( TDbRecord, Addr )] == node ) 539 return; 540 541 // Skip the current record 542 FSR1 += FSR1[offsetof( TDbRecord, DataLength )]; 543 FSR1 += sizeof( TDbRecord ); 544 } 545 } 546 547 //############################################################################################ 548 // 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) 549 #include "DPAcustomHandler.h" 550 //############################################################################################