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