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 @ ( &param3 /*+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 //############################################################################################