1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - Thermometer  *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 0802_TrThermometer.c,v $
    7 // Version: $Revision: 1.21 $
    8 // Date:    $Date: 2020/03/18 18:18:40 $
    9 //
   10 // Revision history:
   11 //   2018/10/25  Release for DPA 3.03
   12 //   2017/11/16  Release for DPA 3.02
   13 //   2017/08/14  Release for DPA 3.01
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 // IQRF Standards documentation https://www.iqrfalliance.org/iqrf-interoperability/
   19 
   20 // This example implements 1 temperature sensor according to the IQRF Sensors standard
   21 
   22 // Default IQRF include (modify the path according to your setup)
   23 #include "IQRF.h"
   24 
   25 // Uncomment to implement Custom DPA Handler for Coordinator
   26 //#define COORDINATOR_CUSTOM_HANDLER
   27 
   28 // Default DPA header (modify the path according to your setup)
   29 #include "DPA.h"
   30 // Default Custom DPA Handler header (modify the path according to your setup)
   31 #include "DPAcustomHandler.h"
   32 // IQRF standards header (modify the path according to your setup)
   33 #include "IQRFstandard.h"
   34 #include "IQRF_HWPID.h"
   35 
   36 //############################################################################################
   37 
   38 // Calibration value address in the EEPROM
   39 #define TEMP_CALIBRATION_ADDRESS  0
   40 
   41 // Initial calibration value is 0
   42 #pragma cdata[ __EESTART + TEMP_CALIBRATION_ADDRESS ] = 0, 0
   43 
   44 // Calibrates temperature at param3, that was read by getTemperature()
   45 void CalibrateParam3();
   46 
   47 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   48 //############################################################################################
   49 bit CustomDpaHandler()
   50 //############################################################################################
   51 {
   52   // This forces CC5X to wisely use MOVLB instructions (doc says:  The 'default' bank is used by the compiler for loops and labels when the algorithm gives up finding the optimal choice)
   53 #pragma updateBank default = UserBank_01
   54 
   55   // Handler presence mark
   56   clrwdt();
   57 
   58   // Sleeping parameters, valid when Time != 0
   59   static TPerOSSleep_Request PerOSSleep_Request;
   60 
   61   // Detect DPA event to handle
   62   switch ( GetDpaEvent() )
   63   {
   64     // -------------------------------------------------
   65     case DpaEvent_Interrupt:
   66       // Do an extra quick background interrupt work
   67       // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible.
   68       // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one.
   69       // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time.
   70       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   71       // ! Make sure race condition does not occur when accessing those variables at other places.
   72       // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt.
   73       // ! Do not call any OS functions except setINDFx().
   74       // ! Do not use any OS variables especially for writing access.
   75       // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage.
   76 
   77       return Carry;
   78 
   79       // -------------------------------------------------
   80     case DpaEvent_Idle:
   81       // Do a quick background work when RF packet is not received
   82 
   83       // Should go to sleep?
   84       if ( PerOSSleep_Request.Time != 0 )
   85       {
   86         // Copy sleep parameters to the DPA request
   87         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
   88         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
   89         // Switch off sleeping time=flag
   90         PerOSSleep_Request.Time = 0;
   91         // Finalize OS Sleep DPA Request
   92         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
   93         _PNUM = PNUM_OS;
   94         _PCMD = CMD_OS_SLEEP;
   95         // Perform local DPA Request to go to sleep
   96         DpaApiLocalRequest();
   97       }
   98       break;
   99 
  100       // -------------------------------------------------
  101     case DpaEvent_DpaRequest:
  102       // Called to interpret DPA request for peripherals
  103       // -------------------------------------------------
  104       // Peripheral enumeration
  105       if ( IsDpaEnumPeripheralsRequest() )
  106       {
  107         // We implement 1 standard peripheral
  108         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  109         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  110         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_TR_THERMOMETER;
  111         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001;
  112 
  113 DpaHandleReturnTRUE:
  114         return TRUE;
  115       }
  116       // -------------------------------------------------
  117       // Get information about peripheral
  118       else if ( IsDpaPeripheralInfoRequest() )
  119       {
  120         if ( _PNUM == PNUM_STD_SENSORS )
  121         {
  122           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  123           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  124           // Set standard version
  125           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  126           goto DpaHandleReturnTRUE;
  127         }
  128 
  129         break;
  130       }
  131       // -------------------------------------------------
  132       else
  133       {
  134         // Handle peripheral command
  135 
  136         // Supported peripheral number?
  137         if ( _PNUM == PNUM_STD_SENSORS )
  138         {
  139           // Supported commands?
  140           switch ( _PCMD )
  141           {
  142             // Invalid command
  143             default:
  144               // Return error
  145               W = ERROR_PCMD;
  146               goto _ERROR_W;
  147 
  148               // Sensor enumeration
  149             case PCMD_STD_ENUMERATE:
  150               if ( _DpaDataLength != 0 )
  151               {
  152 _ERROR_DATA_LEN:
  153                 W = ERROR_DATA_LEN;
  154                 goto _ERROR_W;
  155               }
  156 
  157               _DpaDataLength |= 1; // = 1 (optimization as _DpaDataLength was 0 for sure)
  158               goto _Enumerate;
  159 
  160               // Supported commands. They are handled almost the same way
  161             case PCMD_STD_SENSORS_READ_VALUES:
  162             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  163             {
  164               bit returnCalibration = FALSE;
  165 
  166               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  167               W = _DpaDataLength;
  168               if ( W == 0 ) // Note: must not modify W
  169               {
  170                 // Bitmap is 32 bits long = 4 (Note: using here save MOVLB)
  171                 _DpaDataLength = W = 4;
  172                 // Simulate 1st sensor in the bitmap (states of the other unimplemented sensors do not care)
  173                 _DpaMessage.Request.PData[0].0 = 1; // Note: must not modify W
  174               }
  175 
  176               // Invalid bitmap (data) length (W = _DpaDataLength)?
  177               switch ( W )
  178               {
  179                 // Yes!
  180                 default:
  181                   // Return error
  182                   goto _ERROR_DATA_LEN;
  183 
  184                   // No extra data written to sensor
  185                 case ( (uns8)( sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) ) ):
  186                   break;
  187 
  188                   // Extra data written to sensor
  189                 case ( (uns8)( sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) + 5 ) ):
  190                   // To the 1st i.e. temperature sensor?
  191                   if ( FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 0] != 0 )
  192                   {
  193                     // No, error
  194 _ERROR_DATA:
  195                     W = ERROR_DATA;
  196 _ERROR_W:
  197                     DpaApiReturnPeripheralError( W );
  198                   }
  199 
  200                   // Parse data content
  201                   switch ( FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 1] )
  202                   {
  203                     // Invalid 1st "header" byte
  204                     default:
  205                       goto _ERROR_DATA;
  206 
  207                       // Get calibration temperature
  208                     case STD_SENSOR_TYPE_TEMPERATURE_DATA_GET_1_POINT_CALIBRATION:
  209                       returnCalibration = TRUE;
  210                       break;
  211 
  212                       // Set calibration temperature
  213                     case STD_SENSOR_TYPE_TEMPERATURE_DATA_SET_1_POINT_CALIBRATION:
  214                       // Write calibration to the memory
  215                       bufferINFO[0] = FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 2];
  216                       bufferINFO[1] = FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 3];
  217                       eeWriteData( TEMP_CALIBRATION_ADDRESS, sizeof( uns16 ) );
  218                       break;
  219                   }
  220                   break;
  221               }
  222 
  223               // Get ready return data length for temperature data only (Note: optimization, 2 = 4/2 is used to save)
  224               _DpaDataLength = sizeof( uns16 );
  225 
  226               // Is my only sensor selected?
  227               if ( _DpaMessage.Request.PData[0].0 )
  228               {
  229                 // Measure temperature?
  230                 if ( !returnCalibration )
  231                 {
  232                   // Error reading temperature? Note: param3 holds temperature value after calling getTemperature()
  233                   if ( getTemperature() == -128 )
  234                     // Return standard error value
  235                     STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( param3 );
  236                   else
  237                     CalibrateParam3();
  238                 }
  239                 else
  240                 {
  241                   // No, return calibration
  242                   param3 = 0;
  243                   CalibrateParam3();
  244                 }
  245 
  246                 // Return the sensor type too?
  247                 if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  248                 {
  249                   // 3 bytes (1 byte with type, 2 bytes with temperature value) will be returned
  250                   _DpaDataLength++;
  251 _Enumerate:
  252                   // 1st byte is sensor type
  253                   _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  254                   // 3rd byte is higher byte of temperature value
  255                   _DpaMessage.Response.PData[2] = param3.high8;
  256                   // 2nd byte is lower byte of temperature value
  257                   W = param3.low8;
  258                 }
  259                 else
  260                 {
  261                   // 2 bytes to return
  262                   // 1st byte is lower byte of temperature value
  263                   _DpaMessage.Response.PData[0] = param3.low8;
  264                   // 2nd byte is higher byte of temperature value
  265                   W = param3.high8;
  266                 }
  267 
  268                 // Store 2nd byte for both supported commands
  269                 _DpaMessage.Response.PData[1] = W;
  270                 // Handled!
  271                 goto DpaHandleReturnTRUE;
  272               }
  273               else
  274               {
  275                 // My sensor not selected, so no data returned
  276                 _DpaDataLength = 0;
  277                 goto DpaHandleReturnTRUE;
  278               }
  279             }
  280           }
  281         }
  282 
  283         break;
  284       }
  285 
  286       // -------------------------------------------------
  287     case DpaEvent_FrcValue:
  288       // Called to get FRC value
  289 
  290       // Check for correct FRC and FRC user data (signature byte and sensor index == 0)
  291       switch ( _PCMD )
  292       {
  293         case FRC_STD_SENSORS_1B:
  294         case FRC_STD_SENSORS_2B:
  295           // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  296           FSR1 = &DataOutBeforeResponseFRC[0];
  297 
  298           if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS &&
  299             ( *FSR1 /*DataOutBeforeResponseFRC[1]*/ == 0x00 || *FSR1 /*DataOutBeforeResponseFRC[1]*/ == STD_SENSOR_TYPE_TEMPERATURE ) &&
  300                ( *++FSR1 /*DataOutBeforeResponseFRC[2]*/ & 0x1f ) == 0 )
  301           {
  302             // Return error code
  303             responseFRCvalue2B = 2;
  304             // Get temperature and adjust it for FRC
  305             // Temperature OK?
  306             if ( getTemperature() != -128 )
  307             {
  308               // Calibrate the temperatures
  309               CalibrateParam3();
  310               // Return sensor FRC value 2B
  311               responseFRCvalue2B = param3 ^ 0x8000;
  312 
  313               // Return sensor FRC value 1B
  314               // Check for out of limits
  315               if ( (int16)param3 <= (int16)( 105.5 * 16 ) && (int16)param3 >= ( (int16)-20 * 16 ) )
  316               {
  317                 // CC5x reports "Sign problems, please typecast one operand to unsigned (uns16)"
  318                 // param3 = (int16)param3 / 8;
  319                 param3.high8 = asr( param3.high8 );
  320                 param3.low8 = rr( param3.low8 );
  321                 param3.high8 = asr( param3.high8 );
  322                 param3.low8 = rr( param3.low8 );
  323                 param3.high8 = asr( param3.high8 );
  324                 param3.low8 = rr( param3.low8 );
  325 
  326                 responseFRCvalue = (int8)param3.low8 + 44;
  327               }
  328             }
  329 
  330             // The sensor was measured by FRC, check if there is a sleep request
  331             FSR1++;
  332             if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  333             {
  334               // Remember sleep parameters to go to sleep at the Idle event later
  335               PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4]
  336               PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5]
  337               PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6]
  338             }
  339           }
  340           break;
  341       }
  342       break;
  343 
  344       // -------------------------------------------------
  345     case DpaEvent_FrcResponseTime:
  346       // Called to get FRC response time
  347 
  348       // In this example the FRC command is a fast one
  349       switch ( DataOutBeforeResponseFRC[0] )
  350       {
  351         case FRC_STD_SENSORS_1B:
  352         case FRC_STD_SENSORS_2B:
  353           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  354           break;
  355       }
  356       break;
  357   }
  358 
  359 DpaHandleReturnFALSE:
  360   return FALSE;
  361 }
  362 
  363 
  364 //############################################################################################
  365 void CalibrateParam3()
  366 //############################################################################################
  367 {
  368   // Read calibration
  369   eeReadData( TEMP_CALIBRATION_ADDRESS, sizeof( uns16 ) );
  370   uns16 calib @ bufferINFO[0];
  371   param3 += calib;
  372 }
  373 
  374 //############################################################################################
  375 // 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) 
  376 #include "DPAcustomHandler.h"
  377 //############################################################################################