Кухонный таймер и термопара

AC520

✩✩✩✩✩✩✩
16 Янв 2023
11
5
У меня дома газовая плита, термометр в духовке которой показывает, может быть, освещённость на Марсе, или артериальное давление Байдена - я не знаю. А в сети полно рецептов, где написано: "Готовьте 40 минут при температуре 180 градусов, потом 15 минут при 160...". И тому подобное. Сам я повар - так себе, мне важно выполнить требование инструкции. А тут подошли очередные "железки" с Али...
Идея такая: нано-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 лет назад) у меня в голове еще держится, но в остальном... Как получается - так и "кодю".
 

Вложения

  • Лойс +1
Реакции: Старик Похабыч

Геннадий П

★★★★★★✩
14 Апр 2021
1,799
579
44
У меня дома газовая плита, термометр в духовке которой показывает, может быть, освещённость на Марсе,
Если духовка без принудительной конвекции, то любой термометр будет так показывать.
 

AC520

✩✩✩✩✩✩✩
16 Янв 2023
11
5
@PiratFox, сам всё писал, кроме музыки. Среда… ну которая со всеми пирогами и блинами от ардуино. IDE, вроде.
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,694
472
@AC520, тогда плохо. Ни одного каммента в коде программы, башку же можно сломать, пока разберёшься. Не думаю, что у кого-то из форумчан будет желание помочь Вам что-то там "оптимизировать". Да Вы и сами через короткое время забудете, что там к чему. Писать код без комментариев - крайне дурной тон. Руки нужно отрывать и за меньшие грехи.😁 Не обижайтесь, это шутка.
 
Изменено:

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
240
42
Писать код без комментариев - крайне дурной тон.
Вот как раз писать комменты в коде - это плохой тон, код должен сам комментировать себя, комменты должны содержать только доп. информацию (комменты никто не редактирует, когда редактируют код, поэтому могут не соответствовать действительности)
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,694
472
Вот как раз писать комменты в коде - это плохой тон
Это что-то новое.:) В учебниках пишут другое, что это как раз хорощий тон. Впрочем, это Ваше личное дело. Не хотите комментировать свой же код, так не комментируйте.;) Только я и не говорил, что нужны прям подробные камменты, чуть ли не к каждой строчке.
 
Изменено:

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
240
42
@PiratFox,я имел ввиду бесполезные комментарии (например, комментирующие действия)
 
  • Лойс +1
  • Ахах! +1
Реакции: bort707 и PiratFox