1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - DDC-SE-01    *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: 0002_DDC-SE01.c,v $
    7 // Version: $Revision: 1.38 $
    8 // Date:    $Date: 2021/08/18 20:43:06 $
    9 //
   10 // Revision history:
   11 //   2021/08/18  Release for DPA 4.16
   12 //   2020/01/02  Release for DPA 4.11
   13 //   2018/10/25  Release for DPA 3.03
   14 //   2017/11/16  Release for DPA 3.02
   15 //   2017/08/14  Release for DPA 3.01
   16 //
   17 // *********************************************************************
   18 
   19 // MCR-BuildStdHandler
   20 #message '+CC5X -bu'
   21 
   22 #define _HWPID_     HWPID_IQRF_TECH__DEMO_DDC_SE01
   23 #define _HWPIDver_  0x0001
   24 
   25 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   26 // IQRF Standards documentation https://doc.iqrf.org/
   27 
   28 // This example implements 4 sensors according to the IQRF Sensors standard
   29 // 1st sensor is on-board TR temperature sensor.
   30 // 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.
   31 // 3rd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]).
   32 // 4th sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]).
   33 
   34 // Default IQRF include (modify the path according to your setup)
   35 #include "IQRF.h"
   36 
   37 // Default DPA header (modify the path according to your setup)
   38 #include "DPA.h"
   39 // Default Custom DPA Handler header (modify the path according to your setup)
   40 #include "DPAcustomHandler.h"
   41 // IQRF standards header (modify the path according to your setup)
   42 #include "IQRFstandard.h"
   43 #include "IQRF_HWPID.h"
   44 
   45 //############################################################################################
   46 
   47 // Number of implemented sensors
   48 #define SENSORS_COUNT 4
   49 
   50 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data.
   51 uns16 sensorValue @ param3;
   52 
   53 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable
   54 bit Get0_TemperatureTR();
   55 bit Get1_Temperature();
   56 bit Get2_BinaryData_Light();
   57 bit Get3_BinaryData_Potentiometer();
   58 
   59 // Temperature sensors read routine for both DDC-SE-01 sensor types
   60 void GetTemperature();
   61 // Read preset PIC ADC for DDC-SE-01
   62 uns8 ReadAdc();
   63 
   64 // 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)
   65 void StoreValue( uns8 sensorType );
   66 
   67 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   68 #define OneWire_TRIS         TRISC.3
   69 #define OneWire_IO_IN        PORTC.3
   70 #define OneWire_IO_OUT       LATC.3
   71 
   72 // Writes sensor configuration (resolution)
   73 bit Ds18B20WriteConfig( uns8 value );
   74 
   75 // Resets OneWire
   76 bit OneWireReset();
   77 // Reads OneWire byte
   78 uns8 OneWireReadByte();
   79 // Writes OneWire byte
   80 void OneWireWriteByte( uns8 byte );
   81 
   82 // DS18B20 commands
   83 #define CMD_READROM       0x33
   84 #define CMD_CONVERTTEMP   0x44
   85 #define CMD_CPYSCRATCHPAD 0x48
   86 #define CMD_WSCRATCHPAD   0x4e
   87 #define CMD_MATCHROM      0x55
   88 #define CMD_RPWRSUPPLY    0xb4
   89 #define CMD_RECEEPROM     0xb8
   90 #define CMD_RSCRATCHPAD   0xbe
   91 #define CMD_SKIPROM       0xcc
   92 #define CMD_ALARMSEARCH   0xec
   93 #define CMD_SEARCHROM     0xf0
   94 
   95 // I2C SCL frequency [Hz]
   96 #define I2Cfrequency      50000
   97 
   98 // MCP9802 address
   99 #define I2C_ADR             0b10010110
  100 // Power pin
  101 #define PWR_SENSOR_TRIS     TRISC.7
  102 #define PWR_SENSOR_IO       LATC.7
  103 
  104 // TRUE if DS18B20 is enabled at runtime at startup, FALSE in case of MCP9802
  105 bit isDS18B20;
  106 // Final DS18B20 temperature value read by state machine
  107 uns16 temperature;
  108 
  109 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  110 //############################################################################################
  111 bit CustomDpaHandler()
  112 //############################################################################################
  113 {
  114   // 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)
  115 #pragma updateBank default = UserBank_01
  116 
  117   // Finite machine states
  118   typedef enum
  119   {
  120     S_ResetConvertT = 0,
  121     S_SkipRomConvertT,
  122     S_CmdConvertT,
  123 
  124     S_WaitConvertT,
  125 
  126     S_ResetReadTemp,
  127     S_SkipRomReadTemp,
  128     S_CmdReadTemp,
  129     S_Byte1ReadTemp,
  130     S_Byte2ReadTemp
  131   } TState;
  132 
  133   // Handler presence mark
  134   clrwdt();
  135 
  136   // Sleeping parameters, valid when Time != 0
  137   static TPerOSSleep_Request PerOSSleep_Request;
  138   // Finite machine state
  139   static uns8 state; // = S_ResetConvertT = 0
  140   // Pre-read lower temperature byte
  141   static uns8 temperatureByteLow;
  142   // Conversion timeout counter
  143   static uns8 timeout;
  144 
  145   // Detect DPA event to handle
  146   switch ( GetDpaEvent() )
  147   {
  148     // -------------------------------------------------
  149     case DpaEvent_Interrupt:
  150       // Do an extra quick background interrupt work
  151       // ! 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.
  152       // ! 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.
  153       // ! 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.
  154       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  155       // ! Make sure race condition does not occur when accessing those variables at other places.
  156       // ! 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.
  157       // ! Do not call any OS functions except setINDFx().
  158       // ! Do not use any OS variables especially for writing access.
  159       // ! 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.
  160 
  161       // ms per TMR6 interrupt
  162 #define TICKS_LEN  10
  163 
  164       //  If TMR6 interrupt occurred
  165       if ( TMR6IF )
  166       {
  167         // Unmask interrupt
  168         TMR6IF = 0;
  169         // Decrement count
  170         if ( timeout != 0 )
  171           timeout--;
  172       }
  173       return Carry;
  174 
  175       // -------------------------------------------------
  176     case DpaEvent_Idle:
  177       // Do a quick background work when RF packet is not received
  178 
  179       // Should go to sleep?
  180       if ( PerOSSleep_Request.Time != 0 )
  181       {
  182         // Copy sleep parameters to the DPA request
  183         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
  184         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
  185         // Switch off sleeping time=flag
  186         PerOSSleep_Request.Time = 0;
  187         // Finalize OS Sleep DPA Request
  188         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  189         _PNUM = PNUM_OS;
  190         _PCMD = CMD_OS_SLEEP;
  191         // I2C down
  192         if ( !isDS18B20 )
  193           _DpaApiI2Cshutdown();
  194         // Perform local DPA Request to go to sleep
  195         DpaApiLocalRequest();
  196         // I2C up
  197         if ( !isDS18B20 )
  198           _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  199       }
  200 
  201       // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC
  202       if ( !isDS18B20 )
  203         break;
  204 
  205       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  206       OneWire_IO_OUT = 0;
  207 
  208       skip( state );
  209 #pragma computedGoto 1
  210       goto _S_ResetConvertT;
  211       goto _S_SkipRomConvertT;
  212       goto _S_CmdConvertT;
  213       goto _S_WaitConvertT;
  214       goto _S_ResetReadTemp;
  215       goto _S_SkipRomReadTemp;
  216       goto _S_CmdReadTemp;
  217       goto _S_Byte1ReadTemp;
  218       goto _S_Byte2ReadTemp;
  219 #pragma computedGoto 0
  220       ;
  221       // --------------
  222 _S_Byte2ReadTemp:
  223       temperature.high8 = OneWireReadByte();
  224       temperature.low8 = temperatureByteLow;
  225 
  226 ResetMachine:
  227       state = S_ResetConvertT;
  228       goto ExitMachine;
  229 
  230       // --------------
  231 _S_ResetConvertT:
  232 _S_ResetReadTemp:
  233       if ( !OneWireReset() )
  234       {
  235 _S_Error_Reset:
  236         STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( temperature );
  237         goto ResetMachine;
  238       }
  239       goto NextState;
  240 
  241       // --------------
  242 _S_SkipRomConvertT:
  243 _S_SkipRomReadTemp:
  244       // OneWire: Skip ROM
  245       OneWireWriteByte( CMD_SKIPROM );
  246       goto NextState;
  247 
  248       // --------------
  249 _S_CmdConvertT:
  250       // OneWire: Convert temperature
  251       OneWireWriteByte( CMD_CONVERTTEMP );
  252       // Setup timeout for approx 750 ms (the longest conversion time)
  253       timeout = 2 + 750 / TICKS_LEN;
  254       goto NextState;
  255 
  256       // --------------
  257 _S_WaitConvertT:
  258       if ( OneWireReadByte() == 0xff )
  259         goto NextState;
  260 
  261       // Timeout?
  262       if ( timeout == 0 )
  263         goto _S_Error_Reset;
  264 
  265       goto ExitMachine;
  266 
  267       // --------------
  268 _S_CmdReadTemp:
  269       // OneWire: Read scratchpad
  270       OneWireWriteByte( CMD_RSCRATCHPAD );
  271       goto NextState;
  272 
  273       // --------------
  274 _S_Byte1ReadTemp:
  275       temperatureByteLow = OneWireReadByte();
  276       goto NextState;
  277 
  278       // --------------
  279 NextState:
  280       ++state;
  281 
  282 ExitMachine:
  283       break;
  284 
  285       // -------------------------------------------------
  286     case DpaEvent_Init:
  287       // Do a one time initialization before main loop starts
  288 
  289       // Initialize sensors
  290       // C5 (AN4) as input
  291       moduleInfo();
  292       // Connected TR pins?
  293       if ( !bufferINFO[5].7 )
  294       {
  295         TRISC.6 = 1;
  296         TRISB.4 = 1;
  297       }
  298       TRISA.5 = 1;
  299 
  300       // C1 (AN0) as input
  301       TRISA.0 = 1;
  302 
  303       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  304 #if F_OSC == 16000000
  305       PR6 = 250 - 1;
  306       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  307 #else
  308 #error Unsupported oscillator frequency
  309 #endif
  310 
  311       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  312       if ( Ds18B20WriteConfig( 0b0.00.00000 ) )
  313         // DS18B20 is enabled
  314         isDS18B20 = TRUE;
  315       else
  316       {
  317         // Expect MCP9802 is enabled
  318         I2Ctimeout = 0xFF;
  319         _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  320       }
  321 
  322       break;
  323 
  324       // -------------------------------------------------
  325     case DpaEvent_AfterSleep:
  326       // Called after woken up after sleep
  327       if ( !isDS18B20 )
  328         _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  329 
  330       // Called on wake-up from sleep
  331       TMR6IE = TRUE;
  332       TMR6ON = TRUE;
  333       break;
  334 
  335       // -------------------------------------------------
  336     case DpaEvent_BeforeSleep:
  337       // Called before going to sleep
  338       if ( !isDS18B20 )
  339         _DpaApiI2Cshutdown();
  340 
  341       // -------------------------------------------------
  342     case DpaEvent_DisableInterrupts:
  343       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  344       // Must not use TMR6 any more
  345       TMR6ON = FALSE;
  346       TMR6IE = FALSE;
  347       break;
  348 
  349       // -------------------------------------------------
  350     case DpaEvent_DpaRequest:
  351       // Called to interpret DPA request for peripherals
  352       // -------------------------------------------------
  353       // Peripheral enumeration
  354       if ( IsDpaEnumPeripheralsRequest() )
  355       {
  356         // We implement 1 standard user peripheral
  357         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  358         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  359         _DpaMessage.EnumPeripheralsAnswer.HWPID = _HWPID_;
  360         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = _HWPIDver_;
  361 
  362 DpaHandleReturnTRUE:
  363         return TRUE;
  364       }
  365       // -------------------------------------------------
  366       // Get information about peripheral
  367       else if ( IsDpaPeripheralInfoRequest() )
  368       {
  369         if ( _PNUM == PNUM_STD_SENSORS )
  370         {
  371           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  372           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  373           // Set standard version
  374           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  375           goto DpaHandleReturnTRUE;
  376         }
  377 
  378         break;
  379       }
  380       // -------------------------------------------------
  381       else
  382       {
  383         // Handle peripheral command
  384 
  385         // Supported peripheral number?
  386         if ( _PNUM == PNUM_STD_SENSORS )
  387         {
  388           // Supported commands?
  389           switch ( _PCMD )
  390           {
  391             // Invalid command
  392             default:
  393               // Return error
  394               DpaApiReturnPeripheralError( ERROR_PCMD );
  395 
  396               // Sensor enumeration
  397             case PCMD_STD_ENUMERATE:
  398               if ( _DpaDataLength != 0 )
  399                 goto _ERROR_DATA_LEN;
  400 
  401               // Then just enumerate their types
  402               _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  403               _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE;
  404               _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7;
  405               _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_BINARYDATA7;
  406               W = SENSORS_COUNT;
  407               goto _W2_DpaDataLength;
  408 
  409               // Supported commands. They are handled the same way except one "if" at StoreValue() method
  410             case PCMD_STD_SENSORS_READ_VALUES:
  411             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  412             {
  413               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  414               W = _DpaDataLength;
  415               if ( W == 0 ) // Note: must not modify W
  416               {
  417                 // Actually clears the bitmap
  418 #if &_DpaMessage.Request.PData[0] != &bufferRF[0]
  419 #error
  420 #endif
  421                 clearBufferRF();
  422                 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care)
  423                 _DpaMessage.Request.PData[0].0 = 1;
  424                 // Bitmap is 32 bits long = 4
  425                 _DpaDataLength = W = sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap );
  426               }
  427 
  428               // Invalid bitmap (data) length (W = _DpaDataLength)?
  429               if ( W != sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) )
  430               {
  431 _ERROR_DATA_LEN:
  432                 // Return error
  433                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  434               }
  435 
  436               // Now read the sensors
  437 
  438               // Prepare pointer (minus 1, see below) to store sensor (types and) values to
  439               // 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...
  440               // ... If it would be the case, then ERROR_FAIL must be returned
  441               FSR1 = &_DpaMessage.Response.PData[-1];
  442 
  443               // Store bitmap of sensors to get values from
  444               uns8  sensorsBitmap = FSR1[1];
  445 
  446               // 1st sensor (index 0) selected?
  447               if ( sensorsBitmap.0 )
  448               {
  449                 Get0_TemperatureTR();
  450                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  451               }
  452 
  453               // 2nd sensor (index 1) selected?
  454               if ( sensorsBitmap.1 )
  455               {
  456                 Get1_Temperature();
  457                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  458               }
  459 
  460               // 3rd sensor (index 2) selected?
  461               if ( sensorsBitmap.2 )
  462               {
  463                 Get2_BinaryData_Light();
  464                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  465               }
  466 
  467               // 4th sensor (index 3) selected?
  468               if ( sensorsBitmap.3 )
  469               {
  470                 Get3_BinaryData_Potentiometer();
  471                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  472               }
  473 
  474               // Compute returned data bytes count
  475               W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1;
  476               // Optimization: return W long block of bytes at response
  477 _W2_DpaDataLength:
  478               _DpaDataLength = W;
  479               goto DpaHandleReturnTRUE;
  480             }
  481           }
  482         }
  483 
  484         break;
  485       }
  486 
  487       // -------------------------------------------------
  488     case DpaEvent_FrcValue:
  489       // Called to get FRC value
  490 
  491       // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  492       FSR1 = &DataOutBeforeResponseFRC[0];
  493       // Check for correct FRC user data
  494       if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS )
  495       {
  496         // Actually used sensor index
  497         uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f;
  498         // Test sensor type
  499         switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ )
  500         {
  501           default:
  502             goto DpaHandleReturnFALSE;
  503 
  504             // No type specified, use specified index value
  505           case 0x00:
  506             goto _KeepSensorIndex;
  507 
  508             // For other types make the index value based on the requested index value and sensor type
  509           case STD_SENSOR_TYPE_TEMPERATURE:
  510             if ( sensorIndex > 1 )
  511               goto DpaHandleReturnFALSE;
  512             W = 0 + sensorIndex;
  513             break;
  514 
  515           case STD_SENSOR_TYPE_BINARYDATA7:
  516             if ( sensorIndex > 1 )
  517               goto DpaHandleReturnFALSE;
  518             W = 2 + sensorIndex;
  519             break;
  520         }
  521 
  522         // New sensor index based on type and requested index
  523         sensorIndex = W;
  524 _KeepSensorIndex:
  525 
  526         // Test for supported FRC commands
  527         switch ( _PCMD )
  528         {
  529           default:
  530             goto DpaHandleReturnFALSE;
  531 
  532           case FRC_STD_SENSORS_BIT:
  533           case FRC_STD_SENSORS_1B:
  534           case FRC_STD_SENSORS_2B:
  535             switch ( sensorIndex )
  536             {
  537               default:
  538                 goto DpaHandleReturnFALSE;
  539 
  540               case 0:
  541                 Carry = Get0_TemperatureTR();
  542                 break;
  543 
  544               case 1:
  545                 Carry = Get1_Temperature();
  546                 break;
  547 
  548               case 2:
  549                 Carry = Get2_BinaryData_Light();
  550                 break;
  551 
  552               case 3:
  553                 Carry = Get3_BinaryData_Potentiometer();
  554                 break;
  555             }
  556 
  557             // This type of FRC is not valid for the specified sensor
  558             if ( !Carry )
  559               goto DpaHandleReturnFALSE;
  560 
  561             break;
  562         }
  563 
  564         // Some sensor was measured by FRC, check if there is a sleep request
  565         FSR1++;
  566         if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  567         {
  568           // Remember sleep parameters to go to sleep at the Idle event later
  569           PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4];
  570           PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5];
  571           PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6];
  572         }
  573       }
  574 
  575       break;
  576 
  577       // -------------------------------------------------
  578     case DpaEvent_FrcResponseTime:
  579       // Called to get FRC response time
  580 
  581       // In this example the FRC commands are fast
  582       switch ( DataOutBeforeResponseFRC[0] )
  583       {
  584         case FRC_STD_SENSORS_BIT:
  585         case FRC_STD_SENSORS_1B:
  586         case FRC_STD_SENSORS_2B:
  587           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  588           break;
  589       }
  590       break;
  591   }
  592 DpaHandleReturnFALSE:
  593   return FALSE;
  594 }
  595 
  596 //############################################################################################
  597 // Increases FSR1 and then stores the byte
  598 void setPlusPlusINDF1( uns8 data @ W )
  599 //############################################################################################
  600 {
  601   FSR1++; // Note: must not modify W
  602   setINDF1( data );
  603 }
  604 
  605 //############################################################################################
  606 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...]
  607 void StoreValue( uns8 sensorType )
  608 //############################################################################################
  609 {
  610   // Is the sensor type to be stored too?
  611   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  612     setPlusPlusINDF1( sensorType );
  613 
  614   // Store lower value byte
  615   setPlusPlusINDF1( sensorValue.low8 );
  616 
  617   // No more value bytes to store?
  618   if ( sensorType.7 != 0 )
  619     return;
  620 
  621   // Store higher value byte
  622   setPlusPlusINDF1( sensorValue.high8 );
  623 
  624   // 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.
  625 }
  626 
  627 //############################################################################################
  628 bit setFRCerror()
  629 //############################################################################################
  630 {
  631   responseFRCvalue2B = FRC_STD_FRC_ERROR_VALUE;
  632   return TRUE;
  633 }
  634 
  635 //############################################################################################
  636 bit sensorError;
  637 bit AdjustFrcTemperature()
  638 //############################################################################################
  639 {
  640   // Test for supported FRC commands
  641   switch ( _PCMD )
  642   {
  643     default:
  644       return FALSE;
  645 
  646     case FRC_STD_SENSORS_1B:
  647       // Return sensor FRC value 1B
  648       // Check for out of limits
  649       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) )
  650         return setFRCerror();
  651 
  652       // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  653       responseFRCvalue2B = sensorValue + 4; // Note: do rounding when /8
  654       responseFRCvalue2B /= 8;
  655       responseFRCvalue += 44;
  656       break;
  657 
  658     case FRC_STD_SENSORS_2B:
  659       // Return sensor FRC value 2B
  660       if ( sensorError )
  661         return setFRCerror();
  662 
  663       responseFRCvalue2B = sensorValue ^ 0x8000;
  664       break;
  665   }
  666 
  667   return TRUE;
  668 }
  669 
  670 //############################################################################################
  671 // Sensor index 0: measure temperature using the TR sensor
  672 bit Get0_TemperatureTR()
  673 //############################################################################################
  674 {
  675   // Make sure FSR1 is not modified
  676 
  677   // When error, then adjust the standard error values
  678   sensorError = FALSE;
  679   // Measure temperature using TR sensor
  680   if ( getTemperature() == -128 )
  681   {
  682     sensorError = TRUE;
  683     STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue );
  684   }
  685   else
  686   {
  687     // Extent minus sign bit
  688     if ( param3.11 )
  689       param3 |= 0xF000;
  690     // Return sensor value
  691     sensorValue = param3;
  692   }
  693   // Handler FRC
  694   return AdjustFrcTemperature();
  695 }
  696 
  697 //############################################################################################
  698 // Sensor index 1: measure temperature using one of the DDC-SE-01 sensors
  699 bit Get1_Temperature()
  700 //############################################################################################
  701 {
  702   // Make sure FSR1 is not modified
  703 
  704   // Measure temperature using DDC-SE-01 sensors
  705   sensorError = FALSE;
  706   // Read temperature and check for an error
  707   GetTemperature();
  708   // When error, return standard (FRC) error value(s)
  709   if ( STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) )
  710     sensorError = TRUE;
  711 
  712   // FrcValues
  713   return AdjustFrcTemperature();
  714 }
  715 
  716 //############################################################################################
  717 bit AdjustFrcBinaryData()
  718 //############################################################################################
  719 {
  720   // Test for supported FRC commands
  721   switch ( _PCMD )
  722   {
  723     default:
  724       return FALSE;
  725 
  726     case FRC_STD_SENSORS_BIT:
  727       // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01]
  728 
  729       // Number of shifts to get the bit out of the return value
  730       uns8 bitLoop = ( INDF1 >> 5 ) + 1;
  731       // Value to get the bit from
  732       W = sensorValue.low8;
  733       do
  734       {
  735         // Get the bit to Carry
  736         W = rr( W );
  737         // Next bit
  738       } while ( --bitLoop != 0 ); // Note: must not modify W and Carry
  739       // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set)
  740       responseFRCvalue.1 = 1; // Note: must not modify Carry
  741       // Is bit set?
  742       if ( !Carry )
  743         // Bit is NOT set, return [10]
  744         responseFRCvalue.0 = 0;
  745       break;
  746 
  747     case FRC_STD_SENSORS_1B:
  748       responseFRCvalue = sensorValue.low8 + 4;
  749       break;
  750   }
  751   return TRUE;
  752 }
  753 
  754 //############################################################################################
  755 // Sensor index 2: returns light intensity indicator value using DDC-SE-01
  756 bit Get2_BinaryData_Light()
  757 //############################################################################################
  758 {
  759   // Make sure FSR1 is not modified
  760 
  761   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input
  762   ANSELA.0 = 1;
  763   // ADC setting (AN0 channel)
  764   ADCON0 = 0b0.00000.01;
  765   // Read ADC
  766   sensorValue.low8 = ReadAdc() / 2;
  767   // Return sensor FRC value
  768   return AdjustFrcBinaryData();
  769 }
  770 
  771 //############################################################################################
  772 // Sensor index 3: returns potentiometer value using DDC-SE-01
  773 bit Get3_BinaryData_Potentiometer()
  774 //############################################################################################
  775 {
  776   // Make sure FSR1 is not modified
  777 
  778   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input
  779   ANSELA.5 = 1;
  780   // ADC setting (AN4 channel)
  781   ADCON0 = 0b0.00100.01;
  782   // Read ADC
  783   sensorValue.low8 = ReadAdc() / 2;
  784   // Return sensor FRC value
  785   return AdjustFrcBinaryData();
  786 }
  787 
  788 //############################################################################################
  789 // OneWire and Dallas 18B20 routines
  790 //############################################################################################
  791 
  792 //############################################################################################
  793 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  794 //############################################################################################
  795 {
  796   // 16 MHz
  797   // + 0.75us ( W=val, Call )
  798   for ( ;; )
  799   {         // loop time
  800     nop2(); // 0.50us
  801     nop2(); // 1.00us
  802     nop2(); // 1.50us
  803     nop2(); // 2.00us
  804     nop2(); // 2.50us
  805     nop2(); // 3.00us
  806     nop();  // 3.25us
  807     if ( --val == 0 ) // + 0.75us (W--, BTFS )
  808       return;         // + 0.25us
  809     nop2(); // 4.50us
  810   }         // 5.00us (Goto)
  811 }
  812 //############################################################################################
  813 
  814 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  815 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  816 
  817 //############################################################################################
  818 void OneWireWriteByte( uns8 byte )
  819 //############################################################################################
  820 {
  821   uns8 bitLoop = 8;
  822   do
  823   {
  824     // Next sequence is time precision critical
  825     GIE = FALSE;
  826 
  827     OneWireData0();
  828     nop2();         // 1 us [0.5 us]
  829     nop2();         // [1.0 us]
  830     if ( byte.0 )   // 2.5 us [1.75us]
  831       OneWireData1();
  832 
  833     // End of time precision critical sequence
  834     GIE = TRUE;
  835 
  836     // 60us minimum in total, does not have to be precise
  837     Delay5us( ( 60 - 3 ) / 5 + 1 );
  838 
  839     OneWireData1();
  840 
  841     byte >>= 1;
  842   } while ( --bitLoop != 0 );
  843 }
  844 
  845 //############################################################################################
  846 uns8 OneWireReadByte()
  847 //############################################################################################
  848 {
  849   uns8 result;
  850   uns8 bitLoop = 8;
  851   do
  852   {
  853     // Next sequence is time precision critical
  854     GIE = FALSE;
  855 
  856     OneWireData0();
  857     nop2();         // 1 us [0.5 us]
  858 #if F_OSC == 16000000
  859     nop2();         // [1.0 us]
  860 #endif
  861     OneWireData1();         // 2 us [1.5 us]
  862     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  863 
  864     Carry = 0;              // 17.5 us [16.75 us]
  865     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  866       Carry = 1;
  867 
  868     // End of time precision critical sequence
  869     GIE = TRUE;             // must not modify Carry
  870     result = rr( result );
  871 
  872     // 60us minimum in total, does not have to be precise
  873     Delay5us( ( 60 - 20 ) / 5 + 1 );
  874   } while ( --bitLoop != 0 );
  875 
  876   return result;
  877 }
  878 
  879 //############################################################################################
  880 bit OneWireReset()
  881 //############################################################################################
  882 {
  883   // Setting the pin once to low is enough
  884   OneWire_IO_OUT = 0;
  885   // Reset pulse
  886   OneWireData0();
  887   Delay5us( 500 / 5 );
  888   // Reset pulse end
  889   OneWireData1();
  890   // Next sequence is time precision critical
  891   GIE = FALSE;
  892   // Wait for presence pulse
  893   Delay5us( 70 / 5 );
  894   // End of time precision critical sequence
  895   GIE = TRUE;
  896   // Presence pulse?
  897   if ( OneWire_IO_IN )
  898   {
  899     // No presence, finish initialization sequence
  900     Delay5us( ( 500 - 70 ) / 5 );
  901     return FALSE;
  902   }
  903   else
  904   {
  905     // Presence OK, finish initialization sequence
  906     Delay5us( ( 500 - 70 ) / 5 );
  907     return TRUE;
  908   }
  909 }
  910 
  911 //############################################################################################
  912 void OneWireCmd( uns8 cmd )
  913 //############################################################################################
  914 {
  915   // OneWire: Skip ROM
  916   OneWireWriteByte( CMD_SKIPROM );
  917   // OneWire: Send command
  918   OneWireWriteByte( cmd );
  919 }
  920 
  921 //############################################################################################
  922 bit Ds18B20WriteConfig( uns8 value )
  923 //############################################################################################
  924 {
  925   if ( OneWireReset() )
  926   {
  927     // Write Scratchpad
  928     OneWireCmd( CMD_WSCRATCHPAD );
  929 
  930     // Write TL = ? (we dot not care the value)
  931     OneWireWriteByte( W );
  932     // Write TH = ? (we dot not care the value)
  933     OneWireWriteByte( W );
  934     // Write Config byte
  935     OneWireWriteByte( value );
  936 
  937     if ( OneWireReset() )
  938     {
  939       //  Copy Scratchpad
  940       OneWireCmd( CMD_CPYSCRATCHPAD );
  941       return TRUE;
  942     }
  943   }
  944   return FALSE;
  945 }
  946 
  947 //############################################################################################
  948 void MCP9802GetTemp()
  949 //############################################################################################
  950 {
  951   STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue );
  952 
  953   // MCP9802 address
  954   _DpaApiI2Cstart( I2C_ADR );
  955   if ( I2CwasTimeout )
  956     return;
  957 
  958   // pointer: 1 = configuration register
  959   _DpaApiI2Cwrite( 0x01 );
  960   // configuration: 9-bit ADC
  961   _DpaApiI2CwriteAndStop( 0x00 );
  962 
  963   // MCP9802 address
  964   _DpaApiI2Cstart( I2C_ADR );
  965   // pointer: 0 = temperature
  966   _DpaApiI2CwriteAndStop( 0 );
  967 
  968   // MCP9802 address + read
  969   _DpaApiI2Cstart( I2C_ADR | 1 );
  970   // store the result
  971   sensorValue.high8 = _DpaApiI2Cread( TRUE );
  972   sensorValue.low8 = _DpaApiI2Cread( FALSE );
  973   _DpaApiI2Cstop();
  974 }
  975 
  976 //############################################################################################
  977 // Other routines
  978 //############################################################################################
  979 
  980 //############################################################################################
  981 void GetTemperature()
  982 //############################################################################################
  983 {
  984   // Reads temperature from an enabled sensor
  985   if ( isDS18B20 )
  986     // Temperature is ready at the background
  987     sensorValue = temperature;
  988   else
  989   {
  990     // Temperature value must be read from I2C sensor
  991     MCP9802GetTemp();
  992     if ( !STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) )
  993     {
  994       sensorValue += 0x08;
  995       sensorValue /= 0x10;
  996     }
  997   }
  998 }
  999 
 1000 //############################################################################################
 1001 uns8 ReadAdc()
 1002 //############################################################################################
 1003 {
 1004   // ADC result - left justified, Fosc/8
 1005   ADCON1 = 0b0001.0000;
 1006   // Do a smallest delay for ADC ACQUISITION TIME
 1007   waitMS( 1 );
 1008   // start ADC
 1009   GO = 1;
 1010   // wait for ADC finish
 1011   while ( GO );
 1012   return ADRESH;
 1013 }
 1014 
 1015 //############################################################################################
 1016 // 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)
 1017 #include "DPAcustomHandler.h"
 1018 //############################################################################################