#include <EEPROM.h>
#include <LiquidCrystal_I2C.h> // Подключаем библиотеку для работы с LCD дисплеем 2004 по шине I2C
LiquidCrystal_I2C lcd(0x27,20,4); // Адрес LCD = 0x27
// Не забываем включать подсветку ниже
#include <microDS18B20.h>
MicroDS18B20<8> sensor_t1; // Датчик на D8
MicroDS18B20<9> sensor_t2; // Датчик на D9
MicroDS18B20<10> sensor_t3; // Датчик на D10
#include <GyverButton.h>
GButton butt_Up (3); // Назначение пинов кнопкам
GButton butt_Set (4);
GButton butt_Down (2);
#include <GyverFilters.h>
GFilterRA T1_filter; // фильтры "скользящее среднее" для датчиков T1...T3
GFilterRA T2_filter;
GFilterRA T3_filter;
/*#include <SoftwareSerial.h> //UART через любые пины
SoftwareSerial espSerial = SoftwareSerial(6,7); // arduino RX pin=6,TX pin=7 соединяем arduino RX pin с BT module TX pin
// соединяем arduino TX pin с BT module RX pin */
//-------------VirtuinoCM Library and settings --------------
#include "VirtuinoCM.h"
VirtuinoCM virtuino;
#define V_memory_count 16 // the size of V memory. You can change it to a number <=255)
float V[V_memory_count]; // This array is synchronized with Virtuino V memory. You can change the type to int, long etc.
boolean debug = true;
//-------------------------------------------------------------
uint32_t Timer1_LCD,Timer2_Sensors,Timer4_reset_EStop, Timer5, Timer6_sound_ESTOP, Timer7_sound_t2;
uint8_t bar_value = 50; //входное значение для линейного индикатора
uint8_t bar_i = 0; //кол-во позиций для заполнения линейного индикатора
bool flag, flag1, flag2,flag3 = false,bit_1s, bit_500ms;
bool screen_mode; //0-основной эран, 1-экран настроек
bool screen_calibr; //0-основной эран, 1-экран калибровки датчиков
bool screen_stat; //0-основной эран, 1-экран статистики (до отключения питания)
bool control_out = false; //выход на исп. устройство,
bool control_flag = false; //флаг состояния для клапана
bool change_confirm; // разрешение на изменение уставок
bool EStop_bit = false; // 0-блокировка безопасности (или окончания процесса). При подаче питания - всегда в ESTOP.
bool work_mode = false, work_mode_prev = false; // 0-режим "Ожидание", 1-режим "Работа"
bool flag93 = false; // флаг для снижения отбора при Т1>93С
bool decr_mode = false, decr_mode_prev = false; // 0- работа с декрементом, 1-без ( 0-й бит в settings_EEPROM)
bool sound_mode = false, sound_mode_prev = false; // 0-звук включен ( 1-й бит в settings_EEPROM)
byte settings_EEPROM, settings_EEPROM_prev; // байт дискретных битовых настроек (EEPROM)
byte EEPROM_flag; //битовые флаги для записи в EEPROM при изменении значений
float SP_T1_ESTOP=98.0, SP_T1_ESTOP_prev=98.0; //уставка температуры в кубе для останова процесса (EEPROM)
float SP_T3_ESTOP=70.0, SP_T3_ESTOP_prev=70.0; //уставка температуры в дефлегматоре для останова процесса (EEPROM)
float value_SP=78.0, value_SP_prev=78.0; //уставка (EEPROM)
float value_G=0.2, value_G_prev=0.2; //гистерезис (EEPROM)
float T3_offset=0.0, T2_offset=0.0, T1_offset=0.0; //смещение датчиков (EEPROM)
float value_T3, value_T2, value_T1; //отфильтрованные датчики
float value_T3_raw, value_T2_raw, value_T1_raw; //нефильтрованные датчики
byte big_step, big_step_2; //увеличенные шаги при изменении значений
/* float T1_min; //минимальное значение Т1 для статистики (не сохраняется в EEPROM)
float T1_max; //максимальное значение Т1 для статистики (не сохраняется в EEPROM)
float T2_min; //минимальное значение Т2 для статистики (не сохраняется в EEPROM)
float T2_max; //максимальное значение Т2 для статистики (не сохраняется в EEPROM)
float T3_min; //минимальное значение Т3 для статистики (не сохраняется в EEPROM)
float T3_max; //максимальное значение Т3 для статистики (не сохраняется в EEPROM)*/
byte n_line = 0; //номер текущей строки со стрелкой на экране уставок
byte n_line_set = 0; //номер текущей строки для прокрутки меню экрана уставок
int8_t step_count, step_count_2 = 0;
byte decrement=0; //декремент отбора - минус 10% на каждый стоп клапана. Макс 50%
uint16_t Ton;
uint16_t Toff;
//start================ setup ===============
void setup()
{
//start ======== Virtuino setup ==========
if (debug) {Serial.begin(9600); while (!Serial) continue;}
Serial.setTimeout(50);
virtuino.begin(onReceived,onRequested,256); //Start Virtuino. Set the buffer to 256. With this buffer Virtuino can control about 28 pins (1 command = 9bytes) The T(text) commands with 20 characters need 20+6 bytes
//-end ======== Virtuino setup ==========
lcd.init();
lcd.backlight(); // Включаем подсветку LCD дисплея
lcd.clear(); lcd.setCursor(2, 0); lcd.print("Good luck,"); //пишем "Hi!Good luck!"
lcd.setCursor(8, 1); lcd.print("moonshiner!");lcd.setCursor(8, 3); lcd.print("v 1.14 2024");delay(2000);lcd.clear();
pinMode(13, OUTPUT); //выход управления реле
pinMode(12, OUTPUT); //выход блокировки (окончания процесса)
pinMode(2, INPUT_PULLUP); pinMode(3, INPUT_PULLUP); pinMode(4, INPUT_PULLUP); //входы кнопок
byte degree[8] = {B00111,B00101,B00111,B00000,B00000,B00000,B00000,}; // кодируем символ градуса
lcd.createChar(1, degree); // Создаем символ под номером 1 (наш градус)
//-----------------
byte scoba_L[8] = {B11111,B10000,B10000,B10000,B10000,B10000,B11111,}; // кодируем символ левой скобы для индикатора
lcd.createChar(2, scoba_L);
byte scoba_R[8] = {B11111,B00001,B00001,B00001,B00001,B00001,B11111,}; // кодируем символ правой скобы для индикатора
lcd.createChar(3, scoba_R);
byte empty[8] = {B11111,B00000,B00000,B00000,B00000,B00000,B11111,}; // кодируем символ пустой для индикатора
lcd.createChar(4, empty);
byte full[8] = {B11111,B11111,B11111,B11111,B11111,B11111,B11111,}; // кодируем символ полный для индикатора
lcd.createChar(5, full);
butt_Up.setStepTimeout(100); //скорость изменения значения при удержании кнопки "Up"
butt_Down.setStepTimeout(100); //скорость изменения значения при удержании кнопки "Down"
butt_Set.setTimeout(2000); //длительность удержания кнопки "Set" для разрешение изменения настроек
butt_Up.setClickTimeout(100);
butt_Down.setClickTimeout(100);
butt_Up.setDebounce(20); //антидребезг кнопки Up
butt_Set.setDebounce(20); //антидребезг кнопки Set
butt_Down.setDebounce(20); //антидребезг кнопки Down
EEPROM.put(0, 0.0); //эта запись в EEPROM нужна один раз для новой ардуины
EEPROM.put(4, 0.0);
EEPROM.put(8, 0.0);
EEPROM.put(12, 78.0);
EEPROM.put(16, 0.2);
EEPROM.put(20, 99.5);
EEPROM.put(24, 70.0);
EEPROM.put(28, 0);
EEPROM.get(0, T1_offset); //читаем настройки из EEPROM
EEPROM.get(4, T2_offset); // --//--
EEPROM.get(8, T3_offset); // --//--
EEPROM.get(12, value_SP); value_SP_prev = value_SP; // --//--
EEPROM.get(16, value_G); // --//--
EEPROM.get(20, SP_T1_ESTOP); // --//--
EEPROM.get(24, SP_T3_ESTOP); // --//--
EEPROM.get(28, settings_EEPROM); // --//--
decr_mode = bitRead(settings_EEPROM, 0); // выделяем 0-й бит из байта - режим с/без декремента
sound_mode = bitRead(settings_EEPROM, 1); // выделяем 1-й бит из байта - режим звука "ON/OFF"
T1_filter.setCoef(0.1); // установка коэффициента фильтрации (0.0... 1.0). Чем меньше, тем плавнее фильтр
T1_filter.setStep(100); // установка шага фильтрации (мс). Чем меньше, тем резче фильтр
T2_filter.setCoef(0.1);
T2_filter.setStep(100);
T3_filter.setCoef(0.1);
T3_filter.setStep(100);
}
//end================ setup ===============
//============== loop ================
void loop()
{
virtuinoRun(); // Necessary function to communicate with Virtuino. Client handler
//start ======== Virtuino work ==========
if (flag3){
V[1] = value_T1;
V[2] = value_T2;
V[3] = value_T3;
if (value_SP!= value_SP_prev) {V[4] = value_SP; value_SP_prev = value_SP; bitWrite(EEPROM_flag, 0, true);}
if (V[4]!= value_SP_prev) {value_SP = V[4]; value_SP_prev = V[4]; bitWrite(EEPROM_flag, 0, true);}
V[5] = (value_SP + value_G); //V[5] для отображения в скаде. Пересылка в М5 внутри Virtuino для макс цветной шкалы
if (value_G!= value_G_prev) {V[6] = value_G; value_G_prev = value_G; bitWrite(EEPROM_flag, 1, true);}
if (V[6]!= value_G_prev) {value_G = V[6]; value_G_prev = V[6]; bitWrite(EEPROM_flag, 1, true);}
if (SP_T1_ESTOP!= SP_T1_ESTOP_prev) {V[7] = SP_T1_ESTOP; SP_T1_ESTOP_prev = SP_T1_ESTOP; bitWrite(EEPROM_flag, 2, true);}
if (V[7]!= SP_T1_ESTOP_prev) {SP_T1_ESTOP = V[7]; SP_T1_ESTOP_prev = V[7]; bitWrite(EEPROM_flag, 2, true);}
if (SP_T3_ESTOP!= SP_T3_ESTOP_prev) {V[8] = SP_T3_ESTOP; SP_T3_ESTOP_prev = SP_T3_ESTOP; bitWrite(EEPROM_flag, 3, true);}
if (V[8]!= SP_T3_ESTOP_prev) {SP_T3_ESTOP = V[8]; SP_T3_ESTOP_prev = V[8]; bitWrite(EEPROM_flag, 3, true);}
V[9] = float(decrement);
if (work_mode!= work_mode_prev) {V[11] = work_mode; work_mode_prev = work_mode;} // 0- пауза, 1-работа Пересылка в М11 внутри Virtuino для надписи режима работы
if (V[11] != work_mode_prev) {work_mode = V[11]; work_mode_prev = V[11];}
if (decr_mode!= decr_mode_prev) {V[12] = decr_mode; decr_mode_prev = decr_mode; bitWrite(EEPROM_flag, 4, true);} // 0- c декрементом
if (V[12] != decr_mode_prev) {decr_mode= V[12]; decr_mode_prev = V[12]; bitWrite(EEPROM_flag, 4, true);}
if (sound_mode!= sound_mode_prev) {V[13] = sound_mode; sound_mode_prev = sound_mode; bitWrite(EEPROM_flag, 5, true);} // 0- cо звуком
if (V[13] != sound_mode_prev) {sound_mode= V[13]; sound_mode_prev = V[13]; bitWrite(EEPROM_flag, 5, true);}
V[14] = control_out;
V[15] = EStop_bit; // 0- EStop
//end ======== Virtuino work ==========
}
else {
V[4] = value_SP;
V[5] = (value_SP + value_G);
V[6] = value_G;
V[7] = SP_T1_ESTOP;
V[8] = SP_T3_ESTOP;
V[9] = float(decrement);
V[11] = work_mode;
V[12] = decr_mode;
V[13] = sound_mode;
V[14] = control_out;
V[15] = EStop_bit; // 0- EStop
}
vDelay(50);
flag3 = true;
keys();
lcd2004();
sensors_BT();
control();
// statistic();
sound();
eeprom();
}
/*********************** Функции **************************/
void keys () //--------------функция обработка кнопок------------------
{
butt_Up.tick();
butt_Set.tick();
butt_Down.tick();
if (butt_Set.isDouble() && !screen_calibr && !screen_stat) // входим/выходим в экран уставок двойным "Set"
{screen_mode = !screen_mode;
if (screen_mode && flag) flag = false; // фронт LOW-HIGH - при входе в экран уставок сбрасываем флаг
}
if (!screen_mode && !screen_calibr && !screen_stat) // меняем уставку (SP) на основном экране
{
if (butt_Set.isSingle() && EStop_bit) work_mode = !work_mode; //вкл/выкл работы автоматики RUN/WAIT
if (butt_Up.isSingle() && (value_SP<100.0)) value_SP=(value_SP+0.05); // одиночное нажатие Up
if (butt_Down.isSingle() && (value_SP>0.05)) value_SP=(value_SP-0.05); // одиночное нажатие Down
if (butt_Up.isStep() && (value_SP<100.0)) {value_SP=(value_SP+0.05+float(big_step)); step_count ++;} // обработчик удержания Up с шагами
if (butt_Up.isHold() && (step_count > 60) && (value_SP<95.0)) big_step = 1; // увеличиваем автоинкремент
else {big_step = 0;} //
if (!butt_Up.isHold() && !butt_Down.isHold()) step_count=0; //
if (butt_Down.isStep() && (value_SP>0.05)) {value_SP=(value_SP-0.05-float(big_step_2)); step_count_2 ++;} // обработчик удержания Down с шагами
if (butt_Down.isHold() && (step_count_2 > 60) && (value_SP>5.0)) big_step_2 = 1; // увеличиваем автодекремент
else {big_step_2 = 0;} //
if (!butt_Down.isHold() && !butt_Up.isHold()) step_count_2=0;
}
if (screen_mode && change_confirm) //меняем уставки на экране настроек
{
switch (n_line)
{
case 0: // меняем уставку (SP)
if (butt_Up.isSingle() && (value_SP<100.0)) value_SP=(value_SP+0.05); // одиночное нажатие Up
if (butt_Down.isSingle() && (value_SP>0.05)) value_SP=(value_SP-0.05); // одиночное нажатие Down
if (butt_Up.isStep() && (value_SP<100.0)) {value_SP=(value_SP+0.05+float(big_step)); step_count ++;} // обработчик удержания Up с шагами
if (butt_Up.isHold() && (step_count > 60) && (value_SP<95.0)) big_step = 1; // увеличиваем автоинкремент
else {big_step = 0;} //
if (!butt_Up.isHold() && !butt_Down.isHold()) step_count=0; //
if (butt_Down.isStep() && (value_SP>0.05)) {value_SP=(value_SP-0.05-float(big_step_2)); step_count_2 ++;} // обработчик удержания Down с шагами
if (butt_Down.isHold() && (step_count_2 > 60) && (value_SP>5.0)) big_step_2 = 1; // увеличиваем автодекремент
else {big_step_2 = 0;} //
if (!butt_Down.isHold() && !butt_Up.isHold()) step_count_2=0;
break;
case 1: // меняем гистерезис (G)
if (butt_Up.isSingle() && (value_G<9.95) && (value_G<(value_SP-0.05))) value_G=(value_G+0.05); // одиночное нажатие Up
if (butt_Down.isSingle() && (value_G>0.06)) value_G=(value_G-0.05); // одиночное нажатие Down
if (butt_Up.isStep() && (value_G<9.95) && (value_G<(value_SP-0.05))) value_G=(value_G+0.05); // обработчик удержания Up с шагами
if (butt_Down.isStep() && (value_G>0.06)) value_G=(value_G-0.05); // обработчик удержания Down с шагами
break;
case 2: // меняем уставку окончания процесса по Т3
if (butt_Up.isSingle() && (SP_T3_ESTOP<80.0)) SP_T3_ESTOP=(SP_T3_ESTOP+0.1); // одиночное нажатие Up
if (butt_Down.isSingle() && (SP_T3_ESTOP>30.0)) SP_T3_ESTOP=(SP_T3_ESTOP-0.1); // одиночное нажатие Down
if (butt_Up.isStep() && (SP_T3_ESTOP<80.0)) {SP_T3_ESTOP=(SP_T3_ESTOP+0.1+float(big_step)); step_count ++;} // обработчик удержания Up с шагами
if (butt_Up.isHold() && (step_count > 40) && (SP_T3_ESTOP<75.0)) big_step = 1; // увеличиваем автоинкремент
else {big_step = 0;} //
if (!butt_Up.isHold() && !butt_Down.isHold()) step_count=0; //
if (butt_Down.isStep() && (SP_T3_ESTOP>30.0)) {SP_T3_ESTOP=(SP_T3_ESTOP-0.1-float(big_step_2)); step_count_2 ++;} // обработчик удержания Down с шагами
if (butt_Down.isHold() && (step_count_2 > 40) && (SP_T3_ESTOP>35.0)) big_step_2 = 1; // увеличиваем автодекремент
else {big_step_2 = 0;} //
if (!butt_Down.isHold() && !butt_Up.isHold()) step_count_2=0;
break;
case 3: // меняем уставку окончания процесса по Т1
if (butt_Up.isSingle() && (SP_T1_ESTOP<100.0)) SP_T1_ESTOP=(SP_T1_ESTOP+0.05); // одиночное нажатие Up
if (butt_Down.isSingle() && (SP_T1_ESTOP>70.0)) SP_T1_ESTOP=(SP_T1_ESTOP-0.05); // одиночное нажатие Down
if (butt_Up.isStep() && (SP_T1_ESTOP<100.0)) {SP_T1_ESTOP=(SP_T1_ESTOP+0.05+float(big_step)); step_count ++;} // обработчик удержания Up с шагами
if (butt_Up.isHold() && (step_count > 40) && (SP_T1_ESTOP<95.0)) big_step = 1; // увеличиваем автоинкремент
else {big_step = 0;} //
if (!butt_Up.isHold() && !butt_Down.isHold()) step_count=0; //
if (butt_Down.isStep() && (SP_T1_ESTOP>70.0)) {SP_T1_ESTOP=(SP_T1_ESTOP-0.05-float(big_step_2)); step_count_2 ++;} // обработчик удержания Down с шагами
if (butt_Down.isHold() && (step_count_2 > 40) && (SP_T1_ESTOP>75.0)) big_step_2 = 1; // увеличиваем автодекремент
else {big_step_2 = 0;} //
if (!butt_Down.isHold() && !butt_Up.isHold()) step_count_2=0;
break;
case 4: // меняем режим с/без декремента
if (butt_Up.isSingle() or butt_Down.isSingle()) {decr_mode = ! decr_mode; bitWrite(settings_EEPROM, 0, decr_mode);}
break;
case 5: // меняем режим работы звука
if (butt_Up.isSingle() or butt_Down.isSingle()) {sound_mode = ! sound_mode; bitWrite(settings_EEPROM, 1, sound_mode);}
break;
}
}
//-------------------
if ((butt_Up.isSingle() && butt_Down.isSingle() && !butt_Set.isSingle()) && !screen_mode && !screen_stat) // входим/выходим в экран калибровки датчиков кнопками "Up" + "Down"
{ screen_calibr = !screen_calibr; }
if (screen_calibr && change_confirm) //меняем смещения дптчиков на экране SENSORS
{
switch (n_line)
{
case 0: // меняем смещение датчика 3
if (butt_Up.isSingle() && (T3_offset < 9.99)) T3_offset=(T3_offset+0.01); // одиночное нажатие Up
if (butt_Down.isSingle() && (T3_offset > -9.99)) T3_offset=(T3_offset-0.01); // одиночное нажатие Down
if (butt_Up.isStep() && (T3_offset < 9.99)) T3_offset=(T3_offset+0.01); // обработчик удержания Up с шагами
if (butt_Down.isStep() && (T3_offset > -9.99)) T3_offset=(T3_offset-0.01); // обработчик удержания Down с шагами
break;
case 1: // меняем смещение датчика 2
if (butt_Up.isSingle() && (T2_offset < 9.99)) T2_offset=(T2_offset+0.01); // одиночное нажатие Up
if (butt_Down.isSingle() && (T2_offset > -9.99)) T2_offset=(T2_offset-0.01); // одиночное нажатие Down
if (butt_Up.isStep() && (T2_offset < 9.99)) T2_offset=(T2_offset+0.01); // обработчик удержания Up с шагами
if (butt_Down.isStep() && (T2_offset > -9.99)) T2_offset=(T2_offset-0.01); // обработчик удержания Down с шагами
break;
case 2: // меняем смещение датчика 1
if (butt_Up.isSingle() && (T1_offset < 9.99)) T1_offset=(T1_offset+0.01); // одиночное нажатие Up
if (butt_Down.isSingle() && (T1_offset > -9.99)) T1_offset=(T1_offset-0.01); // одиночное нажатие Down
if (butt_Up.isStep() && (T1_offset < 9.99)) T1_offset=(T1_offset+0.01); // обработчик удержания Up с шагами
if (butt_Down.isStep() && (T1_offset > -9.99)) T1_offset=(T1_offset-0.01); // обработчик удержания Down с шагами
break;
}
}
if (!screen_mode && !screen_calibr && butt_Set.isTriple())
{ screen_stat = !screen_stat; screen_mode = false; // вход/выход в экран статистики
screen_calibr = false;
}
if ((screen_mode or screen_calibr) && !butt_Set.isHold() )
{
if ((screen_mode or screen_calibr) && !change_confirm && butt_Up.isSingle() && n_line >= 1) n_line = --n_line; //двигаем стрелку вверх
if (screen_mode && !change_confirm && butt_Down.isSingle() && n_line <= 4) n_line = ++n_line; //двигаем стрелку вниз макс на 6-ю строку
if (screen_calibr && !change_confirm && butt_Down.isSingle() && n_line <= 1) n_line = ++n_line; //двигаем стрелку вниз макс на 3-ю строку
if ((screen_mode or screen_calibr) && butt_Set.isSingle()) change_confirm = !change_confirm; //нажатием Set 1 раз разрешаем изменения в текущей строке
}
//----------------
if (!(screen_mode or screen_calibr) && !flag) // фронт HIGH-LOW - при выходе из экранов настроек отключаем режим изменения значений
{
flag = true;change_confirm = 0;
}
if (change_confirm && flag1) flag1 = false; // при разрешении изменений сбрасываем флаг для записи в EEPROM
}
void lcd2004() //-------------функция вывод на LCD------------------
{
if (millis() - Timer1_LCD >= 500) // таймер на 1000 мс Вывод на дисплей
{
Timer1_LCD = millis(); // сброс таймера
bit_500ms = !bit_500ms;
String decr_mode_title; //экран настроек - надпись режима "YES/NO" для декремента
String sound_title; //экран настроек - надпись для режима звука "YES/NO"
if (!decr_mode) {decr_mode_title = "YES ";} // меняем надпись "YES/NO" для декремента
if (decr_mode) {decr_mode_title = "NO ";}
if (!sound_mode) {sound_title = "YES";} // меняем надпись "YES/NO" для настройки звука
if (sound_mode) {sound_title = "NO ";}
// формирование строк для экрана настроек
String line_SP; //экран настроек - строка "SP="
line_SP += F("SET: SP=");
if (!bit_500ms && change_confirm && (n_line == 0)) //мигаем параметром при изменении
{line_SP += F(" ");}
if (bit_500ms)
{line_SP += value_SP;
line_SP += F(" ");}
String line_G; //экран настроек - строка "G="
line_G += F(" G=");
if (!bit_500ms && change_confirm && (n_line == 1)) //мигаем параметром при изменении
{line_G += F(" ");}
if (bit_500ms)
{line_G += value_G;
line_G += F(" ");}
String stop_T3; //экран настроек - строка "stop if t3>="
stop_T3 += F(" stop if t3>=");
if (!bit_500ms && change_confirm && (n_line == 2)) //мигаем параметром при изменении
{stop_T3 += F(" ");}
if (bit_500ms)
{stop_T3 += SP_T3_ESTOP;
stop_T3 += F(" ");}
String stop_T1; //экран настроек - строка "stop if t1>="
stop_T1 += F(" stop if t1>=");
if (!bit_500ms && change_confirm && (n_line == 3)) //мигаем параметром при изменении
{stop_T1 += F(" ");}
if (bit_500ms)
{stop_T1 += SP_T1_ESTOP;
stop_T1 += F(" ");}
String Decrement_Mode; //экран настроек - строка для декремента
Decrement_Mode += F(" Decrement=");
if (!bit_500ms && change_confirm && (n_line == 4)) //мигаем параметром при изменении
{Decrement_Mode += F(" ");}
if (bit_500ms)
{Decrement_Mode += decr_mode_title;
Decrement_Mode += F(" ");}
String Sound; //экран настроек - строка "Sound="
Sound += F(" Sound=");
if (!bit_500ms && change_confirm && (n_line == 5)) //мигаем параметром при изменении
{Sound += F(" ");}
if (bit_500ms)
{Sound += sound_title;
Sound += F(" ");}
//start------экран настроек по двойной кнопке Set------------------
if (screen_mode && !screen_calibr && !screen_stat)
{
if (n_line <4)
{
lcd.setCursor(0, 0); //вывод в 1-ю строку
lcd.print (line_SP);
lcd.setCursor(0, 1); //вывод в 2-ю строку
lcd.print (line_G);
lcd.setCursor(0, 2); //вывод в 3-ю строку
lcd.print (stop_T3);
lcd.setCursor(0, 3); //вывод в 4-ю строку
lcd.print (stop_T1);
}
if (n_line == 4)
{
lcd.setCursor(0, 0); //вывод в 2-ю строку
lcd.print (line_G);
lcd.setCursor(0, 1); //вывод в 3-ю строку
lcd.print (stop_T3);
lcd.setCursor(0, 2); //вывод в 4-ю строку
lcd.print (stop_T1);
lcd.setCursor(0, 3); //вывод в 5-ю строку
lcd.print (Decrement_Mode);
}
if (n_line ==5)
{
lcd.setCursor(0, 0); //вывод в 3-ю строку
lcd.print (stop_T3);
lcd.setCursor(0, 1); //вывод в 4-ю строку
lcd.print (stop_T1);
lcd.setCursor(0, 2); //вывод в 5-ю строку
lcd.print (Decrement_Mode);
lcd.setCursor(0, 3); //вывод в 5-ю строку
lcd.print (Sound);
}
}
//end------экран настроек по двойной кнопке Set------------------
//start----экран калибровки датчиков кнопками "Up" + "Down"------------------
if (screen_calibr && !screen_mode && !screen_stat)
{
{ lcd.setCursor(0, 3); //вывод в 4-ю строку (пустая)
lcd.print (" (OFFSETS) ");
}
//------------------------------------------------------------------------
{ String main_screen; //вывод в 3-ю строку
main_screen += F("Kub: t1=");
if (!bit_500ms && change_confirm && (n_line_set == 2)) //мигаем параметром при изменении
main_screen += F(" ");
if (bit_500ms)
{
main_screen += T1_offset;
main_screen += char(1);
if (T1_offset >= 0.0)
main_screen += F(" ");
if (T1_offset < 0.0)
main_screen += F(" ");
}
lcd.setCursor(0, 2);
lcd.print (main_screen);
}
//------------------------------------------------------------------------
{ String main_screen; //вывод во 2-ю строку
main_screen += F("Kolonna: t2=");
if (!bit_500ms && change_confirm && (n_line_set == 1)) //мигаем параметром при изменении
main_screen += F(" ");
if (bit_500ms)
{
main_screen += T2_offset;
main_screen += char(1);
if (T2_offset >= 0.0)
main_screen += F(" ");
if (T2_offset < 0.0)
main_screen += F(" ");
}
lcd.setCursor(0, 1);
lcd.print (main_screen);
}
//------------------------------------------------------------------------
{ String main_screen; //вывод в 1-ю строку
main_screen += F("Top DEF: t3=");
if (!bit_500ms && change_confirm && (n_line_set == 0)) //мигаем параметром при изменении
main_screen += F(" ");
if (bit_500ms)
{
main_screen += T3_offset;
main_screen += char(1);
if (T3_offset >= 0.0)
main_screen += F(" ");
if (T3_offset < 0.0)
main_screen += F(" ");
}
lcd.setCursor(0, 0);
lcd.print (main_screen);
}
}
//end------экран калибровки датчиков кнопками "Up" + "Down"------------------
/* //start----экран статистики тройным нажатием Set""------------------
if (screen_stat && !screen_calibr && !screen_mode)
{
lcd.setCursor(0, 3); //вывод в 4-ю строку
lcd.print (" t1: ");
lcd.print (T1_min);
lcd.print (" ");
lcd.setCursor(13, 3);
lcd.print (T1_max);
lcd.print (" ");
lcd.setCursor(0, 2); //вывод в 3-ю строку
lcd.print (" t2: ");
lcd.print (T2_min);
lcd.print (" ");
lcd.setCursor(13, 2);
lcd.print (T2_max);
lcd.print (" ");
lcd.setCursor(0, 1); //вывод в 2-ю строку
lcd.print (" t3: ");
lcd.print (T3_min);
lcd.print (" ");
lcd.setCursor(13, 1);
lcd.print (T3_max);
lcd.print (" ");
lcd.setCursor(0, 0); //вывод в 1-ю строку
lcd.print (" MIN....MAX " );
}
//end------экран статистики ------------------
*/
//start----экран основной ------------------
if (!screen_mode && !screen_calibr && !screen_stat)
{
n_line=1;
lcd.noCursor();
lcd.noBlink();
change_confirm = 0;
//-------------------вывод в 4-ю строку-----------------------
if (sensor_t1.readTemp()) //если датчик исправен, вывод T1
{ String main_screen;
main_screen += F("t1=");
main_screen += value_T1;
main_screen += char(1);
if (value_T1<10.0) //заполняем пробелами после С
main_screen += F("C ");
if (10.0 <= value_T1 && value_T1<100.0)
main_screen += F("C ");
if (value_T1>=100.0)
main_screen += F("C ");
lcd.setCursor(0, 3); //вывод в начало 4-й строки
lcd.print (main_screen);
}
if (!(sensor_t1.readTemp())) //если датчик не исправен.вывод прочерков
{ lcd.setCursor(0, 3);
lcd.print("t1= --- ");
}
{
String main_screen;
lcd.setCursor(12, 3); //вывод в 4-ю строку в конце
main_screen += F("SP=");
main_screen += (value_SP);
lcd.print(main_screen);
}
//-------------------вывод в 3-ю строку-----------------------
if (sensor_t2.readTemp()) //если датчик исправен, вывод T2
{ String main_screen;
lcd.setCursor(0, 2); //вывод в начало 4-й строки
main_screen += F("t2=");
main_screen += value_T2;
main_screen += char(1);
if (value_T2<10.0) //заполняем пробелами после С
main_screen += F("C* ");
if (10.0 <= value_T2 && value_T2<100.0)
main_screen += F("C* ");
if (value_T2>=100.0)
main_screen += F("C*");
lcd.print (main_screen);
}
if (!(sensor_t2.readTemp())) //если датчик не исправен.вывод прочерков
{ lcd.setCursor(0, 2);
lcd.print("t2= --- ");
}
{
String main_screen;
lcd.setCursor(12, 2); //вывод в 3-ю строку в конце
main_screen += F("Hi=");
main_screen += (value_SP + value_G);
lcd.print(main_screen);
}
//-------------------вывод во 2-ю строку-----------------------
lcd.setCursor(0, 1);
if (sensor_t3.readTemp()) //если датчик исправен, вывод T3
{ String main_screen;
main_screen += F("t3=");
main_screen += value_T3;
main_screen += char(1);
if (value_T3<10.0) //заполняем пробелами после С
main_screen += F("C ");
if (10.0 <= value_T3 && value_T3<100.0)
main_screen += F("C ");
if (value_T3>=100.0)
main_screen += F("C ");
lcd.print (main_screen);
}
else //если датчик не исправен.вывод прочерков
{lcd.print("t3= --- ");}
//----Вывод режима работы в конце строки----
lcd.setCursor(12, 1);
if (work_mode)
{
if ((100-decrement) <100)
{String main_screen;
main_screen += F("RUN ");
main_screen += int (100-decrement);
main_screen += F("%");
lcd.print (main_screen);
}
else
{lcd.print("RUN 100%");}
}
else
{lcd.print(" WAITING");}
//-------------------вывод в 1-ю строку-----------------------
lcd.setCursor(0, 0); //вывод мигающего сообщения STOP
if (!EStop_bit)
{ if (bit_1s) lcd.print(" --- STOP --- ");
else lcd.print(" ");
}
else { //вывод линейного индикатора
//start------------алгоритм линейного индикатора------------------------
char bar[] = {char(2), char(4), char(4), char(4), char(4), char(4), char(4), char(4), //массив для индикатора
char(4), char(4), char(4), char(4), char(4), char(4), char(4), char(4),
char(4), char(4), char(4), char(3), 0};
if (value_T2 >= value_SP) //полоска заполняется слева-направо
{
bar_value = int (((value_T2 - value_SP)/value_G) * 100.0); //уровень индикатора в %
if (bar_value >= 100 or (value_T2 >= (value_SP + value_G))) {bar_value =100;}
bar_i = int (bar_value/5);
if (bar_i >= 19) {bar_i = 19;}
for (int i = 0; i <= bar_i; i++)
{
bar[i] = char(5); // заполним ячейки индикатора квадратиками char(5)
}
}
lcd.print(bar);
}
}
//end---------экран основной ------------------
//start----вывод курсора на экранах с настройками------------------
if (n_line <= 3) n_line_set = n_line; // ограничиваем позицию курсора внизу экрана
if (screen_calibr or screen_mode) // отображаем курсор в режимах настроек
{
if (n_line_set == 0)
{
lcd.setCursor(19,1); lcd.print (" "); lcd.setCursor(19,2); lcd.print (" "); lcd.setCursor(19,3); lcd.print (" ");
}
if (n_line_set == 1)
{
lcd.setCursor(19,0); lcd.print (" "); lcd.setCursor(19,2); lcd.print (" "); lcd.setCursor(19,3); lcd.print (" ");
}
if (n_line_set == 2)
{
lcd.setCursor(19,0); lcd.print (" "); lcd.setCursor(19,1); lcd.print (" "); lcd.setCursor(19,3); lcd.print (" ");
}
if (n_line_set == 3)
{
lcd.setCursor(19,0); lcd.print (" "); lcd.setCursor(19,1); lcd.print (" "); lcd.setCursor(19,2); lcd.print (" ");
}
lcd.setCursor(19,n_line_set);
lcd.print (char(127));
lcd.setCursor(19,n_line_set);
if ((screen_mode or screen_calibr) && change_confirm)
{
lcd.print (char(186));
lcd.setCursor(19,n_line_set);
}
}
//end------вывод курсора на экранах с настройками------------------
}
}
void sensors_BT() //---------функция чтения датчиков и вывод по BT Virtuino------------------
{
if (millis() - Timer2_Sensors >= 1000) // таймер на 1с - чтение датчиков
{
Timer2_Sensors= millis(); // сброс таймера
bit_1s = !bit_1s;
sensor_t1.requestTemp(); // запрос температуры
sensor_t2.requestTemp();
sensor_t3.requestTemp();
if (sensor_t1.readTemp()) value_T1_raw = sensor_t1.getTemp() + T1_offset;
if (sensor_t2.readTemp()) value_T2_raw = sensor_t2.getTemp() + T2_offset;
if (sensor_t3.readTemp()) value_T3_raw = sensor_t3.getTemp() + T3_offset;
}
value_T1 = T1_filter.filteredTime(value_T1_raw); //фильтруем датчики
value_T2 = T2_filter.filteredTime(value_T2_raw);
value_T3 = T3_filter.filteredTime(value_T3_raw);
}
void control() //------------функция управления клапаном---------------------
{
if (( (value_T1 >= SP_T1_ESTOP) //мониторим Т и датчики - тогда ESTOP с задержкой 3 сек
or (value_T3 >= SP_T3_ESTOP)
or !(sensor_t1.readTemp())
or !(sensor_t2.readTemp())
or !(sensor_t3.readTemp()) )
&& (millis() - Timer5 >= 10000) && work_mode) { EStop_bit = false; work_mode = false; decrement = 0;}
if ( !( (value_T1 >= SP_T1_ESTOP) or (value_T3 >= SP_T3_ESTOP) or !(sensor_t1.readTemp()) or !(sensor_t2.readTemp()) or !(sensor_t3.readTemp()) ) )
Timer5 = millis(); //обновляем таймер пока нет ESTOP
if (EStop_bit) //управление клапаном, если work_mode =1 ("RUN") и нет EStop
{
if ( (value_T2 > (value_SP + value_G)) && !control_flag) // меняем декремент (ШИМ от 100% до 50%)
{
control_flag = true;
control_out = false;
if (decrement < 50) {decrement = byte(decrement + 10);}
}
if ((value_T2 <= value_SP) && control_flag) {control_flag = false; }
if ((value_T1 > 93.0) && !flag93 && (decrement < 50)) {decrement = byte(decrement + 10); flag93 = true; }
Ton = uint16_t (10000-(decrement*100)); //вычисление длительности импульса из декремента. 10000 и 100 менять вместе и кратно
Toff = uint16_t (decrement *100); //вычисление длительности паузы из декремента
if (!control_flag && work_mode) {control_out = Generator (Ton, Toff);} //выход на клапан от генератора импульсов
else {control_out = false; }
}
else {control_out = false; control_flag = true; work_mode = false;}
if (decr_mode) decrement = 0;
if (butt_Set.isHold() && (millis() - Timer4_reset_EStop >= 4000) ) { EStop_bit = true; screen_mode = false; flag93= false; decrement = 0;}
if (!butt_Set.isHold()) Timer4_reset_EStop = millis();
digitalWrite(12, EStop_bit);
digitalWrite(13, control_out);
}
bool Generator (word Ton, word Toff) //генератор импульсов: Ton - задание для выхода ON, Toff - задание для выхода OFF (до 65000 мсек)
{
static uint32_t Timer_cv_on,Timer_cv_off;
static bool outg, f1;
if ((!(millis()-Timer_cv_on >= Ton)) && !f1) {outg = true; f1 = true;} //импульс на включение клапана
else {outg = false; Timer_cv_on = millis(); f1 = false;} //
//
if ((!(millis()-Timer_cv_off >= Toff)) && !f1) {outg = false; f1 = true;} //импульс на выключение клапана
else {outg = true; Timer_cv_off = millis();f1 = false;}
return (outg);
}
/*void statistic() //------------функция статистики------------------------
{
if (!flag2 && (millis() >= 5000)) // через 5с от старта подчитываем тек. значений в предельные
{ flag2 = true;
T1_min = value_T1; T2_min = value_T2; T3_min = value_T3;
T1_max = value_T1; T2_max = value_T2; T3_max = value_T3;
}
static uint32_t Timer_stat;
if (millis() - Timer_stat >= 10000) // раз в 10сек
{
Timer_stat = millis();
if (value_T1 < T1_min) T1_min = value_T1; // обновляем статистику
if (value_T1 > T1_max) T1_max = value_T1;
if (value_T2 < T2_min) T2_min = value_T2;
if (value_T2 > T2_max) T2_max = value_T2;
if (value_T3 < T3_min) T3_min = value_T3;
if (value_T3 > T3_max) T3_max = value_T3;
}
}
*/
void sound() //------------функция вывода звука------------------------
{
if (EStop_bit && (value_T2 >= (value_SP + value_G)) && !sound_mode && work_mode) //звук короткий старт/стоп
{
if (!(millis()-Timer7_sound_t2 >= 1000)) {analogWrite (5,128);} // звук 2кГц на 1сек при t2 >SP
else {analogWrite (5,0);}
}
else
{
Timer7_sound_t2 = millis(); //analogWrite (5,0); // сброс таймера и ШИМа
}
if (!EStop_bit && !sound_mode)
{
if (!(millis()-Timer6_sound_ESTOP >= 5000)) {analogWrite (5,128);} // звук 1кГц на 5сек при EStop
else {analogWrite (5,0);}
}
else
{
Timer6_sound_ESTOP = millis(); //analogWrite (5,0); // сброс таймера и ШИМа
}
if (sound_mode) {analogWrite (5,0);}
}
void eeprom() //------------функция записи в EEPROM---------------------
{
if (!change_confirm && !flag1) // фронт HIGH->LOW - при выходе из режима изменения значений записываем в EEPROM
{
flag1 = true; //этот флаг сбрасывается при след. активном режиме изменения значений, см. выше
EEPROM.put(0, T1_offset); //пишем настройки в EEPROM
EEPROM.put(4, T2_offset);
EEPROM.put(8, T3_offset);
EEPROM.put(12, value_SP);
EEPROM.put(16, value_G);
EEPROM.put(20, SP_T1_ESTOP);
EEPROM.put(24, SP_T3_ESTOP);
EEPROM.put(28, settings_EEPROM);
}
//запись в EEProm при изменении уставок и режимов не из экрана настроек, просто по изменению: по BlueTooth или с основного экрана
static uint32_t Timer_eprom_write;
if (millis() - Timer_eprom_write >= 30000) // раз в 30сек проверяем отличия уставок и тогда обновляем их в EEProm
{
Timer_eprom_write = millis();
if (bitRead(EEPROM_flag, 0))
{EEPROM.put(12, value_SP); bitWrite(EEPROM_flag, 0, false);}
if (bitRead(EEPROM_flag, 1))
{EEPROM.put(16, value_G); bitWrite(EEPROM_flag, 1, false);}
if (bitRead(EEPROM_flag, 2))
{EEPROM.put(20, SP_T1_ESTOP); bitWrite(EEPROM_flag, 2, false);}
if (bitRead(EEPROM_flag, 3))
{EEPROM.put(24, SP_T3_ESTOP); bitWrite(EEPROM_flag, 3, false);}
if ( bitRead(EEPROM_flag, 4) or bitRead(EEPROM_flag, 5) )
{EEPROM.put(28, settings_EEPROM);bitWrite(EEPROM_flag, 4, false); bitWrite(EEPROM_flag, 5, false);}
}
}
//============ Функции для Virtuino=======
//============ onCommandReceived=========
/* This function is called every time, when Virtuino app sends a request to server to change a Pin value
* The 'variableType' can be a character like V, T, O V=Virtual pin T=Text Pin O=PWM Pin
* The 'variableIndex' is the pin number index of Virtuino app
* The 'valueAsText' is the value that has sent from the app */
void onReceived(char variableType, uint8_t variableIndex, String valueAsText)
{ if (variableType=='V') {
float value = valueAsText.toFloat(); // convert the value to float. The valueAsText have to be numerical
if (variableIndex < V_memory_count) V[variableIndex] = value; // copy the received value to arduino V memory array
}
}
//=================================
/* This function is called every time Virtuino app requests to read a pin value*/
String onRequested(char variableType, uint8_t variableIndex)
{
if (variableType=='V') {
if (variableIndex < V_memory_count) return String(V[variableIndex]); // return the value of the arduino V memory array
}
return "";
}
//================= virtuinoRun===========
void virtuinoRun(){
while (Serial.available()) {
char tempChar=Serial.read();
if (tempChar==CM_START_CHAR) {
virtuino.readBuffer=CM_START_CHAR;
virtuino.readBuffer+=Serial.readStringUntil(CM_END_CHAR);
virtuino.readBuffer+=CM_END_CHAR;
String* response= virtuino.getResponse();
Serial.print(*response);
break;
}
}
}
void vDelay(int delayInMillis){long t=millis()+delayInMillis;while (millis()<t) virtuinoRun();}