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 //############################################################################################