Compare commits

...

4 Commits

5 changed files with 483 additions and 459 deletions

View File

@ -76,6 +76,14 @@ To to so, set the corresponding fields in the _main.h_ file:
Pinout, button-thresholds and LED-configuration is also present in this file (should be self-explaining). Pinout, button-thresholds and LED-configuration is also present in this file (should be self-explaining).
## Build Instructions
* Required tools: _avr-gcc_, _avr-objcopy_, _avrdude_ (for flashing only), _make_
* All sources are bundled in the `firmware` directory
* Check `Makefile.config` for the correct settings, especially tool and port for automated flashing.
* On first build you might want to set the correct fuse bits, so run `make fuses`
* Run `make compile info program` for compilation, details about binary, and flashing
* Check `make help` for all available commands
## Notes ## Notes
The Triacs need heatsink. The Triacs need heatsink.

View File

@ -1,58 +1,43 @@
# SenseoControl 2.0 #############################################################################
# # SenseoControl 2.0 #
# File: Makefile # Copyright (C) 2013-2018 Stefan Kalscheuer #
# Author: Stefan Kalscheuer # #
# Date: 22.04.2013 # This program is free software: you can redistribute it and/or modify #
# # it under the terms of the GNU General Public License as published by #
# Platform: ATtiny26 # the Free Software Foundation version 3. #
# Internal RC-oscillator 8 MHz, CKDIV8 Enabled # #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
#############################################################################
# Include environment config
include Makefile.config
# Project specific settings # Project specific settings
TARGET = SenseoControl-2.0 TARGET = SenseoControl-2.0
MCU = attiny26 MCU = attiny26
SRC = main.c SRC = main.c
# You probably want to change this to your own programming device
# AVR ISP mkII
#PGMDEV = avrispmkII
#PGMOPT = -P usb # Try -B 10 in case of programming errors
# Pony-STK200
#PGMDEV = pony-stk200
#PGMOPT = -E noreset
# STK500
PGMDEV = stk500v2
PGMOPT = -P /dev/ttyS0
# AVR-GCC and AVRDUDE need to be installed
CC = avr-gcc
OBJCOPY = avr-objcopy
AVRDUDE = avrdude
REMOVE = rm -f
# Some C flags # Some C flags
CFLAGS = -Wall -O3 CFLAGS = -Wall -Wextra -Os
help: help:
@echo @echo
@echo "Availiable targets:" @echo "Availiable targets:"
@echo " help - Displays this help" @echo " help Displays this help"
@echo @echo
@echo " compile - Compiles source code" @echo " compile Compiles source code"
@echo " info - Outputs device memory information" @echo " info Outputs device memory information"
@echo " program - Programs the device" @echo " program Programs the device"
@echo " clean - Deletes temporary files" @echo " clean Deletes temporary files"
@echo " fuses - Writes fuse settings to device (necessary only once per device)" @echo " fuses Writes fuse settings to device (necessary only once per device)"
@echo @echo
@echo " all - Compile, info, program, clean" @echo " all Compile, info, program, clean"
@echo
@echo "IMPORTANT: Device programming may only be possible as super user"
@echo
@echo "See Makefile for contact information."
@echo @echo
all: compile info program clean all: compile info program clean

43
firmware/Makefile.config Normal file
View File

@ -0,0 +1,43 @@
#############################################################################
# SenseoControl 2.0 #
# Copyright (C) 2013-2018 Stefan Kalscheuer #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation version 3. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
#############################################################################
#
# Set toolchain.
# AVR-GCC and AVRDUDE need to be installed
#
CC = avr-gcc
OBJCOPY = avr-objcopy
AVRDUDE = avrdude
REMOVE = rm -f
#
# You probably want to change this to your own programming device.
# Some commonly used examples shown below.
#
# STK500
PGMDEV = stk500v2
PGMOPT = -P /dev/ttyS0
# AVR ISP mkII
#PGMDEV = avrispmkII
#PGMOPT = -P usb # Try -B 10 in case of programming errors
# Pony-STK200
#PGMDEV = pony-stk200
#PGMOPT = -E noreset

View File

@ -1,420 +1,386 @@
/** SenseoControl 2.0 /*****************************************************************************
* SenseoControl 2.0 *
* Copyright (C) 2013-2018 Stefan Kalscheuer *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation version 3. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
*****************************************************************************/
/**
* SenseoControl 2.0
* *
* File: main.c * @file main.c
* Author: Stefan Kalscheuer * @author Stefan Kalscheuer
* Date: 22.04.2013 * @date 2013-04-22
* Comments: Main program * @brief Main program
* Previous project by Paul Wilhelm (2009) - http://mosfetkiller.de/?s=kaffeecontroller
* *
* Platform: ATtiny26 * Platform: ATtiny26
* Internal RC-oscillator 8 MHz, CKDIV8 Enabled * Internal RC-oscillator 8 MHz, CKDIV8 Enabled
*
* License: GNU GPL v3 (see License.txt)
*/ */
#define F_CPU 1000000UL #define F_CPU 1000000UL
// includes // includes
#include <util/delay.h> #include <util/delay.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <avr/io.h> #include <avr/io.h>
#include <stdbool.h>
#include "main.h" #include "main.h"
// variables: // variables:
volatile unsigned int time_counter, user_time_counter = 0, sec_counter = 0; // global and universal time counter (ms) and second-counter (for AutoOff) volatile unsigned int time_counter, user_time_counter = 0, sec_counter = 0; // Global and universal time counter (ms) and second-counter (for AutoOff).
volatile unsigned int button_1_cup_counter = 0, button_2_cup_counter = 0; // button counter volatile unsigned int button_1_cup_counter = 0, button_2_cup_counter = 0; // Button counter.
volatile unsigned char button_power_counter = 0; volatile unsigned char button_power_counter = 0;
volatile unsigned char led = 0; // LED status flags volatile unsigned char led = 0; // LED status flags.
volatile bool water = false, temperature = false, make_clean = false; // water-, temperature-, clean-flags volatile unsigned char state; // Water-, temperature-, clean-flags.
volatile unsigned char make_coffee = 0, pump_time = 0; // pump time, clean mode flag volatile unsigned char make_coffee = 0, pump_time = 0; // Pump time, clean mode flag.
int main (void) /**
{ * Main program.
init (); // initialization *
power_off (); // power off after init sequece * @return This method should never terminate.
*/
int main(void) {
init(); // Initialization.
power_off(); // Power off after init sequece.
while (1) // main loop while (1) { // Main loop.
{ if (sec_counter >= AUTO_OFF_THRESHOLD)
if (sec_counter >= AUTO_OFF_THRESHOLD) button_power_counter = BUTTON_THRESHOLD; // Check for AutoOff Timer (generate OnOff-button push).
button_power_counter = BUTTON_THRESHOLD; // check for AutoOff Timer (generate OnOff-button push)
water = get_water (); // update water state update_water(); // Update water state.
temperature = get_temperature (); // update temperature update_temperature(); // Update temperature.
if (button_power_counter >= BUTTON_THRESHOLD) // button "OnOff" pushed: if (button_power_counter >= BUTTON_THRESHOLD) { // Button "OnOff" pushed:
{ set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // Boiler off
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // Boiler off make_coffee = 0; // Clear coffee flag.
make_coffee = 0; // clear coffee flag
while (button_power_counter > 0) while (button_power_counter > 0); // Wait until button is releasd (debounce)
; // wait until button is releasd (debounce)
power_off (); // call power off sequence power_off(); // Call power off sequence
button_power_counter = BUTTON_THRESHOLD; // debounce again after wake up button_power_counter = BUTTON_THRESHOLD; // Debounce again after wake up
while (button_power_counter > 0) 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:
{
make_clean = true; // 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 (water && temperature) // 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 if (button_1_cup_counter >= BUTTON_CLEAN_THR && button_2_cup_counter >= BUTTON_CLEAN_THR) {
&& button_2_cup_counter >= BUTTON_THRESHOLD) // right coffee button pushed: call coffee // Both coffee buttons pushed: enter clean mode.
{ set_bit(state, S_CLEAN); // Set clean flag.
sec_counter = 0; // reset AutoOff counter 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 (water && temperature) // machine ready: if ((state & S_WATER) && (state & S_TEMP)) { // Machine ready:
{ while (button_1_cup_counter > 0) { // Check if button is pushed long time.
while (button_2_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).
if (button_2_cup_counter > BUTTON_LONG_THR) // button pushed for a long time: button_1_cup_counter = 0; // Clear button counter.
{ }
make_coffee = 2; // set coffee flag to 2 (2 espresso) }
button_2_cup_counter = 0; // clear button counter if (make_coffee != 1) {
} make_coffee = 3; // Set coffee flag to 3 (1 coffee) otherwise.
} }
if (make_coffee != 2) } else if (COFFEE_WISH) { // Save coffee wish.
make_coffee = 4; // set coffee flag to 4 (2 coffee) else make_coffee = 3;
}
else if(COFFEE_WISH) { // Save coffee wish
make_coffee = 4;
}
}
if (water) // water OK:
{
if (make_clean) // if clean-flag is set:
{
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // boiler off
bool escape = false; // init escape-flag
while (water && !escape)
{ // 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);
}
water = get_water (); // update water state
if (button_power_counter > BUTTON_THRESHOLD)
escape = true; // check for power button counter and set escape flag
}
make_clean = false; // clear clean flag
}
else if (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;
user_time_counter = 0; // reset user time counter
bool escape = false; // init escape flag
while (user_time_counter < (pump_time * 1000) && water && !escape)
{ // loop until pump time is reached or water is empty
if (make_coffee > 2
|| (user_time_counter < 2000 || user_time_counter > 4000))
{ // 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);
}
} }
} 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.
water = get_water (); // update water state if ((state & S_WATER) && (state & S_TEMP)) { // Machine ready:
while (button_2_cup_counter > 0) { // Check if button is pushed long time.
if (button_power_counter > BUTTON_THRESHOLD) if (button_2_cup_counter > BUTTON_LONG_THR) { // Button pushed for a long time:
escape = true; // check for power button counter and set escape flag make_coffee = 2; // Set coffee flag to 2 (2 espresso).
} button_2_cup_counter = 0; // Clear button counter.
}
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // pump off }
if (make_coffee != 2) {
make_coffee = 0; // clear coffee flag make_coffee = 4; // Set coffee flag to 4 (2 coffee) otherwise.
}
sec_counter = 0; // reset AutoOff timer } else if (COFFEE_WISH) { // Save coffee wish
make_coffee = 4;
}
} }
}
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 if ((state & S_WATER)) { // Water OK:
} if ((state & S_CLEAN)) { // If clean-flag is set:
set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // Boiler off.
clear_bit(state, S_ESC); // Init escape-flag.
while ((state & S_WATER) && (state & S_ESC)) { // Pump until water is empty or escape flag is set.
if (detect_zero_crossing() <= 100) { // Detect zero crossing.
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);
}
update_water(); // Update water state.
} if (button_power_counter > BUTTON_THRESHOLD) {
set_bit(state, S_ESC); // Check power button counter and set escape flag.
}
}
clear_bit(state, S_CLEAN); // Clear clean flag.
} else if ((state & S_TEMP)) { // 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 for espresso.
} else {
led = GREEN_BLINK; // Set green LED blink for coffee.
}
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;
}
user_time_counter = 0; // Reset user time counter.
clear_bit(state, S_ESC); // Init escape flag.
// loop until pump time is reached or water is empty
while (user_time_counter < (pump_time * 1000) && (state & S_WATER) && !(state & S_ESC)) {
// Check for preinfusion break.
if (make_coffee > 2 || (user_time_counter < 2000 || user_time_counter > 4000)) {
if (detect_zero_crossing() <= 100) { // Detect zero crossing.
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);
}
}
update_water(); // Update water state.
if (button_power_counter > BUTTON_THRESHOLD) {
set_bit(state, S_ESC); // Check for power button counter and set escape flag.
}
}
set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin); // Pump off
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. * Initializes relevant bits, timer and ADC.
*/ */
void init () void init() {
{ clear_bit(ZERO_CROSSING_ddr, ZERO_CROSSING_pin); // Zero crossing dection pins as input-
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(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 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 set_bit(BUTTON_1_CUP_w, BUTTON_1_CUP_pin); // Activate internal pull-ups.
clear_bit(BUTTON_2_CUP_ddr, BUTTON_2_CUP_pin); clear_bit(BUTTON_2_CUP_ddr, BUTTON_2_CUP_pin);
set_bit(BUTTON_2_CUP_w, BUTTON_2_CUP_pin); set_bit(BUTTON_2_CUP_w, BUTTON_2_CUP_pin);
clear_bit(BUTTON_POWER_ddr, BUTTON_POWER_pin); clear_bit(BUTTON_POWER_ddr, BUTTON_POWER_pin);
set_bit(BUTTON_POWER_w, BUTTON_POWER_pin); set_bit(BUTTON_POWER_w, BUTTON_POWER_pin);
set_bit(LED_RED_ddr, LED_RED_pin); // LED pins as output set_bit(LED_RED_ddr, LED_RED_pin); // LED pins as output.
clear_bit(LED_RED_w, LED_RED_pin); // clear outputs (LEDs off) clear_bit(LED_RED_w, LED_RED_pin); // Clear outputs (LEDs off).
set_bit(LED_GREEN_ddr, LED_GREEN_pin); 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:
time_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()
* return: true water OK
* false not enough water
*
* Checks hall sensor for water state.
*/
bool 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 ((water && sense > WATER_LOW) || (!water && sense >= WATER_OK))
return true;
return false;
}
/* function: get_temperature()
* return: true temperature OK
* false temperature too low
*
* Checks NTC sensor for temperature state.
*/
bool 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)
return true;
return false;
}
/* 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 (time_counter < 1000)
time_counter++; // global milliseconds counter and seconds counter (for AutoOff)
else
{
time_counter = 0;
sec_counter++;
}
user_time_counter++; // universal counter (for pump time)
bool leds_blink_on; // status flag for blinking LEDs with 1Hz
if (time_counter < 499)
leds_blink_on = true;
else
leds_blink_on = false;
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); clear_bit(LED_GREEN_w, LED_GREEN_pin);
if (led & (1 << LED_BLUE_ON) set_bit(LED_BLUE_ddr, LED_BLUE_pin);
|| (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); clear_bit(LED_BLUE_w, LED_BLUE_pin);
if (bit_is_clear (BUTTON_1_CUP_r, BUTTON_1_CUP_pin)) clear_bit(SENSOR_MAGNET_ddr, SENSOR_MAGNET_pin); // Sensor pins as input.
{ // left button counter clear_bit(SENSOR_MAGNET_w, SENSOR_MAGNET_pin); // No internal pull-up (for ADC).
if (button_1_cup_counter < 65535) clear_bit(SENSOR_TEMP_ddr, SENSOR_TEMP_pin);
button_1_cup_counter++; clear_bit(SENSOR_TEMP_w, SENSOR_TEMP_pin);
}
else
{
if (button_1_cup_counter > 0)
button_1_cup_counter--;
}
if (bit_is_clear (BUTTON_2_CUP_r, BUTTON_2_CUP_pin)) set_bit(TRIAC_BOILER_ddr, TRIAC_BOILER_pin); // Triac pins as output.
{ // right button counter set_bit(TRIAC_BOILER_w, TRIAC_BOILER_pin); // Set outputs high (triac off).
if (button_2_cup_counter < 65535) set_bit(TRIAC_PUMP_ddr, TRIAC_PUMP_pin);
button_2_cup_counter++; set_bit(TRIAC_PUMP_w, TRIAC_PUMP_pin);
}
else
{
if (button_2_cup_counter > 0)
button_2_cup_counter--;
}
if (bit_is_clear (BUTTON_POWER_r, BUTTON_POWER_pin)) ADCSR = (1 << ADEN) | (1 << ADPS1); // Enable ADC, prescaler division factor 4.
{ // power button counter
if (button_power_counter < 255) // TIMER1
button_power_counter++; set_bit(TCCR1B, CTC1); // Set timer 1 to CTC-Mode.
} clear_bit(TCCR1B, CS11); // Prescaler 8.
else set_bit(TCCR1B, CS12);
{ clear_bit(TCCR1B, CS11);
if (button_power_counter > 0) clear_bit(TCCR1B, CS10);
button_power_counter--; 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.
}
/**
* 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(); // Re-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 point after wake-up.
time_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(); // Re-enable interrupts.
}
/**
* Checks hall sensor for water level.
*/
void update_water(void) {
ADMUX = SENSOR_MAGNET_adc | (1 << ADLAR);
set_bit(ADCSR, ADSC);
loop_until_bit_is_clear(ADCSR, ADSC);
unsigned char sense = ADCH;
if (((state & S_WATER) && sense > WATER_LOW) || (!(state & S_WATER) && sense >= WATER_OK)) {
set_bit(state, S_WATER);
} else {
clear_bit(state, S_WATER);
}
}
/**
* Checks NTC sensor for temperature state.
*/
void update_temperature(void) {
ADMUX = SENSOR_TEMP_adc | (1 << ADLAR);
set_bit(ADCSR, ADSC);
loop_until_bit_is_clear(ADCSR, ADSC);
unsigned char sense = ADCH;
if (sense >= OPERATING_TEMPERATURE) {
set_bit(state, S_TEMP);
} else {
clear_bit(state, S_TEMP);
}
}
/**
* Checks for zero crossing (with fixed offset)
*
* @return Raw ADC value.
*/
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;
}
/**
* Dummy function for wake-up.
*/
ISR ( INT0_vect) {
// Nothing to do here.
}
/**
* Timer interrupt. Increments counters and controls LED.
*/
ISR ( TIMER1_OVF1_vect) {
if (time_counter < 1000)
time_counter++; // Global milliseconds counter and seconds counter (for AutoOff).
else {
time_counter = 0;
sec_counter++;
}
user_time_counter++; // Universal counter (for pump time).
unsigned char leds_blink_on; // Status flag for blinking LEDs with 1Hz.
if (time_counter < 499) {
leds_blink_on = 1;
} else {
leds_blink_on = 0;
}
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--;
}
}
} }

View File

@ -1,79 +1,94 @@
/** SenseoControl 2.0 /*****************************************************************************
* SenseoControl 2.0 *
* Copyright (C) 2013-2018 Stefan Kalscheuer *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation version 3. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
*****************************************************************************/
/**
* SenseoControl 2.0
* *
* File: main.h * @file main.h
* Author: Stefan Kalscheuer * @author Stefan Kalscheuer
* Date: 22.04.2013 * @date 2013-04-22
*
* License: GNU GPL v3 (see License.txt)
*/ */
/******************* /********************
* USER SETTINGS * User settings:
*/ */
#define TIME_1_ESPRESSO 15 // pump times in seconds #define TIME_1_ESPRESSO 15 // Pump times in seconds.
#define TIME_2_ESPRESSO 28 #define TIME_2_ESPRESSO 28
#define TIME_1_COFFEE 26 #define TIME_1_COFFEE 26
#define TIME_2_COFFEE 52 #define TIME_2_COFFEE 52
#define OPERATING_TEMPERATURE 125 // ADC threshold for water temperature #define OPERATING_TEMPERATURE 125 // ADC threshold for water temperature.
#define COFFEE_WISH 0 // save coffee wish while heating up #define COFFEE_WISH 0 // Save coffee wish while heating up.
/* /*
******************* ********************/
*/
// functions for setting and clearing bits // Function macros for setting and clearing bits.
#define set_bit(var, bit) ((var) |= (1 << (bit))) #define set_bit(var, bit) ((var) |= (1 << (bit)))
#define clear_bit(var, bit) ((var) &= (unsigned)~(1 << (bit))) #define clear_bit(var, bit) ((var) &= (unsigned)~(1 << (bit)))
#define ZERO_CROSSING_w PORTA // zero crossing detection #define ZERO_CROSSING_w PORTA // Zero crossing detection.
#define ZERO_CROSSING_r PINA #define ZERO_CROSSING_r PINA
#define ZERO_CROSSING_pin 0 #define ZERO_CROSSING_pin 0
#define ZERO_CROSSING_ddr DDRA #define ZERO_CROSSING_ddr DDRA
#define ZERO_CROSSING_adc 0 #define ZERO_CROSSING_adc 0
#define BUTTON_1_CUP_w PORTB // left button #define BUTTON_1_CUP_w PORTB // Left button.
#define BUTTON_1_CUP_r PINB #define BUTTON_1_CUP_r PINB
#define BUTTON_1_CUP_pin 4 #define BUTTON_1_CUP_pin 4
#define BUTTON_1_CUP_ddr DDRB #define BUTTON_1_CUP_ddr DDRB
#define BUTTON_2_CUP_w PORTB // right button #define BUTTON_2_CUP_w PORTB // Right button.
#define BUTTON_2_CUP_r PINB #define BUTTON_2_CUP_r PINB
#define BUTTON_2_CUP_pin 5 #define BUTTON_2_CUP_pin 5
#define BUTTON_2_CUP_ddr DDRB #define BUTTON_2_CUP_ddr DDRB
#define BUTTON_POWER_w PORTB // power button #define BUTTON_POWER_w PORTB // Power button.
#define BUTTON_POWER_r PINB #define BUTTON_POWER_r PINB
#define BUTTON_POWER_pin 6 #define BUTTON_POWER_pin 6
#define BUTTON_POWER_ddr DDRB #define BUTTON_POWER_ddr DDRB
#define LED_RED_w PORTA // red LED #define LED_RED_w PORTA // Red LED.
#define LED_RED_r PINA #define LED_RED_r PINA
#define LED_RED_pin 3 #define LED_RED_pin 3
#define LED_RED_ddr DDRA #define LED_RED_ddr DDRA
#define LED_RED_ON 0 #define LED_RED_ON 0
#define LED_RED_BLINK 1 #define LED_RED_BLINK 1
#define LED_GREEN_w PORTA // green LED #define LED_GREEN_w PORTA // Green LED.
#define LED_GREEN_r PINA #define LED_GREEN_r PINA
#define LED_GREEN_pin 1 #define LED_GREEN_pin 1
#define LED_GREEN_ddr DDRA #define LED_GREEN_ddr DDRA
#define LED_GREEN_ON 2 #define LED_GREEN_ON 2
#define LED_GREEN_BLINK 3 #define LED_GREEN_BLINK 3
#define LED_BLUE_w PORTA // blue LED #define LED_BLUE_w PORTA // Blue LED.
#define LED_BLUE_r PINA #define LED_BLUE_r PINA
#define LED_BLUE_pin 2 #define LED_BLUE_pin 2
#define LED_BLUE_ddr DDRA #define LED_BLUE_ddr DDRA
#define LED_BLUE_ON 4 #define LED_BLUE_ON 4
#define LED_BLUE_BLINK 5 #define LED_BLUE_BLINK 5
#define SENSOR_MAGNET_w PORTA // hall switch (water) #define SENSOR_MAGNET_w PORTA // Hall switch (water).
#define SENSOR_MAGNET_r PINA #define SENSOR_MAGNET_r PINA
#define SENSOR_MAGNET_pin 5 #define SENSOR_MAGNET_pin 5
#define SENSOR_MAGNET_ddr DDRA #define SENSOR_MAGNET_ddr DDRA
#define SENSOR_MAGNET_adc 4 #define SENSOR_MAGNET_adc 4
#define WATER_LOW 30 // ADC threshold for low water #define WATER_LOW 30 // ADC threshold for low water.
#define WATER_OK 100 // ADC threshold for water OK #define WATER_OK 100 // ADC threshold for water OK.
#define SENSOR_TEMP_w PORTA // NTC (temperature) #define SENSOR_TEMP_w PORTA // NTC (temperature)
#define SENSOR_TEMP_r PINA #define SENSOR_TEMP_r PINA
@ -81,21 +96,28 @@
#define SENSOR_TEMP_ddr DDRA #define SENSOR_TEMP_ddr DDRA
#define SENSOR_TEMP_adc 3 #define SENSOR_TEMP_adc 3
#define TRIAC_BOILER_w PORTA // boiler triac #define TRIAC_BOILER_w PORTA // Boiler triac.
#define TRIAC_BOILER_r PINA #define TRIAC_BOILER_r PINA
#define TRIAC_BOILER_pin 6 #define TRIAC_BOILER_pin 6
#define TRIAC_BOILER_ddr DDRA #define TRIAC_BOILER_ddr DDRA
#define TRIAC_PUMP_w PORTA // pump triac #define TRIAC_PUMP_w PORTA // Pump triac.
#define TRIAC_PUMP_r PINA #define TRIAC_PUMP_r PINA
#define TRIAC_PUMP_pin 7 #define TRIAC_PUMP_pin 7
#define TRIAC_PUMP_ddr DDRA #define TRIAC_PUMP_ddr DDRA
#define AUTO_OFF_THRESHOLD 180 // AutoOff threshold (seconds) #define AUTO_OFF_THRESHOLD 180 // AutoOff threshold (seconds).
#define BUTTON_CLEAN_THR 30 // button threshold for cleaning mode (ms) #define BUTTON_CLEAN_THR 30 // Button threshold for cleaning mode (ms).
#define BUTTON_THRESHOLD 100 // button threshold (ms) #define BUTTON_THRESHOLD 100 // Button threshold (ms).
#define BUTTON_LONG_THR 1500 // button threshold for long time push (ms) #define BUTTON_LONG_THR 1500 // Button threshold for long time push (ms).
// Global state flags.
#define S_WATER 0
#define S_TEMP 1
#define S_CLEAN 2
#define S_ESC 3
// LED color flags.
#define RED 0b00000001 #define RED 0b00000001
#define RED_BLINK 0b00000010 #define RED_BLINK 0b00000010
#define GREEN 0b00000100 #define GREEN 0b00000100
@ -107,9 +129,9 @@
#define VIOLET 0b00010001 #define VIOLET 0b00010001
#define VIOLET_BLINK 0b00100010 #define VIOLET_BLINK 0b00100010
// prototypes: // Prototypes:
void init (); // initialization void init(); // Initialization.
void power_off (); // power off to sleep mode void power_off(); // Power off to sleep mode.
bool get_water (); // update water state void update_water(void); // Update water state.
bool get_temperature (); // update tehmerature state void update_temperature(void); // Update temperature state.
unsigned int detect_zero_crossing (); // detect zero crossing unsigned int detect_zero_crossing(void); // Detect zero crossing.