/****************************************************************
STEAMGEN07.C

This is a steam generator.

WORKING CODE

ERRORS - count the number of quick flashes
    1 - not drained at beginning
    2 - not filled within time limit during initial fill cycle
    3 - not filled within time limit during rinse cycle
    4 - not filled to limit during heating within time limit
    5 - over temperature during preheat or soak cycles
    6 - under temperature after preheat cycle

LED FLASH
    SLOW - filling, preheating, draining
    SOLID - soak mode
    FAST - stop and wait (warn) period

                   ---------
           +5--20-|Vdd    C0|-11--- Heat-A relay
                  |       C1|-12--- Heat-B relay
          Gnd--08-|Vss    C2|-13--- Heat - SSR
          Gnd--19-|Vss    C3|-14--- Fill
         4MHz--10-|Xtal   C4|-15--- Drain
             --09-|Xtal     |
                  |         | PROGRAMMING CONNECTOR
                  |       B7|-28--DATA
                  |       B6|-27--CLK
                  |     MCLR|-01--MCLR
                  |         |     GND
                  | 16F876  |
                  |         |
       Level --21-|B0     C5|-16-- LED1
         Sw1 --22-|B1     C6|-17-- LED2
         Sw2 --23-|B2     B4|-25-- LED RETURNS
      DS1820 --03-|A1     B3|-24-- BEEPER
                  |       B5|-26-- COOLING FAN
                   ---------

**********************************************************/

#include < 16F876.H >
#include < jonsinc.h >
#fuses XT, NOPROTECT, PUT, NOWDT, BROWNOUT, NOLVP, NOCPD, NOWRT

#define RELAY_HEAT_A         PIN_C0
#define RELAY_HEAT_B         PIN_C1
#define RELAY_SSR            PIN_C2
#define RELAY_FILL           PIN_C3
#define RELAY_DRAIN          PIN_C4
#define LED_1                PIN_C5
#define LED_2                PIN_C6
#define SWITCH_LEVEL         PIN_B0
#define SWITCH_1             PIN_B1
#define SWITCH_2             PIN_B2
#define BEEPER               PIN_B3
#define LED_RETURN           PIN_B4
#define DS1820_DATA_PIN      PIN_A1
#define FAN                  PIN_B5
//============================
#define PERCENT_100          4
#define PERCENT_75           3
#define PERCENT_50           2
#define PERCENT_25           1
#define PERCENT_0            0
#define LED_FLASH_NONE       0
#define LED_FLASH_SLOW       1
#define LED_FLASH_FAST       2
#define LED_INIT_FLASH_COUNT 16
#define MINIMUM_INTERVAL     1
#define HIGH_STEAM           1
#define LOW_STEAM            2
//============================
#define STATE_START          0
#define STATE_INITIAL_CHECKS 1
#define STATE_READ_SWITCHES  2
#define STATE_FILL           3
#define STATE_PREHEAT        4
#define STATE_SOAK           5
#define STATE_WARN           6
#define STATE_DRAIN          7
#define STATE_ERROR          9
//============================
#define ERROR_NONE                    0
#define ERROR_NOT_DRAINED             1
#define ERROR_NOT_FILLED_DURING_FILL  2
#define ERROR_NOT_FILLED_DURING_RINSE 3
#define ERROR_FILL_ABSENCE            4
#define ERROR_OVERTEMP                5
#define ERROR_UNDERTEMP               6
//============================
// times in seconds
#define TIME_FILL_LIMIT      240
#define TIME_DRAIN           160
#define TIME_PREBOIL         360
#define TIME_PREHEAT_SHOWER  600
#define TIME_SOAK_MODE       1200
#define TIME_STOP_AND_WAIT   130
#define TIME_FILL_ABSENCE    300
#define TIME_TEMPERATURE_MEAS 10
// times in mS
#define TIME_ADD_WATER_INCR  1000
#define TIME_ADD_WATER_DELAY 100
#define TIME_DRAIN_COOL_FILL 10000
#define TIME_INITIAL_OVERFILL 3000
// times in uS
#define BEEPER_PERIOD_DELAY  518
// number of cycles for beeper
#define BEEP_CYCLES           500
//============================
// DS1820 temperature sensor
#define DS1820_SKIP_ROM          0xCC
#define DS1820_READ_SCRATCHPAD   0xBE
#define DS1820_CONVERT_T         0x44
// temperature in degrees C
#define TEMPERATURE_DRAIN_LIMIT  65
#define TEMPERATURE_OVER         105
#define TEMPERATURE_UNDER        90
//============================

#use delay ( clock=6000000 )
#use standard_io ( A )
#use standard_io ( B )
#use standard_io ( C )

void CheckSwitches ( void );
void AddWaterIfNecessary ( void );
void FlashErrorCode ( cError );
void AllRelaysOff ( void );
void Beep ( char cCount );
void SetHeaterPower ( char cLevel );
char ReadTemperature ( void );
void ResetDS1820 ( void );
void WriteDS1820 ( void );
void ReadDS1820 ( void );
void WaitForConversion ( void );

static char cInterruptCount;
static long iSecondCount, iFillOvertimeCount;
static char cSwitch1Count, cSwitch1on;
static char cSwitch2Count, cSwitch2on;
static char cError, cState, cMode, cOff, cResetCount;
static char cLedFlash, cLedOn, cSSRon, cSSRcount, cDebugMode;
static char cPower, cDutyCycleCount, cDutyCycleState;
static char cShiftBit,cDataOut, cTemperatureSecondCount;
static long iDataIn;

//*************************************************************************

#int_rtcc
void TimerInterrupt ( void )      // 21.760mS tick, 46 interrupts per second
    {
    // ONE-SECOND TICK
    if ( cInterruptCount++ == 47 )      // a little more than one second so flashes divide evenly
        {
        cInterruptCount = 0;            // restart interrupt count
        iSecondCount++;                 // increment second count
        cTemperatureSecondCount++;      // increment temperature second count
        iFillOvertimeCount++;           // increment overtime count
        }
    // SOLID STATE RELAY POWER CYCLING
    if ( cDutyCycleCount++ >= 4 )      // is it 84mS yet? (sets the overall duty cycle granularity)
        {
        cDutyCycleCount = 0;            // reset
        if ( cDutyCycleState == cPower )     // is heat cycle done yet?
            {
            output_low ( RELAY_SSR );  // turn solid state relay off
            }
        else
            {
            if ( cDutyCycleState == 0 )     // if first state
                {
                output_high ( RELAY_SSR );  // turn solid state relay on
                }
            }
        if ( cDutyCycleState++ >= 3 )   // increment state
            {
            cDutyCycleState = 0;        // reset
            }
        }
    // LED FLASH/SOLID CONTROL
    switch ( cLedFlash )
        {
        case LED_FLASH_NONE:
            {
            cLedOn = YES;
            break;
            }
        case LED_FLASH_SLOW:
            {
            if ( ( cInterruptCount % 24 ) == 0 )
                {
                cLedOn ^= 1;
                }
            break;
            }
        case LED_FLASH_FAST:
            {
            if ( ( cInterruptCount % 8 ) == 0 )
                {
                cLedOn ^= 1;
                }
            break;
            }
        }
    if ( cLedOn == YES )
        {
        output_low ( LED_RETURN );     // LED on
        }
    else
        {
        output_high ( LED_RETURN );    // LED off
        }
    // SWITCH 1 SENSE
    if ( input ( SWITCH_1 ) == LOW )    // if pressed
        {
        if ( cSwitch1Count++ >= 92 )     // increment count
            {
            cSwitch1Count = 92;          // prevent wrap, limit to highest needed
            }
        }
    else                                // when button is released
        {
        if ( cSwitch1Count >= 92 )       // if was pressed for two seconds
            {
            cOff = YES;                 // signal OFF
            }
        else
            {
            if ( cSwitch1Count >= 2 )    // else if was pressed momentarily
                {
                cSwitch1on = YES;       // signal normal press
                }
            }
        cSwitch1Count = 0;              // switch up, restart
        }
    // SWITCH 2 SENSE
    if ( input ( SWITCH_2 ) == LOW )    // if pressed
        {
        if ( cSwitch2Count++ >= 92 )     // increment count
            {
            cSwitch2Count = 92;          // prevent wrap, limit to highest needed
            }
        }
    else                                // when button is released
        {
        if ( cSwitch2Count >= 92 )       // if was pressed for two seconds
            {
            cOff = YES;                 // signal OFF
            }
        else
            {
            if ( cSwitch2Count >= 2 )    // else if was pressed momentarily
                {
                cSwitch2on = YES;       // signal normal press
                }
            }
        cSwitch2Count = 0;              // switch up, restart
        }
    // RESET (PRESS BOTH SWITCHES SIMULTANEOUSLY)
    if ( ( input ( SWITCH_1 ) == LOW ) && ( input ( SWITCH_2 ) == LOW ) )    // if pressed for three seconds
        {
        if ( cResetCount++ >= 143 )     // increment count
            {
            cResetCount = 143;          // cap it
            }
        }
    else                                // when button is released
        {
        if ( cResetCount >= 143 )       // if was pressed for five seconds
            {
            reset_cpu();
            }
        cResetCount = 0;              // switch up, restart
        }
    }

//****************************************************************************

void main ( void )
    {
    char cCnt, cTemp;

    delay_ms ( 500 );           // wait enough time after VDD rise
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_128 );
    cSSRcount = 0;     // force SSR off
    cSSRon = 0;
    AllRelaysOff();
    port_b_pullups ( ON );
    cDutyCycleState = 0;
    cDutyCycleCount = 0;
    cPower = 0;
    // flash LEDs
    output_low ( LED_RETURN );     // LED solid on
    for ( cCnt = 0; cCnt < LED_INIT_FLASH_COUNT; cCnt++ )
        {
        output_high ( LED_1 );
        delay_ms ( 30 );
        output_low ( LED_1 );
        output_high ( LED_2 );
        delay_ms ( 30 );
        output_low ( LED_2 );
        }

    // DEBUG MODE (hold all buttons during power up, before interrupts are enabled)
    if ( ( input ( SWITCH_1 ) == LOW ) || ( input ( SWITCH_2 ) == LOW ) )
        {
        while ( ( input ( SWITCH_1 ) == LOW ) || ( input ( SWITCH_2 ) == LOW ) )  // wait until all buttons released
        cDebugMode = ON;
        }
    else
        {
        cDebugMode = OFF;
        }

    enable_interrupts ( INT_RTCC );     // turn on timer interrupt
    enable_interrupts ( GLOBAL );       // enable interrupts
    cSwitch1Count = 0;
    cSwitch2Count = 0;
    cResetCount = 0;
    cSwitch1on = NO;
    cSwitch2on = NO;
    cOff = NO;
    ReadTemperature();                  // dummy read to clear sensor
    cState = STATE_READ_SWITCHES;

    // DEBUG LOOP===============================================================
    // get out of this mode by doing a reset
    if ( cDebugMode == ON )
        {
        cLedFlash = LED_FLASH_NONE;
        output_high ( LED_1 );
        delay_ms ( 300 );
        output_low ( LED_1 );
        output_high ( LED_2 );
        delay_ms ( 300 );
        output_low ( LED_2 );
        while ( TRUE )
            {
            // get float switch status
            if ( input ( SWITCH_LEVEL ) == LOW )
                {
                output_high ( LED_2 );
                }
            else
                {
                output_low ( LED_2) ;
                }
            // switch 1 opens fill valve
            if ( input ( SWITCH_1 ) == LOW )
                {
                output_high ( RELAY_FILL );
                }
            else
                {
                output_low ( RELAY_FILL );
                }
            // switch 1 opens drain valve
            if ( input ( SWITCH_2 ) == LOW )
                {
                output_high ( RELAY_DRAIN );
                }
            else
                {
                output_low ( RELAY_DRAIN );
                }
            }
        }
        // end DEBUG loop ======================================================

    Beep ( 1 );         // beep to signal ready for use
    cMode = 0;          // default no switches

    // MAIN LOOP================================================================
    while ( TRUE )              // do main loop forever
        {
        switch ( cState )
            {
            case STATE_READ_SWITCHES:
                {
                cError = ERROR_NONE;            // default
                cLedFlash = LED_FLASH_NONE;
                output_low ( LED_1 );
                output_low ( LED_2 );
                output_low ( FAN );                // turn off the SSR cooling fan
                CheckSwitches();
                delay_ms ( 100 );
                cOff = NO;                // ignore a switch pressed too long
                if ( cMode != 0 )         // if a switch was pressed
                    {
                    cState = STATE_INITIAL_CHECKS;
                    }
                break;
                }
            case STATE_INITIAL_CHECKS:
                {
                cError = ERROR_NONE;     // default
                cLedFlash = LED_FLASH_SLOW;
                // check thermistor (ensure not overtemp, and record actual temp)

                // check water level (should be empty, otherwise drain)
                if ( input ( SWITCH_LEVEL ) == LOW )      // if water level is high
                    {
                    output_high ( RELAY_DRAIN );   // open drain valve
                    iSecondCount = 0;
                    while ( iSecondCount < TIME_DRAIN );   // allow drain time
                    output_low ( RELAY_DRAIN );    // close drain valve
                    }
                delay_ms ( 1000 );
                if ( input ( SWITCH_LEVEL ) == LOW )    // if water level is still high
                    {
                    cError = ERROR_NOT_DRAINED;
                    cState = STATE_ERROR;
                    }
                else
                    {
                    cState = STATE_FILL;      // otherwise go to fill state
                    }
                break;
                }
            case STATE_FILL:
                {
                // turn on FILL valve
                // watch the level sensor, time the fill
                // turn off the FILL valve
                cError = ERROR_NONE;            // default
                cLedFlash = LED_FLASH_SLOW;
                output_high ( RELAY_FILL );   // open fill valve
                iSecondCount = 0;
                while ( TRUE )
                    {
                    if ( iSecondCount >= TIME_FILL_LIMIT )
                        {
                        cError = ERROR_NOT_FILLED_DURING_FILL;
                        cState = STATE_ERROR;
                        break;
                        }
                    if ( cOff == YES )              // if button was pressed for two seconds
                        {
                        cMode = 0;
                        cState = STATE_DRAIN;
                        break;
                        }
                    if ( input ( SWITCH_LEVEL ) == LOW )    // is water level up to proper level yet?
                        {
                        delay_ms ( TIME_INITIAL_OVERFILL );    // over fill slightly
                        cState = STATE_PREHEAT;
                        break;             // break out early when level switch senses water level
                        }
                    }
                output_low ( RELAY_FILL );    // close fill valve
                delay_ms ( 1000 );
                break;
                }
            case STATE_PREHEAT:
                {
                // turn on the heat relay
                // monitor the temperature (for immediate heat rise over existing, and overheat)
                // time the high heat
                // turn on the fan
                cError = ERROR_NONE;                // default
                cLedFlash = LED_FLASH_SLOW;
                iSecondCount = 0;
                cTemperatureSecondCount = 0;
                iFillOvertimeCount = 0;
                SetHeaterPower ( PERCENT_100 );     // apply power
                while ( TRUE )
                    {
                    AddWaterIfNecessary();
                    if ( iSecondCount >= ( TIME_PREBOIL + TIME_PREHEAT_SHOWER ) )
                        {
                        if ( ReadTemperature() < TEMPERATURE_UNDER )
                            {
                            cError = ERROR_UNDERTEMP;
                            cState = STATE_ERROR;
                            break;
                            }
                        else
                            {
                            Beep ( 5 );     // to signal ready
                            cState = STATE_SOAK;
                            break;
                            }
                        }
                    if ( cOff == YES )              // if button was pressed for two seconds
                        {
                        cMode = 0;
                        cState = STATE_DRAIN;
                        break;
                        }
                    if ( iFillOvertimeCount >= TIME_FILL_ABSENCE )
                        {
                        cError = ERROR_FILL_ABSENCE;
                        cState = STATE_ERROR;
                        break;
                        }
                    if ( ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS ) && ( ReadTemperature() > TEMPERATURE_OVER ) )
                        {
                        cError = ERROR_OVERTEMP;
                        cState = STATE_ERROR;
                        }
                    }
                break;
                }
            case STATE_SOAK:
                {
                // monitor the temperature (for overheat)
                // time the cycle
                cError = ERROR_NONE;            // default
                cLedFlash = LED_FLASH_NONE;
                iSecondCount = 0;
                iFillOvertimeCount = 0;
                while ( TRUE )
                    {
                    AddWaterIfNecessary();
                    CheckSwitches();
                    switch ( cMode )        // allows changes during SOAK cycle
                        {
                        case HIGH_STEAM:
                            {
                            SetHeaterPower ( PERCENT_100 );       // apply power
                            break;
                            }
                        case LOW_STEAM:
                            {
                            SetHeaterPower ( PERCENT_50 );       // apply power;
                            break;
                            }
                        }
                    if ( iSecondCount >= TIME_SOAK_MODE )
                        {
                        cState = STATE_WARN;
                        break;
                        }
                    if ( cOff == YES )              // if button was pressed for two seconds
                        {
                        cState = STATE_DRAIN;
                        break;
                        }
                    if ( iFillOvertimeCount >= TIME_FILL_ABSENCE )
                        {
                        cError = ERROR_FILL_ABSENCE;
                        cState = STATE_ERROR;
                        break;
                        }
                    if ( ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS ) && ( ReadTemperature() > TEMPERATURE_OVER ) )
                        {
                        cError = ERROR_OVERTEMP;
                        cState = STATE_ERROR;
                        }
                    }
                break;
                }
            case STATE_WARN:
                {
                cError = ERROR_NONE;            // default
                cLedFlash = LED_FLASH_FAST;
                iSecondCount = 0;
                cMode = 0;      // default no switches
                while ( TRUE )
                    {
                    // beep once per minute
                    if ( ( iSecondCount % 60 == 0 ) )      // beep once per minute
                        {
                        Beep ( 1 );
                        delay_ms ( 100 );  // ensure it doesn't get here twice in the same iSecondCount value
                        }
                    // if timed out
                    if ( iSecondCount >= TIME_STOP_AND_WAIT )
                        {
                        cMode = 0;          // default no switches
                        cState = STATE_DRAIN;
                        break;
                        }
                    if ( cOff == YES )              // if button was pressed for two seconds
                        {
                        cState = STATE_DRAIN;
                        break;
                        }
                    AddWaterIfNecessary();
                    CheckSwitches();
                    if ( cMode != 0 )           // if a switch was pressed
                        {
                        cState = STATE_SOAK;    // repeat the soak cycle
                        break;
                        }
                    }
                break;
                }
            case STATE_DRAIN:
                {
                SetHeaterPower ( OFF );       // heater power off
                cError = ERROR_NONE;          // default
                cLedFlash = LED_FLASH_SLOW;
                delay_ms ( 1000 );
                // check temperature
                if ( ReadTemperature() <= TEMPERATURE_DRAIN_LIMIT )         // if cool enough to drain immediately
                    {
                    // open for the complete drain time
                    output_high ( RELAY_DRAIN );   // open drain valve
                    iSecondCount = 0;
                    while ( iSecondCount < TIME_DRAIN );   // drain time
                    output_low ( RELAY_DRAIN );    // close drain valve
                    delay_ms ( 1000 );
                    cMode = 0;                      // default no switches
                    cState = STATE_READ_SWITCHES;   // no rinse cycle necessary
                    }
                else
                    {
                    // open FILL valve for a few seconds to cool off tank
                    output_high ( RELAY_FILL );   // open fill valve
                    delay_ms ( TIME_DRAIN_COOL_FILL );
                    output_low ( RELAY_FILL );    // close fill valve
                    // wait until it cools enough for drain valve and piping
                    while ( TRUE )
                        {
                        if ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS )   // if time to measure
                            {
                            if ( ReadTemperature() <= TEMPERATURE_DRAIN_LIMIT )         // if cool enough to drain
                                {
                                break;
                                }
                            }
                        }
                    // open for the complete drain time
                    output_high ( RELAY_DRAIN );   // open drain valve
                    iSecondCount = 0;
                    while ( iSecondCount < TIME_DRAIN );   // drain time
                    output_low ( RELAY_DRAIN );    // close drain valve
                    delay_ms ( 1000 );
                    // rinse cycle
                    output_high ( RELAY_FILL );   // open fill valve
                    iSecondCount = 0;
                    while ( iSecondCount < TIME_FILL_LIMIT )   // maximum fill time
                        {
                        if ( input ( SWITCH_LEVEL ) == LOW )    // is water level up to proper level yet?
                            {
                            break;             // break out early when level switch senses water level
                            }
                        }
                    output_low ( RELAY_FILL );    // close fill valve
                    delay_ms ( 1000 );
                    if ( input ( SWITCH_LEVEL ) == HIGH )    // after filling, is water level not up to proper level?
                        {
                        cError = ERROR_NOT_FILLED_DURING_RINSE;
                        cState = STATE_ERROR;
                        }
                    else
                        {
                        // rinse cycle drain
                        output_high ( RELAY_DRAIN );   // open drain valve
                        iSecondCount = 0;
                        while ( iSecondCount < TIME_DRAIN );   // allow drain time
                        output_low ( RELAY_DRAIN );    // close drain valve
                        cMode = 0;          // default no switches
                        cState = STATE_READ_SWITCHES;
                        }
                    }
                break;
                }
            case STATE_ERROR:
                {
                // always turn off everything
                AllRelaysOff();
                FlashErrorCode ( cError );
                // don't change state so it will come directly back here until reset is pressed
                }
            }
        }
    }

void CheckSwitches ( void )
    {
    if ( cSwitch1on == YES )
        {
        cSwitch1on = NO;
        cSwitch2on = NO;
        cMode = HIGH_STEAM;
        output_high ( LED_1 );
        output_low ( LED_2 );
        }
    if ( cSwitch2on == YES )
        {
        cSwitch1on = NO;
        cSwitch2on = NO;
        cMode = LOW_STEAM;
        output_low ( LED_1 );
        output_high ( LED_2 );
        }
    }

void AddWaterIfNecessary ( void )
    {
    if ( input ( SWITCH_LEVEL ) == HIGH )   // low water
        {
        if ( cMode == LOW_STEAM )                   // if low power mode
            {
            SetHeaterPower ( PERCENT_75 );  // apply more power during water add
            }
        output_high ( RELAY_FILL );         // open fill valve
        delay_ms ( TIME_ADD_WATER_INCR );
        output_low ( RELAY_FILL );          // close fill valve
        delay_ms ( TIME_ADD_WATER_DELAY );
        }
    else
        {
        iFillOvertimeCount = 0;          // reset count if water is up to level
        }
    }

void FlashErrorCode ( char cError )
    {
    char cCnt;

    cLedFlash = LED_FLASH_NONE;         // no flashing from interrupt
    for ( cCnt = 0; cCnt < cError; cCnt++ )
        {
        output_high ( LED_1 );
        output_high ( LED_2 );
        delay_ms ( 100 );
        output_low ( LED_1 );
        output_low ( LED_2 );
        delay_ms ( 200 );
        }
    delay_ms ( 2000 );
    }

void AllRelaysOff ( void )
    {
    output_low ( RELAY_HEAT_A );
    output_low ( RELAY_HEAT_B );
    output_low ( RELAY_SSR );
    output_low ( RELAY_FILL );
    output_low ( RELAY_DRAIN );
    output_low ( FAN );
    }

void Beep ( char cCount )
    {
    char cCnt;
    long iCnt;

    for ( cCnt = 1; cCnt <= cCount; cCnt++ )
        {
        disable_interrupts ( GLOBAL );    // so beeper isn't buzzy due to interruption
        for ( iCnt = 0; iCnt < BEEP_CYCLES; iCnt++ )
            {
            output_high ( BEEPER );
            delay_us ( BEEPER_PERIOD_DELAY / 2 );
            output_low ( BEEPER );
            delay_us ( BEEPER_PERIOD_DELAY / 2 );
            }
        enable_interrupts ( GLOBAL );
        delay_ms ( 1000 );
        }
    }

void SetHeaterPower ( char cLevel )
    {
    if ( cLevel == OFF )
        {
        output_low ( RELAY_HEAT_A );        // turn off both relays
        output_low ( RELAY_HEAT_B );
        cPower = OFF;                       // power handled by interrupt
        output_low ( FAN );                 // turn off the SSR cooling fan
        }
    else
        {
        output_high ( FAN );                // turn on the SSR cooling fan
        output_low ( RELAY_HEAT_A );       // apply power
        delay_ms ( 500 );
        output_low ( RELAY_HEAT_B );       // apply power
        delay_ms ( 500 );
        cPower = cLevel;                    // power handled by interrupt
        }
    }

char ReadTemperature ( void )
        {
        // initiate temperature reading
        ResetDS1820();
        cDataOut = DS1820_SKIP_ROM;
        WriteDS1820();
        cDataOut = DS1820_CONVERT_T;
        WriteDS1820();
        // wait until acquired
        WaitForConversion();
        // retrieve temperature reading
        ResetDS1820();
        cDataOut = DS1820_SKIP_ROM;
        WriteDS1820();
        cDataOut = DS1820_READ_SCRATCHPAD;
        WriteDS1820();
        ReadDS1820();
        cTemperatureSecondCount = 0;         // reset count
        return ( char ) ( iDataIn / 2 );
        }

void ResetDS1820 ( void )
    {
    output_low ( DS1820_DATA_PIN );         // low
    delay_us ( 500 );                               // reset pulse width
    output_float ( DS1820_DATA_PIN );          // high
    delay_us ( 500 );                               // presence pulse width
    }

void WriteDS1820 ( void )             // ~70uS per bit
    {
    disable_interrupts ( GLOBAL );
    for ( cShiftBit = 1; cShiftBit <= 8; ++cShiftBit )
        {
        output_low ( DS1820_DATA_PIN );
        delay_us ( 5 );
        output_bit ( DS1820_DATA_PIN, shift_right ( &cDataOut, 1, 0 ) );
        delay_us ( 60 );
        output_float ( DS1820_DATA_PIN );
        delay_us ( 5 );         // recovery time between slots
        }
    enable_interrupts ( GLOBAL );
    }

void ReadDS1820 ( void )             // ~70uS per bit
    {
    iDataIn = 0;
    disable_interrupts ( GLOBAL );
    for ( cShiftBit = 1; cShiftBit <= 16; ++cShiftBit )
       {
       output_low ( DS1820_DATA_PIN );
       delay_us ( 5 );
       output_float ( DS1820_DATA_PIN );
       delay_us ( 5 );
       shift_right ( &iDataIn, 2, input ( DS1820_DATA_PIN ) );   // sample bit
       delay_us ( 55 );         // includes recovery time between slots
       }
    enable_interrupts ( GLOBAL );
    ResetDS1820();              // terminate remainder of scratchpad register transmission
    }

void WaitForConversion ( void )
   {
   disable_interrupts ( GLOBAL );
   while ( TRUE )
       {
       output_low ( DS1820_DATA_PIN );
       delay_us ( 5 );
       output_float ( DS1820_DATA_PIN );
       delay_us ( 5 );
       if ( input ( DS1820_DATA_PIN ) == 1 )
           {
           break;
           }
       delay_us ( 55 );
       }
   enable_interrupts ( GLOBAL );
   }