Кухонные часы-таймер
- Войдите на сайт для отправки комментариев
Сделал часы на кухню, с таймером. Время и день недели показывают постоянно (я вообще не понимаю зачем нужны часы в которых время показывается секунд 20 в минуту))). Шрифт часов можно выбрать из 3 вариантов. Могут пищать каждый час, длительность писка 200мс, т.ч. отключение на ночь не делал. Во второй строке или число и месяц или день недели и температура или выдержка таймера. Данные меняются каждые 10 секунд, время таймера показывается постоянно пока не отработает выдержку, температура берется с DS3231. В режиме таймера можно задать задержку до 999 минут. Все собрано из готовых блоков с али: 6 индикаторов на MAX7219, модуль для распбери DS3231 (он небольшой и стоит в окне внизу корпуса), пленочная клавиатура и пассивный бипер. Подключение расписано в самом скетче. Корпус печал сам, модели есть тут - https://3dtoday.ru/3d-models/for-home/kitchen/chasy_taymer_na_kukhnyu/.



/*
* Connect:
* MAX7219:
* Pin 11 - DIN
* Pin 13 - CLK
* Pin 10 - CS
* Matrix:
* 0 2 4
* 1 3 5
* Add library 'LedControl' on IDE
*
* I2C MAX3231:
* pin A5 - SCL
* pin A4 - SDA
*
* Keypad:
* Pin 2,3,4,5,A0,A1,A2,A3
*
* Biper:
* Pin 7 and VCC
*
* Управление:
* 'A' - Set time/timer (Установка времени/таймера)
* 'B' - Set mode 'Time/Timer' (Переключение режима "Часы/Таймер")
* 'C' - Start/Stop timer, stop timer sound (Запуск/Остановка таймера, Выключение звука таймера)
* 'D' - Enable/Disable ring every hour (Включение/Выключение сигнала каждый час)
* '*' - Change time font (Смена шрифта часов)
* '#' - Change screen light (Изменение яркости экрана)
*/
#include "LedControl.h"
#include <Wire.h>
#include <avr/wdt.h>
#define DS3231 0x68
#define MAX_LIGHT 5 // Maximum==16
const unsigned char PinBiper=7;
const unsigned char PinOut[] ={A0, A1, A2, A3}; // пины выхода клавиатуры
const unsigned char PinIn[] ={2, 3, 4, 5}; // пины входа клавиатуры
const char key_value[4][4]={
{'A', 'B', 'C', 'D'},
{'3', '6', '9', '#'},
{'2', '5', '8', '0'},
{'1', '4', '7', '*'}};
LedControl lc=LedControl(11,13,10,6);
// Дни недели - П Н В Т С Р Ч Т П Т С Б В С
const unsigned char days[]={16,17,18,19,20,21,04,19,16,19,20,06,18,20};
unsigned char scr_level=1; // Яркость экрана. Настройка сохраняется в DS3231 по аресу 0x09
unsigned char buffer[24][2]; // Буфер экрана
unsigned char mode=0; // Режим: 0 - Дата, 1 - День недели/температура, 2 - Таймер, 3 - Установка времени
unsigned char ds[0x12]; // Буфер DS3231
unsigned char sound_en=0; // Сигнал каждый час. Настройка сохраняется в DS3231 по адресу 0x0C
unsigned int timer_min=0; // Время таймера
unsigned char timer_sec=0;
unsigned char timer_mode=0; // Режим таймера
unsigned char font=0; // Шрифт часов 0-2
// Символы 7x4
const unsigned char DigBig1[] PROGMEM ={
0x3E,0x41,0x41,0x3E,// 0
0x00,0x42,0x7F,0x40,// 1
0x62,0x51,0x49,0x66,// 2
0x22,0x49,0x49,0x36,// 3
0x18,0x14,0x12,0x7F,// 4
0x2F,0x49,0x49,0x33,// 5
0x3E,0x49,0x49,0x32,// 6
0x03,0x79,0x05,0x03,// 7
0x36,0x49,0x49,0x36,// 8
0x26,0x49,0x49,0x3E,// 9
0x0,0x24,0x12,0x0, // Точка 1
0x0,0x12,0x24,0x0, // Точка 2
0,0,0,0}; // Space
const unsigned char DigBig2[] PROGMEM ={
0x7F,0x63,0x63,0x7F,// 0
0x42,0x7F,0x7F,0x40,// 1
0x7B,0x7B,0x6F,0x6F,// 2
0x6B,0x6B,0x7F,0x7F,// 3
0x1F,0x18,0x18,0x7F,// 4
0x6F,0x6B,0x7B,0x7B,// 5
0x7F,0x6B,0x6B,0x7B,// 6
0x03,0x7B,0x7F,0x07,// 7
0x7F,0x6B,0x6B,0x7F,// 8
0x6F,0x6B,0x6B,0x7F,// 9
0x0,0x24,0x12,0x0, // Точка 1
0x0,0x12,0x24,0x0, // Точка 2
0,0,0,0}; // Space
const unsigned char DigBig3[] PROGMEM ={
0x7F,0x41,0x41,0x7F,// 0
0x00,0x42,0x7F,0x40,// 1
0x79,0x49,0x49,0x4F,// 2
0x49,0x49,0x49,0x7F,// 3
0x0F,0x08,0x08,0x7F,// 4
0x4F,0x49,0x49,0x79,// 5
0x7F,0x49,0x49,0x79,// 6
0x01,0x01,0x7D,0x03,// 7
0x7F,0x49,0x49,0x7F,// 8
0x4F,0x49,0x49,0x7F,// 9
0x0,0x24,0x12,0x0, // Точка 1
0x0,0x12,0x24,0x0, // Точка 2
0,0,0,0}; // Space
// Символы 5x3
const unsigned char DigSmall[] PROGMEM ={
0x1F,0x11,0x1F, // 0
0x12,0x1F,0x10, // 1
0x1D,0x15,0x17, // 2
0x15,0x15,0x1F, // 3
7,4,0x1F, // 4
0x17,0x15,0x1D, // 5
0x1F,0x15,0x1D, // 6
1,0x1D,3, // 7
0x1F,0x15,0x1F, // 8
0x17,0x15,0x1F, // 9
0,0x10,0, // Точка 10
0,0x0A,0, // Двоеточие 11
0x0A,0x04,0x0A, // Сигнал 12
0x0E,4,0, // Play 13
6,6,0, // Stop 14
0,0,0, // Space 15
// Дни недели
0x1F,1,0x1F, // П 16
0x1F,0x04,0x1F, // H 17
0x1F,0x15,0x1B, // B 18
0x01,0x1F,0x01, // T 19
0x1F,0x11,0x1B, // C 20
0x1F,0x05,0x07 // P 21
};
//***********************************************************************************************
void setup() {
//Serial.begin(9600);
// Клавиатура
for(unsigned char i=0;i<4;i++) {pinMode(PinOut[i], OUTPUT);digitalWrite(PinOut[i],HIGH);}
for(unsigned char i=0;i<4;i++) {pinMode(PinIn[i], INPUT);digitalWrite(PinIn[i],HIGH);}
// I2C
Wire.begin();
Wire.setClock(400000);
Read_I2C(DS3231,0,0x12,ds);
// Перезапуск DS3231
if(ds[0x0E] & 0x80)
{
ds[0]=ds[1]=ds[2]=0;ds[3]=2;ds[4]=ds[5]=1;ds[6]=0x19;
for(unsigned int i=7;i<0x0E;i++)ds[i]=0;
ds[0x0E]=0x20;
Write_I2C(DS3231,0,15,ds);
}
sound_en=ds[0x0C];
scr_level=ds[9];
// Матричный экран
for(unsigned char address=0;address<6;address++) {lc.shutdown(address,false); lc.setIntensity(address,scr_level); lc.clearDisplay(address);}
for(unsigned char i=0;i<24;i++) {buffer[i][0]=buffer[i][1]=0;}
// Biper
pinMode(PinBiper, OUTPUT);digitalWrite(PinBiper,HIGH);
tone(PinBiper,1000,200);
// WDT
wdt_reset();wdt_enable(WDTO_4S);
}
//***************************************************************************************
//* Запись символа 5x3 в буфер
//* adr - позиция Х (0..23)
//* line - линия Y (0...1)
void WriteSmallChar(unsigned char adr, unsigned char line, unsigned char data)
{
unsigned char tmp=data*3;
buffer[adr++][line] |=pgm_read_byte_near(DigSmall+tmp++)<<3;
buffer[adr++][line] |=pgm_read_byte_near(DigSmall+tmp++)<<3;
buffer[adr][line] |=pgm_read_byte_near(DigSmall+tmp)<<3;
}
//***************************************************************************************
//* Запись символа 7x4 в буфер
void WriteBigChar (unsigned char adr, unsigned char line, unsigned char data)
{
unsigned char tmp=data*4;
const unsigned char *ad=&DigBig1[tmp];
if(font==1) ad=&DigBig2[tmp];
if(font==2) ad=&DigBig3[tmp];
buffer[adr++][line]=pgm_read_byte_near(ad++);
buffer[adr++][line]=pgm_read_byte_near(ad++);
buffer[adr++][line]=pgm_read_byte_near(ad++);
buffer[adr++][line]=pgm_read_byte_near(ad);
}
//***************************************************************************************
//* Вывод буфера на экран
void WriteDisplay()
{
unsigned char z=0;
for(char i=0;i<8;i++) lc.setRow(0,i,buffer[z++][0]);
for(char i=0;i<8;i++) lc.setRow(2,i,buffer[z++][0]);
for(char i=0;i<8;i++) lc.setRow(4,i,buffer[z++][0]);
z=0;
for(char i=0;i<8;i++) lc.setRow(1,i,buffer[z++][1]);
for(char i=0;i<8;i++) lc.setRow(3,i,buffer[z++][1]);
for(char i=0;i<8;i++) lc.setRow(5,i,buffer[z++][1]);
}
//***********************************************************************************************
// Опрос клавиатуры
char Keypad()
{
char key=0;
for (int i = 0; i < 4; i++)
{
digitalWrite(PinOut[i], LOW);
for (int j = 0; j < 4; j++) if (digitalRead(PinIn[j]) == LOW) key=key_value[i][j];
digitalWrite(PinOut[i], HIGH);
}
return key;
}
//***************************************************************************************
//* Чтение регистров I2C
void Read_I2C(unsigned char device, unsigned char adr, unsigned char count,unsigned char *data)
{
unsigned char i=0;
// Установить адрес
Wire.beginTransmission(device);
Wire.write(adr);
Wire.endTransmission();
// Считать данные
Wire.beginTransmission(0x68);
Wire.requestFrom(device, count);
while(Wire.available()){data[i++] = Wire.read();}
Wire.endTransmission();
}
//***************************************************************************************
//* Запись регистров I2C
void Write_I2C (unsigned char device, unsigned char adr, unsigned char count,unsigned char *data)
{
Wire.beginTransmission(device);
Wire.write(adr);
for(unsigned char i=0;i<count;i++) Wire.write(data[i]);
Wire.endTransmission();
}
//***********************************************************************************************
// Очистка нижней части экрана и вывод дня недели
void ClearDate()
{
unsigned char tmp;
buffer[0][1]=buffer[1][1]=buffer[22][1]=buffer[23][1]=0;
tmp=2;
for(unsigned char i=0;i<7;i++) {buffer[tmp++][1]=2;buffer[tmp++][1]=2;buffer[tmp++][1]=0;}
tmp=(ds[3]-1)*3+2;
buffer[tmp++][1]=3;buffer[tmp][1]=3;
}
//***********************************************************************************************
// Вывод таймера
void WriteTimer()
{
unsigned char tmp;
ClearDate();
if(timer_mode==0) WriteSmallChar(0,1,14); // Таймер остановлен
if(timer_mode==2) WriteSmallChar(0,1,13); // Таймер работает
tmp=timer_min%100;
WriteSmallChar(3,1,timer_min/100);
WriteSmallChar(7,1,tmp/10);
WriteSmallChar(11,1,tmp%10);
WriteSmallChar(14,1,11);
WriteSmallChar(17,1,timer_sec/10);
WriteSmallChar(21,1,timer_sec%10);
}
//***********************************************************************************************
// Вывод даты
void WriteData()
{
ClearDate();
if(sound_en==1) WriteSmallChar(0,1,12); // Символ сигнала каждый час
WriteSmallChar(4,1,ds[4]>>4);
WriteSmallChar(8,1,ds[4] & 0xF);
WriteSmallChar(11,1,10);
WriteSmallChar(14,1,ds[5]>>4);
WriteSmallChar(18,1,ds[5] & 0xF);
}
//***********************************************************************************************
// Вывод дня недели и температуры
void WriteTemp()
{
unsigned char tmp=(ds[3]-1)*2;
ClearDate();
if(sound_en==1) WriteSmallChar(0,1,12); // Символ сигнала каждый час
WriteSmallChar(4,1,days[tmp++]);
WriteSmallChar(8,1,days[tmp]);
WriteSmallChar(15,1,ds[0x11]/10);
WriteSmallChar(19,1,ds[0x11]%10);
buffer[23][1]=0x10;
}
//***********************************************************************************************
// Ввод цифры часов или минут
unsigned char EnterBigDigit( unsigned char adr, unsigned char v_min, unsigned char v_max, unsigned char value)
{
unsigned char tmp=0,key;
unsigned char flash=0;
while(1)
{
// Ожидание отпускания кнопки 'A'
if(tmp==0 && Keypad()==0) tmp=1;
// Ожидание ввода цифры и подтверждения 'A'
if(tmp==1)
{
key=Keypad();
if(key>v_min && key<v_max) {value=key-'0';tmp=2;}
}
// Ожидание отпускания кнопки
if(tmp==2 && Keypad()==0) tmp=3;
if(tmp==3)
{
WriteBigChar(adr,0,value);
WriteDisplay();
return value;
}
if(flash) WriteBigChar(adr,0,value);
else WriteBigChar(adr,0,12);
flash=!flash;
WriteDisplay();
wdt_reset();
delay(200);
}
}
//***********************************************************************************************
// Ввод цифры числа, месяца или года
unsigned char EnterSmallDigit( unsigned char adr, unsigned char v_min, unsigned char v_max, unsigned char value)
{
unsigned char tmp=0,key;
unsigned char t1=buffer[adr][1] & 3,t2=buffer[adr+1][1] & 3,t3=buffer[adr+2][1] & 3;
unsigned char flash=0;
while(1)
{
// Ожидание отпускания кнопки 'A'
if(tmp==0 && Keypad()==0) tmp=1;
// Ожидание ввода цифры и подтверждения 'A'
if(tmp==1)
{
key=Keypad();
if(key>v_min && key<v_max) {value=key-'0';tmp=2;}
}
// Ожидание отпускания кнопки
if(tmp==2 && Keypad()==0) tmp=3;
if(tmp==3)
{
buffer[adr][1] =t1; buffer[adr+1][1] =t2; buffer[adr+2][1] =t3;
WriteSmallChar(adr,1,value);
WriteDisplay();
return value;
}
if(flash) WriteSmallChar(adr,1,value);
else {buffer[adr][1] =t1; buffer[adr+1][1] =t2; buffer[adr+2][1] =t3;}
flash=!flash;
WriteDisplay();
wdt_reset();
delay(200);
}
}
//******************************************************************************
// День недели по дате: 1 — понедельник, 2 — вторник и так далее.
unsigned char Calc_Day(unsigned char day, unsigned char month, unsigned int year)
{
unsigned int t1, t2;
unsigned char m[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
t1 = year - 2001;
t2 = m[month-1];
if ( ((year & 0x0003) == 0) && (((month-1) >> 1) != 0) ) t2++;
return (((t1 + (t1 >> 2) + t2 + (day-1)) % 7)+1);
}
//***********************************************************************************************
// Установка времени
void SetupTime()
{
unsigned char tmp,tmp1;
unsigned char hour=ds[2],minute=ds[1],date=ds[4],month=ds[5],year=ds[6];
unsigned char days_of_month[]={0,0x31,0x28,0x31,0x30,0x31,0x30,0x31,0x31,0x30,0x31,0x30,0x31};
// Ввод десятков часов
tmp=EnterBigDigit(1,'0'-1,'3',hour>>4);
// Ввод единиц часов
if(tmp==2)
{
// Для 20++ часов - максимум 3
if((hour & 0xF)>3) hour=0x20;
tmp1=EnterBigDigit(6,'0'-1,'4',hour & 0xF);
}
else tmp1=EnterBigDigit(6,'0'-1,'9'+1,hour & 0xF);
hour=(tmp<<4) | tmp1;
// Ввод минут
tmp=EnterBigDigit(14,'0'-1,'6',minute>>4);
tmp1=EnterBigDigit(19,'0'-1,'9'+1,minute & 0xF);
minute=(tmp<<4) | tmp1;
// Вывод года
ClearDate();
WriteSmallChar(7,1,2);
WriteSmallChar(11,1,0);
WriteSmallChar(15,1,year>>4);
WriteSmallChar(19,1,year & 0xF);
WriteDisplay();
// Ввод года
tmp=EnterSmallDigit(15,'0'-1,'9'+1,year>>4);
tmp1=EnterSmallDigit(19,'0'-1,'9'+1,year & 0xF);
year=(tmp<<4) | tmp1;
// Вывод числа и месяца
WriteData();
// Ввод месяца
tmp=EnterSmallDigit(14,'0'-1,'2',month>>4);
if(tmp==1)
{
if((month & 0xF)>2) month=0x10;
tmp1=EnterSmallDigit(18,'0'-1,'3',month & 0xF);
}
else tmp1=EnterSmallDigit(18,'0'-1,'9'+1,month & 0xF);
month=(tmp<<4) | tmp1;
// Ввод даты
if( (year%4)==0)days_of_month[2]=0x29;
tmp1=(month>>4)*10+(month & 0xF);
if((date>>4)>(days_of_month[tmp1]>>4)) tmp=EnterSmallDigit(4,'0'-1,(days_of_month[tmp1]>>4)+0x31,days_of_month[tmp1]>>4);
else tmp=EnterSmallDigit(4,'0'-1,(days_of_month[tmp1]>>4)+0x31,date>>4);
if(tmp==(days_of_month[tmp1]>>4))
{
// Для числа 3++ (в феврале 2++) максимальное значение == 0/1 (для февраля 28/29)
date=tmp<<4;
tmp1=EnterSmallDigit(8,'0'-1,((days_of_month[tmp1] & 0xF)+0x31),date & 0xF);
}
else
{
// Нулевой даты не бывает
if(!tmp) tmp1=EnterSmallDigit(8,'1'-1,'9'+1,date & 0xF);
else tmp1=EnterSmallDigit(8,'0'-1,'9'+1,date & 0xF);
}
date=(tmp<<4) | tmp1;
ds[0]=0;ds[1]=minute,ds[2]=hour,ds[4]=date,ds[5]=month,ds[6]=year;
ds[3]=Calc_Day((date>>4)*10+(date & 0xF),(month>>4)*10+(month & 0xF),2000+(year>>4)*10+(year & 0xF));
Write_I2C(DS3231,0,7,&ds[0]);
}
//***********************************************************************************************
void loop() {
unsigned char tmp;
static unsigned char old_sec=0xFF;
static unsigned char delay_10=10; // Задержка до смены режима "Дата/Температура"
static unsigned int adr=0; // Позиция редактируемого символа таймера
static unsigned char flash=0; // Мигание разряда
static unsigned char next_digit=0; // Ожидание отпускания клавиши
static unsigned char sec_upd=0; // Прошла 1 секунда
static unsigned int old_timer=0; // Последнее введеное значение таймера
static unsigned int timer_sound=0; // Задержка выключения звука таймера
Read_I2C(DS3231,0,0x12,ds);
// Вывод времени
if(ds[2]>>4) WriteBigChar(1,0,ds[2]>>4);
else WriteBigChar(1,0,12);
WriteBigChar(6,0,ds[2] & 0xF);
if(ds[0] & 1) WriteBigChar(10,0,10);
else WriteBigChar(10,0,11);
WriteBigChar(14,0,ds[1]>>4);
WriteBigChar(19,0,ds[1] & 0xF);
// Прошла секунда
if(ds[0]!=old_sec)
{
old_sec=ds[0];
wdt_reset();
if(!timer_sound) digitalWrite(PinBiper,HIGH); // Отключение бипера
else
{
timer_sound--;
// Возврат в режим часов
if(!timer_sound) mode=0;
}
sec_upd=1;
// Сигнал каждый час
if(sound_en && !ds[0] && !ds[1] && !timer_sound) tone(PinBiper,1000,200);
// Смена режимов каждые десять секунд
// В режиме таймера и установки времени режимы не меняются
if(mode<2) {delay_10--; if(!delay_10) {delay_10=10; mode++;if(mode==2) mode =0; }}
}
// Режимы таймера:
// 0 == Остановлен
// 1 == Установка значений
// 2 == Работает
// Опрос клавиатуры
tmp=Keypad();
if(tmp==0) next_digit=0;
if(tmp=='A')
{
if(mode<2) { mode=3; WriteData(); SetupTime(); mode=0; } // Установка времени
if(mode==2 && !timer_mode) // Установка таймера
{
timer_mode=1;
timer_min=timer_sec=adr=0;
if(timer_sound) {timer_sound=0; noTone(PinBiper);}
}
}
// Смена шрифта часов
if(tmp=='*' && !next_digit)
{
next_digit=1;
font++;if(font==3)font=0;
}
// Изменение яркости экрана
if(tmp=='#' && !next_digit)
{
scr_level++;if(scr_level==MAX_LIGHT) scr_level=0;
for(unsigned char address=0;address<6;address++) lc.setIntensity(address,scr_level);
ds[0]=scr_level; Write_I2C(DS3231,0x09,1,&ds[0]);
next_digit=1;
}
// Таймер
if(tmp=='B' && !next_digit)
{
if(mode<2){mode=2;timer_mode=0;timer_min=old_timer;timer_sec=0;WriteTimer();} // Вход в режим таймера
else { mode=0;timer_mode=0; WriteData(); } // Выход из режима таймера
next_digit=1;
}
// Установка времени таймера
if(mode==2 && timer_mode==1)
{
// Ввод значения
if(tmp>0 && tmp<('9'+1) && !next_digit)
{
if(!adr) {timer_min=(tmp-0x30)*100;adr++;}
else if(adr==1) {timer_min+=(tmp-0x30)*10;adr++;}
// После ввода последней цифры переход таймера в режим "Стоп"
else if(adr==2) {timer_min+=(tmp-0x30); old_timer=timer_min;timer_mode=0;adr++;}
next_digit=1;
}
// Мигание разряда
ClearDate();WriteTimer();
if(adr==0 && flash) {buffer[3][1] &=3;buffer[4][1] &=3;buffer[5][1] &=3;}
if(adr==1 && flash) {buffer[7][1] &=3;buffer[8][1] &=3;buffer[9][1] &=3;}
if(adr==2 && flash) {buffer[11][1] &=3;buffer[12][1] &=3;buffer[13][1] &=3;}
flash=!flash;
}
// Выключение звука таймера
if((tmp=='B' || tmp=='C') && timer_sound) {timer_sound=0; noTone(PinBiper);}
// Включение/Выключение таймера. Нельзя запустить таймер при нулевом счетчике
if(tmp=='C' && (timer_mode==0 || timer_mode==2) && (timer_min!=0 || timer_sec!=0) && !next_digit)
{
if(timer_mode==2) timer_mode=0;
else timer_mode=2;
next_digit=1;
WriteTimer();
}
// Счет таймера
if(mode==2 && timer_mode==2)
{
if(sec_upd)
{
sec_upd=0;
// Уменьшение секунд таймера
if(timer_sec || timer_min) timer_sec--;
// Время кончилось
if(!timer_min && !timer_sec)
{
timer_mode=0;
tone(PinBiper,1500,30000);
timer_sound=31;
}
// Уменьшение минут таймера
else {if(timer_sec==0xFF) {timer_sec=59;timer_min--;}}
WriteTimer();
}
}
// Сигнал каждый час вкл/выкл
if(tmp=='D' && mode<2 && !next_digit) {ds[0]=sound_en=!sound_en; Write_I2C(DS3231,0x0C,1,&ds[0]);next_digit=1; }
if(mode==0) WriteData(); // Вывод даты
if(mode==1) WriteTemp(); // Вывод дня недели и температуры
WriteDisplay();
delay(200);
}
Давно искал такое совмещение, часы и таймер. Статья заинтересовала, но к сожалению по скетчу для меня не все понятно,как понял схему, нарисовал в протеусе, но она не работает.Будьте добры показать схемку.
Красиво!
Хотя цифровая клавиатура, ИМХО, здесь чрезмерна. 4 кнопки направления, энтер и сброс.