Итак, была у меня гравитационная пеллетная горелка Begemott. Штука клёвая, но в межсезонье и летом работает из рук вон плохо, т.к. она на естественной тяге и когда тяги нет — дымит-коптит и горят гофры. К дымоходу оооочень критична горелочка. Думал я думал, и решил поставить себе АПГ-25 от Теплодара, но покупать её полностью мне не хотелось. бункер у меня и шнек уже был, горелка сама подавалась весной 2017 года за 13к рублей в Спб. Купил её, а также arduino nano и пару твердотельных релюшек. Написал прошивочку и теперь активно использую данную связку и постоянно совершенствую её. Косяков и недоделок много, но она работает. =) И наддувная горелка вышла мне менее, чем в 15к рублей. Выкладываю по просьбам прошивочку.
#include <DallasTemperature.h> #include <OneWire.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <EEPROM.h> #include "DHT.h" /* Установки PIN входов и выходов */ #define ADKeyboardPin 0 // Analog In #define FireSensorIn 1 // Analog In #define DHTPIN 11 // Digital In #define WindFanOut 3 // Digital Out PWM #define PumpRelayOut 5 // Digital Out #define BurnPinIn 6 // Digital In #define ShnackRelayOut 7 // Digital Out #define LampRelayOut 8 // Digital Out #define DS18B20Pin 9 // Digital IN #define ErrorRelayPin 10 // Digital Out #define SDAPin A4 // Analog Out #define SCLPin A5 // Analog Out #define DHTTYPE DHT22 #define REVERSRELAY 0 // type relay (high level on or low level on) #define DSBIT 9 DeviceAddress t_pod = {0x28, 0xFF, 0xCE, 0x0A, 0x62, 0x16, 0x03, 0x9D}; DeviceAddress t_obr = {0x28, 0xFF, 0xF9, 0x6D, 0x71, 0x16, 0x05, 0x2D}; DHT dht(DHTPIN, DHTTYPE); LiquidCrystal_I2C lcd(0x3F, 16, 2); OneWire oneWire(DS18B20Pin); DallasTemperature sensors(&oneWire); /* Основные заводские настройки */ // Тц | Время цикла | Сумма времени подачи и паузы работы привода шнека в режимах нагрева и поддержания 20 секунд int Tc = 20; // Тпр* | Подача розжиг | Время подачи топлива в режиме розжига 60 секунд int Tpr = 60; // Тпн* | Подача нагрев | Время подачи в режиме нагрева 8 секунд int Tpn = 5; // Тпп* | Подача поддержание | Время подачи в режиме поддержания 3 секунд int Tpp = 1; // Wp | Вентилятор розжиг | Интенсивность работы вентилятора при розжиге 50% int Wr = 50; // Wн* | Вентилятор нагрев | Интенсивность работы вентилятора в режиме нагрева 75% int Wn = 75; // Wп | Вентилятор поддержание | Интенсивность работы вентилятора в режиме поддержания 30% int Wp = 30; // Wо* | Вентилятор ожидание | Интенсивность работы вентилятора в режиме ожидания пуска 50% int Wo = 50; // tу* | Установка температуры | Пороговое значение температуры теплоносителя для перехода из режима нагрева в режим поддержания 60 ˚С int tu = 60; // tг | Гистерезис установки температуры | Разность между температурами перехода из нагрева в поддержание и температурой обратного перехода из поддержания в нагрев 2 ˚С int tg = 2; // Тф | Время фиксации пламени | Продолжительность задержки перехода из розжига к нагреву после появления сигнала пламени или обратного перехода к розжигу после исчезновения сигнала наличия пламени 30 секунд int Tf = 30; // Тр | Время розжига | Продолжительность работы лампы розжига. По истечении этого времени должен появиться сигнал наличия пламени 7 минут int Tr = 7; // Тв | Время выжигания | Длительность продувки на максимальной мощности вентилятора для очищения жаровни от продуктов горения 5 минут int Tv = 5; // Fу | Установка пламени | Минимальная яркость пламени перехода от розжига к рабочим режимам и обратно 20% int Fu = 20; // ta | Перегрев | Температура аварийного отключенияподачи топлива при превышении максимально допустимого значения температуры теплоносителя в трубе подачи 90˚С int ta = 90; // tп | Температура подачи | Текущие показания датчика температуры подачи ˚С int tp = 0; // tо | Температура обратки | Текущие показания датчика температуры обратки ˚С int to = 0; // tв | Температура воздуха | Текущие показания датчика температуры воздуха в помещении ˚С int tv = 0; // Fп | Пламя | Текущие показания датчика пламени % int Fp = 0; // St | Этап цикла | 0 - ожидание пуска, 1 - Розжиг, 2 - Нагрев, 3 - Поддержание, 4 - Выжигание int St = 0; /* Временные интервалы, тайминги и служебные промежуточные переменные */ long menu = 0; int Error = 0; // Текущие микросекунды unsigned long currentMillis = 0; unsigned long oneSecondStepPoll = 0; unsigned long displaySecondStepPoll = 0; int DisplayPage = 0; int CurrPWMStatus = 0; int CurrLampStatus = 0; // Для проверки наличия пламени int Fire = 0; int Firecount = 0; int FirstBurnStep = 0; // Этапы розжига: 0 - Загрузка топлива, 1 - Розжиг int stat_BurnPinIn = 0; // Включена горелка или нет unsigned long CurrBurnTimeStatus = 0; unsigned long MillisBySt0, MillisBySt1, MillisBySt4, MillisBySt6 = 0; // Для работы шнека по периоду unsigned long ShnackStopMillis = 0; int ShnackStep = 0; // Этапы цикла шнека: 0 - Начало цикла, 1 - Подача, 2 - Ожидание начала нового цикла int CurrShnackStatus = 0; unsigned long ShnackNextStartMillis = 0; int CurrPumpStatus = 0; // Статус циркуляционника /* Системные Функции */ void debug() { Serial.print(currentMillis); Serial.print("\t Step: "); Serial.print(St); Serial.print("\t Fire: "); Serial.print(Fire); Serial.print("\t Fire %: "); Serial.print(Fp); Serial.print("\t Firecount: "); Serial.print(Firecount); Serial.print("\t CurrBurnTimeStatus: "); Serial.print(CurrBurnTimeStatus); Serial.print("\t Podacha: "); Serial.print(tp); Serial.print("\t Obratka: "); Serial.print(to); Serial.print("\t Fan %: "); Serial.print(CurrPWMStatus); Serial.print("%"); Serial.print("\t Shnack: "); Serial.print(CurrShnackStatus); Serial.print("\t ShnackStep: "); Serial.print(ShnackStep); Serial.print("\t ShnackStopMillis: "); Serial.print(ShnackStopMillis); Serial.print("\t ShnackNextStartMillis: "); Serial.print(ShnackNextStartMillis); Serial.print("\t FirstBurnStep: "); Serial.print(FirstBurnStep); Serial.println(""); } void setupPins() { pinMode(WindFanOut, OUTPUT); pinMode(FireSensorIn, INPUT); pinMode(LampRelayOut, OUTPUT); pinMode(ShnackRelayOut, OUTPUT); pinMode(ErrorRelayPin, OUTPUT); pinMode(BurnPinIn, INPUT); pinMode(ADKeyboardPin, INPUT); pinMode(PumpRelayOut, OUTPUT); setPinStatus("off", PumpRelayOut, CurrPumpStatus, REVERSRELAY); setPinStatus("off", ShnackRelayOut, CurrShnackStatus, 0); setPinStatus("off", LampRelayOut, CurrLampStatus, REVERSRELAY); SetPWM(WindFanOut, 15); Ds18b20Setup(); } void setPinStatus(String stat, int pin, int &info, int is_revers) { if (stat == String("on")) { if (is_revers == 1) { digitalWrite(pin, LOW); } else { digitalWrite(pin, HIGH); } info = 1; } else { if (is_revers == 1) { digitalWrite(pin, HIGH); } else { digitalWrite(pin, LOW); } info = 0; } } void getTemperature(DeviceAddress deviceAddress, int &ret) { int tempC = sensors.getTempC(deviceAddress); if (tempC == -127.00 || tempC == 0.00 || tempC == 255.94 || tempC == 139.38 || tempC == 85.0) { // return } else { ret = tempC; } } void Ds18b20Setup() { sensors.begin(); sensors.setResolution(t_pod, DSBIT); sensors.setResolution(t_obr, DSBIT); sensors.requestTemperatures(); } void ds18b20Poll() { getTemperature(t_pod, tp); getTemperature(t_obr, to); sensors.setWaitForConversion(false); sensors.requestTemperatures(); } // Функция перевода секунд в микросекунды long SecToMicroSec(long seco) { long retval = 0; retval = seco * 1000; return retval; } // Функция перевода минут в микросекунды long MinToMicroSec(long minutes) { long retval = 0; retval = minutes * 60000; return retval; } // Функция установки напряжения на аналоговый PWM выход в процентах void SetPWM(int pin, long percent) { CurrPWMStatus = percent; long value = (long) (255 / 100 * percent); analogWrite(pin, value); } void GetAllInfo() { stat_BurnPinIn = digitalRead(BurnPinIn); float Fptmp = analogRead(FireSensorIn); Fptmp = 100 - ((Fptmp / 1023) * 100); Fp = (long) Fptmp; pollDHT(); ds18b20Poll(); } void pollDHT() { float curr_dht; //float h = dht.readHumidity(); curr_dht = dht.readTemperature(); if (isnan(curr_dht)) { return; } tv = curr_dht; } void ShowInfo() { // output_string - куда выводить, массив char // "%02i %2c %4.3f" формат вывода аргументов: // %02(два поля - сначала нули)i(int) // %2(два поля)c(char) // %4(4 поля всего).2(после запятой)f(float) // про форматы - htp://www.chitay.org/c/13/printf.htm char output_string[16]; if (menu == 0) { if (DisplayPage == 1) { lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "tp=%02i to=%02i", tp, to); lcd.print(output_string); lcd.setCursor(0, 1); sprintf(output_string, "tu=%02i tv=%02i", tu, tv); lcd.print(output_string); } if (DisplayPage == 2) { lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "Fp=%02i W=%02i St=%02i", Fp, CurrPWMStatus, St); lcd.print(output_string); lcd.setCursor(0, 1); sprintf(output_string, "L=%02i Error=%02i", CurrLampStatus, Error); lcd.print(output_string); DisplayPage = 0; // Повторяем по кругу. Обязательный флаг на последнем элементе меню. } } } void GetDataFromEEPROM() { int currconfig = 0; currconfig = EEPROM_int_read(0); if (currconfig == 1) { Tc = EEPROM_int_read(2); Tpr = EEPROM_int_read(4); Tpn = EEPROM_int_read(6); Tpp = EEPROM_int_read(8); Wr = EEPROM_int_read(10); Wn = EEPROM_int_read(12); Wp = EEPROM_int_read(14); Wo = EEPROM_int_read(16); tu = EEPROM_int_read(18); tg = EEPROM_int_read(20); Tf = EEPROM_int_read(22); Tr = EEPROM_int_read(24); Tv = EEPROM_int_read(26); Fu = EEPROM_int_read(28); ta = EEPROM_int_read(30); } else { SaveDataToEEPROM(); } } void SaveDataToEEPROM() { EEPROM_int_write(0, 1); EEPROM_int_write(2, Tc); EEPROM_int_write(4, Tpr); EEPROM_int_write(6, Tpn); EEPROM_int_write(8, Tpp); EEPROM_int_write(10, Wr); EEPROM_int_write(12, Wn); EEPROM_int_write(14, Wp); EEPROM_int_write(16, Wo); EEPROM_int_write(18, tu); EEPROM_int_write(20, tg); EEPROM_int_write(22, Tf); EEPROM_int_write(24, Tr); EEPROM_int_write(26, Tv); EEPROM_int_write(28, Fu); EEPROM_int_write(30, ta); } // 0 - работаем, 1 - закончил работу int ShnackWorkTime(unsigned long Time, int period) { long workTime = SecToMicroSec(Time); long diff = 0; workTime = workTime / 10; if (ShnackStopMillis == 0) { ShnackStopMillis = currentMillis + workTime; } diff = currentMillis - ShnackStopMillis; if (period == 0) { if (diff > 0) { // Stop setPinStatus("off", ShnackRelayOut, CurrShnackStatus, 0); ShnackStopMillis = 0; return 1; } else { setPinStatus("on", ShnackRelayOut, CurrShnackStatus, 0); // Start return 0; } } else { if (ShnackStep == 0) { ShnackNextStartMillis = currentMillis + SecToMicroSec(Tc); ShnackStep = 1; } if (ShnackStep == 1) { if (diff > 0) { // Stop setPinStatus("off", ShnackRelayOut, CurrShnackStatus, 0); ShnackStep = 2; } else { // Start setPinStatus("on", ShnackRelayOut, CurrShnackStatus, 0); } } if (ShnackStep == 2 && currentMillis >= ShnackNextStartMillis) { ShnackStep = 0; ShnackStopMillis = 0; } } } void CheckWorkStep() { // Если tо<tу, автоматически включается режим нагрева (включая гистерезис) if (tp - tg < tu) { St = 2; } // Если tо>tу, автоматически включается режим поддержания заданной температуры (включая гистерезис) if (tp + tg > tu) { St = 3; } //Если температура теплоносителя в трубе подачи (tп) превышает температуру аварийного отключения (tа), загорается красный индикатор , подача топлива прекращается и не возобновляется до тех пор, пока значение (tп) не станет меньше или равно (tа). На экране ПУ появляется сообщение об ошибке. if (tp > ta || to > ta) { St = 5; } // Розжиг, если потухли на нагреве или поддержании горения if ((St == 2 || St == 3) && Fire == 0) { St = 4; } } // Return 0 - Идёт проверка, 1 - Появилось пламя, 2 - Истекло время ожидания пламени int checkFireByTime(unsigned long chktime, unsigned long &CheckFireByTimeMillis) { // Начинаем проверку. if (CheckFireByTimeMillis == 0) { CheckFireByTimeMillis = currentMillis; } // Превышено время розжига if (CheckFireByTimeMillis > 0 && currentMillis - CheckFireByTimeMillis > chktime ) { if (Fire == 1) { return 1; // Огонь есть } else { return 2; // Истекло время поджига } } return 0; } void SetLogiq() { int CurrShnackStatusRet = 0; tu = calc_int_out(); if (St != 0) { MillisBySt0 = 0; } if (St != 1) { MillisBySt1 = 0; } if (St != 4) { MillisBySt4 = 0; } if (St != 6) { MillisBySt6 = 0; } // Режим ожидания пуска if (St == 0 && stat_BurnPinIn == 1) { Error = 0; FirstBurnStep = 0; // В режиме ожидания пуска включается вентилятор на мощности, соответствующей режиму поддержания SetPWM(WindFanOut, Wp); // todo check why not visible fire after reboot if (Fire == 1) { CheckWorkStep(); } else { // Если пламя отсутствует, то по истечении времени фиксации пламени горелка переходит в режим розжига и на экране появляется надпись «розжиг» long checkTf = SecToMicroSec(Tf) + 5000; CurrBurnTimeStatus = checkFireByTime(checkTf, MillisBySt0); if (CurrBurnTimeStatus == 2) { St = 1; ShnackStopMillis = 0; } if (CurrBurnTimeStatus == 1) { CheckWorkStep(); } } } if (St == 0 && stat_BurnPinIn == 0) { SetPWM(WindFanOut, 15); } // Розжиг if (St == 1) { if (FirstBurnStep == 0) { CurrShnackStatusRet = ShnackWorkTime(Tpr, 0); if (CurrShnackStatusRet == 1) { FirstBurnStep = 1; } } else { setPinStatus("on", LampRelayOut, CurrLampStatus, REVERSRELAY); SetPWM(WindFanOut, Wr); if (Fire == 1) { CheckWorkStep(); } else { // время розжига и кол-во попыток. А также сигнализируем о неисправности. long checkTr = MinToMicroSec(Tr); CurrBurnTimeStatus = checkFireByTime(checkTr, MillisBySt1); if (CurrBurnTimeStatus == 2) { Error = 1; St = 6; } } } } if (St != 1) { setPinStatus("off", LampRelayOut, CurrLampStatus, REVERSRELAY); } // Нагрев if (St == 2) { FirstBurnStep = 0; SetPWM(WindFanOut, Wn); ShnackWorkTime(Tpn, Tc); CheckWorkStep(); } // Поддержание if (St == 3) { FirstBurnStep = 0; SetPWM(WindFanOut, Wp); ShnackWorkTime(Tpp, Tc); CheckWorkStep(); } if (St >= 2) { setPinStatus("on", PumpRelayOut, CurrPumpStatus, REVERSRELAY); } else { setPinStatus("off", PumpRelayOut, CurrPumpStatus, REVERSRELAY); } // Выжигание if (St == 4) { SetPWM(WindFanOut, 100); FirstBurnStep = 0; long checkTv = MinToMicroSec(Tv); CurrBurnTimeStatus = checkFireByTime(checkTv, MillisBySt4); if (CurrBurnTimeStatus == 2) { St = 0; } } // Перегрев обратки или подачи if (St == 5) { SetPWM(WindFanOut, Wp); FirstBurnStep = 0; CheckWorkStep(); } // Ошибка и остановка горелки if (St == 6) { FirstBurnStep = 0; if (Error == 1) { SetPWM(WindFanOut, 100); long checkTv = MinToMicroSec(Tv); CurrBurnTimeStatus = checkFireByTime(checkTv, MillisBySt6); if (CurrBurnTimeStatus == 2) { Error = 0; } } else { SetPWM(WindFanOut, 15); } } if (St >= 4) { setPinStatus("off", ShnackRelayOut, CurrShnackStatus, 0); } // Если горелка не работает if (stat_BurnPinIn == 0 && St != 0) { St = 4; } if (stat_BurnPinIn == 1 && St == 4) { if (Fire == 1) { St = 2; } } } // Определение пламени void checkFire() { if (Fp >= Fu) { if (Firecount < Tf) { Firecount++; } } else { if (Firecount > 0) { Firecount--; } } if (Firecount == 0) { Fire = 0; } if (Firecount == Tf) { Fire = 1; } } /* Show and edit menu */ void action(String actionval, int &val, int minimal, int maximal) { if (actionval == "Up") { if (val < maximal) { val++; } if (val >= maximal) { val = minimal; } } if (actionval == "Down") { if (val > minimal) { val--; } if (val <= minimal) { val = maximal; } } } void ShowMenu(String actionval) { char output_string[16]; switch (menu) { // Тц | Время цикла | Сумма времени подачи и паузы работы привода шнека в режимах нагрева и поддержания 20 секунд case 1: action(actionval, Tc, 5, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tc=%02is", Tc); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("05-99s Def: 20s"); break; // Тпр* | Подача розжиг | Время подачи топлива в режиме розжига 60 секунд case 2: action(actionval, Tpr, 1, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tpr=%02is", Tpr); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-99s Def: 60s"); break; // Тпн* | Подача нагрев | Время подачи в режиме нагрева 8 секунд case 3: action(actionval, Tpn, 1, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tpn=%02is", Tpn); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-99s Def: 8s"); break; // Тпп* | Подача поддержание | Время подачи в режиме поддержания 3 секунд case 4: action(actionval, Tpp, 1, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tpp=%02is", Tpp); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-99s Def: 3s"); break; // Wp | Вентилятор розжиг | Интенсивность работы вентилятора при розжиге 50% case 5: action(actionval, Wr, 15, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Wr=%02i%%", Wr); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("15-99% Def: 50%"); break; // Wн* | Вентилятор нагрев | Интенсивность работы вентилятора в режиме нагрева 75% case 6: action(actionval, Wn, 15, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Wn=%02i%%", Wn); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("15-99% Def: 75%"); break; // Wп | Вентилятор поддержание | Интенсивность работы вентилятора в режиме поддержания 30% case 7: action(actionval, Wp, 15, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Wp=%02i%%", Wp); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("15-99% Def: 30%"); break; // Wо* | Вентилятор ожидание | Интенсивность работы вентилятора в режиме ожидания пуска 50% case 8: action(actionval, Wo, 15, 99); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Wo=%02i%%", Wo); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("15-99% Def: 50%"); break; // tу* | Установка температуры | Пороговое значение температуры теплоносителя для перехода из режима нагрева в режим поддержания 60 ˚С case 9: action(actionval, tu, 20, 80); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP tu=%02iC", tu); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("20-80C Def: 60C"); break; // tг | Гистерезис установки температуры | Разность между температурами перехода из нагрева в поддержание и температурой обратного перехода из поддержания в нагрев 2 ˚С case 10: action(actionval, tg, 1, 5); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP tg=%02iC", tg); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-05C Def: 02C"); break; // Тф | Время фиксации пламени | Продолжительность задержки перехода из розжига к нагреву после появления сигнала пламени или обратного перехода к розжигу после исчезновения сигнала наличия пламени 30 секунд case 11: action(actionval, Tf, 10, 60); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tf=%02is", Tf); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("10-60s Def: 30s"); break; // Тр | Время розжига | Продолжительность работы лампы розжига. По истечении этого времени должен появиться сигнал наличия пламени 7 минут case 12: action(actionval, Tr, 1, 15); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tr=%02im", Tr); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-15m Def: 07m"); break; // Тв | Время выжигания | Длительность продувки на максимальной мощности вентилятора для очищения жаровни от продуктов горения 5 минут case 13: action(actionval, Tv, 1, 15); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Tv=%02im", Tv); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("01-15m Def: 05m"); break; // Fу | Установка пламени | Минимальная яркость пламени перехода от розжига к рабочим режимам и обратно 20% case 14: action(actionval, Fu, 10, 50); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP Fu=%02i%%", Fu); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("10-50% Def: 20%"); break; // ta | Перегрев | Температура аварийного отключенияподачи топлива при превышении максимально допустимого значения температуры теплоносителя в трубе подачи 90˚С case 15: action(actionval, ta, 70, 90); lcd.clear(); lcd.setCursor(0, 0); sprintf(output_string, "SETUP ta=%02iC", ta); lcd.print(output_string); lcd.setCursor(0, 1); lcd.print("70-90C Def: 90C"); break; case 16: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Save and Quit"); if (actionval == "Select") { lcd.clear(); DisplayPage = 0; SaveDataToEEPROM(); menu = 0; } break; // NO Save case 17: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Quit no save"); if (actionval == "Select") { lcd.clear(); DisplayPage = 0; GetDataFromEEPROM(); menu = 0; } break; // Load Defaults case 18: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Load Defaults"); if (actionval == "Select") { EEPROM_int_write(0, 0); lcd.clear(); lcd.print("NOW RESTART!"); delay(100000000); } break; default: break; } } void CheckMenu() { int x; x = analogRead (ADKeyboardPin); if (x < 1000 || menu != 0) { ShowMenu("none"); delay(50); if (x > 1000) return; if (x < 60) { // Right if (menu > 0) { menu++; } if (menu >= 19) { menu = 1; } ShowMenu("none"); delay(100); } else if (x < 200) { // Up ShowMenu("Up"); } else if (x < 400) { // Down ShowMenu("Down"); } else if (x < 600) { // Left if (menu >= 1) { menu--; } if (menu == 0) { menu = 18; } ShowMenu("none"); delay(100); } else if (x < 800) { // Select if (menu == 0) { menu++; } ShowMenu("Select"); delay(100); } delay(50); } } // чтение int EEPROM_int_read(int addr) { byte raw[2]; for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i); int &num = (int&)raw; return num; } // запись void EEPROM_int_write(int addr, int num) { byte raw[2]; (int&)raw = num; for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]); } float calc_temp() { // // Функция расчитывает температуру контура отпления // float a, b, c, x; float temp_v, temp_n; float iv_k = 1.0; // Коэффициент кривой TODO вынести в конфиг и EEPROM // Температура контура отопления в зависимости от наружной температуры // Tn = ax2 + bx + c // a = -0,21k — 0,06 // b = 6,04k + 1,98 // с = -5,06k + 18,06 // x = -0.2*t1 + 5 x = (-0.2 * tv) + 5; a = (-0.21 * iv_k) - 0.06; b = (6.04 * iv_k) + 1.98; c = (-5.06 * iv_k) + 18.06; temp_n = (a * x * x) + (b * x) + c; // Расчетная температура конура отопления // T = Tn + Tk + Tt temp_v = temp_n; // Ограничиваем температуру if (temp_v > 75) temp_v = 75; if (temp_v < 0) temp_v = 0; return temp_v; } int calc_int_out() { // // Расчитываем значение переменной int_out на основе temp_v // или выходное значение платы управления на основе расчетной температуры конура отопления // int int_out; float temp_v = calc_temp(); // Температура котла зависит от значений платы управления нелинейно, поэтому воспользуемся табличными данными // Номер элемента в массиве совпадает с расчетным значением int_out //int temptable[] = {0, 0, 32, 35, 38, 41, 43, 46, 48, 51, 53, 55, 57, 59, 60, 62, 63, 64, 66, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 79, 80}; int temptable[] = {24, 28, 32, 35, 38, 41, 43, 46, 48, 51, 53, 55, 57, 59, 60, 62, 63, 64, 66, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 79, 80}; for (int i = 1; i <= 31; i++) { // ищем подходящую темепературу if (temp_v <= temptable[i]) { float j1, j2; j1 = temptable[i] - temp_v; j2 = temp_v - temptable[i - 1]; // выбираем лучшее значение if (j2 > j1) { int_out = i; } else { int_out = i - 1; } break; } } return temptable[int_out]; } /* Функции микроконтроллера */ void setup() { Serial.begin(115200); dht.begin(); lcd.init(); lcd.backlight();// Включаем подсветку дисплея lcd.clear(); lcd.setCursor(0, 0); lcd.print("Controller v0.1"); lcd.setCursor(0, 1); lcd.print("By V.Dyakov"); GetDataFromEEPROM(); setupPins(); delay(3000); } void loop() { currentMillis = millis(); CheckMenu(); SetLogiq(); if (currentMillis - oneSecondStepPoll >= 2000) { GetAllInfo(); checkFire(); debug(); oneSecondStepPoll = currentMillis; } if (currentMillis - displaySecondStepPoll >= 3000) { DisplayPage++; ShowInfo(); displaySecondStepPoll = currentMillis; } }
Как связаться с автором? Нужна помощь
Как автору задать несколько вопросов?
Автор красава! Счет бы для благодарности. И естественно тоже бы попросил чертежи.
доброго времени суток. есть ли у кого макет сего кода в flprog