1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - DDC-SE-01    *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 0002_DDC-SE01.c,v $
    7 // Version: $Revision: 1.19 $
    8 // Date:    $Date: 2018/10/25 09:51:28 $
    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 http://www.iqrf.org/DpaTechGuide/
   18 
   19 // This example implements 4 sensors according to the IQRF Sensors standard
   20 // 1st sensor is on-board TR temperature sensor.
   21 // 2nd sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE-01 board (according to the HW jumper position) chosen at the runtime based on the SW detection.
   22 // 3rd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]).
   23 // 4th sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]).
   24 
   25 // Default IQRF include (modify the path according to your setup)
   26 #include "IQRF.h"
   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 // Number of implemented sensors
   39 #define SENSORS_COUNT 4
   40 
   41 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data.
   42 uns16 sensorValue @ param3;
   43 
   44 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable
   45 bit Get0_TemperatureTR();
   46 bit Get1_Temperature();
   47 bit Get2_BinaryData_Light();
   48 bit Get3_BinaryData_Potentiometer();
   49 
   50 // Temperature sensors read routine for both DDC-SE-01 sensor types
   51 void GetTemperature();
   52 // Read preset PIC ADC for DDC-SE-01
   53 uns8 ReadAdc();
   54 
   55 // Stores sensor value byte(s) to the FSR1[+1...], in case of PCMD_STD_SENSORS_READ_TYPES_AND_VALUES sensor type is stored before value byte(s)
   56 void StoreValue( uns8 sensorType );
   57 
   58 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   59 #define OneWire_TRIS         TRISC.3
   60 #define OneWire_IO_IN        PORTC.3
   61 #define OneWire_IO_OUT       LATC.3
   62 
   63 // Writes sensor configuration (resolution)
   64 bit Ds18B20WriteConfig( uns8 value );
   65 
   66 // Resets OneWire
   67 bit OneWireReset();
   68 // Reads OneWire byte
   69 uns8 OneWireReadByte();
   70 // Writes OneWire byte
   71 void OneWireWriteByte( uns8 byte );
   72 
   73 // DS18B20 commands
   74 #define CMD_READROM       0x33
   75 #define CMD_CONVERTTEMP   0x44
   76 #define CMD_CPYSCRATCHPAD 0x48
   77 #define CMD_WSCRATCHPAD   0x4e
   78 #define CMD_MATCHROM      0x55
   79 #define CMD_RPWRSUPPLY    0xb4
   80 #define CMD_RECEEPROM     0xb8
   81 #define CMD_RSCRATCHPAD   0xbe
   82 #define CMD_SKIPROM       0xcc
   83 #define CMD_ALARMSEARCH   0xec
   84 #define CMD_SEARCHROM     0xf0
   85 
   86 // I2C routines
   87 void i2c_init();
   88 void i2c_shutdown();
   89 void i2c_waitForIdle();
   90 void i2c_start();
   91 void i2c_repStart();
   92 void i2c_stop();
   93 uns8 i2c_read( bit ack );
   94 void i2c_write( uns8 i2cWriteData );
   95 
   96 // MCP9802 address
   97 #define I2C_ADR             0b10010110
   98 // Power pin
   99 #define PWR_SENSOR_TRIS     TRISC.7
  100 #define PWR_SENSOR_IO       LATC.7
  101 
  102 // Special temperature value to indicate a sensor error, compatible with IQRF sensor standard
  103 #define ERROR_TEMPERATURE 0x8000
  104 
  105 // TRUE if DS18B20 is enabled at runtime at startup, FALSE in case of MCP9802
  106 bit isDS18B20;
  107 // Final DS18B20 temperature value read by state machine
  108 uns16 temperature;
  109 
  110 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  111 //############################################################################################
  112 bit CustomDpaHandler()
  113 //############################################################################################
  114 {
  115   // 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)
  116 #pragma updateBank default = UserBank_01
  117 
  118   // Finite machine states
  119   typedef enum
  120   {
  121     S_ResetConvertT = 0,
  122     S_SkipRomConvertT,
  123     S_CmdConvertT,
  124 
  125     S_WaitConvertT,
  126 
  127     S_ResetReadTemp,
  128     S_SkipRomReadTemp,
  129     S_CmdReadTemp,
  130     S_Byte1ReadTemp,
  131     S_Byte2ReadTemp
  132   } TState;
  133 
  134   // Handler presence mark
  135   clrwdt();
  136 
  137   // Sleeping parameters, valid when Time != 0
  138   static TPerOSSleep_Request PerOSSleep_Request;
  139   // Finite machine state
  140   static uns8 state; // = S_ResetConvertT = 0
  141   // Pre-read lower temperature byte
  142   static uns8 temperatureByteLow;
  143   // Conversion timeout counter
  144   static uns8 timeout;
  145 
  146   // Detect DPA event to handle
  147   switch ( GetDpaEvent() )
  148   {
  149     // -------------------------------------------------
  150     case DpaEvent_Interrupt:
  151       // Do an extra quick background interrupt work
  152       // ! 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.
  153       // ! 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.
  154       // ! 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.
  155       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  156       // ! Make sure race condition does not occur when accessing those variables at other places.
  157       // ! 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.
  158       // ! Do not call any OS functions except setINDFx().
  159       // ! Do not use any OS variables especially for writing access.
  160       // ! 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.
  161 
  162       // ms per TMR6 interrupt
  163 #define TICKS_LEN  10
  164 
  165       //  If TMR6 interrupt occurred
  166       if ( TMR6IF )
  167       {
  168         // Unmask interrupt
  169         TMR6IF = 0;
  170         // Decrement count
  171         if ( timeout != 0 )
  172           timeout--;
  173       }
  174       return Carry;
  175 
  176       // -------------------------------------------------
  177     case DpaEvent_Idle:
  178       // Do a quick background work when RF packet is not received
  179 
  180       // Should go to sleep?
  181       if ( PerOSSleep_Request.Time != 0 )
  182       {
  183         // Copy sleep parameters to the DPA request
  184         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
  185         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
  186         // Switch off sleeping time=flag
  187         PerOSSleep_Request.Time = 0;
  188         // Finalize OS Sleep DPA Request
  189         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  190         _PNUM = PNUM_OS;
  191         _PCMD = CMD_OS_SLEEP;
  192         // Perform local DPA Request to go to sleep
  193         DpaApiLocalRequest();
  194       }
  195 
  196       // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 
  197       if ( !isDS18B20 )
  198         break;
  199 
  200       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  201       OneWire_IO_OUT = 0;
  202 
  203       skip( state );
  204 #pragma computedGoto 1
  205       goto _S_ResetConvertT;
  206       goto _S_SkipRomConvertT;
  207       goto _S_CmdConvertT;
  208       goto _S_WaitConvertT;
  209       goto _S_ResetReadTemp;
  210       goto _S_SkipRomReadTemp;
  211       goto _S_CmdReadTemp;
  212       goto _S_Byte1ReadTemp;
  213       goto _S_Byte2ReadTemp;
  214 #pragma computedGoto 0
  215       ;
  216       // --------------
  217 _S_Byte2ReadTemp:
  218       temperature.high8 = OneWireReadByte();
  219       temperature.low8 = temperatureByteLow;
  220 
  221 ResetMachine:
  222       state = S_ResetConvertT;
  223       goto ExitMachine;
  224 
  225       // --------------
  226 _S_ResetConvertT:
  227 _S_ResetReadTemp:
  228       if ( !OneWireReset() )
  229       {
  230 _S_Error_Reset:
  231         temperature = ERROR_TEMPERATURE;
  232         goto ResetMachine;
  233       }
  234       goto NextState;
  235 
  236       // --------------
  237 _S_SkipRomConvertT:
  238 _S_SkipRomReadTemp:
  239       // OneWire: Skip ROM
  240       OneWireWriteByte( CMD_SKIPROM );
  241       goto NextState;
  242 
  243       // --------------
  244 _S_CmdConvertT:
  245       // OneWire: Convert temperature
  246       OneWireWriteByte( CMD_CONVERTTEMP );
  247       // Setup timeout for approx 750 ms (the longest conversion time)
  248       timeout = 2 + 750 / TICKS_LEN;
  249       goto NextState;
  250 
  251       // --------------
  252 _S_WaitConvertT:
  253       if ( OneWireReadByte() == 0xff )
  254         goto NextState;
  255 
  256       // Timeout?
  257       if ( timeout == 0 )
  258         goto _S_Error_Reset;
  259 
  260       goto ExitMachine;
  261 
  262       // --------------
  263 _S_CmdReadTemp:
  264       // OneWire: Read scratchpad
  265       OneWireWriteByte( CMD_RSCRATCHPAD );
  266       goto NextState;
  267 
  268       // --------------
  269 _S_Byte1ReadTemp:
  270       temperatureByteLow = OneWireReadByte();
  271       goto NextState;
  272 
  273       // --------------
  274 NextState:
  275       ++state;
  276 
  277 ExitMachine:
  278       break;
  279 
  280       // -------------------------------------------------
  281     case DpaEvent_Init:
  282       // Do a one time initialization work before main loop starts
  283 
  284       // Initialize sensors
  285       // C5 (AN4) as input 
  286       moduleInfo();
  287       // Connected TR pins?
  288       if ( !bufferINFO[5].7 )
  289       {
  290         TRISC.6 = 1;
  291         TRISB.4 = 1;
  292       }
  293       TRISA.5 = 1;
  294 
  295       // C1 (AN0) as input 
  296       TRISA.0 = 1;
  297 
  298       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  299 #if F_OSC == 16000000
  300       PR6 = 250 - 1;
  301       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  302 #else
  303 #error Unsupported oscillator frequency
  304 #endif
  305 
  306       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  307       if ( Ds18B20WriteConfig( 0b0.00.00000 ) )
  308         // DS18B20 is enabled
  309         isDS18B20 = TRUE;
  310       else
  311         // Expect MCP9802 is enabled
  312         i2c_init();
  313 
  314       break;
  315 
  316       // -------------------------------------------------
  317     case DpaEvent_AfterSleep:
  318       // Called after woken up after sleep
  319       if ( !isDS18B20 )
  320         i2c_init();
  321 
  322       // Called on wake-up from sleep
  323       TMR6IE = TRUE;
  324       TMR6ON = TRUE;
  325       break;
  326 
  327       // -------------------------------------------------
  328     case DpaEvent_BeforeSleep:
  329       // Called before going to sleep
  330       if ( !isDS18B20 )
  331         i2c_shutdown();
  332 
  333       // -------------------------------------------------
  334     case DpaEvent_DisableInterrupts:
  335       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM)
  336       // Must not use TMR6 any more
  337       TMR6ON = FALSE;
  338       TMR6IE = FALSE;
  339       break;
  340 
  341       // -------------------------------------------------
  342     case DpaEvent_DpaRequest:
  343       // Called to interpret DPA request for peripherals
  344       // -------------------------------------------------
  345       // Peripheral enumeration
  346       if ( IsDpaEnumPeripheralsRequest() )
  347       {
  348         // We implement 1 standard user peripheral
  349         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  350         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  351         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_DDC_SE01;
  352         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001;
  353 
  354 DpaHandleReturnTRUE:
  355         return TRUE;
  356       }
  357       // -------------------------------------------------
  358       // Get information about peripheral
  359       else if ( IsDpaPeripheralInfoRequest() )
  360       {
  361         if ( _PNUM == PNUM_STD_SENSORS )
  362         {
  363           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  364           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  365           // Set standard version
  366           _DpaMessage.PeripheralInfoAnswer.Par1 = 13;
  367           goto DpaHandleReturnTRUE;
  368         }
  369 
  370         break;
  371       }
  372       // -------------------------------------------------
  373       else
  374       {
  375         // Handle peripheral command
  376 
  377         // Supported peripheral number?
  378         if ( _PNUM == PNUM_STD_SENSORS )
  379         {
  380           // Supported commands?
  381           switch ( _PCMD )
  382           {
  383             // Invalid command
  384             default:
  385               // Return error
  386               DpaApiReturnPeripheralError( ERROR_PCMD );
  387 
  388               // Sensor enumeration
  389             case PCMD_STD_ENUMERATE:
  390               if ( _DpaDataLength != 0 )
  391                 goto _ERROR_DATA_LEN;
  392 
  393               // Then just enumerate their types
  394               _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  395               _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE;
  396               _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7;
  397               _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_BINARYDATA7;
  398               W = SENSORS_COUNT;
  399               goto _W2_DpaDataLength;
  400 
  401               // Supported commands. They are handled the same way except one "if" at StoreValue() method
  402             case PCMD_STD_SENSORS_READ_VALUES:
  403             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  404             {
  405               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  406               W = _DpaDataLength;
  407               if ( W == 0 ) // Note: must not modify W
  408               {
  409                 // Actually clears the bitmap
  410 #if &_DpaMessage.Request.PData[0] != &bufferRF[0]
  411 #error
  412 #endif
  413                 clearBufferRF();
  414                 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care)
  415                 _DpaMessage.Request.PData[0].0 = 1;
  416                 // Bitmap is 32 bits long = 4
  417                 _DpaDataLength = W = 4;
  418               }
  419 
  420               // Invalid bitmap (data) length (W = _DpaDataLength)?
  421               if ( W != 4 )
  422               {
  423 _ERROR_DATA_LEN:
  424                 // Return error
  425                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  426               }
  427 
  428               // Now read the sensors
  429 
  430               // Prepare pointer (minus 1, see below) to store sensor (types and) values to
  431               // Note: 4 sensors at this example cannot return more than DPA_MAX_DATA_LENGTH bytes of data, so it does not have to be checked...
  432               // ... If it would be the case, then ERROR_FAIL must be returned
  433               FSR1 = &_DpaMessage.Response.PData[-1];
  434 
  435               // Store bitmap of sensors to get values from
  436               uns8  sensorsBitmap = FSR1[1];
  437 
  438               // 1st sensor (index 0) selected?
  439               if ( sensorsBitmap.0 )
  440               {
  441                 Get0_TemperatureTR();
  442                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  443               }
  444 
  445               // 2nd sensor (index 1) selected?
  446               if ( sensorsBitmap.1 )
  447               {
  448                 Get1_Temperature();
  449                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  450               }
  451 
  452               // 3rd sensor (index 2) selected?
  453               if ( sensorsBitmap.2 )
  454               {
  455                 Get2_BinaryData_Light();
  456                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  457               }
  458 
  459               // 4th sensor (index 3) selected?
  460               if ( sensorsBitmap.3 )
  461               {
  462                 Get3_BinaryData_Potentiometer();
  463                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  464               }
  465 
  466               // Compute returned data bytes count
  467               W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1;
  468               // Optimization: return W long block of bytes at response
  469 _W2_DpaDataLength:
  470               _DpaDataLength = W;
  471               goto DpaHandleReturnTRUE;
  472             }
  473           }
  474         }
  475 
  476         break;
  477       }
  478 
  479       // -------------------------------------------------
  480     case DpaEvent_FrcValue:
  481       // Called to get FRC value
  482 
  483       // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  484       FSR1 = &DataOutBeforeResponseFRC[0];
  485       // Check for correct FRC user data
  486       if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS )
  487       {
  488         // Actually used sensor index
  489         uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f;
  490         // Test sensor type
  491         switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ )
  492         {
  493           default:
  494             goto DpaHandleReturnFALSE;
  495 
  496             // No type specified, use specified index value
  497           case 0x00:
  498             goto _KeepSensorIndex;
  499 
  500             // For other types make the index value based on the requested index value and sensor type
  501           case STD_SENSOR_TYPE_TEMPERATURE:
  502             if ( sensorIndex > 1 )
  503               goto DpaHandleReturnFALSE;
  504             W = 0 + sensorIndex;
  505             break;
  506 
  507           case STD_SENSOR_TYPE_BINARYDATA7:
  508             if ( sensorIndex > 1 )
  509               goto DpaHandleReturnFALSE;
  510             W = 2 + sensorIndex;
  511             break;
  512         }
  513 
  514         // New sensor index based on type and requested index
  515         sensorIndex = W;
  516 _KeepSensorIndex:
  517 
  518         // Test for supported FRC commands
  519         switch ( _PCMD )
  520         {
  521           default:
  522             goto DpaHandleReturnFALSE;
  523 
  524           case FRC_STD_SENSORS_BIT:
  525           case FRC_STD_SENSORS_1B:
  526           case FRC_STD_SENSORS_2B:
  527             switch ( sensorIndex )
  528             {
  529               default:
  530                 goto DpaHandleReturnFALSE;
  531 
  532               case 0:
  533                 Carry = Get0_TemperatureTR();
  534                 break;
  535 
  536               case 1:
  537                 Carry = Get1_Temperature();
  538                 break;
  539 
  540               case 2:
  541                 Carry = Get2_BinaryData_Light();
  542                 break;
  543 
  544               case 3:
  545                 Carry = Get3_BinaryData_Potentiometer();
  546                 break;
  547             }
  548 
  549             // This type of FRC is not valid for the specified sensor
  550             if ( !Carry )
  551               goto DpaHandleReturnFALSE;
  552 
  553             break;
  554         }
  555 
  556         // Some sensor was measured by FRC, check if there is a sleep request
  557         FSR1++;
  558         if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  559         {
  560           // Remember sleep parameters to go to sleep at the Idle event later
  561           PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4];
  562           PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5];
  563           PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6];
  564         }
  565       }
  566 
  567       break;
  568 
  569       // -------------------------------------------------
  570     case DpaEvent_FrcResponseTime:
  571       // Called to get FRC response time
  572 
  573       // In this example the FRC commands are fast 
  574       switch ( DataOutBeforeResponseFRC[0] )
  575       {
  576         case FRC_STD_SENSORS_BIT:
  577         case FRC_STD_SENSORS_1B:
  578         case FRC_STD_SENSORS_2B:
  579           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  580           break;
  581       }
  582       break;
  583   }
  584 DpaHandleReturnFALSE:
  585   return FALSE;
  586 }
  587 
  588 //############################################################################################
  589 // Increases FSR1 and then stores the byte
  590 void setPlusPlusINDF1( uns8 data @ W )
  591 //############################################################################################
  592 {
  593   FSR1++; // Note: must not modify W
  594   setINDF1( data );
  595 }
  596 
  597 //############################################################################################
  598 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...]
  599 void StoreValue( uns8 sensorType )
  600 //############################################################################################
  601 {
  602   // Is the sensor type to be stored too?
  603   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  604     setPlusPlusINDF1( sensorType );
  605 
  606   // Store lower value byte
  607   setPlusPlusINDF1( sensorValue.low8 );
  608 
  609   // No more value bytes to store?
  610   if ( sensorType.7 != 0 )
  611     return;
  612 
  613   // Store higher value byte
  614   setPlusPlusINDF1( sensorValue.high8 );
  615 
  616   // Note: this example implements sensors returning only 1 or 2 bytes of data. If another data widths are returned, then it must be implemented explicitly.
  617 }
  618 
  619 //############################################################################################
  620 bit setFRCerror()
  621 //############################################################################################
  622 {
  623   responseFRCvalue2B = 2;
  624   return TRUE;
  625 }
  626 
  627 //############################################################################################
  628 bit sensorError;
  629 bit AdjustFrcTemperature()
  630 //############################################################################################
  631 {
  632   // Test for supported FRC commands
  633   switch ( _PCMD )
  634   {
  635     default:
  636       return FALSE;
  637 
  638     case FRC_STD_SENSORS_1B:
  639       // Return sensor FRC value 1B
  640       // Check for out of limits
  641       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20