Очередная паяльная станция

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

Ну и гуд )) Не забывать, если будет обновление, что нужно подправить индикацию.

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

OlegK пишет:
Можно попробовать джля проверки сделать так - отключить цепь смещения по входу, на вход подать напряжение с батарейки 2А, 3А + делитель с подстроечником порядка 0-20...30 мВ и добиться подстроечником в ОС ОУ выходного напряжения, близкого к Uпит. Не забываем про полярность. Далее, подключаем ТП и чем-нибудь нагреваем (внутрь фена можно дунуть зажигалкой) и убедиться, что усилитель реагирует и показания возрастают при нагреве. Затем можно восстановить цепь смешения и выполнить настройку, как описывалось в теме..

поставил на входе 25 миливольт , при настройке R2 от нуля до максимума , выход на ОУ = 0,24 - 0,55 вольт соответственно 

напомню - на плате установленны 2*AD8551как дальше то быть ?

номиналы согласно приведенной схемы, на табло выводится 24 - 56 градцсов соответственно 

подключаю термопару показывает 0 , эта же термо пара на мультиметре работае правильно , показывает изменение температуры

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
поставил на входе 25 миливольт , при настройке R2 от нуля до максимума , выход на ОУ = 0,24 - 0,55 вольт соответственно

Мало... С номиналами 2кОм/200кОм коэфф. усиления каскада будет около 100 и при подаче на вход 25мВ ожидается 2,5В на выходе.  У Вас же что-то в районе 20...
Правильные ли номиналы запаяны в обвязке (R7, R2, R3)?

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

OlegK пишет:
Правильные ли номиналы запаяны в обвязке (R7, R2, R3)?

2ком,100ком- подстроечный миногооборотный,100ком

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

ОУ где покупались? И смущает ещё вывод 1, подключённый к земле - хоть он и NC, но бывает, что и не факт...

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

ОУ имеете в виду оригинал или с али ? - у меня оригинал

попробовать обрезать первую ногу ?

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
попробовать обрезать первую ногу ?

Ну, как минимум, стОит попробовать хотя бы в одном канале...
Не знаю, на что ещё грешить - плата, вроде правильно разведена (разве, что я бы земляную петлю разомкнул), номиналы соответствуют, а усиления нет. Получается, что в ОУ проблема, ибо даже симулятор показывает, что должно всё работать -

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

так у меня там не петля, плата 2х сторонняя , 2я сторона сплошная медь, которая подпаяна к земле на 1й стороне

или все равно разрезать землю по кругу?

Русл@н
Offline
Зарегистрирован: 14.04.2016

В ручке паяльника обломился провод, чем его заменить, так чтобы больше туда не соваться? Сейчас распаял провод от принтера LPT кажись. Но он толстоват.

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

я брал хвост от мышы PS/2 , только тот что по толще , а вобще есть силиконовый 5и жильный в продаже 

тут на вкус и цвет все фломастеры разные )))

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
или все равно разрезать землю по кругу?

Оно на работоспособность не влияет, но земляных петель я стараюсь избегать. Я бы разомкнул вот тут -

Но сперва нужно разобраться, почему не работает усилитель...

Русл@н пишет:
В ручке паяльника обломился провод, чем его заменить, так чтобы больше туда не соваться

Я заказывал у китайцев два метра. А временно можно задействовать заземляющий провод.

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

OlegK

спасибо за советы, завтра попробую ,

самое интерстное , что оба канала ведут себя одинаково

у меня имеются и другие раил ту раил ОУ

например в дипе - такой подойдет (простите за тривиальные вопросы любителя)

http://pdf.datasheetcatalog.com/datasheet/analogdevices/AD8042AR-REEL7.pdf

или такой

http://pdf.datasheetcatalog.com/datasheet/analogdevices/OP279GRU.pdf

смд такой

http://pdf1.alldatasheet.com/datasheet-pdf/view/49051/AD/OP282GS.html

http://pdf1.alldatasheet.com/datasheet-pdf/view/49015/AD/OP484ES.html

вот только теряюсь какой лучше , под какой платку переразвесьти ?

 

 

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
например в дипе - такой подойдет (простите за тривиальные вопросы любителя)

Да я и сам не великий спец ))
Как по мне, все указанные ОУ отличные, но,как бы, не совсем "измерительные" и не все могут работать при однополярном питании.

AD8042AR - В принципе, подойдёт, но в силу широкополосности склонен к возбуждению, поэтому я бы предусмотрел посадочное место под конденсатор пик на 100 параллельно резисторам ОС. Дрейф нуля от температуры даже чуть больше типовой величины LM358, предполагаю, измеренная Т будет уплывать на пару-тройку градусов при прогреве ОУ.

OP279GRU - этот уже получше и не слишком широкополосен, может работать при однополярном питании.

OP282GS - требует двуполярного питания.

OP484ES - 4 канала, лучше приберечь для какого-нибудь другого проекта.

Цитата:
вот только теряюсь какой лучше , под какой платку переразвесьти ?

Универсальнее будет развести под сдвоенный ОУ в DIP (OP279), но поставить "кроватку", куда всегда можно подоткнуть хоть LM358, для проверки, хоть AD8552 на переходничке.

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
самое интерстное , что оба канала ведут себя одинаково

Вот это и заставляет задуматься - или ОУ виноваты или попробовать 1 ногу отрезать, что в первую очередь и можно попробовать...

SERG_K
Offline
Зарегистрирован: 06.08.2017

Добрый день. Наконец то  пришел паяльник. Паяльник работает, температуру поддерживает, но такое ощущение что в нем живет «радистка Кэт».Попробовал поменять частоту ШИМ как было сказано в посте 566  в моей ардуино нано это не работает, частота ШИМ не меняется. Осциллограф  (теперь 3 нога) показал, что в стабильном режиме ( когда Т меняется на 1градус )  паяльник работает в обычном ключевом режиме причем высокий уровень сигнала  промодулированн частой 490Гц (успевает проскочить 8 имульсов.заполнение 50/255). Интервалы между пакетами неодинаковые, отсюда и морзянка.

Избавился от «радистки» так

void S_P() {

    int TempPower = sKp * (SetSolderT - GetSolderT);

   if (TempPower==sKp) TempPower = 255;

    SPower = constrain(TempPower, 0, 255);

    analogWrite(pinSolderPwm, SPower);}

Работа паяльника стабильна  и наконец то тихо.

Spinne
Spinne аватар
Offline
Зарегистрирован: 03.09.2017

Зарегистрировался только для того, чтобы сказать автору ОГРОМНОЕ СПАСИБО за его разработку и поддержку проекта)) Пока что еще только на коленке собираю, но очень толковое творение) Единственное, что не устроило в авторской версии - кнопки. Но тут -  на вкус и цвет))) Потому: переписал прошивку под энкодер) Если кому надо - могу выложить с позволения автора вариант прошивки под энкодер на базе версии 1.5)

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

Spinne, приветствую!
Уверен, вариант с энкодером обязательно найдёт своего пользователя, поэтому запускайте и выкладывайте.
Я размещу в "шапке", с указанием Вашего авторства варианта прошивки.

Igra343
Offline
Зарегистрирован: 10.07.2017

Еще раз переделал плату теперьпроблема в том что температуру без елм358 0 град как ставишь его вслот температура 380-400 по каналам фена и пояльника хотя они и не прдключены....

Igra343
Offline
Зарегистрирован: 10.07.2017

Еще раз переделал плату теперьпроблема в том что температуру без елм358 0 град как ставишь его вслот температура 380-400 по каналам фена и пояльника хотя они и не прдключены....

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

Igra343 пишет:
Еще раз переделал плату теперьпроблема в том что температуру без елм358 0 град

Проблема то в чём? Без LM-ки на входе контроллера будет 0, соответственно и температура 0.
#define LM358 раскомментирован? Если нет, то раскомментируйте.

Цитата:
как ставишь его вслот температура 380-400 по каналам фена и пояльника хотя они и не прдключены

А что ожидается получить, не подключая?
Если не подключены фен-паяльник, то смещение (цепь подстроечника "начало") будет давить ОУ до его максимума - это используется для определения обрыва цепи термодатчика.

ЗЫ. Или я не так понял и имелось ввиду, что не включены кнопкой каналы фена-паяла?

Spinne
Spinne аватар
Offline
Зарегистрирован: 03.09.2017

Авторство-то - ваше) Просто, как опцию, добавил энкодер) Честно говорю: в железе провернено не полностью - у меня пока только процессорная часть распаяна на макетке. Но, думаю, проблем не будет, так как модифицировал только ту часть прошивки, которая за управление отвечает. Подключение осталось на тех же пинах, что и кнопки: A1 - кнопка энкодера,  A2,A3 - сам энкодер. Подтяжка - внутренняя. Плюс распаять кондеры между входами и землей - для фильтрации помех. У меня - по 22n

/* * * * * * * * * * * * * * * * * * */
/*       Soldering Station v 1.5     */
/*           coded by OlegK          */
/*    arduino.ru/forumy/proekty      */
/* * * * * * * * * * * * * * * * * * */

#include <EEPROM.h>
#include <CyberLib.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce2.h>
#include <avr/io.h>
#include <avr/interrupt.h>

/* it's fuckin' magic! */
#if 1
__asm volatile ("nop");
#endif

/* Options */

/* if you need to create themperature diagramm then uncomment it */
//#define NEED_GRAPH

/* if used not rail-to-rail OPA, then uncomment it */
//#define LM358

/* if used passive busser, then uncomment it */
//#define PASSIVE_BUZZER

/* if you want deactivate additional protection, then COMMENT it */
#define HA_ADV_PROT_ON
#define S_ADV_PROT_ON

/* if used LCD 1602, then uncomment it */
//#define LCD_1602

/* if used ENCODER, then uncomment it */
#define ENCODER 

/* End options */


#define FIRMWARE_VERSION "1.5enc"

#ifdef LCD_1602
LiquidCrystal_I2C lcd(0x3F, 16, 2);
#else
LiquidCrystal_I2C lcd(0x3F, 20, 4);
#endif

/* Degree symbol */
uint8_t degree[8] = {
    B01100,
    B10010,
    B10010,
    B01100,
    B00000,
    B00000,
    B00000,
    B00000
};

/* Arrow symbol */
uint8_t arrow[8] =  {
    B00000,
    B00100,
    B00010,
    B11111,
    B00010,
    B00100,
    B00000,
    B00000
};

/* mode selector */
#define  modeSolder 1
#define  modeHotAir 2
#define  modeFanPWM 3

boolean need_S_countdown = false;
byte selected_Mode = modeSolder;
byte HA_countdown = 1;
byte S_countdown = 1;
uint16_t HA_sleeptime = 10;
uint16_t S_sleeptime = 10;
uint16_t Graph_count = 0;

#ifdef ENCODER
uint32_t Enc_ButtonPressTime = 0;
boolean Enc_ButtonState;
#else
uint32_t UPbuttonPressTime = 0;
uint32_t DWNbuttonPressTime = 0;
boolean UPbuttonState, DWNbuttonState;
#endif

uint32_t SONbuttonPressTime = 0;
uint32_t HAONbuttonPressTime = 0;
boolean SONbuttonState, HAONbuttonState;

byte Count;
uint16_t Duration, Interval;

char bspace[ ] = "    ";

#define pinBuzzer 12
#define pinFanPwm 6
#define pinSolderPwm 5

#define min_solder_temp 100
#define max_solder_temp 400
#define min_hotair_temp 50
#define max_hotair_temp 450
#define min_rpm 30
#define max_rpm 100
#define default_temp 280
#define default_rpm 50

/* Buttons */
#define sw_HA 10
#define sw_S 9
#define bt_SON 13
#define bt_HAON 14

#ifdef ENCODER
#define e_button 15 //pin A1 button
#define e_dir 16    //pin A2 enc_a
#define e_step 17   //pin A3 enc_b
#else
#define bt_Sel 15   //pin A1
#define bt_Up 16    //pin A2
#define bt_Dwn 17   //pin A3
#endif

/* Bounce killers */
Bounce swHotAir = Bounce();
Bounce swSolder = Bounce();
Bounce SolderOnButton = Bounce();
Bounce HotAirOnButton = Bounce();

#ifdef ENCODER
Bounce Enc_Button = Bounce();
#else
Bounce SelButton = Bounce();
Bounce UpButton = Bounce();
Bounce DwnButton = Bounce();
#endif

/* Hot Air */

/* states */
#define st_stop 0
#define st_work 1
#define st_pause 2
#define st_protection 3
//#define st_lowpower 4

byte hotair_state = st_stop;

volatile uint16_t ots = 9990;
volatile float HAPower = 0.0;
uint16_t GetHotAirT = 0;
uint16_t SetHotAirT = 100;
byte SetHotAirRPM = 100;
boolean HA_temp_stable = false;
boolean need_Cooling = true;
boolean scr_blink = false;

byte ha_error = 0;
boolean HA_prot_beep = false;
boolean ha_f1 = false;
boolean ha_f2 = false;
uint32_t prevHAcontrol;

/* HA PI regulator */
#define Kp 1.0
#define Ki 0.007 //0.003 //0.005 //0.05
int integral = 0;

/* Soldering iron */
uint16_t GetSolderT = 0;
uint16_t SetSolderT = 100;
boolean S_temp_stable = false;
boolean SolderON = false;
boolean SolderProtect = false;
int SPower = 0;
//byte solder_state = st_stop;

byte s_error = 0;
uint32_t prevScontrol;
boolean S_prot_beep = false;
boolean s_f1 = false;
boolean s_f2 = false;

/* Solder P regulator */
#define sKp 50

uint16_t last_HotAirT, last_SolderT;
byte last_RPM;


/********************************************* MAIN PROCEDURES *********************************************/

void setup() {
  //ADC speedup
  //ADCSRA &= ~(1 <<ADPS2) | (1 <<ADPS1) | (1 <<ADPS0); // reset 128
  //ADCSRA |= 1 <<ADPS2; // set to 16 (1MHz)
  
    pinMode(3, INPUT_PULLUP); //Zero cross pin
    D5_Out; //pinSolder
    D5_Low;
    D4_Out; //pinSolderProt
    D4_Low;
    D7_Out; //pinHotAirProt
    D7_Low;
    D8_Out; //pinHotAir
    D8_Low;
    D12_Out; //pinBuzzer
    D12_Low;
    ButtonsSetup();

#ifdef NEED_GRAPH
    Serial.begin(9600);
#endif

    lcd.begin();
    lcd.backlight();
    lcd.createChar(0, degree);
    lcd.createChar(1, arrow);
    Splash();
    MemRead();

    delay_ms(2000);

    initDisplay();
    attachInterrupt(1, ZC, FALLING);
}

void loop() {
    ScanButtons();

    /* Off-timer for HotAir, countdown with 1 min */
    static uint32_t prevHAmillis = millis();
    if (hotair_state == st_pause) {
        if (millis() - prevHAmillis > 60000) {
            prevHAmillis = millis();
            if (HA_countdown > 1) {
                HA_countdown--;
                if (HA_countdown == 1) {
                    Beep(100);    //Beep, if 1 minute left
                }
            } else {
                hotair_state = st_stop;
                Beep(200);
            }
        }
    } else {
        prevHAmillis = millis();
    }

    /* Off-timer for Solder, countdown with 1 min */
    static uint32_t prevSmillis = millis();
    if (need_S_countdown) {
        if (millis() - prevSmillis > 60000) {
            if (S_countdown > 1) {
                S_countdown--;
                if (S_countdown == 1) {
                    Beep(100);    //Beep, if 1 minute left
                }
            } else {
                MemSolder();
                need_S_countdown = false;
                SolderON = false;
                Beep(200);
            }
            prevSmillis = millis();
        }
    } else {
        prevSmillis = millis();
    }

    /* Update LCD with 500ms interval */
    static uint32_t prevDisplayMillis = millis();
    if (millis() - prevDisplayMillis > 500) {
        scr_blink = ! scr_blink;
        prevDisplayMillis = millis();
        DisplayUpdate();

        /* Send data to Serial port */
#ifdef NEED_GRAPH
        if (SolderON || hotair_state == st_work) {
            Graph_count++;
            //Serial.print(Graph_count);
            //Serial.print(";");
        } else {
            Graph_count = 0;
        }
        if (SolderON && hotair_state != st_work) {
            Serial.println(GetSolderT);
        } else if (!SolderON && hotair_state == st_work) {
            Serial.println(GetHotAirT);
        }
#endif
    }

    WorkWithHotAir();
    WorkWithSolder();
}

/* HotAir working procedure */
void WorkWithHotAir() {

    /* Read the thermocouple value */
    //GetHotAirT = A7_Read >> 1;

#ifdef LM358
    GetHotAirT = getOversampled_HA();
#else
    GetHotAirT = getOversampled_HA() >> 1;
#endif

    switch (hotair_state) {
    case st_stop: {
        HotAirOff();
        Cooling();
        break;
    }

    case st_work: {
        /* Set the cooler rpm (convert from 30-100% to 80-255 PWM) */
        analogWrite(pinFanPwm, map(SetHotAirRPM, min_rpm, max_rpm, 80, 255));

        ha_error = HADoProtect();

        /* Turn ON protection relay */
        if (ha_error == 0) D7_High;

        /* Themperature PI regulator */
        HA_PI();

        /* If themperature was stable for 100 times (+/-2 degrees), then signalize about it */
        int delta = ABS(SetHotAirT, GetHotAirT);
        static byte HAgood;
        if (!HA_temp_stable) {
            if (delta < 3) {
                HAgood++;
                if (HAgood == 100) {
                    Beep(50);
                    delay_ms(200);
                    Beep(50);
                    HAgood = 0;
                    HA_temp_stable = true;
                }
            } else {
                HAgood = 0;
            }
        } else {
            if (delta > 5) {
                HA_temp_stable = false;
            }
        }
        break;
    }

    case st_pause: {
        HAPower = 0.0;
        HA_temp_stable = false;
        CalctImpulseControl();
        Cooling();
        ha_error = HADoProtect();
        break;
    }

    case st_protection: {
        need_Cooling = true;
        break;
    }

    } //switch (state)
}

/* Solder working procedure */
void WorkWithSolder() {

    /* Read the thermoresistor value */
    //GetSolderT = A6_Read >> 1;
#ifdef LM358
    GetSolderT = getOversampled_S();
#else
    GetSolderT = getOversampled_S() >> 1;
#endif

    if ( SolderON ) {
        s_error = SDoProtect();

        /* Turn ON relay */
        if (s_error == 0) D4_High;

        /* themperature P regulator */
        S_P();

        /* If themperature was stable for 200 times loop (+/- 2 degrees) then signalize about it */
        uint16_t delta = ABS(SetSolderT, GetSolderT);
        static byte Sgood;
        if (!S_temp_stable) {
            if (delta < 3) {
                Sgood++;
                if (Sgood == 200) {
                    Beep(50);
                    delay_ms(200);
                    Beep(50);
                    //Sgood = 0;
                    S_temp_stable = true;
                }
            } else {
                Sgood = 0;
            }
        } else {
            if (delta > 5) {
                S_temp_stable = false;
            }
        }
    } else {
        SolderOff();
        need_S_countdown = false;
        S_countdown = 1;
    }
}

/***************************************** END OF MAIN PROCEDURES ******************************************/



/****************************************** PROTECTION & ON-OFF ********************************************/

/* HotAir protection */
byte HADoProtect() {

    /* ------------------------------------------------------------------------------------------------------- */

    /* Crytical protection: high overheat or thermocouple value is not valid or wire break */
    if (GetHotAirT > max_hotair_temp + 20) {
        HAProtectionOut();
        return 1;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Crytical protection: thermocouple value is not valid or wires short circuit */
    if (GetHotAirT < 10) {
        HAProtectionOut();
        return 2;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Overheat protection */
    if (ha_f1 && GetHotAirT > SetHotAirT + 20) {
        ha_f1 = false;
        HAProtectionOut();
        return 3;
    }

    if (ha_f2 && GetHotAirT < SetHotAirT + 15) {
        ha_f1 = true;
        ha_f2 = false;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Deviation themperature protection +/- 10 degrees */
    //if (HA_temp_stable) {
    //ha_f3 = true;
    //}
//
    //if (ha_f3) {
    //if (GetHotAirT > SetHotAirT + 10) {
    //HAProtectionOut();
    //return 4;
    //}
//
    //if (GetHotAirT < SetHotAirT - 10) {
    //HAProtectionOut();
    //return 5;
    //}
    //}

    /* ------------------------------------------------------------------------------------------------------- */

#ifdef HA_ADV_PROT_ON

    /* Advanced protection: the themperature falls down & power > 0 */
    /* & */
    /* Advanced protection: the themperature is not changed within 10 secs */
    /* & */
    /* Advanced protection: the themperature rise up & power < 0 */

    if (!HA_temp_stable) {
        static uint16_t prev_t;
        static byte t_cnt = 0;
        static byte t_cnt2 = 0;
        static boolean ha_ctrl = true;

        if (ha_ctrl) {
            prev_t = GetHotAirT;
            ha_ctrl = false;
            prevHAcontrol = millis();
        }

        if (!ha_ctrl && millis() - prevHAcontrol > 1000) {
            ha_ctrl = true;

            if (HAPower > 0.0) {

                /* themperature falls or not changed */
                if (prev_t >= GetHotAirT && GetHotAirT < SetHotAirT) {
                    t_cnt++;
                    if (t_cnt == 5) {
                        HAProtectionOut();
                        t_cnt = 0;
                        return 6;
                    }
                } else t_cnt = 0;

            } else { //HAPower == 0.0

                /* themperature rise */
                if (prev_t < GetHotAirT && GetHotAirT > SetHotAirT) {
                    t_cnt2++;
                    if (t_cnt2 == 5) {
                        HAProtectionOut();
                        t_cnt2 = 0;
                        return 7;
                    }
                } else t_cnt2 = 0;
            }
        }
    } else prevHAcontrol = millis();

#endif

    /* ------------------------------------------------------------------------------------------------------- */

    /* if everything is OK */
    return 0;
}

/* Solder protection */
byte SDoProtect() {

    /* ------------------------------------------------------------------------------------------------------- */

    /* Crytical protection: high overheat or thermoresistor value is not valid or wire break */
    if (GetSolderT > max_solder_temp + 20) {
        SProtectionOut();
        return 1;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Crytical protection: thermoresistor value is not valid or wires short circuit */
    if (GetSolderT < 10) {
        SProtectionOut();
        return 2;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Overheat protection */
    if (s_f1 && GetSolderT > SetSolderT + 20) {
        s_f1 = false;
        SProtectionOut();
        return 3;
    }

    if (s_f2 && GetSolderT < SetSolderT + 15) {
        s_f1 = true;
        s_f2 = false;
    }

    /* ------------------------------------------------------------------------------------------------------- */

    /* Deviation themperature protection +/- 10 degrees */
    //if (S_temp_stable) {
    //s_f3 = true;
    //}
//
    //if (s_f3) {
    //if (GetSolderT > SetSolderT + 10) {
    //SProtectionOut();
    //return 4;
    //}
    //Not required for soldering iron
    //if (GetSolderT < SetSolderT - 10) {
    //SProtectionOut();
    //return 5;
    //}
    // }

    /* ------------------------------------------------------------------------------------------------------- */

#ifdef S_ADV_PROT_ON

    /* Advanced protection: the themperature falls down & power > 0 */
    /* & */
    /* Advanced protection: the themperature is not changed within 5 secs */
    /* & */
    /* Advanced protection: the themperature rise up & power < 0 */
    if (!S_temp_stable) {
        static uint16_t prev_t;
        static byte t_cnt = 0;
        static byte t_cnt2 = 0;
        static boolean s_ctrl = true;

        if (s_ctrl) {
            prev_t = GetSolderT;
            s_ctrl = false;
            prevScontrol = millis();
        }

        if (!s_ctrl && millis() - prevScontrol > 1000) {
            s_ctrl = true;

            if (SPower > 0) {

                /* themperature falls or not changed */
                if (prev_t >= GetSolderT && GetSolderT < SetSolderT) {
                    t_cnt++;
                    if (t_cnt == 5) {
                        SProtectionOut();
                        t_cnt = 0;
                        return 6;
                    }
                } else t_cnt = 0;
            } else { //SPower == 0.0

                /* themperature rise */
                if (prev_t < GetSolderT && GetSolderT > SetSolderT) {
                    t_cnt2++;
                    if (t_cnt2 == 5) {
                        SProtectionOut();
                        t_cnt2 = 0;
                        return 7;
                    }
                } else t_cnt2 = 0;
            }
        }
    } else prevScontrol = millis();

#endif

    /* ------------------------------------------------------------------------------------------------------- */

    /* everything is OK */
    return 0;
}

/* Solder full off */
void SolderOff() {
    analogWrite(pinSolderPwm, 0);
    D5_Low;
    D4_Low;
    if (S_prot_beep) {
        S_prot_beep = false;
        Beep(1000);
        MemSolder();
    }
}

/* HotAir full off */
void HotAirOff() {
    HAPower = 0.0;
    D8_Low;
    delay_ms(10);
    D7_Low;
    hotair_state = st_stop;
    if (HA_prot_beep) {
        HA_prot_beep = false;
        Beep(1000);
        MemHotAir();
    }
}

/* Cooling the heater until the temperature is below 50 degrees */
void Cooling() {
    if (GetHotAirT >= 50 && need_Cooling) {
        analogWrite(pinFanPwm, 255);
    } else {
        analogWrite(pinFanPwm, 0);
        need_Cooling = false;
    }
}

/* internal procedure */
void set_ha_f() {
    boolean a = SetHotAirT >= GetHotAirT;
    ha_f1 = a;
    ha_f2 = !a;
}

/* internal procedure */
void set_s_f() {
    boolean a = SetSolderT >= GetSolderT;
    s_f1 = a;
    s_f2 = !a;
}

/* Solder protection */
void SProtectionOut() {
    SolderProtect = true;
    S_prot_beep = true;
    SolderON = false;
    SolderOff();
}

/* HotAir protection out */
void HAProtectionOut() {
    analogWrite(pinFanPwm, 255);
    HA_prot_beep = true;
    HotAirOff();
    hotair_state = st_protection;
}

/****************************************** END OF PROTECTION & ON-OFF *************************************/



/************************************************ OTHER ROUTINES *******************************************/

/* Read integer value */
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;
}

/* Write integer value */
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]);
    }
}

/* Read last parameters from memory */
void MemRead() {
    SetSolderT = EEPROM_int_read(0);
    if (SetSolderT < min_solder_temp || SetSolderT > max_solder_temp)  {
        SetSolderT = default_temp;
        last_SolderT = SetSolderT;
    }
    SetHotAirT = EEPROM_int_read(4);
    if (SetHotAirT < min_hotair_temp || SetHotAirT > max_hotair_temp) {
        SetHotAirT = default_temp;
        last_HotAirT = SetHotAirT;
    }
    SetHotAirRPM = EEPROM_int_read(8);
    if (SetHotAirRPM < min_rpm || SetHotAirRPM > max_rpm) {
        SetHotAirRPM = default_rpm;
        last_RPM = SetHotAirRPM;
    }
}

/* Write last used solder themperature to memory */
void MemSolder() {
    if (last_SolderT != SetSolderT) {
        EEPROM_int_write(0, SetSolderT);
        last_SolderT = SetSolderT;
    }
}

/* Write last used HotAir themperature and fan r.p.m. to memory */
void MemHotAir() {
    if (last_HotAirT != SetHotAirT) {
        EEPROM_int_write(4, SetHotAirT);
        last_HotAirT = SetHotAirT;
    }
    if (last_RPM != SetHotAirRPM) {
        EEPROM_int_write(8, SetHotAirRPM);
        last_RPM = SetHotAirRPM;
    }
}

/* Sound procedure */
void Beep(uint16_t duration) {
#ifdef PASSIVE_BUZZER
    tone(pinBuzzer, 1000, duration);
#else
    D12_High;
    delay_ms(duration);
    D12_Low;
#endif
}

/* HotAir oversampling function */
uint16_t getOversampled_HA() {
    uint32_t tmp = 0;
    for (byte z = 0; z < 64; z++) {
        tmp +=  A7_Read;
    }
    return tmp >> 6;
}

/* Solder oversampling function */
uint16_t getOversampled_S() {
    uint32_t tmp = 0;
    for (byte z = 0; z < 64; z++) {
        tmp +=  A6_Read;
    }
    return tmp >> 6;
}

/* Get absolute difference function */
uint16_t ABS(uint16_t a, uint16_t b) {
    if (a > b) {
        return (a - b);
    }
    return (b - a);
}

/****************************************** END OF OTHER ROUTINES ******************************************/



/*************************************** TRIAC CONTROL & AUTOMATHIC ****************************************/

/* Zero cross INT1 */
void ZC() {
    StartTimer1(HeaterOn, ots);
    RestartTimer1();
}

/* Triac open impulse */
void HeaterOn() {
    if (HAPower > 0.0) {
        D8_High;
        delay_us(100);
    }
    D8_Low;
    StopTimer1();
}

/* Calculate triac open delay */
void CalctImpulseControl() {
    ots = (uint16_t)(acos(HAPower / 50.0 - 1.0 ) * 9900.0 / pi);
}

/* HotAir PI regulator */
void HA_PI() {
    int err = SetHotAirT - GetHotAirT;
    float tmp_power = ((Kp * err) + (Ki * integral));
    float max_power = map(SetHotAirT, min_hotair_temp, max_hotair_temp, 10.0, 60.0);
    if (tmp_power < max_power && tmp_power > 0.0) {
        integral += err;
    }
    HAPower = constrain(tmp_power, 0.0, max_power);
    CalctImpulseControl();
}

/* Solder P regulator */
void S_P() {
    int TempPower = sKp * (SetSolderT - GetSolderT);
    SPower = constrain(TempPower, 0, 255);
    analogWrite(pinSolderPwm, SPower);
}

/* Solder P regulator with soft start */
//void S_P() {
//int TempPower = sKp * (SetSolderT - GetSolderT);
//byte maxPower = 255;
//if (GetSolderT < 100) {
//maxPower = 100;
//}
//SPower = constrain(TempPower, 0, maxPower);
//analogWrite(pinSolderPwm, SPower);
//}

/************************************* END OF TRIAC CONTROL & AUTOMATHIC ***********************************/



/**************************************** INTERFACE CONTROLS & TIMERS **************************************/

/* Scan buttons */
void ScanButtons() {

    /* HotAir stand switch */
    if (swHotAir.update() && hotair_state != st_stop) {
        if (swHotAir.fell()) {
            hotair_state = st_pause;
            need_Cooling = true;
            resetHotAirCountown();
        } else {
            if (hotair_state == st_pause) {
                hotair_state = st_work;
                integral = 0;
                set_ha_f() ;
            }
        }
        Beep(50);
    } else if (hotair_state == st_work && D10_Read == LOW) {
        D7_High;
        hotair_state = st_pause;
        need_Cooling = true;
        resetHotAirCountown();
    }

    /* Solder stand switch */
    if (swSolder.update() && SolderON) {
        resetSolderCountdown();
        Beep(50);
    }

    /* Solder "on-off" button */
    if (SolderOnButton.update()) {
        if (SolderOnButton.read()) {
            SONbuttonState = false;
        } else {
            if (!SolderON) {
                SolderON = true;
                SolderProtect = false;
                Activate_S_countdown();
                Graph_count = 0;
                selected_Mode = modeSolder;
                resetSolderStablePoint();
                set_s_f();
            } else {
                resetSolderCountdown();
            }
            SONbuttonState = true;
            SONbuttonPressTime = millis();
            Beep(50);
        }
    }

    if  (SONbuttonState) {
        if ( millis() - SONbuttonPressTime >= 1000 ) { //long press
            SONbuttonPressTime = millis();
            if (SolderON) {
                SolderON = false;
                MemSolder();
                Beep(200);
            }
        }
    }

    /* HotAir "on-off" button */
    if (HotAirOnButton.update()) {
        if (HotAirOnButton.read()) {
            HAONbuttonState = false;
        } else {
            if (hotair_state != st_work) {
                hotair_state = st_work;
                Graph_count = 0;
                selected_Mode = modeHotAir;
                need_Cooling = true;
                resetHotAirStablePoint();
                set_ha_f();
                integral = 0;
            } else {
                resetHotAirCountown();
            }
            HAONbuttonState = true;
            HAONbuttonPressTime = millis();
            Beep(50);
        }
    }

    if  (HAONbuttonState) {
        if ( millis() - HAONbuttonPressTime >= 1000 ) { //long press
            HAONbuttonPressTime = millis();
            if (hotair_state == st_work || hotair_state == st_pause) {
                hotair_state = st_stop;
                need_Cooling = true;
                MemHotAir();
                Beep(200);
            }
        }
    }

#ifdef ENCODER    
    /* Encoder button short press */
    if (Enc_Button.update()) {
        if (Enc_Button.fell()) {
            Beep(50);
            Enc_ButtonPressTime = millis();
            Enc_ButtonState = true;
        } else {
            if (Enc_ButtonState) {
                (selected_Mode < modeFanPWM) ? (selected_Mode++) : (selected_Mode = modeSolder);
            }
            Enc_ButtonState = false;
        }
    }
#else
    /* Select button */
    if (SelButton.update()) {
        if (SelButton.fell()) {
            Beep(50);
            (selected_Mode < modeFanPWM) ? (selected_Mode++) : (selected_Mode = modeSolder);
        }
    }

    /* UP button */
    static boolean short_press_flag = false;

    if (UpButton.update()) {
        if (UpButton.rose()) {
            UPbuttonState = false;
            short_press_flag = false;
        } else {
            switch (selected_Mode) {
            case modeSolder:
                if (SetSolderT < max_solder_temp) {
                    SetSolderT += 5;
                }
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                if (SetHotAirT < max_hotair_temp) {
                    SetHotAirT += 5;
                }
                break;
            case modeFanPWM:
                if (SetHotAirRPM < max_rpm) {
                    SetHotAirRPM += 5;
                }
                break;
            }

            if (selected_Mode > 1) {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }

            UPbuttonState = true;
            short_press_flag = true;
            UPbuttonPressTime = millis();
            Beep(50);
        }
    }

    /* UP button (long press) */
    if  (UPbuttonState) {
        if ( millis() - UPbuttonPressTime >= 500 ) {
            UPbuttonPressTime = millis();
            byte step = 10;
            if (short_press_flag) {
                step = 5;
                short_press_flag = false;
            }
            switch (selected_Mode) {
            case modeSolder:
                SetSolderT += step;
                if (SetSolderT > max_solder_temp) SetSolderT = max_solder_temp;
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                SetHotAirT += step;
                if (SetHotAirT > max_hotair_temp) SetHotAirT = max_hotair_temp;
                break;
            case modeFanPWM:
                SetHotAirRPM += step;
                if (SetHotAirRPM > max_rpm) SetHotAirRPM = max_rpm;
                break;
            }
            if (selected_Mode > 1) {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }
        }
    }

    /* Down button */
    if (DwnButton.update()) {
        if (DwnButton.read()) {
            DWNbuttonState = false;
            short_press_flag = false;
        } else {
            switch (selected_Mode) {
            case modeSolder:
                if (SetSolderT > min_solder_temp) {
                    SetSolderT -= 5;
                }
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                if (SetHotAirT > min_hotair_temp) {
                    SetHotAirT -= 5;
                }
                break;
            case modeFanPWM:
                if (SetHotAirRPM > min_rpm) {
                    SetHotAirRPM -= 5;
                }
                break;
            }

            if (selected_Mode > 1) {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }

            DWNbuttonState = true;
            short_press_flag = true;
            DWNbuttonPressTime = millis();
            Beep(50);
        }
    }

    /* Down button (long press) */
    if  (DWNbuttonState) {
        if ( millis() - DWNbuttonPressTime >= 500 ) {
            DWNbuttonPressTime = millis();

            byte step = 10;
            if (short_press_flag) {
                step = 5;
                short_press_flag = false;
            }

            switch (selected_Mode) {
            case modeSolder:
                SetSolderT -= step;
                if (SetSolderT < min_solder_temp) SetSolderT = min_solder_temp;
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                SetHotAirT -= step;
                if (SetHotAirT < min_hotair_temp) SetHotAirT = min_hotair_temp;
                break;
            case modeFanPWM:
                SetHotAirRPM -= step;
                if (SetHotAirRPM < min_rpm) SetHotAirRPM = min_rpm;
                break;
            }

            if (selected_Mode > 1) {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }
        } 
    }
#endif   
}

#ifdef ENCODER 
/* Encoder interrrupt */
ISR(PCINT1_vect) {
    static byte old_n=PINC & B1100; // ????? B00001100 ??? ? ?????? ?????? ?????? 2 ????
    byte new_n = PINC & B1100;

    if (old_n == B0100 && new_n == B1100 || old_n == B1000 && new_n == B0000) { //CW turn
            
            switch (selected_Mode)
      {
            case modeSolder:
                if (SetSolderT < max_solder_temp)
              { SetSolderT += 5; }
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                if (SetHotAirT < max_hotair_temp)
        { SetHotAirT += 5; }
                break;
            case modeFanPWM:
                if (SetHotAirRPM < max_rpm) 
        { SetHotAirRPM += 5; }
                break;
            }

            if (selected_Mode > 1) 
      {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }
            Beep(50);
    }
    
    if (old_n == B1000 && new_n == B1100 || old_n == B0100 && new_n == B0000) { //CCW turn
            
            switch (selected_Mode) 
      {
            case modeSolder:
                if (SetSolderT > min_solder_temp)
              { SetSolderT -= 5; }
                resetSolderStablePoint();
                set_s_f();
                resetSolderCountdown();
                break;
            case modeHotAir:
                if (SetHotAirT > min_hotair_temp) 
        { SetHotAirT -= 5; }
                break;
            case modeFanPWM:
                if (SetHotAirRPM > min_rpm) 
        { SetHotAirRPM -= 5; }
                break;
            }

            if (selected_Mode > 1) 
      {
                set_ha_f();
                resetHotAirStablePoint();
                resetHotAirCountown();
            }
            Beep(50);
    }
    old_n = new_n;
}
#endif

/* Buttons initialise */
void ButtonsSetup() {
    pinMode(sw_HA, INPUT_PULLUP);
    swHotAir.attach(sw_HA);
    swHotAir.interval(50);

    pinMode(sw_S, INPUT_PULLUP);
    swSolder.attach(sw_S);
    swSolder.interval(50);

    pinMode(bt_SON, INPUT_PULLUP);
    SolderOnButton.attach(bt_SON);
    SolderOnButton.interval(5);

    pinMode(bt_HAON, INPUT_PULLUP);
    HotAirOnButton.attach(bt_HAON);
    HotAirOnButton.interval(5);

#ifdef ENCODER
    pinMode(e_button, INPUT_PULLUP);//enc button 
    Enc_Button.attach(e_button);
    Enc_Button.interval(5);
    
    pinMode(e_dir, INPUT_PULLUP);   //pin A2 enc_a
    
    pinMode(e_step, INPUT_PULLUP);  //pin A3 enc_b

    PCICR=1<<PCIE1;     // enable interrrupt for group 1
    PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.   
#else
    pinMode(bt_Sel, INPUT_PULLUP);
    SelButton.attach(bt_Sel);
    SelButton.interval(10);

    pinMode(bt_Up, INPUT_PULLUP);
    UpButton.attach(bt_Up);
    UpButton.interval(5);

    pinMode(bt_Dwn, INPUT_PULLUP);
    DwnButton.attach(bt_Dwn);
    DwnButton.interval(5);
#endif
}

/* internal procedure */
void resetHotAirStablePoint() {
    HA_temp_stable = false;
}

/* internal procedure */
void resetSolderStablePoint() {
    S_temp_stable = false;
}

/* Reset HotAir countdown */
void resetHotAirCountown() {
    HA_countdown = HA_sleeptime;
}

/* Reset Solder countdown */
void resetSolderCountdown() {
    S_countdown = S_sleeptime;
}

/* Activate Solder countdown procedure */
void Activate_S_countdown() {
    need_S_countdown = true;
    S_countdown = S_sleeptime;
}

/**************************************** INTERFACE CONTROLS & TIMERS **************************************/


/************************************************* DISPLAY *************************************************/

/* "Hello" screen */
void Splash() {
    lcd.clear();
#ifdef LCD_1602
    lcd.setCursor(0, 0);
    lcd.print(F("Soldering Station"));
    lcd.setCursor(6, 1);
#else
    lcd.setCursor(2, 1);
    lcd.print(F("Soldering Station"));
    lcd.setCursor(7, 2);
#endif
    lcd.print(F("v "));
    lcd.print(FIRMWARE_VERSION);
}

/* Set LCD design */
void initDisplay() {
    lcd.clear();
#ifdef LCD_1602

#else
    lcd.setCursor(11, 0);
    lcd.print(F("Set"));

    lcd.setCursor(16, 0);
    lcd.print(F("Act"));

    lcd.setCursor(0, 1);
    lcd.print(F("Solder"));

    lcd.setCursor(0, 2);
    lcd.print(F("HotAir"));

    lcd.setCursor(0, 3);
    lcd.print(F("FanRPM"));
#endif
}

/* Update LCD */
void DisplayUpdate() {

#ifdef LCD_1602

    lcd.setCursor(0, 0);
    lcd.print(bspace);
    lcd.setCursor(0, 0);
    if (SolderProtect) {
        lcd.print(F("!"));
        lcd.print(s_error);
    } else if (SolderON) {
        lcd.print(S_countdown);
    }

    lcd.setCursor(3, 0);
    lcd.print(bspace);
    lcd.setCursor(3, 0);
    lcd.print(SetSolderT);
    lcd.write((byte)0);

    byte pos;
    pos = GetPos(GetSolderT);
    lcd.setCursor(8, 0);
    lcd.print(bspace);
    lcd.setCursor(8+pos, 0);
    if (GetSolderT > 505) {
        lcd.print(F("---"));
    } else {
        lcd.print(GetSolderT);
    }
    lcd.write((byte)0);

    lcd.setCursor(0, 1);
    lcd.print(bspace);
    lcd.setCursor(0, 1);

    switch (hotair_state) {
    case st_stop: {
        break;
    }

    case st_work: {
        if (HA_temp_stable) {
            lcd.print(F(" *"));
        } else {
            lcd.print(F(" :"));
        }
        break;
    }

    case st_pause: {
        lcd.print(HA_countdown);
        break;
    }

    case st_protection: {
        lcd.print(F("!"));
        lcd.print(ha_error);
        break;
    }

    }

    pos = GetPos(SetHotAirT);
    lcd.setCursor(3, 1);
    lcd.print(bspace);
    lcd.setCursor(3+pos, 1);
    lcd.print(SetHotAirT);
    lcd.write((byte)0);

    lcd.setCursor(8, 1);
    lcd.print(bspace);
    lcd.setCursor(8, 1);

    if (GetHotAirT > 505) {
        lcd.print(F("---"));
    } else {
        pos = GetPos(GetHotAirT);
        lcd.setCursor(8+pos, 1);
        lcd.print(GetHotAirT);
    }
    lcd.write((byte)0);

    lcd.setCursor(13, 1);
    lcd.print(bspace);
    lcd.setCursor(13, 1);

    pos = GetPos(SetHotAirRPM);
    if (need_Cooling && hotair_state != st_work) {
        lcd.print(F("100%"));
    } else {
        lcd.setCursor(13+pos, 1);
        lcd.print(SetHotAirRPM);
        lcd.print(F("%"));
    }

    lcd.setCursor(2, 0);
    lcd.print(F(" "));
    lcd.setCursor(2, 1);
    lcd.print(F(" "));
    lcd.setCursor(12, 1);
    lcd.print(F(" "));

    switch (selected_Mode) {
    case modeSolder: {
        lcd.setCursor(2, 0);
        break;
    }

    case modeHotAir: {
        lcd.setCursor(2, 1);
        break;
    }

    case modeFanPWM: {
        lcd.setCursor(12, 1);
        break;
    }
    }
    lcd.write((byte)1);

//if (hotair_state == ha_pause) {
//(scr_blink) ? (lcd.backlight()) : (lcd.noBacklight());
//} else {
//lcd.backlight();
//}

    if (hotair_state == st_pause) {
        lcd.setCursor(2, 1);
        lcd.blink_on();
    } else {
        lcd.blink_off();
    }

#else //LCD2004

    lcd.setCursor(6, 1);
    lcd.print(bspace);
    lcd.setCursor(7, 1);
    if (SolderProtect) {
        lcd.print(F("!"));
        lcd.print(s_error);
    } else if (SolderON) {
        lcd.print(S_countdown);
    }

    lcd.setCursor(11, 1);
    lcd.print(bspace);
    lcd.setCursor(11, 1);
    lcd.print(SetSolderT);
    lcd.write((byte)0);

    byte pos;
    pos = GetPos(GetSolderT);
    lcd.setCursor(16, 1);
    lcd.print(bspace);
    lcd.setCursor(16+pos, 1);
    if (GetSolderT > 505) {
        lcd.print(F("---"));
    } else {
        lcd.print(GetSolderT);
    }

    lcd.write((byte)0);

    lcd.setCursor(6, 2);
    lcd.print(bspace);
    lcd.setCursor(7, 2);

    switch (hotair_state) {
    case st_stop: {
        break;
    }

    case st_work: {
        if (HA_temp_stable) {
            lcd.print(F(" *"));
        } else {
            lcd.print(F(" :"));
        }
        break;
    }

    case st_pause: {
        lcd.print(HA_countdown);
        break;
    }

    case st_protection: {
        lcd.print(F("!"));
        lcd.print(ha_error);
        break;
    }

    }

    pos = GetPos(SetHotAirT);
    lcd.setCursor(11, 2);
    lcd.print(bspace);
    lcd.setCursor(11+pos, 2);
    lcd.print(SetHotAirT);
    lcd.write((byte)0);

    lcd.setCursor(16, 2);
    lcd.print(bspace);
    lcd.setCursor(16, 2);

    if (GetHotAirT > 505) {
        lcd.print(F("---"));
    } else {
        pos = GetPos(GetHotAirT);
        lcd.setCursor(16+pos, 2);
        lcd.print(GetHotAirT);
    }
    lcd.write((byte)0);

    lcd.setCursor(11, 3);
    lcd.print(bspace);
    lcd.setCursor(11, 3);

    pos = GetPos(SetHotAirRPM);
    if (need_Cooling && hotair_state != st_work) {
        lcd.print(F("100%"));
    } else {
        lcd.setCursor(11+pos, 3);
        lcd.print(SetHotAirRPM);
        lcd.print(F("%"));
    }

    for (byte z = 1; z < 4; z++) {
        lcd.setCursor(10, z);
        lcd.print(F(" "));
    }

    lcd.setCursor(10, selected_Mode);
    //lcd.print(F(">"));
    lcd.write((byte)1);

    //if (hotair_state == st_pause) {
    //(scr_blink) ? (lcd.backlight()) : (lcd.noBacklight());
    //} else {
    //lcd.backlight();
    //}

    if (hotair_state == st_pause) {
        lcd.setCursor(6, 2);
        lcd.blink_on();
    } else {
        lcd.blink_off();
    }

#endif

}

/* Get print position */
byte GetPos(uint16_t number) {
    if (number >= 100) {
        return 0;
    } else if (number < 10) {
        return 2;
    }
    return 1;
}

/*********************************************** END OF DISPLAY ********************************************/
  

 

SERG_K
Offline
Зарегистрирован: 06.08.2017

 

Спасибо Олегу и Руслану за проект. Собрал станцию на прошивке Руслана v 2.2  немного подправив код и плату под себя. Заменил регулирование на ПИД ,  сделал расчет отставания открывающего импульса по прервванию Zero cross INT1 . Рассчитывается для каждой полуволны.  Убрал морзянку из паяльника. С ПИД фен держит температуру +-2 градуса во всем диапазоне температур. Ну и несколько фото. Еще раз спасибо за проделанную работу.

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017
SERG_K
 
можете Ваш проект выложить ?
если не сложно , с коментами , что и где менялось 
п.с. нагуглдрайве если можно
заранее спасибо
 
OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

Spinne пишет:
Авторство-то - ваше) Просто, как опцию, добавил энкодер)

Я и имел ввиду авторство модификации под энкодер.

Spinne и остальные, кто не в курсе.
Плиз.
При добавлении километровых листингов пользуйтесь опцией редактора кода "Сворачивать код", что на вкладке "Дополнительно"...

kret.a.v@gmail.com
Offline
Зарегистрирован: 14.07.2017

OlegK   таки да!       

OlegK пишет:
это и заставляет задуматься - или ОУ виноваты или попробовать 1 ногу отрезать, что в первую очередь и можно попробовать...

кто бы мог подумать ... по даташиту все , и гдето на форуме прочел что неиспользуемые ноги рекомендуется кидать на землю, а оно вот оно как 

в общем откусил первые ноги , и оба канала заработали , а самое главное , красиво так , четенько )))

спасибо Вам за подсказки !!!

берусь оформлять в корпус , после откалибрую.....

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

kret.a.v@gmail.com пишет:
гдето на форуме прочел что неиспользуемые ноги рекомендуется кидать на землю

Не совсем неверное утверждение. Это смотря чьи ноги - входы логических элементов  - это одно, операционники - иное.
В частности, для неиспользуемых половинок ОУ есть такие рекомендации.
А по выводам "NC" я уже озвучивал выше.

Цитата:
спасибо Вам за подсказки !!!

Нуигуд ))

Spinne
Spinne аватар
Offline
Зарегистрирован: 03.09.2017

Извините... Я на форуме первые дни - не разбирался еще с такими нюансами((( прошу понять, и пггггостить) Хотел поправить, но уже недоступно редактирование((((

SERG_K
Offline
Зарегистрирован: 06.08.2017

Доброе утро. Олег вы не пробовали делать программную настройку температуры?  Может так проще, а то настраивать точно резисторами удовольствие еще то.

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

SERG_K пишет:
настраивать точно резисторами удовольствие еще то.

Согласен. Но в отличие от варианта без смещения ОУ по входу, тут достигается более-менее соответствующая НСХ термопары выходная характеристика. Без смещения, с программной интерполяцией, вне точки настройки возникает погрешность, тем больше, чем дальше от этой точки.
А вообще, пробовал, в проекте контроллера фена (в ветке есть тема), там тоже меня не особо устраивает, т.к. всё равно требует какой-то предварительной установки смещения и усиления, пусть и не точной.
Нужен какой-то более простой вариант.

uragan
Offline
Зарегистрирован: 23.02.2015

OlegK пишет:

SERG_K пишет:
настраивать точно резисторами удовольствие еще то.

Согласен. Но в отличие от варианта без смещения ОУ по входу, тут достигается более-менее соответствующая НСХ термопары выходная характеристика. Без смещения, с программной интерполяцией, вне точки настройки возникает погрешность, тем больше, чем дальше от этой точки.
А вообще, пробовал, в проекте контроллера фена (в ветке есть тема), там тоже меня не особо устраивает, т.к. всё равно требует какой-то предварительной установки смещения и усиления, пусть и не точной.
Нужен какой-то более простой вариант.

програмная настройка по двум точкам.  Первая - кипение, задает высоту , вторая - любая другая точка, где-нибудь около 300 гр, задает наклон тарировочной прямой. Пользуюсь таким образом, все устраивает. У "Овна" что-то подобное применяется.

SERG_K
Offline
Зарегистрирован: 06.08.2017

uragan пишет:

програмная настройка по двум точкам.  Первая - кипение, задает высоту , вторая - любая другая точка, где-нибудь около 300 гр, задает наклон тарировочной прямой. Пользуюсь таким образом, все устраивает. У "Овна" что-то подобное применяется.

[/quote]

Программно я так себе и представлял, может только температуре первой точки взять комнатную её проще и померить и в воду паяльник сувать не надо , ну вторая ближе к концу диапазона(снижаем ошибку). Измерял термопарой на кончике жала, завернув в ватный диск(позаимствовал у жены) хорошо получается, измерения стабильны, и что главное повторяемые. uragan если можно выложите код  посиотреть как это реализовано у Вас. Заранее спасибо.

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

SERG_K пишет:
может только температуре первой точки взять комнатную её проще и померить

Не проще. При одинаковой температуре горячего и опорного спая (а именно это у вас будет при комнатной температуре) термопара не выдаст НИЧЕГО, т.е. 0.

Цитата:
и в воду паяльник сувать не надо

А для фена это вообще противопоказано ))

SERG_K
Offline
Зарегистрирован: 06.08.2017

Смещение должно быть и К усиление предварительно  в любом случае расчитывать надо иначе улетим в насыщение ОУ это понятно.

SERG_K
Offline
Зарегистрирован: 06.08.2017

     Запаиваем расчетные резисторы вместо подстроечных. Включаем станцию при комнатной Т она показала положим 29 градусов или 15 это неважно у нас 24 это первая пара.        Устанавливаем на станции 300 измеряем температуру на жале пусть будет 270 это вторая пара, ну а потом программный пересчет. Коэффициенты в память.

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

Вы только что, в общих чертах, озвучили способ, применённый в контроллере фена ))

SERG_K
Offline
Зарегистрирован: 06.08.2017

Как я понял по коду у Вас  напряжение с термопары усиливается до напряжения соответствующего 0-5V 0-1024 тоесть точно градус в градус. Я предлагаю не заморачиваться с точной настройкой , несоответствие К усиления и смещение корректировать программно

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

В КПФ так и есть - две точки и линейная интерполяция между ними. Точной установки не требуется. Это устраняет требование несколько раз нагревать - остужать для укладки в диапазон. Там достаточно один раз нагреть и дать остыть до комнатной.

uragan
Offline
Зарегистрирован: 23.02.2015

SERG_K пишет:

     Запаиваем расчетные резисторы вместо подстроечных. Включаем станцию при комнатной Т она показала положим 29 градусов или 15 это неважно у нас 24 это первая пара.        Устанавливаем на станции 300 измеряем температуру на жале пусть будет 270 это вторая пара, ну а потом программный пересчет. Коэффициенты в память.

Не надо ничего пересчитывать. Первым коэффициентом в кипятке настраиваем 100 гр, и вторым любую известную температуру градусов около 300. И все. Коэффициенты в епроом. Между и за точками мапом. Если интересна температура больше 100 то чаще всего и смещение не нужно..

   Программы нет никакой. Одна строка маповская. Ну и естественно осреднение из нескольких замеров.

SERG_K
Offline
Зарегистрирован: 06.08.2017

Олег извините КПФ в этом проекте? , просто я этого алгоритма в коде не вижу. И еще вопрос  HA_PI() просто в loop() интервалы цикла не стабильны, помоему для PI, а особенно для PID это важно, считается пормальным когда интервалы отличаются не более 1%.

SERG_K
Offline
Зарегистрирован: 06.08.2017

Это и имелось в виду. Просто для других проектов нужны температуры от комнатной.

OlegK
OlegK аватар
Offline
Зарегистрирован: 26.11.2014

SERG_K пишет:
Олег извините КПФ в этом проекте?

Это отдельный проект, чисто для фена.

Цитата:
в loop() интервалы цикла не стабильны

Поэтому и применёно не ПИД-регулирование. Дифференциальная составляющая в данном случае будет лишней и усложнит настройку. Выше писалось о применении ПИД, но, как по мне, точность поддержания в итоге не улучшилась по сравнению с исходным вариантом. У меня 1-2 град. Хватает...

Вы же и писали -

SERG_K пишет:
С ПИД фен держит температуру +-2 градуса во всем диапазоне температур.

Цитата:
сделал расчет отставания открывающего импульса по прервванию Zero cross INT1 . Рассчитывается для каждой полуволны.

Для каждой полуволны отдельно? Что это даёт?

SERG_K
Offline
Зарегистрирован: 06.08.2017

Я сделал PID по прерыванию , так как с PI в loop все плавало и настроить ни чего толком не удалось, коэффициенты PID как то я подобрал быстро, система заработала так как доктор прописал, изменение каждого К прогнозируемо. Точность поддержания внизу диапазона +-1градус после 300 +-2, да, я еще при расчете Т фена вместо 64 замеров сделал 128 стало лучше.(похоже LM358 сильно шумит)

SERG_K
Offline
Зарегистрирован: 06.08.2017

Уверенность что считает именно то что нужно.

Посмотрел код КПФ ,  алгоритм настройки почти такой какой я и хотел. 

yuraanapa
Offline
Зарегистрирован: 31.05.2017

Spinne очень понравился ваш вариант с энкодером но все равно две кнопки приходится использовать,а нельзя скажем поставить два энкодера и убрать кнопки вообще ? В постояном режиме энкодер паяльник регулирет температуру,коротки нажим включает,длинный выключает,в постоянном режиме энкодер фена регулирует температуру,коротркии жим включает длинный выключает,два нажима регулирует скорость фена,при простое более трех четырех секунд переходит в основной режим(это на основе идеи GEN из соседней ветки)

Федор у вас на плате контроллера не было подтяжки с D13(вкл.паяльника) через резистор 470 к питанию пришлось на плате допаивать без него паяльник не включался,а вообще здорово такая маленькая платка получилась и 168 прошка в дело ушла, нано останется для более прожорливых проектов и еще если кто будет такой же невнимательный как я делая плату Федора нужно по три задних ноги на прошке откусить или панельку не полностью распаивать,долго не мог понять почему программа не стартует(второй красный светодиод не горит),почему то печатает выделеным шрифтом,не отключается.

Spinne
Spinne аватар
Offline
Зарегистрирован: 03.09.2017

Могу попробовать, если найдутся еще желающие) Но, если честно, вариант с 2мя энкодерами кажется извращением, даже для меня - не каждый захочет запоминать как, и какую комбинацию нажать, чтобы что-то настроить) Хотя, не спорю - вопрос привычки) На работе приходится пользовать старенькое чудовище китайского производства, в котором по две кнопки на паяльник и фен для регулировки температуры, и резистор на регулировку оборотов фена))

yuraanapa
Offline
Зарегистрирован: 31.05.2017

Так в том то и дело что управление упрощается,логика использования остается прежней,единственно управление мощностью фена изменится,зато удаляется само понятие выбора устройства,все уже выбрано.Лично для меня было не удобно,чтоб изменить нагрев нужно посмотреить где находится стрелка,при необходимости выставить в нужное положение и только потом увеличить или уменьшить перед этим хоть и немного но потратив время на определение куда переместить руку,здесь же ты изменяешь температуру не задумываясь и не тратя время ни на что,управление же скоростью потока применяется реже и запомнить что нужно щелкнуть два раза не составит труда

Федор
Offline
Зарегистрирован: 06.07.2017

yuraanapa пишет:

Федор у вас на плате контроллера не было подтяжки с D13(вкл.паяльника) через резистор 470 к питанию пришлось на плате допаивать без него паяльник не включался...

Приветствую.

Да, все верно, я совсем забыл сказать, подтяжки 13-ого пина нет т.к. светодиод на борту ардуины был демонтирован.

SERG_K
Offline
Зарегистрирован: 06.08.2017

Добрый день. Предлагаю для обсуждения алгоритм настройки температуры паяльника и фена:

- при первом включении, в дальнейшем, нажатием Set  на 2 секунды переходим в режим калибровки

На экран

> Solder

   Hot air

Кнопками   +   -   Выбираем устройство подтверждаем нажатием Set.

   1. Если значение на АЦП в диапазоне 20-30, выводим на экран.         

 Enter Т1  00

Вводим комнатную температуру  подтверждаем Set.

Adc_cool = ADC1;

T_cool = T1;

 Переходим на п.2

Если значение на АЦП вне диапазона, выводим на экран если меньше

+   R1                      

                если больше

-    R1

Регулируем подстроечником сопротивление R1 , когда значение попадет в диапазон  Beep(200)  и переход на п.1.

2.После подтверждения ввода T1  подаем на устройство 10% мощности.

 Выводим на экран

 Enter Т2  000

После стабилизации  вводим значение измеренной температуры. Подтверждаем Set.

Adc_10 = ADC2;

T_10 = T2;

Проверяем для 400 граусов паяльник и 500 фен.

GetSolderT = map(500 ,Adc_cool, Adc_10, T_cool, T_10);

Если GetSolderT попадает в диапазон  500-700 АЦП   переходим на п3.

Если нет, на экран если меньше

+   R2

                если больше

-    R2

Регулируем подстроечником сопротивление R2.  Когда значение попадет в диапазон  Beep(200).  

Проверяем еще раз нижнюю точку диапазона п.1.

Если значение  в диапазоне 20-30 переход на п.3, если нет подстраеваем R1 и п.2 еще раз.

3. Нагреваем паяльник до 300 фен до 400 Выводим на экран

 Enter Т3  000

После стабилизации  вводим значение измеренной температуры. Подтверждаем Set.

Adc_hot = ADC3;

T_hot = T3;

Записываем  Adc_cool, Adc_hot, T_cool, T_hot в память.

GetSolderT = map(GetSolderT, Adc_cool, Adc_hot, T_cool, T_hot);

Примерно так. При желании можно сделать несколько профилей для разных насадок и жал.

Igra343
Offline
Зарегистрирован: 10.07.2017

У меня еще проблемка не работет нагреватель фена реле шелкает. Вентилятор работет...

yuraanapa
Offline
Зарегистрирован: 31.05.2017

del