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.41 $
    8 // Date:    $Date: 2023/03/07 08:03:13 $
    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 _ZeroQuantityValue;
  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                                 break;
  360                               }
  361                             }
  362                             break;
  363 
  364                           case FRC_STD_SENSORS_2B:
  365                             if ( FSR1[0] == STD_SENSOR_TYPE_BINARYDATA30 )
  366                             {
  367                               shadowDef uns32 quantity @ ( &param3 /*+param4*/ );
  368                               // Read 4B FRC data from RAM
  369                               quantity.low8 = FSR1[1];
  370                               quantity.midL8 = FSR1[2];
  371                               quantity.midH8 = FSR1[3];
  372                               quantity.high8 = FSR1[4];
  373                               // Is there a quantityValue value to convert?
  374 #if !defined( __CC5XFREE__ )
  375                               if ( quantity > FRC_STD_FRC_RESERVED )
  376 #else
  377                               if ( ( quantity.midL8 | quantity.midH8 | quantity.high8 ) != 0 || quantity.low8 > 0x03 )
  378 #endif
  379                               {
  380                                 // Get raw value from FRC value
  381 #if !defined( __CC5XFREE__ )
  382                                 quantity -= 4;
  383 #else
  384                                 W = 4;
  385                                 quantity.low8 -= W;
  386                                 W = 0;
  387                                 quantity.midL8 = subWFB( quantity.midL8 );
  388                                 quantity.midH8 = subWFB( quantity.midH8 );
  389                                 quantity.high8 = subWFB( quantity.high8 );
  390 #endif
  391 
  392                                 // Get bits.0-14?
  393                                 if ( PerStdSensorFrc.SensorIndex.5 )
  394                                 {
  395                                   // No, get bits.15-29
  396                                   quantity.midL8 = rl( quantity.midL8 );
  397                                   quantity.midH8 = rl( quantity.midH8 );
  398                                   quantity.high8 = rl( quantity.high8 );
  399                                   quantity.low16 = quantity.high16;
  400                                 }
  401                                 // Make FRC value from raw value
  402                                 quantity.15 = 0;
  403                                 quantity.low16 += 4;
  404                               }
  405 
  406                               setINDF0( quantity.low8 );
  407                               FSR0++;
  408                               setINDF0( quantity.midL8 );
  409                               FSR0--;
  410                               goto _ZeroQuantityValue;
  411                             }
  412                             break;
  413                         }
  414                         goto _breakFindQuantity;
  415                       }
  416 
  417                       // Copy FRC quantity data
  418                       switch ( _PCMD )
  419                       {
  420                         default: // FRC_STD_SENSORS_4B:
  421                           CopyQuantity1B();
  422                           CopyQuantity1B();
  423                           // Fall through
  424 
  425                         case FRC_STD_SENSORS_2B:
  426                           CopyQuantity1B();
  427                           // Fall through
  428 
  429                         case FRC_STD_SENSORS_1B:
  430                           CopyQuantity1B();
  431                           break;
  432                       }
  433                       // Break the "find the quantity loop"
  434                       goto _breakFindQuantityNoAdvance;
  435                     }
  436 
  437                     // Decrease data length
  438                     dataLength -= quantitySizePlus1;
  439                     // Advance DB pointer to the next quantity
  440                     FSR1 += quantitySizePlus1;
  441                     // More quantities?
  442                   } while ( dataLength > 0 );
  443 _breakFindQuantity:
  444                 }
  445 
  446                 // Move in the output buffer
  447                 if ( frcValueSize != 0 )
  448                   // Move pointer to the next FRCvalue in the buffer INFO
  449                   FSR0L += frcValueSize;
  450                 else
  451                 {
  452                   // Advance next 2bit FRC mask and index
  453                   W = lsl( bitFrcMask );
  454                   if ( Carry )
  455                     FSR0++; // Note: must not modify Carry
  456                   bitFrcMask = rl( bitFrcMask );
  457                 }
  458 
  459 _breakFindQuantityNoAdvance:
  460               }
  461             } while ( node < MAX_ADDRESS );
  462 
  463             // Start FRC value aggregation.
  464             // Important:
  465             //  This node must be discovered and the feature "FRC Aggregation" must be set from MICRORISC (i.e. the TR transceiver manufacturer)
  466             //  otherwise the aggregation will not work.
  467             DpaApiAggregateFrc();
  468             // And indicate
  469             pulseLEDR();
  470             pulseLEDG();
  471             break;
  472           }
  473         }
  474       }
  475       break;
  476 
  477       // -------------------------------------------------
  478     case DpaEvent_FrcResponseTime:
  479       // Called to get FRC response time
  480 
  481       // In this example the FRC commands are fast
  482       switch ( DataOutBeforeResponseFRC[0] )
  483       {
  484         case FRC_STD_SENSORS_BIT:
  485         case FRC_STD_SENSORS_1B:
  486         case FRC_STD_SENSORS_2B:
  487         case FRC_STD_SENSORS_4B:
  488           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  489           break;
  490       }
  491       break;
  492 
  493       // -------------------------------------------------
  494     case DpaEvent_DpaRequest:
  495       // Called to interpret DPA request for peripherals
  496       if ( IsDpaEnumPeripheralsRequest() )
  497       {
  498         // -------------------------------------------------
  499         // Peripheral enumeration
  500         _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x345F;
  501         return TRUE;
  502       }
  503 
  504       break;
  505   }
  506 
  507   return FALSE;
  508 }
  509 
  510 //############################################################################################
  511 void CopyQuantity1B()
  512 //############################################################################################
  513 {
  514 #pragma updateBank default = UserBank_01
  515 #pragma updateBank exit = UserBank_01
  516   // Advance quantity pointer and copy original quantity value to the FRC value
  517   setINDF0( *++FSR1 );
  518   // Advance FRC value pointer
  519   FSR0++;
  520   // Reset (invalidate) quantity value 0 for the next "not updated value" FRC
  521   setINDF1( 0 );
  522 }
  523 
  524 //############################################################################################
  525 void FSR1toDB()
  526 //############################################################################################
  527 {
  528   FSR1 = LINEAR_DB_ADDRESS;
  529 }
  530 
  531 //############################################################################################
  532 void FindNodeAtDb( uns8 node )
  533 //############################################################################################
  534 {
  535 #pragma updateBank default = UserBank_01
  536   FSR1toDB();
  537   for ( ;; )
  538   {
  539     // End of the DB or node found?
  540     if ( FSR1[offsetof( TDbRecord, Addr )] == 0 || FSR1[offsetof( TDbRecord, Addr )] == node )
  541       return;
  542 
  543     // Skip the current record
  544     FSR1 += FSR1[offsetof( TDbRecord, DataLength )];
  545     FSR1 += sizeof( TDbRecord );
  546   }
  547 }
  548 
  549 //############################################################################################
  550 // 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)
  551 #include "DPAcustomHandler.h"
  552 //############################################################################################