/**************************************************************** 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 ); }