Контроллер для пивоварни (моя версия)
- Войдите на сайт для отправки комментариев
Пт, 06/03/2015 - 15:27
Знакомый решил заняться пивоварением и попросил написать для него контроллер для нагревательного бака.
Далее фото и код.





#include <Wire.h> #include <PID_v1.h> #include <TimeHelpers.h> #include <OneWire.h> #include <LiquidCrystal_I2C.h> #include <DS1307.h> #include <RTClib.h> #include <EEPROM.h> #define ONE_WIRE_BUS 10 // Шина датчика температуры #define P_Temp 0 // Поле масива с данными температуры #define P_Time 1 // Поле масива с данными времени #define P_Alar 2 // Поле масива с данными сигнализации #define TEN1 13 // Порт шины нагревательного элемента 1 #define TEN2 6 // Порт шины нагревательного элемента 2 #define BUTON_MENU 4 // Порт входа кнопки меню (притянут к +) #define BUZZ 8 // Порт выхода на пьезоизлучатель #define noTrem 150 // Задержка антидребезга RTC_DS1307 rtc; // Инициализация Часов точного времени OneWire ds(10); // Инициализация шины датчика измерения температуры LiquidCrystal_I2C lcd(0x27, 20, 4); // Инициализация дисплея double Setpoint = EEPROM.read(110); // Создание переменной устанавливаемой температуры (и передача данных из памяти) double Input; // Создание переменной входящих данных в ПИД регулятор double Output; // Создание переменной исходящих данных из ПИД регулятора double consKp = 5, consKi = 50, consKd = 0.5; // Создание переменных констант ПИД регулятора (Обычный режим)(быстрый режим) //double tochKp = 5, tochKi = 2, tochKd = 0.05; // Создание переменных констант ПИД регулятора (точный режим)(медленный режим) PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); // Инициализация ПИД регулятора DateTime now; // Создания объекта Часов точного времени byte type_s; // Создание переменной типа датчика измерения температуры byte data[12]; // Создание переменной данных датчика температуры byte addr[8]; // Создание переменной адреса датчика температуры float celsius; // Создание переменной значения измерений датчика температуры int Moschnost; // Создание переменной мощности нагревательного элемента int U_D = 5; // Создание переменной джойстика (движение вверх, вниз) int L_R = 0; // Создание переменной джойстика (движение в лево, в право) boolean Vhod_v_menu = 0; // Создание переменной флага входа в меню int T_Upr = EEPROM.read(111); // Создание переменной указывающий тип управления (и передача данных из памяти) int _HR = 0; // Создание переменной хранения значения часов int _MI = 0; // Создание переменной хранения значения минут int _SE = 0; // Создание переменной хранения значения секунд int A_H = 0; // Создание переменной хранения значения часов при изменении времени int A_M = 0; // Создание переменной хранения значения минут при изменении времени boolean chg_Time = 0; // Создание переменной флага изменения значения времени #define PunkMenu 20 // Указатель на количество пунктов меню boolean Dreft0 = 0; // Создание переменной флага отрисовки 0 поля меню boolean Dreft1 = 0; // Создание переменной флага отрисовки 1 поля меню boolean Dreft2 = 0; // Создание переменной флага отрисовки 2 поля меню boolean Dreft3 = 0; // Создание переменной флага отрисовки 3 поля меню boolean Dreft4 = 0; // Создание переменной флага отрисовки 4 поля меню boolean Dreft5 = 0; // Создание переменной флага отрисовки 5 поля меню boolean Dreft_MAS_0 = 0; // Создание переменной флага отрисовки 6 поля меню boolean Dreft_MAS_1 = 0; // Создание переменной флага отрисовки 7 поля меню boolean Dreft_MAS_2 = 0; // Создание переменной флага отрисовки 8 поля меню boolean Dreft_MAS_3 = 0; // Создание переменной флага отрисовки 9 поля меню boolean Dreft_MAS_4 = 0; // Создание переменной флага отрисовки 10 поля меню // Temperatura,Vremya,Alarm int Pat[5][3] = {{26,1,0}, // Создание масива данных програмируемых периодов {28,0,0}, // Создание масива данных програмируемых периодов {30,0,0}, // Создание масива данных програмируемых периодов {40,0,0}, // Создание масива данных програмируемых периодов {50,0,0}}; // Создание масива данных програмируемых периодов int P_Numb = 0; // Создание переменной определения пункта масива програмируемых периодов (общего назначения) int R_Numb = 0; // Создание переменной определения пункта масива програмируемых периодов (отрисовка меню) long NeoTime = 0; // Создание переменной хранения значения unix времени конца выполнения программы boolean rasTempBol = 0; // Создание переменной флага необходимости расчета времени long unixt; // Создание переменной хранения текущего unix времени long Sec; // Создание переменной временивыполнения программы int cur = 1; // Создание переменной указателя элемента моссива (отрисовка меню) boolean StartTime = 0; // Создание переменной начала работы по заданному переоду (Р1 - Р5) boolean Rabota = 1; // Создание переменной дополнительная переменная управления переключением периодов (Р1 - Р5) boolean Alarm = 0; // Создание переменной значения сигнализации boolean chgTemp = 0; // Создание переменной флага изменения значения температуры boolean flag = 0; // Создание переменной флага управления пьезо излучателем void setup() { rtc.begin(); lcd.init(); lcd.backlight(); pinMode(TEN1, OUTPUT); pinMode(TEN2, OUTPUT); pinMode(BUZZ, OUTPUT); pinMode(BUTON_MENU, INPUT_PULLUP); // DO_EVERY(250,{ }); if ( !ds.search(addr)) { ds.reset_search(); delay(250); return; } if (Setpoint > 90){Setpoint = 20;} // на случай если значение в памяти будет отсутствовать или быть неккоректным if (T_Upr > 3 || T_Upr < 0){T_Upr = 0;} // на случай если значение в памяти будет отсутствовать или быть неккоректным myPID.SetMode(AUTOMATIC); lcd.clear(); tone(BUZZ, 3000, 500); // сигнал готовности устройства } void loop() { getTemp(); Input = celsius; // глупо... но пусть будет. now = rtc.now(); Pause(); podderzhTemp(); Menu(); /* double gap = abs(Setpoint-Input); if (gap <= 5){myPID.SetTunings(consKp, consKi, consKd);} else {myPID.SetTunings(tochKp, tochKi, tochKd);} */ myPID.Compute(); if (T_Upr == 0){analogWrite(TEN1, 0);analogWrite(TEN2, 0);} if (T_Upr == 1){analogWrite(TEN1, 255);analogWrite(TEN2, 255);} if (T_Upr == 2){analogWrite(TEN1, Output);analogWrite(TEN2, Output);} if (T_Upr == 3){ Setpoint = Pat[P_Numb][P_Temp]; Alarm = Pat[P_Numb][P_Alar]; int tempIN = abs(Input); int SetpIN = abs(Setpoint); if (SetpIN == tempIN) {StartTime = 1;} analogWrite(TEN1, Output);analogWrite(TEN2, Output); } _HR = now.hour(); _MI = now.minute(); _SE = now.second(); Moschnost = map(Output, 0, 255, 0, 100); if (Vhod_v_menu == 0) { lcd.setCursor(0, 0);lcd.print("T.ustC "); lcd.print(Setpoint); if (T_Upr == 3) {lcd.print(" PREF");} if (T_Upr == 2) {lcd.print(" AUTO");} if (T_Upr < 2) {lcd.print(" MANUAL");} lcd.setCursor(0, 1); lcd.print("T.zidC "); lcd.print(Input); if (T_Upr == 3) { lcd.setCursor(15, 1); lcd.print("PAT "); lcd.print(P_Numb + 1); }else{lcd.setCursor(13, 1);lcd.print("PAT OFF");} lcd.setCursor(0, 2);lcd.print("P.ten% "); if (Moschnost <= 100) {lcd.setCursor(7, 2);lcd.print(Moschnost); lcd.print(" ");} if (Moschnost < 10) {lcd.setCursor(7, 2);lcd.print(Moschnost); lcd.print(" ");} if (Output <= 255) {lcd.setCursor(14, 2);lcd.print(Output); lcd.print("");} if (Output < 100) {lcd.setCursor(14, 2);lcd.print(Output); lcd.print(" ");} if (Output < 10) {lcd.setCursor(14, 2);lcd.print(Output); lcd.print(" ");} lcd.setCursor(0, 3); if (T_Upr != 3) {lcd.print("Time: ");} if (_HR < 10) {lcd.print("0");lcd.print(_HR);}else{lcd.print(_HR);}lcd.print(":"); if (_MI < 10) {lcd.print("0");lcd.print(_MI);}else{lcd.print(_MI);}lcd.print(":"); if (_SE < 10) {lcd.print("0");lcd.print(_SE);}else{lcd.print(_SE);} if (T_Upr == 3) { lcd.setCursor(10, 3);lcd.print("Count:"); if (StartTime != 0) { if (Rabota == 1){ if (Sec < 10){ lcd.setCursor(16, 3); lcd.print(" ");lcd.print(Sec);} if (Sec < 100 && Sec > 10){ lcd.setCursor(16, 3); lcd.print(" "); lcd.print(Sec);} if (Sec < 1000 && Sec > 100){ lcd.setCursor(16, 3); lcd.print(" "); lcd.print(Sec);} if (Sec < 10000 && Sec > 1000){lcd.setCursor(16, 3); lcd.print(Sec);} } }else{ if (Rabota == 1){ long Pr = Pat[P_Numb][P_Time] * 60; if (Pr < 10){ lcd.setCursor(16, 3); lcd.print(" ");lcd.print(Pr);} if (Pr < 100 && Pr > 10){ lcd.setCursor(16, 3); lcd.print(" "); lcd.print(Pr);} if (Pr < 1000 && Pr > 100){ lcd.setCursor(16, 3); lcd.print(" "); lcd.print(Pr);} if (Pr < 10000 && Pr > 1000){lcd.setCursor(16, 3); lcd.print(Pr);} } } }else{lcd.setCursor(15, 3);lcd.print(" ");} } } void Pause(){ getPressedButton(); if (analogRead(0) > 1000) {Rabota = 1;Alarm = 0;} // Reset Pause if (Rabota == 0) { DO_EVERY(1000,{ if (flag == 0) {lcd.setCursor(13, 1); lcd.print("*"); tone(BUZZ, 1000); flag = 1;} else {lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ); flag = 0;} }); }else{if (T_Upr == 3){lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ); flag = 0;}} } void podderzhTemp(){ if (StartTime == 1){ rasTemp(); unixt = now.unixtime(); Sec = NeoTime - unixt; if (Rabota = 1){ if (Sec <= 0){ if (Alarm == 1) { Rabota = 0; }else{ if (P_Numb <= 4){ rasTempBol = 0; StartTime = 0; P_Numb++; if (Pat[P_Numb][P_Time] == 0){StopRab();} }else{StopRab();} } } } } } void StopRab(){ Setpoint = 10; T_Upr = 0; P_Numb = 0; StartTime = 0; Rabota = 1; Alarm = 0; U_D = 5; L_R = 0; lcd.setCursor(15, 1);lcd.print(" STOP "); lcd.setCursor(13, 1);lcd.print("PAT OFF"); tone(BUZZ, 400, 100); // сигнал завершения работы всех программ tone(BUZZ, 500, 200); // сигнал завершения работы всех программ tone(BUZZ, 700, 300); // сигнал завершения работы всех программ tone(BUZZ, 1000, 200); // сигнал завершения работы всех программ tone(BUZZ, 6000, 100); // сигнал завершения работы всех программ } void rasTemp(){ if (rasTempBol == 0){NeoTime = (Pat[P_Numb][P_Time] * 60) + now.unixtime();rasTempBol = 1;} } void Menu() { getPressedButton(); if (Vhod_v_menu == 0) { // выключатель освещения :) if (analogRead(1) < 100) {lcd.backlight();} if (analogRead(1) > 1000) {lcd.noBacklight();} } if (digitalRead(BUTON_MENU) == 0) { U_D = 5; L_R = 0; lcd.backlight(); if (Vhod_v_menu == 0) { Vhod_v_menu = 1; ClearNol(); lcd.print("0. Setup"); lcd.setCursor(0, 1);lcd.print(" left or right"); Dreft0 = 1; delay(100); } else { lcd.clear(); lcd.setCursor(8, 1);lcd.print("EXIT"); chg_Time = 0; L_R = 0; Vhod_v_menu = 0; if (EEPROM.read(110) != Setpoint) { // Сохранение значения температуры if (T_Upr != 3 && chgTemp == 1){ EEPROM.write(110, Setpoint); lcd.setCursor(8, 1); lcd.print("SAVE"); } } if (EEPROM.read(111) != T_Upr) { // Сохранение значения режима работы if (T_Upr != 3){ EEPROM.write(111, T_Upr); lcd.setCursor(8, 1); lcd.print("SAVE"); } } delay(1000); lcd.clear(); } } //---------------------------------------------------------------------------------- if (Vhod_v_menu == 1) { if (L_R == 0) { if (Dreft0 == 0) { ClearNol(); lcd.print("0. Setup"); lcd.setCursor(0, 1);lcd.print(" left or right"); Dreft0 = 1;}} else {Dreft0 = 0;} //------------- if (L_R == 1) { if (Dreft1 == 0) { chgTemp = 0; ClearNol(); lcd.print("1. Setup Temp"); lcd.setCursor(0, 2);lcd.print(Setpoint); Dreft1 = 1;}} else {Dreft1 = 0;} //------------- if (L_R == 2) { if (Dreft2 == 0) { ClearNol(); lcd.print("2. Setup Rele ON-OFF"); lcd.setCursor(0, 2); if (T_Upr == 0) {lcd.print("1-4 OFF ");} if (T_Upr == 1) {lcd.print("2-4 ON ");} if (T_Upr == 2) {lcd.print("3-4 AUTO ");} if (T_Upr == 3) {lcd.print("4-4 PREF ");} Dreft2 = 1;}} else {Dreft2 = 0;} //------------- if (L_R == 3) { if (Dreft3 == 0) { A_H = now.hour(); ClearNol(); lcd.print("3. Setup Time hour"); lcd.setCursor(0, 1); now = rtc.now(); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second()); lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(" hour"); Dreft3 = 1;}} else {Dreft3 = 0;} //------------- if (L_R == 4) { if (Dreft4 == 0) { A_M = now.minute(); ClearNol(); lcd.print("4. Setup Time minute"); lcd.setCursor(0, 1); now = rtc.now(); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second()); lcd.setCursor(0, 2); lcd.print(A_M); lcd.print(" minute"); Dreft4 = 1;}} else {Dreft4 = 0;} //------------- if (L_R == 5) { if (Dreft5 == 0) { ClearNol(); lcd.print("5. Setup SET Time"); lcd.setCursor(0, 1); now = rtc.now(); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second()); lcd.print(" - OLD"); lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(":"); lcd.print(A_M); lcd.print(":00"); lcd.print(" - NEW"); lcd.setCursor(0, 3); if (chg_Time == 1){lcd.print("Press UP to SET");}else{lcd.print("Time not changed");} Dreft5 = 1;}} else {Dreft5 = 0;} //------------- if (L_R == 6 || L_R == 11 || L_R == 16) { if (Dreft_MAS_0 == 0) { if (L_R == 6) {Print_Pat_Screan_Temp();} if (L_R == 11) {Print_Pat_Screan_Time();} if (L_R == 16) {Print_Pat_Screan_Alarm();} lcd.setCursor(1, 3);lcd.print("^^"); Dreft_MAS_0 = 1;}} else {Dreft_MAS_0 = 0;} if (L_R == 7 || L_R == 12 || L_R == 17) { if (Dreft_MAS_1 == 0) { if (L_R == 7) {Print_Pat_Screan_Temp();} if (L_R == 12) {Print_Pat_Screan_Time();} if (L_R == 17) {Print_Pat_Screan_Alarm();} lcd.setCursor(5, 3);lcd.print("^^"); Dreft_MAS_1 = 1;}} else {Dreft_MAS_1 = 0;} if (L_R == 8 || L_R == 13 || L_R == 18) { if (Dreft_MAS_2 == 0) { if (L_R == 8) {Print_Pat_Screan_Temp();} if (L_R == 13) {Print_Pat_Screan_Time();} if (L_R == 18) {Print_Pat_Screan_Alarm();} lcd.setCursor(9, 3);lcd.print("^^"); Dreft_MAS_2 = 1;}} else {Dreft_MAS_2 = 0;} if (L_R == 9 || L_R == 14 || L_R == 19) { if (Dreft_MAS_3 == 0) { if (L_R == 9) {Print_Pat_Screan_Temp();} if (L_R == 14) {Print_Pat_Screan_Time();} if (L_R == 19) {Print_Pat_Screan_Alarm();} lcd.setCursor(13, 3);lcd.print("^^"); Dreft_MAS_3 = 1;}} else {Dreft_MAS_3 = 0;} if (L_R == 10 || L_R == 15 || L_R == 20) { if (Dreft_MAS_4 == 0) { if (L_R == 10) {Print_Pat_Screan_Temp();} if (L_R == 15) {Print_Pat_Screan_Time();} if (L_R == 20) {Print_Pat_Screan_Alarm();} lcd.setCursor(17, 3);lcd.print("^^"); Dreft_MAS_4 = 1;}} else {Dreft_MAS_4 = 0;} //################################################################## if (U_D > 5) { //------------- if (L_R == 1) { Setpoint = Setpoint + 0.5; chgTemp = 1; lcd.setCursor(0, 2); lcd.print(Setpoint); U_D = 5; } //------------- if (L_R == 2) { if (T_Upr < 4) {T_Upr = T_Upr + 1;} lcd.setCursor(0, 2); if (T_Upr == 0) {lcd.print("1-4 OFF ");} if (T_Upr == 1) {lcd.print("2-4 ON ");} if (T_Upr == 2) {lcd.print("3-4 AUTO ");} if (T_Upr == 3) {lcd.print("4-4 PREF ");} U_D = 5; } //------------- if (L_R == 3) { A_H = A_H + 1; chg_Time = 1; if (A_H > 23) {A_H = 23;} if (A_H < 0) {A_H = 0;} lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(" hour"); U_D = 5; } //------------- if (L_R == 4) { A_M = A_M + 1; chg_Time = 1; if (A_M > 59) {A_M = 59;} if (A_M < 0) {A_M = 0;} lcd.setCursor(0, 2); lcd.print(A_M); lcd.print(" minute"); U_D = 5; } //------------- if (L_R == 5) { if (chg_Time == 1){ rtc.adjust(DateTime(2015, 2, 27, A_H, A_M, 0)); chg_Time = 0; lcd.setCursor(0, 0); lcd.clear(); lcd.print("6. Setup SET Time"); lcd.setCursor(0, 1); now = rtc.now(); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second()); lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(":"); lcd.print(A_M); lcd.print(":00"); lcd.setCursor(0, 3); lcd.print("Save - ok"); } U_D = 5; } //------------- if (L_R == 6 || L_R == 11 || L_R == 16) { if (L_R == 6) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 11) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 16) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(1, 3);lcd.print("^^"); U_D = 5; } if (L_R == 7 || L_R == 12 || L_R == 17) { if (L_R == 7) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 12) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 17) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(5, 3);lcd.print("^^"); U_D = 5; } if (L_R == 8 || L_R == 13 || L_R == 18) { if (L_R == 8) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 13) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 18) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(9, 3);lcd.print("^^"); U_D = 5; } if (L_R == 9 || L_R == 14 || L_R == 19) { if (L_R == 9) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 14) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 19) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(13, 3);lcd.print("^^"); U_D = 5; } if (L_R == 10 || L_R == 15 || L_R == 20) { if (L_R == 10) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 15) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 20) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(17, 3);lcd.print("^^"); U_D = 5; } //---------------------------------------------------------------------------------- } else if (U_D < 5) { if (L_R == 1) { Setpoint = Setpoint - 0.5; chgTemp = 1; lcd.setCursor(0, 2); lcd.print(Setpoint); U_D = 5; } //------------- if (L_R == 2) { if (T_Upr > 0) {T_Upr = T_Upr - 1;} lcd.setCursor(0, 2); if (T_Upr == 0) {lcd.print("1-4 OFF ");} if (T_Upr == 1) {lcd.print("2-4 ON ");} if (T_Upr == 2) {lcd.print("3-4 AUTO ");} if (T_Upr == 3) {lcd.print("4-4 PREF ");} U_D = 5; } //------------- if (L_R == 3) { A_H = A_H - 1; chg_Time = 1; if (A_H > 23) {A_H = 23;} if (A_H < 0) {A_H = 0;} lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(" hour"); U_D = 5; } //------------- if (L_R == 4) { A_M = A_M - 1; chg_Time = 1; if (A_M > 59) {A_M = 59;} if (A_M < 0) {A_M = 0;} lcd.setCursor(0, 2); lcd.print(A_M); lcd.print(" minute"); U_D = 5; } //------------- if (L_R == 5) { if (chg_Time == 1){ A_H = now.hour(); A_M = now.minute(); chg_Time = 0; lcd.setCursor(0, 0); lcd.clear(); lcd.print("6. Setup SET Time"); lcd.setCursor(0, 1); now = rtc.now(); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second()); lcd.setCursor(0, 2); lcd.print(A_H); lcd.print(":"); lcd.print(A_M); lcd.print(":00"); lcd.setCursor(0, 3); lcd.print("Changed - Cancel"); } U_D = 5; } //------------- if (L_R == 6 || L_R == 11 || L_R == 16) { if (L_R == 6) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 11) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 16) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(1, 3);lcd.print("^^"); U_D = 5; } if (L_R == 7 || L_R == 12 || L_R == 17) { if (L_R == 7) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 12) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 17) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(5, 3);lcd.print("^^"); U_D = 5; } if (L_R == 8 || L_R == 13 || L_R == 18) { if (L_R == 8) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 13) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 18) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(9, 3);lcd.print("^^"); U_D = 5; } if (L_R == 9 || L_R == 14 || L_R == 19) { if (L_R == 9) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 14) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 19) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(13, 3);lcd.print("^^"); U_D = 5; } if (L_R == 10 || L_R == 15 || L_R == 20) { if (L_R == 10) { CHG_P_Temp(); Print_Pat_Screan_Temp(); } if (L_R == 15) { CHG_P_Time(); Print_Pat_Screan_Time(); } if (L_R == 20) { CHG_P_Alar(); Print_Pat_Screan_Alarm(); } lcd.setCursor(17, 3);lcd.print("^^"); U_D = 5; } //------------- } } } void CHG_P_Temp(){ if (U_D > 5) { if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] < 100) {Pat[0][P_Temp] += 1;}} if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] < 100) {Pat[1][P_Temp] += 1;}} if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] < 100) {Pat[2][P_Temp] += 1;}} if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] < 100) {Pat[3][P_Temp] += 1;}} if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] < 100) {Pat[4][P_Temp] += 1;}}} else{ if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] > 0) {Pat[0][P_Temp] -= 1;}} if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] > 0) {Pat[1][P_Temp] -= 1;}} if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] > 0) {Pat[2][P_Temp] -= 1;}} if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] > 0) {Pat[3][P_Temp] -= 1;}} if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] > 0) {Pat[4][P_Temp] -= 1;}}} } void CHG_P_Time(){ if (U_D > 5) { if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] < 32000) {Pat[0][P_Time] += 1;}} if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] < 32000) {Pat[1][P_Time] += 1;}} if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] < 32000) {Pat[2][P_Time] += 1;}} if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] < 32000) {Pat[3][P_Time] += 1;}} if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] < 32000) {Pat[4][P_Time] += 1;}}} else{ if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] > 0) {Pat[0][P_Time] -= 1;}} if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] > 0) {Pat[1][P_Time] -= 1;}} if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] > 0) {Pat[2][P_Time] -= 1;}} if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] > 0) {Pat[3][P_Time] -= 1;}} if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] > 0) {Pat[4][P_Time] -= 1;}}} } void CHG_P_Alar(){ if (U_D > 5) { if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 1;} if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 1;} if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 1;} if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 1;} if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 1;}} else{ if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 0;} if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 0;} if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 0;} if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 0;} if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 0;}} } void Print_Pat_Screan_Temp(){ ClearNol(); cur = 1; lcd.print("6. Setup PAT Temp"); for (int i = 1; i <= 5; i++) { if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);} cur += 4; } cur = 1; for (int i = 0; i < 5; i++) { R_Numb = i; if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);} cur += 4; lcd.print(Pat[R_Numb][P_Temp]); } } void Print_Pat_Screan_Time(){ ClearNol(); cur = 1; lcd.print("7. Setup PAT Time"); for (int i = 1; i <= 5; i++) { if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);} cur += 4; } cur = 1; for (int i = 0; i < 5; i++) { R_Numb = i; if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);} cur += 4; if (Pat[R_Numb][P_Time] < 10){lcd.print(Pat[R_Numb][P_Time]);lcd.print(" ");}else{lcd.print(Pat[R_Numb][P_Time]);} } } void Print_Pat_Screan_Alarm(){ ClearNol(); cur = 1; lcd.print("8. Setup PAT Alarm"); for (int i = 1; i <= 5; i++) { if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);} cur += 4; } cur = 1; for (int i = 0; i < 5; i++) { R_Numb = i; if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);} cur += 4; if (Pat[R_Numb][P_Alar] == 0){lcd.print("OF");}else{lcd.print("ON");} } } void ClearNol(){ lcd.clear(); lcd.setCursor(0, 0); } void getPressedButton() { DO_EVERY(noTrem,{if (analogRead(0) < 100) {L_R--;}}); DO_EVERY(noTrem,{if (analogRead(0) > 1000){L_R++;}}); DO_EVERY(noTrem,{if (analogRead(1) < 100) {U_D++;}}); DO_EVERY(noTrem,{if (analogRead(1) > 1000){U_D--;}}); if (L_R < 0){L_R = PunkMenu;} if (L_R > PunkMenu){L_R = 0;} } void getTemp() { ds.reset(); ds.select(addr); ds.write(0x44, 1); ds.reset(); ds.select(addr); ds.write(0xBE); for ( int i = 0; i < 9; i++) { data[i] = ds.read(); } int16_t raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; if (data[7] == 0x10) { raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); if (cfg == 0x00) raw = raw & ~7; else if (cfg == 0x20) raw = raw & ~3; else if (cfg == 0x40) raw = raw & ~1; } celsius = (float)raw / 16.0; return; }
Следующяя более оптимизированная версия прошивки
переработанно меню, добавленно управление рециркуляционным насосом, добавленны дополнительные 5 программ (P1 - P9), добавленна возможность изменять интегральный коэфициент ПИД регулятора.
Система протестированна на чайнике, по результатам тестов был удален НЧ фильтр между SSR и UNL.
Добрый день. Проконсультируйте будет ваша прошивка работать на Мега 2560 и экран
TFT 320QUT и что для этого надо.
Прошивка работать будет, но для использования указанной вами модели экрана необходимо использоваь другую библиотеку (я использую LiquidCrystal_I2C.h вам она не подайдет), и соответственно поменять больше половины кода отрисовывания менюшек. Одним словом вам будет проще написать с вой код.
Предварительный монтаж. В дальнейшем всё будет собранно на одной плате.
Также снгодня будут купленны разьемы на корпус для подключения тена, насоса, 220 вольт, и датчика температуры.
Датчик температуры будет запаян Оловянно-медным припоем в отверстие 6,5 высверленное в болте. Пустота между датчиком и болтом будет заполнена теплопроводящим клей-герметиком.
Купленны разьемы для подключения внешних устройств (тен, насос, датчик ds18b20) кнопка ресет, и клавишный общий выключатель.
Да, было бы не плохо научиться писать код.
Пока нравиться! Смущает только здоровый радиатор у пластика, но это все суеверия. Ждем фото общего готовго проджекта!
Почти готовый коньтроллер.
Почти готовый БАК.
Тестовый запуск всей конструкции.
Работа Бака
https://youtu.be/vtRUYCUEHXE
Работа контроллера
https://youtu.be/e4MfLSn30cs
Прект входит в заключительную стадию отладки и тестирования.
Испытание чиллира.
Итак первая партия домашнего пива прошла 50% отстаивания в бутылках (на следующие выходные буду пробовать)
Ссылки с рецептами.
http://rossibeer.ru/recepty-piva
http://www.castlemalting.com/CastleMaltingBeerRecipes.asp?Language=Russian
Котел 5091 http://www.klenmarket.ru/shop/inventory/kitchen-equipment/boilers/boiler-50-l-professional-luxstahl-101307/
Внутренний бак 6950 http://hbpro.ru/showcase/zheleznyj-kit-30l.7/
Насос 4750 http://вортекс-центр.рф/shop/n-gvs/bw152r12ot.html
ТЭН 2100 http://hbpro.ru/showcase/tehn-dlja-klona-20l.18/
Кран шаровой ½ дюйма, ножки для бака, фитинг для насоса (врезка в бак ½ дюйма, переходник американка вн-внеш резьба ½ дюйма, уголок внутр-внеш резьба ½ дюйма), прокладки ~ 1500-2000
Болт+гайки из нержи для датчика ~ 200 рублей
Чиллер 3500 http://hbpro.ru/showcase/chiller-dlja-klona-20l.24/
Из инструмента пришлось купить тиски, сверла кобальтовые (по нерже) разного диаметра, припой безсвинцовый, термоклей. На это еще где-то 3500 вышло.
Итого средняя стоимость проекта 31650 в среднем.
Пока нравиться! Смущает только здоровый радиатор у пластика, но это все суеверия. Ждем фото общего готовго проджекта!
Данный радиатор больше для перестраховки, в проццесе работы он немного тепленький. в следующей версии прошивки, я буду использовать немного другой алгоритм управления ТЕНом, поэтому от такого радиатора можно будет отказатся.
Процесс варки пивного сусла.
https://youtu.be/vjlG784vqak
Первая партия готова!
И как это на вкус?
И как это на вкус?
Ну мой комментарий вкуса будет предвзят априори, Но друзья которые пробовали говорят что не уступает магазинному разливному а то и превосходит его.
Вкус мягкий, насыщенный, в меру алкогольный (приблизительно 5,5 - 6 градусов).
Хороший вкус у пива -- это когда его хочется выпить много, за исключением случаев, когда хочется выпить все равно чего. :)
Круто! Реально круто! позже детальней рассмотрю, логика особенно интересна для пивоварения. А насчет сэма, пока меня трудно переубедить, хочу свою теорию реализовать в практику.
Переосмыслев всю логику и ошибки предыдущего контроллера начал разрабатывать новую версию с более одекватным меню, улучшенной производительностью и логикой, а также энкодером и SD кртой для загрузки файлов с рецептами.
готовый на 70% код (профильный режим в проццесе написания)
для простоты проект разбит на 8 файлов (но это не обязательно можно весь код сохранить в один файл)
Работа с меню осуществляется с помощью энкодера и его кнопки. Находясь на домашнем экране поворот энкодера переключает отдин из 3х пунктов меню (см. скриншот выше). При двойном клике (два key_up до 200ms) активируется соответствующий выбранный пунк. Длительное (2000ms) нажатие переводит в меню настроек (скриншоты в проццесе), далее вращение энкодера переключает по пунктам меню. короткий клик на выбраном пункте меню вводит в состояние настройки параметра, второй короткий клик в меню применяет заданные параметры. Длительный клик находясь в меню запускает процедуру сохранения !только измененых параметров! и выводит на домашний экран.
Отдельное спасибо Клапауций 999 за класс для кнопки, и leshak за <TimeHelpers.h>
Формат ini файла с рецептом
Отдельное спасибо Клапауций 999 за класс для кнопки
ок - рад, что не мне одному показалось это нужным.
Максим! как успехи с новой версией проекта? добил его
Переосмыслев всю логику и ошибки предыдущего контроллера начал разрабатывать новую версию с более одекватным меню, улучшенной производительностью и логикой, а также энкодером и SD кртой для загрузки файлов с рецептами.
Аналогично. Пересмотрев существующие решения и желания пивоваров, начали разработку новой автоматики с touch LCD и WiFi.
Небольшой демо-ролик
За подробностями можно следить тут http://hbpro.ru/threads/wi-fi-kontroller-na-esp8266.233/
Здравствуйте! maxi_10, продолжение будет?)) очень ждем) или, если может где-нибудь на другом ресурсе описывали, можно ссылочку?
Здраствуйте уважаемый maxi_10! Заинтересовал ваш проект. Какова судьба и как с продолжением?
Здравствуйте, я собрал автоматику на LCD 3.2 на Mega кому интерестно https://youtu.be/BvQUjaGohdY
День добрый! не могу найти библиотеку <TimeHelpers.h>.
Привет всем!!! а я с компа варю) https://youtu.be/Ua9WxJoCwpc