У меня дома газовая плита, термометр в духовке которой показывает, может быть, освещённость на Марсе, или артериальное давление Байдена - я не знаю. А в сети полно рецептов, где написано: "Готовьте 40 минут при температуре 180 градусов, потом 15 минут при 160...". И тому подобное. Сам я повар - так себе, мне важно выполнить требование инструкции. А тут подошли очередные "железки" с Али...
Идея такая: нано-168, несколько светодиодов, max6675, энкодер, дисплей 1602. Задаём температуру и время. Если температура не та - пищим каждые две минуты, издалека цветом светодиода показываем "холодно/горячо" или "норм" (тогда не пищим). По окончании времени - играем мелодию из Тетриса.
На фото будет видно, что я хотел замутить включение всего прибора кнопкой, а выключение автоматически (через мосфет), или по длинному нажатию энкодера, но купив мосфет, припаяв его с кнопкой - что-то у меня не задалось. Я на свалке отрыл какие-то советские полевые транзисторы - тоже не срослось, психанул и на китайские сопли приклеил выключатель, который нашёл. Заставил конструкцию противно пищать, когда всё закончилось, а её не выключили. Как куплю нужный мосфет - перепаяю (Ха-ха. Сам в это мало верю. Но кнопку и неисправный мосфет пока на плате оставил.)
Сам проект для себя назвал "технопорно", поскольку собирал всё на макетной плате, постарался чтобы все детали с проводами торчали наружу.
На фотке что-то страшное, круглое и серое - не пугайтесь, это крутилка для энкодера от какой-то авиационной фигни. А если кто-то увидит чёрные провода припаянные к микросхеме MAX6674 - это я пытался выпаять "гребёнку", припаянную в Китае не в ту сторону, намаялся, поотрывал дорожки, плюнул, припаялся к ногам микросхемы.
Ну и большое спасибо AlexGyver за библиотеки. И вообще за популяризацию всего вотэтого.
Если вдруг гуру подскажут как оптимизировать код - буду рад. Что-то из школьного QBasic (который был 30 лет назад) у меня в голове еще держится, но в остальном... Как получается - так и "кодю".
Идея такая: нано-168, несколько светодиодов, max6675, энкодер, дисплей 1602. Задаём температуру и время. Если температура не та - пищим каждые две минуты, издалека цветом светодиода показываем "холодно/горячо" или "норм" (тогда не пищим). По окончании времени - играем мелодию из Тетриса.
На фото будет видно, что я хотел замутить включение всего прибора кнопкой, а выключение автоматически (через мосфет), или по длинному нажатию энкодера, но купив мосфет, припаяв его с кнопкой - что-то у меня не задалось. Я на свалке отрыл какие-то советские полевые транзисторы - тоже не срослось, психанул и на китайские сопли приклеил выключатель, который нашёл. Заставил конструкцию противно пищать, когда всё закончилось, а её не выключили. Как куплю нужный мосфет - перепаяю (Ха-ха. Сам в это мало верю. Но кнопку и неисправный мосфет пока на плате оставил.)
Сам проект для себя назвал "технопорно", поскольку собирал всё на макетной плате, постарался чтобы все детали с проводами торчали наружу.
C++:
/*Код для кухонного таймера.
* Устройство состоит из: ардуино нано, дисплей 1602, энкодер, термопара max6675, трёх светодиодов, 4 сопротивления, пищалка, мосфет.
* Задача устройства: контролировать время приготовления таймером обратного отсчета, сравнивать показания термопары в духовке с
* заранее заданым значением температуры.
* Ввод/вывод: значения температуры и времени (минут) приготовления задаются энкодером. Одинарный "клик" уводит "вглубь меню", двойной - возвращает.
* Светодиоды меняют свой цвет в зависимости от температуры духовки +- 5 градусов. При несоответствии раз в 2 минуты звучит двухсекундный сигнал.
*/
// это для дисплея
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#include <GyverPower.h>
//это для термопары
#include "max6675.h"
int thermoSCK = 8; //D2
int thermoSO = 2; //D3
int thermoCS = 4; //D4
int TempCook = 160; //Начальное значение
int TempReal;
int Timer = 60; //Начальное значение
int Sec = 0; //для получения секунд из millis
int Min = 0; //для получения минут из millis
int MinOFF = 0; //для получения минут из millis
int ShowTime; // для вывода данных через интервал времени (мерцание экрана)
unsigned long Proctime; // для работы с millis
MAX6675 thermocouple(thermoSCK, thermoCS, thermoSO);
//это для энкодера
#define CLK 10
#define DT 11
#define SW 9
#include "GyverEncoder.h"
Encoder encoder(CLK, DT, SW, TYPE2); // для работы c кнопкой
//это светодиоды
int LedB = 7; //голубой
int LedG = 6; //зеленый
int LedR = 5; //красный
bool Final = false; //флаг для однократного проигрывания финальной мелодии
//это музыка
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
#define REST 0
int tempo = 144; //можно увеличить темп
int buzzer = 3; //номер пина пьезопищалки
int melody[] = {
NOTE_E5, 4, NOTE_B4, 8, NOTE_C5, 8, NOTE_D5, 4, NOTE_C5, 8, NOTE_B4, 8,
NOTE_A4, 4, NOTE_A4, 8, NOTE_C5, 8, NOTE_E5, 4, NOTE_D5, 8, NOTE_C5, 8,
NOTE_B4, -4, NOTE_C5, 8, NOTE_D5, 4, NOTE_E5, 4,
NOTE_C5, 4, NOTE_A4, 4, NOTE_A4, 8, NOTE_A4, 4, NOTE_B4, 8, NOTE_C5, 8,
NOTE_D5, -4, NOTE_F5, 8, NOTE_A5, 4, NOTE_G5, 8, NOTE_F5, 8,
NOTE_E5, -4, NOTE_C5, 8, NOTE_E5, 4, NOTE_D5, 8, NOTE_C5, 8,
NOTE_B4, 4, NOTE_B4, 8, NOTE_C5, 8, NOTE_D5, 4, NOTE_E5, 4,
NOTE_C5, 4, NOTE_A4, 4, NOTE_A4, 4, REST, 4,
};
int notes = sizeof(melody) / sizeof(melody[0]) / 2;
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
void setup() {
// Пины для светодиодов
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
// pinMode(13, OUTPUT);
//digitalWrite(13, LOW);
//начальное заполнение экрана
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("CookT:");
lcd.setCursor(0, 1);
lcd.print("RealT:");
lcd.setCursor(10, 0);
lcd.print("Timer:");
//проверка светодиодов на свечение
digitalWrite(LedB, HIGH);
digitalWrite(LedG, HIGH);
digitalWrite(LedR, HIGH);
delay(2000);
digitalWrite(LedB, LOW);
digitalWrite(LedG, LOW);
digitalWrite(LedR, LOW);
Proctime = millis();
}
void loop() {
if (millis() - Proctime > 1000) {
TempReal = thermocouple.readCelsius();
Proctime = millis();
Sec = Sec + 1; //здесь из millis делаем секунды
ShowTime = ShowTime + 1; //эта переменная используется для того, чтобы обновлять значения на экране раз в 5 секунд
if (ShowTime == 5) {
lcd.setCursor(6, 0);
lcd.print(TempCook);
lcd.setCursor(6, 1);
lcd.print(" ");
lcd.setCursor(6, 1);
lcd.print(TempReal);
lcd.setCursor(13, 1);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(Timer);
ShowTime = 0;
}
if (Sec > 59) {
Min = Min + 1; //здесь из секунд получаются минуты
if (Final) { //это условие отвечает за пищание прибора после окончания таймера
MinOFF = MinOFF + 1;
}
Sec = 0;
Timer = Timer - 1; //здесь значение таймера уменьшается каждую минуту
if (Timer <= 0) {
if (!Final) { //это условие проигрывает мелодию по окончанию таймера и включает флаг окончания работы
song();
Final = true;
}
Timer = 0;
}
}
if (Final == true and MinOFF == 1) { //это условие заставляет пищать прибор каждую минуту после окончания работы
tone(buzzer, 500, 500);
tone(buzzer, 800, 500);
MinOFF = 0;
}
}
encoder.tick();
// if (encoder.isHolded()) { //этот блок - неудача с электронным отключением прибора
// digitalWrite(13, LOW);
// }
/*
*Весь этот блок - работа с энкодером. При нажатии на энкодер появляется курсор, который поворотом энкодера
*можно перемещать между изменяемыми величинами: температурой приготовления или таймером.
*При двукратном клике из этого режима выходит, при однократном клике курсор мигает на изменяемой позиции и поворотом
*энкодера значения изменяются. Двукратный клик выводит курсор из режима редактирования значений в режим выбора значений,
*повторный двойной клик выводит систему из режима выбора изменяемых значений.
*/
bool flag1 = true; //эти два флага нужны для последовательного входа в "подменю" и выхода из него
bool flag2 = true;
int CursorPos;
if (encoder.isSingle()) {
lcd.setCursor(6, 0);
lcd.cursor();
while (flag1) {
encoder.tick();
if (encoder.isLeft()) {
lcd.setCursor(6, 0);
lcd.cursor();
CursorPos = 1;
}
if (encoder.isRight()) {
lcd.setCursor(13, 1);
lcd.cursor();
CursorPos = 2;
}
if (encoder.isDouble()) {
flag1 = false;
lcd.noCursor();
}
if (encoder.isSingle()) {
while (flag2) {
encoder.tick();
lcd.blink();
if (encoder.isRight()) {
if (CursorPos == 1) {
TempCook = TempCook + 1;
lcd.setCursor(6, 0);
lcd.print(" ");
lcd.setCursor(6, 0);
lcd.print(TempCook);
}
if (CursorPos == 2) {
Timer = Timer + 1;
lcd.setCursor(13, 1);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(Timer);
Final = false;
}
}
if (encoder.isLeft()) {
if (CursorPos == 1) {
TempCook = TempCook - 1;
lcd.setCursor(6, 0);
lcd.print(" ");
lcd.setCursor(6, 0);
lcd.print(TempCook);
}
if (CursorPos == 2) {
Timer = Timer - 1;
lcd.setCursor(13, 1);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(Timer);
}
}
if (encoder.isDouble()) {
flag2 = false;
lcd.noBlink();
}
}
flag2 = true;
}
}
flag1 = true;
}
/*
* Этот блок отвечает за свечение светодиодов. И пищание прибора при неверной температуре каждые 2 минуты.
*/
if (TempReal < TempCook - 5) {
digitalWrite(LedB, HIGH);
digitalWrite(LedG, LOW);
digitalWrite(LedG, LOW);
if (Min == 2 and !Final) {
tone(buzzer, 1000, 1000);
Min = 0;
}
} else if (TempReal > TempCook + 5) {
digitalWrite(LedG, LOW);
digitalWrite(LedR, HIGH);
digitalWrite(LedB, LOW);
if (Min == 2 and !Final) {
tone(buzzer, 1000, 1000);
Min = 0;
}
} else {
digitalWrite(LedR, LOW);
digitalWrite(LedG, HIGH);
digitalWrite(LedB, LOW);
}
}
void song() {
for (int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2) {
divider = melody[thisNote + 1];
if (divider > 0) {
noteDuration = (wholenote) / divider;
} else if (divider < 0) {
noteDuration = (wholenote) / abs(divider);
noteDuration *= 1.5; // increases the duration in half for dotted notes
}
tone(buzzer, melody[thisNote], noteDuration * 0.9);
delay(noteDuration);
noTone(buzzer);
}
}
На фотке что-то страшное, круглое и серое - не пугайтесь, это крутилка для энкодера от какой-то авиационной фигни. А если кто-то увидит чёрные провода припаянные к микросхеме MAX6674 - это я пытался выпаять "гребёнку", припаянную в Китае не в ту сторону, намаялся, поотрывал дорожки, плюнул, припаялся к ногам микросхемы.
Ну и большое спасибо AlexGyver за библиотеки. И вообще за популяризацию всего вотэтого.
Если вдруг гуру подскажут как оптимизировать код - буду рад. Что-то из школьного QBasic (который был 30 лет назад) у меня в голове еще держится, но в остальном... Как получается - так и "кодю".
Вложения
-
55.2 KB Просмотры: 27