Всем привет, меня зовут Дима, и я сварщик живу я в Украине, в городе Харьков. Когда то газ был дешевый и некого не парило отопление дома, но в 2008 он подорожал почти на 100% и я утеплил дом пенопластом, стало легче жил дальше... но пришел 2014 год и газ подорожал в 7 раз, что стало очень накладно с расходом 300-500 кубометров в месяц и я сварил себе котел на дровах шахтного типа. но через две зимы пришел к тому что дрова топливо хоть и дешевое но очень не стабильное... заинтересовался пеллетами и сделал "гравитационную" горелку, стало лучше. качество топлива стабильное, засыпал в бункер и нагрел теплоаккамулятор но напрягало что гравитационная горелка вредная к тяге в дымоходе и получалось что нужно 2-4 часа в день что бы нагреть ТА, и без присмотра её не оставить... и сделал свою первую факельную горелку, управление сделал на модулях из китая, выставил время подачи и паузы подкрутил вентилятор и работает но хотелось сделать "автоматику" которая была бы автоматической и знала что делать с нештатными ситуациями но знания мои в програмировании не позволяли... потом я сделал котел в мастерскую и тоже контроллер на модулях, но в мастерской ТА нет, и котел греет радиаторы на прямую. а хотелось чтоб он мог поддерживать заданную темературу и не палил лишнего, и конце 2019 года понеслось... напряг своих друзей кто понимал больше меня начать контроллер, что то получалось, как я думал, но к весне 2020 года я таки понял как надо сделать и теперь только "допиливаю" свое детище в общем вот: #define PID_INTEGER #define PID_INTEGRAL_WINDOW 50 #define BTN1 4 #define BTN2 5 #define BTN3 2 #define BTN4 3 #define BTN5 A0 #include <GyverPID.h> #include <GyverButton.h> #include <GyverTimer.h> #include <GyverTM1637.h> #include <OneWire.h> #include <DallasTemperature.h> #define CLK A4 #define DIO A5 #define ONE_WIRE_BUS 11 GyverTM1637 disp(CLK, DIO); GyverPID pid(1, 0.05, 0.8, 10000); OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); DeviceAddress insideThermometer {0x28, 0xB8, 0x86, 0xD5, 0x33, 0x20, 0x01, 0xCA }; //УКАЗАТЬ имя датчика ////////////////////кнопки//////////////////////////////////////////// GButton startButtonPin(BTN1); //пин кнопки Старт GButton stopButtonPin(BTN2); //пин кнопки Стоп GButton plusButtonPin(BTN3); //пин кнопки "+" GButton minusButtonPin(BTN4); //пин кнопки "-" GButton disp_ButtonPin(BTN5); //пин кнопки дисплея ////////////////////датчики/////////////////////////////////////////// const int photoresistorPin = 12; //пин фоторезистора const int infraredSensorPin = A3; //пин ИК датчика ////////////////////иполнители//////////////////////////////////////// const int feederAugerPin = 6; //пин шнека питателя const int pumpPin = 7; //пин цикуляционного насоса const int heatingElementPin = 8; //пин зажигалки const int stokerAugerPin = 9; //пин шнека стокера const int pwmPin = 10; //пин для ШИМ const int firebarPin = 13; //пин подвижного колосника ////////////////////переменные/////////////////////////////////////// int desiredTemperature; int currentTemp; byte working; byte start_count; byte photoresistor; int p_pause; int w_pause; byte start1; int val_f; int val[3]; int index; byte starting; byte plusButton; byte minusButton; byte dispButton; int dose; byte displ; signed char fan_value; int PWM_val; byte fanspeed_pr; byte pid_out_last; int switch_start; int switch_stop; int stop_FPB; int startpwm; int disp_on; uint8_t start_1; uint8_t stop_1; int fire_off; int reg1; GTimer start_timer0(MS); GTimer start_timer1(MS); GTimer start_timer2(MS); GTimer start_timer3(MS); GTimer stop_timer1(MS); GTimer stop_timer2(MS); int middle_of_3(int a, int b, int c) { int x[2]; if (a > b) {x[0] = b; x[1] = a;} else {x[0] = a; x[1] = b;} if (x[1] > c) {return (x[0]< c) ? c : x[0];} else {return x[1];} } void setup() { start_1=0; stop_1=0; disp_on=1; switch_stop=9; switch_start=9; pid_out_last=100; PWM_val=0; displ=0; working=0; start1=0; start_count=0; desiredTemperature=500; stop_FPB=1; startpwm=0; fire_off=0; disp.clear(); disp.brightness(0); // яркость, 0 - 7 (минимум - максимум) startButtonPin.setTickMode(AUTO); stopButtonPin.setTickMode(AUTO); plusButtonPin.setTickMode(AUTO); minusButtonPin.setTickMode(AUTO); disp_ButtonPin.setTickMode(AUTO); pinMode(photoresistorPin, INPUT); pinMode(infraredSensorPin, INPUT); pinMode(pumpPin, OUTPUT); pinMode(feederAugerPin, OUTPUT); pinMode(stokerAugerPin, OUTPUT); pinMode(pwmPin, OUTPUT); pinMode(heatingElementPin, OUTPUT); pinMode(firebarPin, OUTPUT); digitalWrite(feederAugerPin, HIGH); digitalWrite(stokerAugerPin, HIGH); digitalWrite(heatingElementPin, HIGH); digitalWrite(pumpPin, HIGH); digitalWrite(firebarPin, HIGH); pid.setDirection(NORMAL); pid.setLimits(1,9); pid.setpoint = desiredTemperature; sensors.begin(); start_timer0.setTimeout(10000);//время продувки перед запуском горелки start_timer1.setTimeout(20000);//время загрузки пеллет на розжиг start_timer2.setTimeout(180000);//время работы зажигалки start_timer3.setTimeout(25000);//время стабилизации пламени stop_timer1.setTimeout(100000);//время дожига пеллет перед останокой горелки stop_timer2.setTimeout(300000);//время продувки перед останокой горелки //Serial.begin(9600); } void loop() { /*static uint32_t timer_ser; if(millis() - timer_ser >= 2000){ timer_ser = millis(); Serial.println(pid.output); Serial.println(reg1); } */ button(); fillPillets(); pid.input = currentTemp; pid.setpoint = desiredTemperature; pid.getResultTimer(); reg_limits(); average_fire(); temp(); work(); startWork(); finishWork(); auto_start(); //fire_bar(); startPWM(); screen(); } void reg_limits(){ reg1=desiredTemperature-currentTemp; if(reg1>=40){ pid.setLimits(1,9); }else if(reg1>=30){ pid.setLimits(1,7); }else if(reg1>=20){ pid.setLimits(1,5); }else if(reg1>=10){ pid.setLimits(1,3); } } void fire_bar(){ //Функция таймера работы подвижного колосника static uint32_t oldMillis = millis(); static uint32_t onOffTime=500000; uint32_t newMillis = millis(); boolean firebar_On = digitalRead(firebarPin); if(newMillis-oldMillis>=onOffTime){ oldMillis=millis(); firebar_On=!firebar_On; onOffTime=5000+(300000*firebar_On);//!firebar_On для мосфета, firebar_On для реле управление по LOW. digitalWrite(firebarPin,firebar_On); } } void temp(){ //Функция обработки сигнала датчика температуры static uint32_t temp_timer; sensors.requestTemperatures(); if(millis()-temp_timer>1000){ temp_timer=millis(); currentTemp = sensors.getTempC(insideThermometer)*10;} } void average_fire(){ //Функция обработки сигнала датчика огня if (++index > 2) index = 0; val[index] = analogRead(A3); val_f = middle_of_3(val[0], val[1], val[2]); } void startPWM() { //функция управления ШИМ вентилятора static int fanspeed; if(startpwm==1){ fan_value=constrain(fan_value,-30,30); fanspeed_pr = PWM_val+fan_value; fanspeed_pr = constrain(fanspeed_pr, 0, 100); fanspeed = map(fanspeed_pr, 0, 100, 0, 253); analogWrite(pwmPin, fanspeed); } } void button(){ //Функция обработки кнопок desiredTemperature = constrain(desiredTemperature, 300, 800); if(disp_ButtonPin.isClick()){ displ+=1; if(displ >= 4)displ=0; } if(startButtonPin.isClick() and photoresistor==1){ startpwm=1; switch_start=0; } if(stopButtonPin.isClick()){ startpwm=1; stop_FPB=0; working=0; start1=0; switch_stop=0; } if(currentTemp>=800 and working==1){ startpwm=1; stop_FPB=0; working=0; start1=0; switch_stop=0; } if(currentTemp>=350){ digitalWrite(pumpPin, LOW);} else{digitalWrite(pumpPin, HIGH); } if(currentTemp<50 and start_count>=3){ digitalWrite(pumpPin, LOW); } } void auto_start(){ //Функция автостарта горелки при потере огня static uint32_t timer_fire; if(millis() - timer_fire >= 100){ timer_fire = millis(); if(val_f>1000 and start1==1){ fire_off+=1; } else{fire_off=0; } } if(fire_off==600 and start1==1){ start1=0; working=0; pid_out_last=100; switch_start=5; } } void screen(){ //функция вывода информации на дисплей static int for_disp; if(disp_on==1){ switch (displ){ //вывод температур, изменение заданной температуры case 0 : for_disp = desiredTemperature * 10; for_disp = for_disp + currentTemp/10; disp.displayInt(for_disp); disp.point(true); if(plusButtonPin.isClick()){ desiredTemperature+=50; plusButtonPin.resetStates(); } if(minusButtonPin.isClick()){ desiredTemperature-=50; minusButtonPin.resetStates(); } break; //вывод расхода грамм\час case 1 : disp.displayInt(dose); disp.point(false); break; //вывод скорости вентилятора в процентах с возможностью корректировки case 2 : disp.displayInt(fanspeed_pr); disp.displayByte(0, 0x71); disp.point(false); if(plusButtonPin.isClick() and fanspeed_pr<100){ fan_value+=5; plusButtonPin.resetStates(); } if(minusButtonPin.isClick()){ fan_value-=5; minusButtonPin.resetStates(); } break; //вывод уровня пламени 0-1000 или таймер если огня нет case 3 : if(val_f<1000){ disp.displayInt(val_f); disp.point(false); }else{ disp.displayInt(fire_off/10); disp.point(false);} break; } } } void fillPillets() { //Функция подачи пеллет из бункера в стокер static uint32_t timer_photo; if(millis() - timer_photo >= 500){ timer_photo = millis(); photoresistor = digitalRead(photoresistorPin); } if(stop_FPB==1){ if (photoresistor == 0) { digitalWrite(feederAugerPin, LOW);} else { digitalWrite(feederAugerPin, HIGH);} } } void startWork(){ //функция розжига switch(switch_start){ case 0: PWM_val=100; if(val_f<900){ switch_start=4;} break; case 1: PWM_val=20; digitalWrite(stokerAugerPin, LOW); if(val_f<600){ switch_start=2;} break; case 2: digitalWrite(stokerAugerPin, HIGH); switch_start=3; break; case 3: PWM_val=90; digitalWrite(heatingElementPin, LOW); break; case 4: digitalWrite(heatingElementPin, HIGH); p_pause=3750; w_pause=200; PWM_val=60; work_timer(); break; case 5: digitalWrite(heatingElementPin, HIGH); start_count+=1; if(start_count<=3){ switch_start=0;} else{ switch_start=9; start1=0; working=0; } break; case 6: start1=1; working=1; switch_start=8; break; } if(switch_start==0){ if(start_timer0.isReady())switch_start=1;}else{start_timer0.start();} if(switch_start==1){ if(start_timer1.isReady())switch_start=2;}else{start_timer1.start();} if(switch_start==3){ if(val_f<750)switch_start=4;} if(switch_start==3){ if(start_timer2.isReady())switch_start=5;}else{start_timer2.start();} if(switch_start==4){ if(start_timer3.isReady())switch_start=6;}else{start_timer3.start();} } void work() { //Функция выбора режима мощности работы горелки if (working == 1) { start_count = 0; if (pid_out_last != pid.output){ digitalWrite(stokerAugerPin,HIGH); switch (pid.output) { //p_pause=пауза подачи. w_pause=подача. PWM_val=вентилятор в процентах. dose=расход грамм\час. case 0 _pause=10000;w_pause=100;PWM_val=35;dose = 180;break; case 1 _pause=8520;w_pause=100;PWM_val=40;dose = 210;break; case 2 _pause=7500;w_pause=100;PWM_val=45;dose = 240;break; case 3 _pause=6000;w_pause=100;PWM_val=50;dose = 300;break; case 4 _pause=5000;w_pause=100;PWM_val=60;dose = 360;break; case 5 _pause=4500;w_pause=100;PWM_val=65;dose = 400;break; case 6 _pause=3000;w_pause=155;PWM_val=75;dose = 850;break; case 7 _pause=3750;w_pause=200;PWM_val=80;dose = 1000;break; case 8 _pause=3300;w_pause=200;PWM_val=90;dose = 1630;break; case 9 _pause=3000;w_pause=300;PWM_val=100;dose = 2000;break; } } pid_out_last = pid.output; work_timer(); } } void work_timer(){ //Функция таймер работы горелки static uint32_t oldMillis = millis(); static uint16_t onOffTime; uint32_t newMillis = millis(); boolean soker_On = digitalRead(stokerAugerPin); if(newMillis-oldMillis>=onOffTime){ oldMillis=millis(); soker_On=!soker_On; onOffTime=w_pause+((p_pause - w_pause)*soker_On);//!soker_On для мосфета, soker_On для реле управление по LOW. digitalWrite(stokerAugerPin,soker_On); } } void finishWork(){ //Функция остановки работы горелки switch(switch_stop){ case 0: disp_on=0; stop_FPB=0; p_pause=3000; w_pause=250; PWM_val=100; disp.displayByte(0x6d, 0x78, 0x3f, 0x73); disp.point(false); work_timer(); break; case 1: digitalWrite(stokerAugerPin, HIGH); stop_FPB=0; PWM_val=100; break; case 2: disp_on=1; PWM_val=0; startpwm=0; stop_FPB=1; switch_stop=3; break; } if(switch_stop==0){ if(stop_timer1.isReady()) switch_stop=1;}else{stop_timer1.start();} if(switch_stop==1){ if(stop_timer2.isReady()){ PWM_val=0; switch_stop=2;} }else{stop_timer2.start();} } контроллеру нужно задать режимы работы в скетче, а затем он управляется при помощи четырех кнопок. пуск, стоп, плюс и минус температура. есть еще кнопка которая переключает дисплей, показывать температуру, расход пеллет, значение вентилятора (с возможностью коррекции), и значение датчика огня но то уже я делал для себя... основные это температура и вентилятор. в планах сделать что бы режимы задавались не рукописно, а вводить контроллеру например вес некого объема пеллет и он относительно этого веса перещитывал режимы подачи и паузы. у меня есть канал на ютубе где я делаю это все https://www.youtube.com/channel/UCW6PoUoch5vHGwUPKVOn-cA
Как я понимаю содержание не соответствует теме. Котел шахтный?Какой? Горелка факельная? Какая? Подача я так понял стартстоп. Вентилятор какой? Чем управляется? Как соотносится подача топлива и объем воздуха? Какие используются пеллеты, по какой цене? --- добавлено: Oct 14, 2021 7:43 AM --- Задавать надо не вес топлива, а требуемую температуру в помещении, а контроллер должен учивывать температуру наружного воздуха, учитывать теплопотери, расчитывать обороты вентилятора и скорость подачи.
Взагалі то код краще було би у вкладеному файлі викласти. А то деякі місця смайликами позамінялися Та і схему, хоча би блочно бажано було би показати. Можна і з коду виловити, що куди підключено, але все ж таки, не всі це зможуть.