1 // *********************************************************************
    2 //   Custom DPA Handler code example - DDC-SE-01 and/or DDC-RE-01      *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-DDC-Core.c,v $
    7 // Version: $Revision: 1.19 $
    8 // Date:    $Date: 2020/02/20 17:18:58 $
    9 //
   10 // Revision history:
   11 //   2020/01/02  Release for DPA 4.11
   12 //   2017/03/13  Release for DPA 3.00
   13 //   2016/12/22  Release for DPA 2.28
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 
   19 /* This example implements HWP for DDC-SE-01 and/or DDC-RE-01
   20 The example will not work at demo DPA version because demo DPA does not support PCMD != 0
   21 
   22 DDC-SE-01: HWPID=0x001F
   23 DDC-RE-01: HWPID=0x002F
   24 DDC-SE-01 & DDC-RE-01: HWPID=0x003F
   25 
   26 ----------- DDC-SE-01
   27 Request: PNUM = 0x20, PCMD = 0x31
   28 Response:
   29  PData[0]=Temperature at [C]
   30  PData[1]=Photoresistor value 0x00-0xFF
   31  PData[2]=Potentiometer value 0x00-0xFF
   32  returns ERROR_FAIL when error reading temperature
   33 
   34 FRC=0xC0, returns temperature at C, 127 for 0 C, 0x80 for error reading temperature
   35 FRC=0xC1, returns photoresistor value, returns 1 instead of 0
   36 FRC=0xC2, returns potentiometer value, returns 1 instead of 0
   37 
   38 ----------- DDC-RE-01
   39 Request: PNUM = 0x20, PCMD = 0x32
   40  PData[0]=Relay1Ctrl
   41  PData[1]=Relay2Ctrl
   42 
   43 Response:
   44  PData[0]=Relay1State
   45  PData[1]=Relay2State
   46 
   47 RelayCtrl: 0=Switch the Relay off, 1=Switch the Relay on, Other=Do not control the Relay
   48 RelayState: 0=Relay was off, 1=Relay was on
   49 */
   50 
   51 // *********************************************************************
   52 
   53 // Default IQRF include (modify the path according to your setup)
   54 #include "IQRF.h"
   55 
   56 // Default DPA header (modify the path according to your setup)
   57 #include "DPA.h"
   58 // Default Custom DPA Handler header (modify the path according to your setup)
   59 #include "DPAcustomHandler.h"
   60 // I2C Master library
   61 #include "lib/I2Cmaster.c"
   62 
   63 #if defined( DDC_SE_01 ) && defined( DDC_RE_01 )
   64 #define ThisHWPID 0x003F
   65 #elif defined( DDC_SE_01 )
   66 #define ThisHWPID 0x001F
   67 #elif defined( DDC_RE_01 )
   68 #define ThisHWPID 0x002F
   69 #else
   70 #error Symbol(s) DDC_SE_01 and/or DDC_RE_01 must be defined
   71 #endif
   72 
   73 //############################################################################################
   74 
   75 #if defined( DDC_SE_01 )
   76 
   77 // Special temperature value to indicate a sensor error 
   78 #define ERROR_TEMPERATURE 0xF800
   79 
   80 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   81 #define OneWire_TRIS         TRISC.3
   82 #define OneWire_IO_IN        PORTC.3
   83 #define OneWire_IO_OUT       LATC.3
   84 
   85 // Writes sensor configuration (resolution)
   86 bit Ds18B20WriteConfig( uns8 value );
   87 
   88 // Resets OneWire
   89 bit OneWireReset();
   90 // Reads OneWire byte
   91 uns8 OneWireReadByte();
   92 // Writes OneWire byte
   93 void OneWireWriteByte( uns8 byte );
   94 
   95 // DS18B20 commands
   96 #define CMD_READROM       0x33
   97 #define CMD_CONVERTTEMP   0x44
   98 #define CMD_CPYSCRATCHPAD 0x48
   99 #define CMD_WSCRATCHPAD   0x4e
  100 #define CMD_MATCHROM      0x55
  101 #define CMD_RPWRSUPPLY    0xb4
  102 #define CMD_RECEEPROM     0xb8
  103 #define CMD_RSCRATCHPAD   0xbe
  104 #define CMD_SKIPROM       0xcc
  105 #define CMD_ALARMSEARCH   0xec
  106 #define CMD_SEARCHROM     0xf0
  107 
  108 // I2C SCL frequency [Hz]
  109 #define I2Cfrequency      50000
  110 
  111 // Own implementation with timeout
  112 #define i2c_waitForIdle_REDEFINE
  113 
  114 // TRUE if I2C timeout occurred
  115 bit i2cTimeout;
  116 
  117 // MCP9802 address
  118 #define I2C_ADR             0b10010110
  119 // Power pin
  120 #define PWR_SENSOR_TRIS     TRISC.7
  121 #define PWR_SENSOR_IO       LATC.7
  122 
  123 // Sensors read routines
  124 uns16 GetTemperature();
  125 uns8  ReadAdcPhotoresistor();
  126 uns8  ReadAdcPotentiometer();
  127 
  128 // TRUE if DS18B20 is enabled, FALSE in case of MCP9802
  129 bit isDS18B20;
  130 // Final DS18B20 temperature value
  131 static uns16 temperature;
  132 
  133 #endif
  134 
  135 #if defined( DDC_RE_01 )
  136 
  137 // C.5 = C8 = Relay#1
  138 #define RELAY1_LAT  LATC.5 
  139 #define RELAY1_TRIS TRISC.5
  140 
  141 // C.2 = C2 = Relay#2
  142 #define RELAY2_LAT  LATC.2 
  143 #define RELAY2_TRIS TRISC.2
  144 
  145 #endif
  146 
  147 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  148 //############################################################################################
  149 bit CustomDpaHandler()
  150 //############################################################################################
  151 {
  152 #if defined( DDC_SE_01 )
  153   // Finite machine states
  154   typedef enum
  155   {
  156     S_ResetConvertT = 0,
  157     S_SkipRomConvertT,
  158     S_CmdConvertT,
  159 
  160     S_WaitConvertT,
  161 
  162     S_ResetReadTemp,
  163     S_SkipRomReadTemp,
  164     S_CmdReadTemp,
  165     S_Byte1ReadTemp,
  166     S_Byte2ReadTemp
  167   } TState;
  168 
  169 #endif
  170 
  171   // Handler presence mark
  172   clrwdt();
  173 
  174 #if defined( DDC_SE_01 )
  175   // Finite machine state
  176   static uns8 state; // = S_ResetConvertT = 0
  177   // Pre-read lower temperature byte
  178   static uns8 temperatureByteLow;
  179   // Conversion timeout counter
  180   static uns8 timeout;
  181 #endif
  182 
  183   // Detect DPA event to handle
  184   switch ( GetDpaEvent() )
  185   {
  186     // -------------------------------------------------
  187     case DpaEvent_Interrupt:
  188       // Do an extra quick background interrupt work
  189       // ! 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.
  190       // ! 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.
  191       // ! 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.
  192       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  193       // ! Make sure race condition does not occur when accessing those variables at other places.
  194       // ! 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.
  195       // ! Do not call any OS functions except setINDFx().
  196       // ! Do not use any OS variables especially for writing access.
  197       // ! 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.
  198 
  199 #if defined( DDC_SE_01 )
  200 #define TICKS_LEN  10
  201 
  202 //  If TMR6 interrupt occurred
  203       if ( TMR6IF )
  204       {
  205         // Unmask interrupt
  206         TMR6IF = 0;
  207         // Decrement count
  208         if ( timeout != 0 )
  209           timeout--;
  210       }
  211 #endif
  212       return Carry;
  213 
  214 #if defined( DDC_SE_01 )
  215       // -------------------------------------------------
  216     case DpaEvent_Idle:
  217       // Do a quick background work when RF packet is not received
  218 
  219       // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 
  220       if ( !isDS18B20 )
  221         break;
  222 
  223       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  224       OneWire_IO_OUT = 0;
  225 
  226       skip( state );
  227 #pragma computedGoto 1
  228       goto _S_ResetConvertT;
  229       goto _S_SkipRomConvertT;
  230       goto _S_CmdConvertT;
  231       goto _S_WaitConvertT;
  232       goto _S_ResetReadTemp;
  233       goto _S_SkipRomReadTemp;
  234       goto _S_CmdReadTemp;
  235       goto _S_Byte1ReadTemp;
  236       goto _S_Byte2ReadTemp;
  237 #pragma computedGoto 0
  238       ;
  239       // --------------
  240 _S_Byte2ReadTemp:
  241       temperature.high8 = OneWireReadByte();
  242       temperature.low8 = temperatureByteLow;
  243 
  244 ResetMachine:
  245       state = S_ResetConvertT;
  246       goto ExitMachine;
  247 
  248       // --------------
  249 _S_ResetConvertT:
  250 _S_ResetReadTemp:
  251       if ( !OneWireReset() )
  252       {
  253 _S_Error_Reset:
  254         temperature = ERROR_TEMPERATURE;
  255         goto ResetMachine;
  256       }
  257       goto NextState;
  258 
  259       // --------------
  260 _S_SkipRomConvertT:
  261 _S_SkipRomReadTemp:
  262       // OneWire: Skip ROM
  263       OneWireWriteByte( CMD_SKIPROM );
  264       goto NextState;
  265 
  266       // --------------
  267 _S_CmdConvertT:
  268       // OneWire: Convert temperature
  269       OneWireWriteByte( CMD_CONVERTTEMP );
  270       // Setup timeout for approx 750 ms (the longest conversion time)
  271       timeout = 2 + 750 / TICKS_LEN;
  272       goto NextState;
  273 
  274       // --------------
  275 _S_WaitConvertT:
  276       if ( OneWireReadByte() == 0xff )
  277         goto NextState;
  278 
  279       // Timeout?
  280       if ( timeout == 0 )
  281         goto _S_Error_Reset;
  282 
  283       goto ExitMachine;
  284 
  285       // --------------
  286 _S_CmdReadTemp:
  287       // OneWire: Read scratchpad
  288       OneWireWriteByte( CMD_RSCRATCHPAD );
  289       goto NextState;
  290 
  291       // --------------
  292 _S_Byte1ReadTemp:
  293       temperatureByteLow = OneWireReadByte();
  294       goto NextState;
  295 
  296       // --------------
  297 NextState:
  298       ++state;
  299 
  300 ExitMachine:
  301       break;
  302 
  303       // -------------------------------------------------
  304     case DpaEvent_FrcValue:
  305       // Called to get FRC value
  306 
  307       // Which FRC command?
  308       switch ( _PCMD )
  309       {
  310         case FRC_USER_BYTE_FROM + 0:
  311           // Returns temperature
  312           uns16 curTemperature = GetTemperature();
  313           if ( curTemperature == ERROR_TEMPERATURE )
  314             responseFRCvalue = 0x80;
  315           else
  316           {
  317             responseFRCvalue = curTemperature.low8;
  318             if ( responseFRCvalue == 0 )
  319               responseFRCvalue = 127;
  320           }
  321           break;
  322 
  323         case FRC_USER_BYTE_FROM + 1:
  324           // Returns photoresistor value
  325           responseFRCvalue = ReadAdcPhotoresistor();
  326 _Zero2One:
  327           if ( responseFRCvalue == 0 )
  328             responseFRCvalue |= 1;
  329           break;
  330 
  331         case FRC_USER_BYTE_FROM + 2:
  332           // Returns potentiometer value
  333           responseFRCvalue = ReadAdcPotentiometer();
  334           goto _Zero2One;
  335       }
  336       break;
  337 
  338       // -------------------------------------------------
  339     case DpaEvent_FrcResponseTime:
  340       // Called to get FRC response time
  341 
  342       if ( DataOutBeforeResponseFRC[0] >= ( FRC_USER_BYTE_FROM + 0 ) && DataOutBeforeResponseFRC[0] <= ( FRC_USER_BYTE_FROM + 2 ) )
  343         responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  344       break;
  345 #endif
  346 
  347       // -------------------------------------------------
  348     case DpaEvent_DpaRequest:
  349     {
  350       // Called to interpret DPA request for peripherals
  351       // -------------------------------------------------
  352       // Peripheral enumeration
  353       if ( IsDpaEnumPeripheralsRequest() )
  354       {
  355         // We implement 1 user peripheral
  356         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  357 #ifdef FlagUserPer  // Get ready for upcoming DPA 3.00
  358         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  359 #endif
  360         _DpaMessage.EnumPeripheralsAnswer.HWPID = ThisHWPID;
  361         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0000;
  362 
  363 DpaHandleReturnTRUE:
  364         return TRUE;
  365       }
  366       // -------------------------------------------------
  367       // Get information about peripheral
  368       else if ( IsDpaPeripheralInfoRequest() )
  369       {
  370         if ( _PNUM == PNUM_USER + 0 )
  371         {
  372           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  373 #if !defined( DDC_RE_01 )
  374           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  375 #else
  376           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  377 #endif
  378           goto DpaHandleReturnTRUE;
  379         }
  380 
  381         break;
  382       }
  383       // -------------------------------------------------
  384       else
  385       {
  386         // Handle peripheral command
  387         if ( _PNUM == PNUM_USER + 0 )
  388         {
  389           switch ( _PCMD )
  390           {
  391 #if defined( DDC_SE_01 )
  392             case 0x31:
  393               // Read sensors
  394               if ( _DpaDataLength != 0 )
  395                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  396 
  397               // Read temperature and check for an error
  398               uns16 temperature = GetTemperature();
  399               if ( temperature == ERROR_TEMPERATURE )
  400                 DpaApiReturnPeripheralError( ERROR_FAIL );
  401               _DpaMessage.Response.PData[0] = temperature.low8;
  402 
  403               // Read other sensors
  404               _DpaMessage.Response.PData[1] = ReadAdcPhotoresistor();
  405               _DpaMessage.Response.PData[2] = ReadAdcPotentiometer();
  406 
  407               // Return 3 bytes
  408               _DpaDataLength = 3;
  409               goto DpaHandleReturnTRUE;
  410 #endif
  411 
  412 #if defined( DDC_RE_01 )
  413             case 0x32:
  414               // Control and return state of relays
  415               if ( _DpaDataLength != 2 )
  416                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  417 
  418               // Relay #1
  419               // Get
  420               userReg0 = 0;
  421               if ( RELAY1_LAT )
  422                 userReg0.0 = 1;
  423 
  424               // Set
  425               switch ( _DpaMessage.Request.PData[0] )
  426               {
  427                 case 0:
  428                   RELAY1_LAT = 0;
  429                   break;
  430                 case 1:
  431                   RELAY1_LAT = 1;
  432                   break;
  433               }
  434               _DpaMessage.Response.PData[0] = userReg0;
  435 
  436               // Relay #2
  437               // Get
  438               userReg0 = 0;
  439               if ( RELAY2_LAT )
  440                 userReg0.0 = 1;
  441 
  442               // Set
  443               switch ( _DpaMessage.Request.PData[1] )
  444               {
  445                 case 0:
  446                   RELAY2_LAT = 0;
  447                   break;
  448                 case 1:
  449                   RELAY2_LAT = 1;
  450                   break;
  451               }
  452               _DpaMessage.Response.PData[1] = userReg0;
  453               // Receives and returns 2 bytes
  454               goto DpaHandleReturnTRUE;
  455 #endif
  456 
  457               // Invalid command
  458             default:
  459               DpaApiReturnPeripheralError( ERROR_PCMD );
  460               break;
  461           }
  462         }
  463       }
  464 
  465       break;
  466     }
  467 
  468     // -------------------------------------------------
  469     case DpaEvent_Init:
  470       // Do a one time initialization before main loop starts
  471 
  472 #if defined( DDC_SE_01 )
  473       // Initialize sensors
  474 
  475       // C5 (AN4) as input 
  476       moduleInfo();
  477       // Connected TR pins?
  478       if ( !bufferINFO[5].7 )
  479       {
  480         TRISC.6 = 1;
  481         TRISB.4 = 1;
  482       }
  483       TRISA.5 = 1;
  484 
  485       // C1 (AN0) as input 
  486       TRISA.0 = 1;
  487 
  488       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  489 #if F_OSC == 16000000
  490       PR6 = 250 - 1;
  491       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  492 #else
  493 #error Unsupported oscillator frequency
  494 #endif
  495 
  496       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  497       if ( Ds18B20WriteConfig( 0b0.00.00000 ) )
  498         // DS18B20 is enabled
  499         isDS18B20 = TRUE;
  500       else
  501         // Expect MCP9802 is enabled
  502         i2c_init();
  503 #endif
  504 
  505 #if defined( DDC_RE_01 )
  506       // Initialize relays
  507       RELAY1_LAT = 0;
  508       RELAY2_LAT = 0;
  509       RELAY1_TRIS = 0;
  510       RELAY2_TRIS = 0;
  511 
  512 #endif
  513       break;
  514 
  515 #if defined( DDC_SE_01 )
  516       // -------------------------------------------------
  517     case DpaEvent_AfterSleep:
  518       // Called after woken up after sleep
  519       if ( !isDS18B20 )
  520         i2c_init();
  521 
  522       // Called on wake-up from sleep
  523       TMR6IE = TRUE;
  524       TMR6ON = TRUE;
  525       break;
  526 
  527       // -------------------------------------------------
  528     case DpaEvent_BeforeSleep:
  529       // Called before going to sleep
  530       if ( !isDS18B20 )
  531         i2c_shutdown();
  532 
  533       // -------------------------------------------------
  534     case DpaEvent_DisableInterrupts:
  535       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  536       // Must not use TMR6 any more
  537       TMR6ON = FALSE;
  538       TMR6IE = FALSE;
  539       break;
  540 #endif
  541   }
  542   return FALSE;
  543 }
  544 
  545 //############################################################################################
  546 #if defined( DDC_SE_01 )
  547 
  548 //############################################################################################
  549 // OneWire and Dallas 18B20 routines
  550 //############################################################################################
  551 
  552 //############################################################################################
  553 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  554 //############################################################################################
  555 {
  556   // 16 MHz
  557   // + 0.75us ( W=val, Call )
  558   for ( ;; )
  559   {         // loop time
  560     nop2(); // 0.50us
  561     nop2(); // 1.00us
  562     nop2(); // 1.50us
  563     nop2(); // 2.00us
  564     nop2(); // 2.50us
  565     nop2(); // 3.00us
  566     nop();  // 3.25us
  567     if ( --val == 0 ) // + 0.75us (W--, BTFS ) 
  568       return;         // + 0.25us 
  569     nop2(); // 4.50us
  570   }         // 5.00us (Goto)
  571 }
  572 //############################################################################################
  573 
  574 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  575 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  576 
  577 //############################################################################################
  578 void OneWireWriteByte( uns8 byte )
  579 //############################################################################################
  580 {
  581   uns8 bitLoop = 8;
  582   do
  583   {
  584     // Next sequence is time precision critical
  585     GIE = FALSE;
  586 
  587     OneWireData0();
  588     nop2();         // 1 us [0.5 us]
  589     nop2();         // [1.0 us]
  590     if ( byte.0 )   // 2.5 us [1.75us]
  591       OneWireData1();
  592 
  593     // End of time precision critical sequence
  594     GIE = TRUE;
  595 
  596     // 60us minimum in total, does not have to be precise
  597     Delay5us( ( 60 - 3 ) / 5 + 1 );
  598 
  599     OneWireData1();
  600 
  601     byte >>= 1;
  602   } while ( --bitLoop != 0 );
  603 }
  604 
  605 //############################################################################################
  606 uns8 OneWireReadByte()
  607 //############################################################################################
  608 {
  609   uns8 result;
  610   uns8 bitLoop = 8;
  611   do
  612   {
  613     // Next sequence is time precision critical
  614     GIE = FALSE;
  615 
  616     OneWireData0();
  617     nop2();         // 1 us [0.5 us]
  618 #if F_OSC == 16000000
  619     nop2();         // [1.0 us]
  620 #endif
  621     OneWireData1();         // 2 us [1.5 us]
  622     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  623 
  624     Carry = 0;              // 17.5 us [16.75 us]
  625     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  626       Carry = 1;
  627 
  628     // End of time precision critical sequence
  629     GIE = TRUE;             // must not modify Carry
  630     result = rr( result );
  631 
  632     // 60us minimum in total, does not have to be precise
  633     Delay5us( ( 60 - 20 ) / 5 + 1 );
  634   } while ( --bitLoop != 0 );
  635 
  636   return result;
  637 }
  638 
  639 //############################################################################################
  640 bit OneWireReset()
  641 //############################################################################################
  642 {
  643   // Setting the pin once to low is enough
  644   OneWire_IO_OUT = 0;
  645   // Reset pulse
  646   OneWireData0();
  647   Delay5us( 500 / 5 );
  648   // Reset pulse end
  649   OneWireData1();
  650   // Next sequence is time precision critical
  651   GIE = FALSE;
  652   // Wait for presence pulse
  653   Delay5us( 70 / 5 );
  654   // End of time precision critical sequence
  655   GIE = TRUE;
  656   // Presence pulse?
  657   if ( OneWire_IO_IN )
  658   {
  659     // No presence, finish initialization sequence
  660     Delay5us( ( 500 - 70 ) / 5 );
  661     return FALSE;
  662   }
  663   else
  664   {
  665     // Presence OK, finish initialization sequence
  666     Delay5us( ( 500 - 70 ) / 5 );
  667     return TRUE;
  668   }
  669 }
  670 
  671 //############################################################################################
  672 void OneWireCmd( uns8 cmd )
  673 //############################################################################################
  674 {
  675   // OneWire: Skip ROM
  676   OneWireWriteByte( CMD_SKIPROM );
  677   // OneWire: Send command
  678   OneWireWriteByte( cmd );
  679 }
  680 
  681 //############################################################################################
  682 bit Ds18B20WriteConfig( uns8 value )
  683 //############################################################################################
  684 {
  685   if ( OneWireReset() )
  686   {
  687     // Write Scratchpad
  688     OneWireCmd( CMD_WSCRATCHPAD );
  689 
  690     // Write TL = ? (we dot not care the value)
  691     OneWireWriteByte( W );
  692     // Write TH = ? (we dot not care the value)
  693     OneWireWriteByte( W );
  694     // Write Config byte
  695     OneWireWriteByte( value );
  696 
  697     if ( OneWireReset() )
  698     {
  699       //  Copy Scratchpad
  700       OneWireCmd( CMD_CPYSCRATCHPAD );
  701       return TRUE;
  702     }
  703   }
  704   return FALSE;
  705 }
  706 
  707 //############################################################################################
  708 void i2c_waitForIdle()
  709 //############################################################################################
  710 {
  711   i2cTimeout = FALSE;
  712   uns8 timeout;
  713   // Wait for idle and not writing
  714   timeout = 0;
  715   while ( ( SSPCON2 & 0b0001.1111 ) != 0 || RW_ )
  716     if ( ++timeout == 0 )
  717     {
  718       i2cTimeout = TRUE;
  719       break;
  720     }
  721 }
  722 
  723 //############################################################################################
  724 uns16 MCP9802GetTemp()
  725 //############################################################################################
  726 {
  727   // MCP9802 address
  728   i2c_start( I2C_ADR );
  729   if ( i2cTimeout )
  730     return ERROR_TEMPERATURE;
  731 
  732   // pointer: 1 = configuration register
  733   i2c_write( 0x01 );
  734   // configuration: 9-bit ADC
  735   i2c_write( 0x00 );
  736   i2c_stop();
  737 
  738   // MCP9802 address
  739   i2c_start( I2C_ADR );
  740   // pointer: 0 = temperature
  741   i2c_write( 0 );
  742   i2c_stop();
  743 
  744   // MCP9802 address + read
  745   i2c_start( I2C_ADR | 1 );
  746   uns16 temperature;
  747   // store the result
  748   temperature.high8 = i2c_read( TRUE );
  749   temperature.low8 = i2c_read( FALSE );
  750   i2c_stop();
  751   return temperature;
  752 }
  753 
  754 //############################################################################################
  755 uns16 GetTemperature()
  756 //############################################################################################
  757 {
  758   // Reads temperature from an enabled sensor
  759   uns16 temperatureResult;
  760 
  761   if ( isDS18B20 )
  762   {
  763     // Temperature is ready at the background
  764     temperatureResult = temperature;
  765     if ( temperatureResult != ERROR_TEMPERATURE )
  766     {
  767       temperatureResult += 0x08;
  768       temperatureResult /= 0x10;
  769     }
  770   }
  771   else
  772   {
  773     // Temperature value must be read from I2C sensor
  774     temperatureResult = MCP9802GetTemp();
  775     if ( temperatureResult != ERROR_TEMPERATURE )
  776     {
  777       temperatureResult += 0x80;
  778       temperatureResult /= 0x100;
  779     }
  780   }
  781 
  782   return temperatureResult;
  783 }
  784 
  785 //############################################################################################
  786 uns8 ReadAdc()
  787 //############################################################################################
  788 {
  789   // ADC result - left justified, Fosc/8
  790   ADCON1 = 0b0001.0000;
  791   // start ADC
  792   GO = 1;
  793   // wait for ADC finish
  794   while ( GO );
  795   return ADRESH;
  796 }
  797 
  798 //############################################################################################
  799 uns8  ReadAdcPhotoresistor()
  800 //############################################################################################
  801 {
  802   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input 
  803   ANSELA.0 = 1;
  804   // ADC setting (AN0 channel)
  805   ADCON0 = 0b0.00000.01;
  806   return ReadAdc();
  807 }
  808 
  809 //############################################################################################
  810 uns8  ReadAdcPotentiometer()
  811 //############################################################################################
  812 {
  813   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input 
  814   ANSELA.5 = 1;
  815   // ADC setting (AN4 channel)
  816   ADCON0 = 0b0.00100.01;
  817   return ReadAdc();
  818 }
  819 //############################################################################################
  820 #endif
  821 //############################################################################################
  822 // I2C Master library
  823 #include "lib/I2Cmaster.c"
  824 // 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) 
  825 #include "DPAcustomHandler.h"
  826 //############################################################################################