2017-07-03 19:59:30 +02:00

413 lines
15 KiB
C

/** SenseoControl 2.0
*
* File: main.c
* Author: Stefan Kalscheuer
* Date: 22.04.2013
* Comments: Main program
* Previous project by Paul Wilhelm (2009) - http://mosfetkiller.de/?s=kaffeecontroller
*
* Platform: ATtiny26
* Internal RC-oscillator 8 MHz, CKDIV8 Enabled
*
* License: GNU GPL v3 (see License.txt)
*/
#define F_CPU 1000000UL
// includes
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include "main.h"
// variables:
volatile uint16_t ms_counter = 0; // global and universal time counter (ms) and second-counter (for AutoOff)
volatile uint8_t sec_counter = 0;
volatile uint16_t button_1_cup_counter = 0, button_2_cup_counter = 0; // button counter
volatile uint8_t button_power_counter = 0;
volatile uint8_t led = 0; // LED status flags
volatile uint8_t flags = 0; // Flags: * * * * esc clean temperature water
volatile uint8_t make_coffee = 0, pump_time = 0; // pump time, clean mode flag
int main (void)
{
init (); // initialization
power_off (); // power off after init sequece
while (1) // main loop
{
if (sec_counter >= AUTO_OFF_THRESHOLD)
button_power_counter = BUTTON_THRESHOLD; // check for AutoOff Timer (generate OnOff-button push)
get_water (); // update water state
get_temperature (); // update temperature
if (button_power_counter >= BUTTON_THRESHOLD) // button "OnOff" pushed:
{
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // Boiler off
make_coffee = 0; // clear coffee flag
while (button_power_counter > 0)
; // wait until button is releasd (debounce)
power_off (); // call power off sequence
button_power_counter = BUTTON_THRESHOLD; // debounce again after wake up
while (button_power_counter > 0)
;
}
if (button_1_cup_counter >= BUTTON_CLEAN_THR
&& button_2_cup_counter >= BUTTON_CLEAN_THR) // both coffee buttons pushed: clean mode:
{
set_bit(flags, FLAG_CLEAN); // clean flag true
led = BLUE; // set blue LED
while (button_1_cup_counter > 0 && button_2_cup_counter > 0)
; // debounce buttons
}
else if (button_1_cup_counter >= BUTTON_THRESHOLD
&& button_2_cup_counter < BUTTON_THRESHOLD) // left coffee button pushed: call espresso
{
sec_counter = 0; // reset AutoOff counter
if ((flags & 0b00000011) == 3) // machine ready:
{
while (button_1_cup_counter > 0) // check if button is pushed long time
{
if (button_1_cup_counter > BUTTON_LONG_THR) // button pushed for a long time:
{
make_coffee = 1; // set coffee flag to 1 (1 espresso)
button_1_cup_counter = 0; // clear button counter
}
}
if (make_coffee != 1)
make_coffee = 3; // set coffee flag to 3 (1 coffee) else
}
else if(COFFEE_WISH) { // Save coffee wish
make_coffee = 3;
}
}
else if (button_1_cup_counter < BUTTON_THRESHOLD
&& button_2_cup_counter >= BUTTON_THRESHOLD) // right coffee button pushed: call coffee
{
sec_counter = 0; // reset AutoOff counter
if ((flags & 0b00000011) == 3) // machine ready:
{
while (button_2_cup_counter > 0) // check if button is pushed long time
{
if (button_2_cup_counter > BUTTON_LONG_THR) // button pushed for a long time:
{
make_coffee = 2; // set coffee flag to 2 (2 espresso)
button_2_cup_counter = 0; // clear button counter
}
}
if (make_coffee != 2)
make_coffee = 4; // set coffee flag to 4 (2 coffee) else
}
else if(COFFEE_WISH) { // Save coffee wish
make_coffee = 4;
}
}
if (bit_is_set(flags, FLAG_WATER)) // water OK:
{
if (bit_is_set(flags, FLAG_CLEAN)) // if clean-flag is set:
{
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // boiler off
clear_bit(flags, FLAG_ESC); // clear escape flag
while (bit_is_set(flags, FLAG_WATER) && bit_is_clear(flags, FLAG_ESC))
{ // pump until water is empty or escape flag is set
unsigned int sense = detect_zero_crossing (); // detect zero crossing
if (sense <= 100)
{
clear_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // generate trigger impulse for pump triac
_delay_ms (3);
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin);
}
get_water (); // update water state
if (button_power_counter > BUTTON_THRESHOLD)
set_bit(flags, FLAG_ESC); // check for power button counter and set escape flag
}
clear_bit(flags, FLAG_CLEAN); // clear clean flag
}
else if (bit_is_set(flags, FLAG_TEMPERATURE)) // temperature OK:
{
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // boiler off
led = GREEN; // set green LED
if (make_coffee > 0) // if coffee flag is set:
{
if (make_coffee < 3)
led = ORANGE_BLINK; // set orange LED blink
else
led = GREEN_BLINK; // set green LED blink
if (make_coffee == 1)
pump_time = TIME_1_ESPRESSO; // 1 cup of espresso (2s preinfusion included)
else if (make_coffee == 2)
pump_time = TIME_2_ESPRESSO; // 2 cups of espresso (2s preinfusion included)
else if (make_coffee == 3)
pump_time = TIME_1_COFFEE; // 1 cup of coffee
else if (make_coffee == 4)
pump_time = TIME_2_COFFEE; // 2 cups of coffee
else
make_coffee = 0;
sec_counter = 0; // reset user time counter
clear_bit(flags, FLAG_ESC); // clear escape flag
while (sec_counter < pump_time && bit_is_set(flags, FLAG_WATER) && bit_is_clear(flags, FLAG_ESC))
{ // loop until pump time is reached or water is empty
if (make_coffee > 2
|| (sec_counter < 2 || sec_counter > 4))
{ // check for preinfusion break
unsigned int sense = detect_zero_crossing (); // detect zero crossing
if (sense <= 100)
{
clear_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // generate trigger impulse for pump triac
_delay_ms (3);
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin);
}
}
get_water (); // update water state
if (button_power_counter > BUTTON_THRESHOLD)
set_bit(flags, FLAG_ESC); // check for power button counter and set escape flag
}
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // pump off
clear_bit(flags, FLAG_CLEAN); // clear clean flag
make_coffee = 0; // clear coffee flag
sec_counter = 0; // reset AutoOff timer
}
}
else // temperature too low:
{
clear_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // boiler on
if (make_coffee > 0) // set red/blue LED blink if coffee wish is saved
led = VIOLET_BLINK;
else // set red LED blink if no coffee wish is saved
led = RED_BLINK;
}
}
else // water too low:
{
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // boiler off
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // pump off
led = BLUE_BLINK; // set blue LED blink
}
}
}
/* function: init()
* return: void
*
* Initializes relevant bits, timer and ADC.
*/
void init ()
{
clear_bit(ZERO_CROSSING_ddr, ZERO_CROSSING_pin); // zero crossing dection pins as input
clear_bit(ZERO_CROSSING_w, ZERO_CROSSING_pin); // no internal pull-up (for ADC)
clear_bit(BUTTON_1_CUP_ddr, BUTTON_1_CUP_pin); // button pins as input
set_bit(BUTTON_1_CUP_w, BUTTON_1_CUP_pin); // activate internal pull-ups
clear_bit(BUTTON_2_CUP_ddr, BUTTON_2_CUP_pin);
set_bit(BUTTON_2_CUP_w, BUTTON_2_CUP_pin);
clear_bit(BUTTON_POWER_ddr, BUTTON_POWER_pin);
set_bit(BUTTON_POWER_w, BUTTON_POWER_pin);
set_bit(LED_RED_ddr, LED_RED_pin); // LED pins as output
clear_bit(LED_RED_w, LED_RED_pin); // clear outputs (LEDs off)
set_bit(LED_GREEN_ddr, LED_GREEN_pin);
clear_bit(LED_GREEN_w, LED_GREEN_pin);
set_bit(LED_BLUE_ddr, LED_BLUE_pin);
clear_bit(LED_BLUE_w, LED_BLUE_pin);
clear_bit(SENSOR_MAGNET_ddr, SENSOR_MAGNET_pin); // sensor pins as input
clear_bit(SENSOR_MAGNET_w, SENSOR_MAGNET_pin); // no internal pull-up (for ADC)
clear_bit(SENSOR_TEMP_ddr, SENSOR_TEMP_pin);
clear_bit(SENSOR_TEMP_w, SENSOR_TEMP_pin);
set_bit(TRIAC_BOILER_ddr, TRIAC_BOILER_pin); // triac pins as output
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // set outputs high (triac off)
set_bit(TRIAC_PUMP_ddr, TRIAC_PUMP_pin);
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin);
ADCSR = (1 << ADEN) | (1 << ADPS1); // enable ADC, prescaler division factor 4
// TIMER1
set_bit(TCCR1B, CTC1); // set timer 1 to CTC-Mode
clear_bit(TCCR1B, CS11); // prescaler 8
set_bit(TCCR1B, CS12);
clear_bit(TCCR1B, CS11);
clear_bit(TCCR1B, CS10);
OCR1C = 124; // period of 1 ms
cli (); // disable interrupts
clear_bit(GIMSK, INT0); // disable interrupt 0
set_bit(TIMSK, TOIE1); // activate timer 1
sei (); // enable interrupts
}
/* function: power_off()
* return: void
*
* Clear bits and set controller to sleep mode.
*/
void power_off ()
{
cli (); // disable interrupts
set_bit(GIMSK, INT0); // activate interrupt 0 (for wake-up)
clear_bit(TIMSK, TOIE1); // deactivate timer 1
sei (); // enable interrupts
clear_bit(LED_RED_w, LED_RED_pin); // clear LED outputs
clear_bit(LED_GREEN_w, LED_GREEN_pin);
clear_bit(LED_BLUE_w, LED_BLUE_pin);
set_bit(MCUCR, SM1); // activate power-down mode
clear_bit(MCUCR, SM0);
set_bit(MCUCR, SE);
asm volatile("sleep"::);
// entrance after wake-up:
ms_counter = 0; // reset counter
sec_counter = 0;
cli (); // disable interrupts
clear_bit(GIMSK, INT0); // disable interrupt 0
set_bit(TIMSK, TOIE1); // enable timer 1
sei (); // enable interrupts
}
/* function: get_water()
*
* Checks hall sensor for water state.
*/
void get_water ()
{
ADMUX = SENSOR_MAGNET_adc | (1 << ADLAR); // ADLAR
set_bit(ADCSR, ADSC);
loop_until_bit_is_clear (ADCSR, ADSC);
unsigned char sense = ADCH;
if ((bit_is_set(flags, FLAG_WATER) && sense > WATER_LOW) || (bit_is_clear(flags, FLAG_WATER) && sense >= WATER_OK))
set_bit(flags, FLAG_WATER);
else
clear_bit(flags, FLAG_WATER);
}
/* function: get_temperature()
*
* Checks NTC sensor for temperature state.
*/
void get_temperature ()
{
ADMUX = SENSOR_TEMP_adc | (1 << ADLAR); // ADLAR
set_bit(ADCSR, ADSC);
loop_until_bit_is_clear (ADCSR, ADSC);
unsigned char sense = ADCH;
if (sense >= OPERATING_TEMPERATURE)
set_bit(flags, FLAG_TEMPERATURE);
else
clear_bit(flags, FLAG_TEMPERATURE);
}
/* function: detect_zero_crossing()
* return: unsigned int ADC value
*
* Checks for zero crossing (with fixed offset)
*/
unsigned int detect_zero_crossing ()
{
ADMUX = ZERO_CROSSING_adc;
set_bit(ADCSR, ADSC);
loop_until_bit_is_clear (ADCSR, ADSC);
unsigned char sense_L = ADCL;
unsigned char sense_H = ADCH;
return (sense_H << 8) | sense_L;
}
/* interrupt function: INT0_vect
*
* Dummy function for wake-up.
*/
ISR ( INT0_vect)
{
}
/* interrupt function: TIMER1_OVF1_vect
*
* Timer interrupt. Increments counters and controls LED.
*/
ISR ( TIMER1_OVF1_vect)
{
if (ms_counter < 1000)
ms_counter++; // global milliseconds counter and seconds counter (for AutoOff)
else
{
ms_counter = 0;
sec_counter++;
}
uint8_t leds_blink_on = (ms_counter < 500); // status flag for blinking LEDs with 1Hz
if (led & (1 << LED_RED_ON) || (led & (1 << LED_RED_BLINK) && leds_blink_on))
set_bit(LED_RED_w, LED_RED_pin);
else
clear_bit(LED_RED_w, LED_RED_pin);
if (led & (1 << LED_GREEN_ON)
|| (led & (1 << LED_GREEN_BLINK) && leds_blink_on))
set_bit(LED_GREEN_w, LED_GREEN_pin);
else
clear_bit(LED_GREEN_w, LED_GREEN_pin);
if (led & (1 << LED_BLUE_ON)
|| (led & (1 << LED_BLUE_BLINK) && leds_blink_on))
set_bit(LED_BLUE_w, LED_BLUE_pin);
else
clear_bit(LED_BLUE_w, LED_BLUE_pin);
if (bit_is_clear (BUTTON_1_CUP_r, BUTTON_1_CUP_pin))
{ // left button counter
if (button_1_cup_counter < 65535)
button_1_cup_counter++;
}
else
{
if (button_1_cup_counter > 0)
button_1_cup_counter--;
}
if (bit_is_clear (BUTTON_2_CUP_r, BUTTON_2_CUP_pin))
{ // right button counter
if (button_2_cup_counter < 65535)
button_2_cup_counter++;
}
else
{
if (button_2_cup_counter > 0)
button_2_cup_counter--;
}
if (bit_is_clear (BUTTON_POWER_r, BUTTON_POWER_pin))
{ // power button counter
if (button_power_counter < 255)
button_power_counter++;
}
else
{
if (button_power_counter > 0)
button_power_counter--;
}
}