Контроллер пелетного котла

Тема у розділі 'Твердотопливные котлы', створена користувачем Smarodina, 14 жов 2021.

  1. Smarodina

    Smarodina Прораб

    Повідомлення:
    503
    Симпатії:
    258
    Адреса:
    Харьков
    Всем привет, меня зовут Дима, и я сварщик:) живу я в Украине, в городе Харьков. Когда то газ был дешевый и некого не парило отопление дома, но в 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 :p_pause=10000;w_pause=100;PWM_val=35;dose = 180;break;
    case 1 :p_pause=8520;w_pause=100;PWM_val=40;dose = 210;break;
    case 2 :p_pause=7500;w_pause=100;PWM_val=45;dose = 240;break;
    case 3 :p_pause=6000;w_pause=100;PWM_val=50;dose = 300;break;
    case 4 :p_pause=5000;w_pause=100;PWM_val=60;dose = 360;break;
    case 5 :p_pause=4500;w_pause=100;PWM_val=65;dose = 400;break;
    case 6 :p_pause=3000;w_pause=155;PWM_val=75;dose = 850;break;
    case 7 :p_pause=3750;w_pause=200;PWM_val=80;dose = 1000;break;
    case 8 :p_pause=3300;w_pause=200;PWM_val=90;dose = 1630;break;
    case 9 :p_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
     
  2. Владмир

    Владмир Заслужений майстер

    Повідомлення:
    7.573
    Симпатії:
    7.923
    Адреса:
    Харьков
    Как я понимаю содержание не соответствует теме.
    Котел шахтный?Какой?
    Горелка факельная? Какая?
    Подача я так понял стартстоп.
    Вентилятор какой? Чем управляется? Как соотносится подача топлива и объем воздуха?
    Какие используются пеллеты, по какой цене?
    --- добавлено: Oct 14, 2021 7:43 AM ---
    Задавать надо не вес топлива, а требуемую температуру в помещении, а контроллер должен учивывать температуру наружного воздуха, учитывать теплопотери, расчитывать обороты вентилятора и скорость подачи.
     
    Останнє редагування модератором: 22 жов 2021
  3. Ant0ny2a

    Ant0ny2a Бригадир

    Повідомлення:
    166
    Симпатії:
    197
    Адреса:
    м. Суми
    Взагалі то код краще було би у вкладеному файлі викласти. А то деякі місця смайликами позамінялися :p
    Та і схему, хоча би блочно бажано було би показати. Можна і з коду виловити, що куди підключено, але все ж таки, не всі це зможуть.
     
    Владмир подобається це.
  4. Mediaservice

    Mediaservice Бригадир

    Повідомлення:
    909
    Симпатії:
    227
    Адреса:
    Днепр

Поділитися цією сторінкою