ПИД регулятор температуры на Arduino, термопаре и модуле MAX6675


В данной статье мы рассмотрим создание на основе платы Arduino ПИД регулятора температуры, который можно использовать для управления температурой нагревательного элемента 3D принтера, а при небольшой модификации его также можно использовать для управления температурой паяльника. Еще при небольшой доработке с его помощью можно управлять симистором (TRIAC), с помощью которого управлять, к примеру, частотой оборотов электродвигателя переменного тока. В общем, возможности ПИД (пропорционально-интегрально-дифференциальный) регулятора практически безграничны.

Внешний вид ПИД регулятора температуры на Arduino, термопаре и модуле MAX6675

Ранее на нашем сайте мы рассматривали принципы работы ПИД (proportional-integral-derivative, PID) контроллера при создании самобалансирующегося робота и энкодера для двигателя постоянного тока.

Что такое ПИД регулятор температуры

Как следует из названия, ПИД регулятор температуры предназначен для управления температурой на основе использования ПИД алгоритма, который улучшает точность процесс регулировки. Принцип работы данного алгоритма основан на использовании обратной связи. ПИД регулятор температуры использует специальную математическую формулу для расчета разницы между текущим значением температуры и его требуемым значением. Затем он включает исполнительный механизм чтобы поддерживать температуру на заданном уровне. Подобный алгоритм действий не только уменьшает влияние окружающей среды, но также уменьшает ошибки регулирования, которые присущи обычным алгоритмам, основанным на механизме включения/выключения.

Как работает ПИД регулятор температуры

В любом ПИД алгоритме мы сначала должны уяснить, что он должен делать. В нашем случае нам необходимо поддерживать заданную температуру нагревательного элемента, которая устанавливается с помощью инкрементального энкодера. Первое, что нам необходимо делать для этого – это измерять значение температуры. Для этого мы будем использовать термопару типа K в связке с модулем преобразователя термопары на микросхеме MAX6675 (MAX6675 Cold-Junction-Compensated K-Thermocouple to Digital Converter IC), что даст нам возможность измерять значения температуры вплоть до нескольких сотен градусов. Модуль MAX6675 в данном случае выполняет роль преобразователя аналогового сигнала в цифровой.

Внешний вид термопары типа K вместе с модулем преобразователя MAX6675 показаны на следующем рисунке.

Внешний вид термопары типа K вместе с модулем преобразователя MAX6675

Технические характеристики термопары типа K вместе с модулем MAX6675:

  • Тип преобразователя: аналогово-цифровой (АЦП) с компенсацией холодного спая;
  • Разрядность преобразователя: 12 бит;
  • Шаг измерения: 0,25°C ;
  • Точность: 1,5°C;
  • Интерфейс подключения к контроллеру: SPI;
  • Напряжение питания: 3 – 5,5 В постоянного тока;
  • Габариты модуля: 32 x 15 x 14 мм;
  • Тип термопары: К (хромель-алюмелевая);
  • Диапазон измеряемой температуры: 0 – +600°С;
  • Диаметр резьбы термопары: 6 мм;
  • Длина резьбы термопары: 13 мм;
  • Длина кабеля термопары: 50 см;
  • Вес комплекта: 25 г.

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

Обзор модуля MAX6675

Функция термопары состоит в обнаружении разницы температуры между концами ее проводников. Рабочий спай термопары может измерять температуру в диапазоне от 0°C до +1023.75°C. Холодный конец (температура окружающей среды, в которой находится микросхема MAX6675) может варьироваться только от -20 °C до +85 °C. В то время как температура на холодном конце колеблется, MAX6675 продолжает точно определять разницу температур на противоположном конце.

MAX6675 распознает и корректирует изменения температуры окружающей среды с помощью компенсации холодного спая термопары. Устройство преобразует показания температуры окружающей среды в напряжение с помощью чувствительного к температуре диода. Чтобы выполнить фактическое измерение температуры термопары, MAX6675 измеряет напряжение с выхода термопары и чувствительного диода.

Внешний вид модуля преобразователя MAX6675

Назначение контактов модуля MAX6675:

  • GND – «-«, питание модуля;
  • VCC – «+», питание модуля;
  • SCK – тактовые импульсы;
  • CS – вывод интерфейс SPI;
  • SO – вывод интерфейс SPI.

Внутренняя схема устройства передает напряжение диода (характеризующее температуру окружающей среды) и напряжение термопары (характеризующую разницу температуры на дальнем конце и температуры окружающей среды) в функцию преобразования, хранящуюся в АЦП для расчета температуры горячего (рабочего) спая (соединения) термопары.

Внутренняя схема модуля преобразователя MAX6675

Оптимальная производительность MAX6675 достигается, когда холодный спай термопары и MAX6675 находятся при одной и той же температуре. MAX6675 включает в себя оборудование для преобразования сигнала термопары в напряжение, совместимое с входными каналами АЦП. Входы T+ и T подключаются к внутренней схеме, что уменьшает появление ошибок, вызванных тепловыми шумами в проводах термопары. Более подробную информацию о принципах работы данного устройства можно найти в техническом описании (даташите) микросхемы MAX6675.

Необходимые компоненты

  1. Плата Arduino Nano (купить на AliExpress).
  2. OLED дисплей 128х64 (купить на AliExpress).
  3. Инкрементальный энкодер с кнопкой (купить на AliExpress).
  4. Модуль преобразователя термопары на MAX6675 с термопарой типа К (купить на AliExpress).
  5. Макетная плата.
  6. Соединительные провода.

Внешний вид компонентов, необходимых для сборки проекта, показан на следующем рисунке.

Внешний вид компонентов, необходимых для сборки проекта

Схема проекта

Схема ПИД регулятора температуры на Arduino, термопаре и модуле MAX6675 представлена на следующем рисунке.

Схема ПИД регулятора температуры на Arduino, термопаре и модуле MAX6675В данном проекте мы использовали датчик MAX6675 для считывания данных температуры с термопары. MAX6675 представляет собой Cold-Junction-Compensated K- Thermocouple to Digital Converter module и он подключается непосредственно к плате Arduino. Питание схемы осуществляется от контакта +5V платы Arduino. Для изменения режимов работы проекта и установки необходимой температуры мы использовали инкрементальный энкодер. Более подробно о его подключении к плате Arduino можно прочитать в этой статье. Отображать информацию проекта мы будем на экране OLED дисплея 128х64 с интерфейсом I2C. О его подключении к плате Arduino на нашем сайте можно прочитать в этой статье.

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

Внешний вид собранной на макетной плате конструкции проекта показан на следующем рисунке.

Внешний вид собранной на макетной плате конструкции проекта

Объяснение программы для Arduino

Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.

Для корректной работы программы вам необходимо скачать и установить следующие библиотеки (либо же вы можете установить их с помощью менеджера библиотек Arduino IDE:

Далее первым делом в программе нам необходимо подключить все используемые библиотеки и объявить все используемые контакты платы Arduino. Далее нам необходимо задать значения для коэффициентов Kp, Ki и Kd, которые будут использоваться для формирования выходного значения нашего ПИД регулятора температуры. Автор проекта (ссылка на оригинал приведена в конце статьи) сразу предупреждает, что он использовал "метод проб и ошибок" для определения значений этих коэффициентов, при желании вы их можете переопределить для своего проекта.

Далее в программе мы объявим все необходимые переменные и создадим три объекта: для работы с ПИД регулятором, OLED дисплеем и термопарой. Переменные clockPin, clockPinState, debounce и encoder_btn_count будут использоваться для чтения данных с энкодера. Переменная temperature_value_c будет хранить значение температуры, считанное с термопары. В переменной encoder_btn_count будет храниться количество нажатий кнопки энкодера.

Затем в функции void setup() мы инициализируем последовательную связь со скоростью 9600 бод (для целей отладки) с помощью функции Serial.begin(). В нашем случае мы эту функцию заключили в выражения #ifdef и #endif чтобы нам было просто отключить монитор последовательного порта когда код программы будет полностью готов.

Далее мы зададим режимы работы используемых контактов.

Затем мы инициализируем ПИД регулятор с помощью функции  pid.begin(). После этого мы зададим цель (целевую точку, setpoint) для нашего ПИД алгоритма с помощью функции setpoint(). Целевая точка – это точка, которую будет стремиться достичь ПИД алгоритм в процессе своей работы. Далее мы вызовем функцию tune(), которая произведет первоначальную настройку ПИД алгоритма. В качестве аргументов в данную функцию будут передаваться значения коэффициентов __Kp, __Ki и __Kd. И, наконец, мы зададим пределы работы ПИД регулятора с помощью функции pid.limit(0, 255), в результате чего выходные значения ПИД алгоритма не будут выходить из диапазона 0-255 (диапазон изменения коэффициента заполнения ШИМ сигнала на контактах платы Arduino).

Далее мы будем проверять доступен ли OLED дисплей (возможна ли работа с ним) и если он доступен, то мы будем продолжать выполнение программы. Если же он не доступен, то мы будем выводить в окно монитора последовательной связи сообщение об ошибке и дальше программу выполнять не будем.

После этого мы укажем ротацию дисплея, выведем на него начальную заставку дисплея и очистим его экран. Далее зададим размер и цвет выводимого на экран текста с помощью функций setTextSize и setTextColour. Затем зададим позицию курсора на дисплее и выведем на экран приветственное сообщение.

Далее мы запрограммируем функцию set_temp(). Внутри данной функции мы будем устанавливать температуру нагревательного элемента и проверять состояние кнопки энкодера. Если ее состояние равно двум, то мы будем очищать экран дисплея, устанавливать размер текста и выводить значение температуры на экран дисплея.

После этого мы запрограммируем функцию read_encoder() – как следует из ее названия, в ней мы будем считывать значения с энкодера и проверять вращается ли он по часовой, или против часовой стрелки. Если энкодер вращается по часовой стрелке, то мы будем увеличивать значение счетчика (counter), а если против часовой – мы будем уменьшать значение счетчика. Также мы будем проверять состояние кнопки энкодера чтобы переключаться между режимами установки температуры и ее мониторинга.

И, наконец, в функции loop мы будем вызывать функции read_encoder() и set_temp, эти функции будут вызываться последовательно. Также мы будем проверять состояние кнопки энкодера. Если оно равно единице, то мы будем считывать значение температуры с термопары и подавать ее на вход ПИД алгоритма. После того как ПИД алгоритм произведет необходимые расчеты, мы будем подавать рассчитанное значение в функцию analogWrite, которая будет формировать на его основе ШИМ сигнал с требуемым коэффициентом заполнения. Когда все это будет сделано, мы будем обновлять состояние экрана дисплея.

Тестирование работы ПИД регулятора температуры

Для тестирования работы проекта его автор подключил к ему мультиметр Meco 450B+ чтобы с его помощью измерять коэффициент заполнения ШИМ сигнала на контакте 11 платы Arduino. В качестве нагревательного элемента автор использовал экструдер (extruder, приспособление для выдавливания расплавленного покрытия) от 3D принтера, который запитывается от внешнего источника с напряжением 12V. Плата Arduino получает питание по кабелю от компьютера.

Подключение мультиметра к нашему проекту

Теперь, чтобы установить значение температуры, необходимо нажать кнопку инкрементального энкодера, в результате чего будет задана целевая точка (к которой он будет стремиться) для работы ПИД алгоритма. После этого нагревательный элемент начнет нагреваться чтобы достичь установленной температуры. На экране мультиметра вы при этом должны наблюдать увеличение коэффициента заполнения ШИМ сигнала. Для теста автор проекта установил температуру 64*C.

Установка температуры для тестирования работы ПИД регулятора

После того как температура достигнет требуемого значения коэффициент заполнения ШИМ сигнала начнет уменьшаться. Затем система войдет в установившийся режим и вы сможете наблюдать пилообразное изменение коэффициента заполнения ШИМ сигнала в небольших пределах, что будет свидетельствовать о том, что ПИД алгоритм будет пытаться исправить погрешности в своей работе.

Тестирование работы проекта

Исходный код программы (скетча)

Видео, демонстрирующее работу проекта

Источник статьи

(Проголосуй первым!)
Загрузка...
18 847 просмотров

Комментарии

ПИД регулятор температуры на Arduino, термопаре и модуле MAX6675 — 69 комментариев

  1. В общем сделал под себя лень покупать oled, а lcd полно в столе :-)) пока не запускал только на коммутацию 230 - буду наверное использовать SSR ..но это не точно.. энкодер работает прекрасно - как оказалось две противоположные задачи ему мешали жить - мах ненавидит быстрый опрос и перестает работать вовсе если код в лупе слишком быстрый или без задержек, а вот энкодер даже работая в прерывании хочет чтобы луп вращался поскорее..
    Что и получилось сделать.
    Энкодер работает прекрасно в том числе с быстрой перемоткой очень удобно..

    #include "GyverEncoder.h" // Подключаем библиотеку для работы с энкодером
    #include "TimerOne.h" // необходима для работы в прерывании - завасимость у энкодера
    #define CLK 3 // Указываем к какому порту подключен вывод CLK
    #define DT 4 // Указываем к какому порту подключен вывод DT
    #define SW 2 // Указываем к какому порту подключен вывод SW
    Encoder enc1(CLK, DT, SW);

    #include "GyverPID.h"
    GyverPID regulator(4, 0.590, 0.02, 10); // Подключение и настройка библиотеки PID Gyver

    #include "GyverMAX6675.h" // Подключаем библиотеку для работы с термопарой
    #define CLK_PIN 10 // Пин SCK
    #define DATA_PIN 8 // Пин SO
    #define CS_PIN 9 // Пин CS
    GyverMAX6675 thermocouple;

    #include "LiquidCrystal_I2C.h" // Подключаем библиотеку для работы с дисплеем
    LiquidCrystal_I2C lcd(0x27, 16, 2); // адрес, столбцов, строк

    volatile float setTemp = 60; // Создаём переменные для регулирования температуры - задание после включения
    unsigned long timing;
    int cur_temp = 0;
    int set_temp = 0;

    void setup()
    {
    Serial.begin(115200); // Открытие последовательного порта на скорости 115200
    enc1.setType(TYPE2); // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
    Timer1.initialize(1000); // установка таймера на каждые 1000 микросекунд (= 1 мс)
    Timer1.attachInterrupt(timerIsr); // запуск таймера

    regulator.setDirection(NORMAL); // направление регулирования (NORMAL/REVERSE). ПО УМОЛЧАНИЮ СТОИТ NORMAL
    regulator.setLimits(0, 255); // пределы (ставим для 8 битного ШИМ). ПО УМОЛЧАНИЮ СТОЯТ 0 И 255
    lcd.init(); // инициализация
    lcd.backlight(); // включить подсветку
    }

    void timerIsr() { // прерывание таймера
    enc1.tick(); // отработка теперь находится здесь
    if (enc1.isRight()) setTemp = setTemp + 1; // поворот = изменение на 1 градус
    if (enc1.isLeft()) setTemp = setTemp - 1;
    if (enc1.isFastR()) setTemp = setTemp + 5; // быстрый поворот = изменение на 5 градусов
    if (enc1.isFastL()) setTemp = setTemp - 5;
    if (setTemp >= 350) setTemp = 350; if (setTemp 1000){ cur_temp = thermocouple.getTempInt();
    timing = millis();}
    regulator.input = cur_temp; // сообщаем регулятору текущую температуру
    lcd.setCursor(0,1);
    lcd.print("Tmp.current: ");
    lcd.print(cur_temp); // Вывод значения текущей температуры на дисплей
    if (cur_temp < 100) lcd.print(" ");
    set_temp = round(setTemp); // Округление значения и отброс дробной части
    regulator.setpoint = set_temp; // сообщаем регулятору температуру, которую он должен поддерживать
    lcd.setCursor(0,0);
    lcd.print("Tmp.is set: ");
    lcd.print(set_temp); // Вывод заданной температуры на дисплей
    if (set_temp < 100) lcd.print(" ");
    analogWrite(11, regulator.getResultTimer()); //вывод на пин управления значение с регулятора

    Serial.println(set_temp);
    Serial.println(cur_temp);
    Serial.println(regulator.getResultTimer()); //вывод в монитор порта значения с регулятора
    }

    • И если кто не заметил в скетче провода на энкодер и датчик нафигачены от вольного - было лень смотреть какой куда пока паялом тыкал. И вывод в порт не закоментировал.. строчит как пулемет.

      • Не могу понять что пошло не так при выставлении кода как текста но в нем смещение и явная ошибка вставки.. файлом приаттачить не даёт по этому ссылку на облако
        https://disk.yandex.ru/d/b4x23A-l1EpNSw

        • Это просто работает система защиты комментариев от нежелательных кодов - она некоторые вещи иногда обрезает в коде. Спасибо вам за конструктивную помощь данному проекту на нашем сайте

  2. Попробовал собрать данный проектик. Все работает, отображается на дисплее. Температуру можно задавать энкодером, только в сторону увеличения работает стабильно, а в сторону уменьшения нужно вращать энкодер очень медленно, но тоже работает. Единственное алгоритм ПИД-регулирования не совсем ПИД. Поясню: включающий сигнал идет всегда равномерно и прямолинейно в виде ШИМ, и как только температура достигает порога, то ШИМ отключается совсем. Получается, что расчеты и подстройка по обратной связи с датчика температуры не участвует, и как следствие не учитывается инерционность. Первый разогрев, выставленный на 40 градусов улетел аж за 100 градусов! А потом, когда нагреватель остывал, разбег был уже в 5 градусов, что в принципе приемлемо. Но вот главный алгоритм ПИД почему-то не функционирует должным образом. Попробую поиграться с коэффициентами... Автору большое спасибо за труды! Подобных статей в сети увы мало.

    • И вам спасибо за конструктивный комментарий для нашего сайта, даже не думал что эта статья вызовет такой интерес, я думал таких проектов в сети достаточно много. Я рад что у вас получилось регулировать температуру и в меньшую сторону, хоть и не так эффективно как в большую сторону. А то у многих, судя по комментариям, регулировка вниз совсем не получается. Если у вас получится усовершенствовать проект просьба отписаться об этом тоже в комментариях, буду признателен

  3. У меня энкодер типа PEC11R подключил на средний вывод землю, поставил подтягивающие резисторы. Всё как положено. Никак не убавляется температура, при вращении ручки в любую сторону температура только прибавляется.
    У меня неправильный энкодер?
    Где производить исправления, если мне нужно исправить код работы экодера? как я понял - основной код находится в подключаемой библиотеке. Это занчит, что именно её смотреть надо? Я начинающий.

    • Нет, это какой то баг в скетче, который пока никто найти не может. Если бы я сам сейчас имел возможность собрать данный проект, я бы заменил энкодер на 2 кнопки чтобы уж точно найти ошибку

  4. Всем привет. Нашел закономерность. Если пытаться убавлять температуру в режиме нажатой кнопки, всё работает как следует.

  5. Помощь зала и шансы 50 на 50 не помогли, помог звонок другу. Ту часть скетча, которая отвечает за обслуживания энкодера, он переписал полностью, в результате я имею работающий пид регулятор. Вывод мосфет подключен к твердотельному реле, которое регулирует токами площадки для ремонта светодиодных планок. https://aliexpress.ru/item/1005003511180163.html?sku_id=12000026113857857&spm=a2g2w.productlist.search_results.10.24a74aa6tbfuhn Использую для "накатывания шаров" на микросхемах с BGA монтажом.

    • #include
      #include
      #include
      #include
      #include
      #include "max6675.h"

      #define MAXTEMP 190

      // Define Rotary Encoder Pins (контакты энкодера)
      #define pinA 3
      #define pinB 2
      #define SW_PIN 4

      // MAX6675 Pins
      #define thermoDO 8
      #define thermoCS 9
      #define thermoCLK 10

      // Mosfet Pin (контакты Mosfet транзистора)
      #define mosfet_pin 11

      // Serial Enable
      #define __DEBUG__
      #define SCREEN_WIDTH 128 // OLED display width, in pixels
      #define SCREEN_HEIGHT 64 // OLED display height, in pixels
      #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)

      /*In this section we have defined the gain values for the
      proportional, integral, and derivative controller I have set
      the gain values with the help of trial and error methods.
      */

      #define __Kp 30 // Proportional constant
      #define __Ki 0.7 // Integral Constant
      #define __Kd 200 // Derivative Constant

      int set_temperature = 150; // This set_temperature value will increas or decreas if when the rotarty encoder is turned
      //(значение температуры, будет изменяться при вращении ручки энкодера)
      float temperature_value_c = 0.0; // stores temperature value (будет хранить значение температуры)
      long debounce = 0; // Debounce delay (задержка для устранения эффекта дребезга контактов)
      int encoder_btn_count = 0; // used to check encoder button press (счетчик нажатий кнопки энкодера)

      MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); // Create an instance for the MAX6675 Sensor Called "thermocouple" (создаем объект для работы с термопарой)
      Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Create an instance for the SSD1306 128X64 OLED "display" (создаем объект для работы с дисплеем)
      PIDController pid; // Create an instance of the PID controller class, called "pid" (создаем объект для работы с ПИД алгоритмом)

      //----------------------------------------------------------------------------------
      volatile long pause = 50; // Пауза для борьбы с дребезгом A/B
      volatile long lastTurn = 0; // Переменная для хранения времени последнего изменения

      volatile int count = 0; // Счетчик оборотов
      int actualcount = 0; // Временная переменная определяющая изменение основного счетчика

      volatile int state = 0; // Статус одного шага - от 0 до 4 в одну сторону, от 0 до -4 - в другую

      volatile int pinAValue = 0; // Переменные хранящие состояние пина, для экономии времени
      volatile int pinBValue = 0; // Переменные хранящие состояние пина, для экономии времени

      //----------------------------------------------------------------------------------
      void setup()
      {
      #ifdef __DEBUG__
      Serial.begin(9600);
      #endif

      pinMode(mosfet_pin, OUTPUT); // MOSFET output PIN
      pinMode(pinA, INPUT); // Encoder A Pin
      pinMode(pinB, INPUT); // Encoder B Pin
      pinMode(SW_PIN, INPUT_PULLUP); // Encoder SW Pin

      //---
      attachInterrupt(digitalPinToInterrupt(pinA), pinA_handler, CHANGE); // Настраиваем обработчик прерываний по изменению сигнала
      attachInterrupt(digitalPinToInterrupt(pinB), pinB_handler, CHANGE); // Настраиваем обработчик прерываний по изменению сигнала

      pid.begin(); // initialize the PID instance (инициализируем ПИД алгоритм)
      pid.setpoint(MAXTEMP); // The "goal" the PID controller tries to "reach" (задаем целевую точку для его работы)
      pid.tune(__Kp, __Ki, __Kd); // Tune the PID, arguments: kP, kI, kD (производим начальную настройку ПИД алгоритма)
      pid.limit(0, 255); // Limit the PID output between 0 and 255, this is important to get rid of integral windup! (задаем пределы работы ПИД алгоритма)

      if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
      {
      #ifdef __DEBUG__
      Serial.println(F("SSD1306 allocation failed"));
      #endif
      for (;;)
      ; // Don't proceed, loop forever (бесконечный цикл)
      }

      //
      display.setRotation(2); // Rotate the Display
      display.display(); // Show initial display buffer contents on the screen -- the library initializes this with an Adafruit splash screen.
      display.clearDisplay(); // Cleear the Display
      display.setTextSize(2); // Set text Size
      display.setTextColor(WHITE); // set LCD Colour
      display.setCursor(48, 0); // Set Cursor Position
      display.println("PID"); // Print the this Text
      display.setCursor(0, 20); // Set Cursor Position
      display.println("Temperatur"); // Print the this Text
      display.setCursor(22, 40); // Set Cursor Position
      display.println("Control"); // Print the this Text
      display.display(); // Update the Display
      delay(2000); // Delay of 200 ms

      } // setup

      //----------------------------------------------------------------------------------
      void pinA_handler()
      {
      if (micros() - lastTurn < pause)
      return;
      // Если с момента последнего изменения состояния не прошло
      // достаточно времени - выходим из прерывания

      pinAValue = digitalRead(pinA); // Получаем состояние пинов A и B
      pinBValue = digitalRead(pinB);

      cli(); // Запрещаем обработку прерываний, чтобы не отвлекаться
      if (state == 0 && !pinAValue && pinBValue || state == 2 && pinAValue && !pinBValue)
      {
      state += 1; // Если выполняется условие, наращиваем переменную state
      lastTurn = micros();
      }
      if (state == -1 && !pinAValue && !pinBValue || state == -3 && pinAValue && pinBValue)
      {
      state -= 1; // Если выполняется условие, наращиваем в минус переменную state
      lastTurn = micros();
      }
      setValues(state); // Проверяем не было ли полного шага из 4 изменений сигналов (2 импульсов)
      sei(); // Разрешаем обработку прерываний

      if (pinAValue && pinBValue && state != 0)
      state = 0; // Если что-то пошло не так, возвращаем статус в исходное состояние
      }

      //----------------------------------------------------------------------------------
      void pinB_handler()
      {
      if (micros() - lastTurn < pause)
      return;
      pinAValue = digitalRead(pinA);
      pinBValue = digitalRead(pinB);

      cli();
      if (state == 1 && !pinAValue && !pinBValue || state == 3 && pinAValue && pinBValue)
      {
      state += 1; // Если выполняется условие, наращиваем переменную state
      lastTurn = micros();
      }
      if (state == 0 && pinAValue && !pinBValue || state == -2 && !pinAValue && pinBValue)
      {
      state -= 1; // Если выполняется условие, наращиваем в минус переменную state
      lastTurn = micros();
      }
      setValues(state); // Проверяем не было ли полного шага из 4 изменений сигналов (2 импульсов)
      sei();

      if (pinAValue && pinBValue && state != 0)
      state = 0; // Если что-то пошло не так, возвращаем статус в исходное состояние
      }

      //----------------------------------------------------------------------------------
      void setValues(int state) // Устанавливаем значения
      {
      if (state == 4 || state == -4) // Если переменная state приняла заданное значение приращения
      {
      count += (int)(state / 4); // Увеличиваем/уменьшаем счетчик
      lastTurn = micros(); // Запоминаем последнее изменение

      // установка значений температуры
      if (encoder_btn_count == 2)
      {
      if (state == 4)
      {
      set_temperature = set_temperature - 1; // decrement the counter.
      }
      else if (state == -4)
      {
      set_temperature = set_temperature + 1; // Encoder is rotating CW so increment (энкодер вращается против часовой стрелке)
      }

      if (set_temperature MAXTEMP)
      {
      set_temperature = MAXTEMP; // if the counter value is grater than MAXTEMP then set it back to MAXTEMP
      }

      #ifdef __DEBUG__
      Serial.println(set_temperature);
      // print the set temperature value on the serial monitor window
      //(выводим установленное значение температуры в окно монитора последовательной связи)
      #endif
      }
      }
      }

      //----------------------------------------------------------------------------------
      void display_setting_temp()
      {
      // check if the button is pressed twice and its in temperature set mode. (если кнопка нажата дважды, то - режим установки температуры)
      if (encoder_btn_count == 2)
      {
      display.clearDisplay(); // clear the display
      display.setTextSize(2); // Set text Size
      display.setCursor(16, 0); // set the diplay cursor
      display.print("Set Temp."); // Print Set Temp. on the display
      display.setCursor(45, 25); // set the cursor
      display.print(set_temperature); // print the set temperature value on the display
      display.display(); // Update the Display
      }
      }

      //----------------------------------------------------------------------------------
      void read_encoder_button()
      {

      if (digitalRead(SW_PIN) == LOW) // If we detect LOW signal, button is pressed
      {
      if (millis() - debounce > 80) // debounce delay
      {
      encoder_btn_count++; // Increment the values
      if (encoder_btn_count > 2)
      encoder_btn_count = 1;
      #ifdef __DEBUG__
      Serial.println(encoder_btn_count);
      #endif
      }
      debounce = millis(); // update the time variable
      }

      } // read_encoder_button

      //----------------------------------------------------------------------------------
      void loop()
      {
      read_encoder_button(); // Call The Read Encoder Function
      display_setting_temp(); // Call the Set Temperature Function

      if (encoder_btn_count == 1)
      // check if the button is pressed and its in Free Running Mode --
      // in this mode the arduino continiously updates the screen and adjusts the PWM output according to the temperature.
      {
      temperature_value_c = thermocouple.readCelsius(); // Read the Temperature using the readCelsius methode from MAX6675 Library.
      int output = pid.compute(temperature_value_c); // Let the PID compute the value, returns the optimal output (вычисляем оптимальное значение с помощью ПИД алгоритма)
      analogWrite(mosfet_pin, output); // Write the output to the output pin
      pid.setpoint(set_temperature); // Use the setpoint methode of the PID library to (устанавливаем целевую точку для ПИД алгоритма)
      display.clearDisplay(); // Clear the display
      display.setTextSize(2); // Set text Size
      display.setCursor(16, 0); // Set the Display Cursor
      display.print("Cur Temp."); // Print to the Display
      display.setCursor(45, 25); // Set the Display Cursor
      display.print(temperature_value_c); // Print the Temperature value to the display in celcius
      display.display(); // Update the Display

      #ifdef __DEBUG__
      Serial.print(temperature_value_c); // Print the Temperature value in *C on serial monitor (выводим значение температуры в окно монитора последовательной связи)
      Serial.print(" "); // Print an Empty Space
      Serial.println(output); // Print the Calculate Output value in the serial monitor.
      #endif

      delay(200); // Wait 200ms to update the OLED dispaly. (ждем 200 мс чтобы обновить OLED дисплей)
      }
      } // loop

        • Да, спасибо вам за помощь. Надеюсь она пригодится другим пользователям нашего сайта, которых заинтересовал данный проект

        • Добрый день!

          Подскажите новичку, а можно переделать скетч не под нагрев, а для охлаждения? Не тыкните носом где что поменять надо?
          Заранее спасибо!

          • Добрый вечер. В 47 и 49 строчках попробуйте изменить целевую и предельные температуры

      • Не работает ваш скетч
        1. Не нравится строка #define MAXTEMP 190
        2. Arduino: 1.8.19 (Windows 10), Плата:"Arduino Nano, ATmega328P (Old Bootloader)"

        C:\Users\nikol\Documents\Arduino\sketch_nov27a\sketch_nov27a.ino: In function 'void setValues(int)':

        sketch_nov27a:8:17: error: expected ')' before numeric constant

        #define MAXTEMP 150

        ^

        C:\Users\nikol\Documents\Arduino\sketch_nov27a\sketch_nov27a.ino:182:21: note: in expansion of macro 'MAXTEMP'

        if (set_temperature MAXTEMP)

        ^~~~~~~

        Несколько библиотек найдено для "Adafruit_SSD1306.h"

        Используется: C:\***\libraries\Adafruit_SSD1306

        Не используется: C:\***\libraries\Adafruit_SSD1306_Wemos_Mini_OLED

        exit status 1

        expected ')' before numeric constant

        • Замените #define MAXTEMP 150 на простое объявление константы, например:
          const int MAXTEMP = 150;

  6. привет, а что делать с шим сигналом? как его потом использовать? (сори за простой вопрос, хочу сделать 1 в 1 схему для управления экструдером).

  7. Здравствуйте! Поясните, пож-та, для чего подключается библиотека SPI.h, если фактически жтот протокол не используется?

    • Добрый вечер. По интерфейсу SPI подключается модуль MAX6675. Его использует библиотека для работы с данным модулем

  8. Спасибо за статью, код не рабочий, но мы с товарищем его допилили, использовали аналогичные библиотеки (некоторые библиотеки использовали от AlexGyver) не знаю можно ли в коментарии выкладывать код, если кому нужно пишите в ТГ на имя в профиле. Код использовал для регулировки температуры нагрева старого советского утюга.

    • Да, можно выкладывать, но сложный код может немного порезаться в комментариях, поэтому лучше всего в этом случае закачать его куда-нибудь, например, на яндекс диск, а здесь ссылку написать для его скачивания

    • А что там не рабочее? Ваш вариант можете мне прислать на email хочу сделать подогрев плат.

    • Lexus_774 Здравствуйте.Можно как то получить ваш код пид регулятора?В ТГ я не зарегистрирован.

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

  9. У кого неправильно работает энкодер - не уменьшает температуру при установке, смотрите комменты к исходной статье (https://circuitdigest.com/comment/35594#comment-35594). Там 31.08.2022 опубликовано решение этой проблемы. Нужно всего лишь внести небольшое исправление в исходный код.

    • Да, спасибо за подсказку. Не успел отследить появление этого комментария на сайте-первоисточнике

      • Я сам случайно наткнулся, у меня проект сохранен с вашего сайта. И я довольно давно его сохранил у себя, но недавно понадобился регулятор, решил собрать этот пока из Китая едет заказ. Зная, что есть некая ошибка в работе сначала хотел сам попытаться разобраться, но поскольку программист из меня "такой себе" ... проверил исходный проект и вот оно - решение. Проверю исправление - отпишусь.

        • Ну если у вас все таки получится доделать данный проект, то будем признательны если отпишитесь нам об этом

        • Да, я сейчас тоже проверил. Теперь до 10 поднимается и вниз идёт непредсказуемо.

  10. Собрал проект, включаю плату: отображается заставка
    Жму кнопку энкодера отображается текущая темппература с датчика
    Нажимаю энкодер повторно, захожу в окно задания температуры, там горит число 150 и его нельзя не убавить не прибавить, 150 так же не устанавливается при нажатии на кнопку энкодера

    • Это связано, скорее всего, с не очень корректной обработкой дребезга контактов энкодера в программе. Попробуйте поизменять значение 80 в строке 100

      • Что-то не помогает, такое ощущение, что энкодер постоянно крутится в сторону увеличения
        Один раз включил плату, захожу в окно установки температуры и она с 0 до 150 прибавилась, пытаюсь крутить в обратную сторону, на мгновение вижу цифру 147 и опять устанавливается 150.

        • Ну в строке 88 программы прописано же уменьшение задаваемой температуры при вращении энкодера влево. А можете попробовать заменить энкодер на 2 кнопки - при нажатии одной будете увеличивать температуру, а при нажатии другой - уменьшать температуру

          • Решил забить пока на эту проблему, хотел просто нагреть термоблок 3д принтера собранной системой до определенной температуры.
            Указал 240 градусов, но проблема в том, что когда температура достигает 180 градусов, то LED дисплей вылетает в заставку, которая появляется при включении платы, а нагревание соответственно останавливается, в чем может быть проблема? Транзистор 69N03LT

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

            • Ну ладно, не выйдет тупо скопировать проект не вникая в код, так не хотелось углублять в программирование...)

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

  11. Добрый день, подскажите пожалуйста, не могу понять куда именно подключать нагреватель и какой максильной мощности можно подключить нагреватель (тэн)

    • Необходимо подключить концы термопары туда, температуру чего хотите регулировать. А сам нагреватель нужно подключать к разъемам, обозначенным на схеме LOAD. Его максимальная мощность зависит от того, какой максимальный ток способен выдержать MOSFET транзистор, с помощью которого осуществляется управление температурой нагревательного элемента

  12. Согласен с "Юрий" . Я собрал схему по вашему описанию, загрузил скетч и получил точь в точь проблему, описанную "Юрий". Так же грешил на окисленные контакты в энкодере, но замена его на новый результата не дало. Полагаю есть ошибка в скетче, возможно в части компенсации дребезга. Сам я больше электроник чем программист, без помощи не обойдусь. Может уже есть решение?

    • Добрый вечер. Посмотрел повнимательнее программу. Для устранения эффекта дребезга контактов попробуйте поизменять значение переменной debounce в строке № 33. По умолчанию значение этой переменной в программе равно нулю, поэтому при нулевом своем значении она с дребезгом контактов никак не борется. То есть автор программы предусмотрел в ней фрагмент кода, отвечающий за борьбу с дребезгом контактов (строки с 100 по 108), но почему то решил значение переменной, отвечающей за это, оставить по умолчанию нулевым. Также можно попробовать поизменять значение 80 в строке 100 (скорее всего, это более вероятнее поможет)

  13. Не понял куда нагреватель то подключен и как он понимает когда нагревать а когда нет???

    • Ну подключаете вы концы термопары сами туда, температуру чего хотите регулировать. Регулятор измеряет разницу между требуемой температурой и измеренной и затем на основе ПИД алгоритма вычисляет насколько нужно увеличивать температуру нагревательного элемента, которая управляется с помощью коэффициента заполнения ШИМ сигнала

      • Еще вопрос какой в этой конкретно схеме примера используется транзистор?
        Я хочу собрать такую же схему, хочу регулировать температуру нагревателя принтера в районе 250-260 градусов.
        На какие номиналы транзистора мне надо смотреть? Какие у него должны быть допустимые пределы характеристик?

        • Добрый вечер. Там на схеме подписано что используется Mosfet транзистор IRF540. Можно ли его заменить каким то другим? Честно говоря, не знаю, не очень хорошо ориентируюсь в Mosfet транзисторах, к сожалению

  14. Что можно сказать, для поддержания температуры пид библиотека гавно(хотя возможно я что-то не так делаю). При достижении целевой точки алгоритм полностью выключает питание что идет в разрез с адекватным удержанием температуры. Если посмотреть на роботу пид в 3д принтерах то можно заметить что полностью нагрев не отключается никогда, лишь меняется скважность импульсов в определенных пределах.

    • Стоит добавить что речь идет про удержание температуры в пределах 0,2 градуса.

      • Алексей, все познается в сравнении. Не буду утверждать что проект данного ПИД регулятора обеспечивает сногсшибательные результаты, но, на мой взгляд, на "середнячка" он тянет.
        Попробуйте аналогичные проекты регуляторов с других сайтов, сравните. Будем признательны, если отпишитесь здесь нам о результатах сравнения

        • Я не спорю, если использовать для проектов где не нужна высокая точность и +-5 градусов не на что не влияет то да он не плох. На просторах интернета нашел вот такую библиотеку: alexgyver.ru/gyverpid/ с ней получилось вместится в погрешность одного градуса. Опять же, не хочу оскорбить автора, если вы сами в этом разобрались и написали работающий код то это уже достойно похвалы. Просто хотел сказать что есть что совершенствовать))

          • Ну совершенствовать всегда есть что. )) Но я рад что у вас получилось добиться того, чего вы хотели

  15. Собрал схему , загрузил скетч , открыл СОМ , вращаю энкодер влево - значение уменьшается , вправо -увеличивается (в это время на oled висит "заставка") , нажимаю кнопку , выбираю Set Temp , вращаю влево - увеличивается , в право - увеличивается , уменьшить не могу

    • А если второй раз нажать кнопку энкодера ничего не происходит? Просто в функции set_temp() проверяется равно ли состояние кнопки двум или нет

      • Нажатие кнопки работает без проблем , а именно перебрасывает с CurTemp в SetTemp . До нажатия кнопки висит заставка "Pid Temperatur Control" если в это время посмотреть в монитор порта и покрутить енкодер заданная температура меняется в зависимости от направления вращения , после выбора SetTemp - значение не убавить . Компоненты использованы в точности как на фото , пробовал 3 разных екнодера и 2 платы ардуино - никаких изменений

        • А вы можете проконтролировать как в этих режимах меняется значение переменной set_temperature в зависимости от вращения ручки энкодера? Можете добавить в программу вывод значения этой переменной в окно монитора последовательной связи?

          • Проконтролировал , получилось вот что : при попытке уменьшить происходит нечто подобное дребезгу контактов ( первый сдвиг -3 градуса , следующие сдвиги энкодара +3 +6 +32 +4 -5, хотя шаг стоит в 3 градуса ) Энкодеры использовал принципиально разные как и платы и провода и на макетке и паял - эффекта нет. Пробовал даже с другой ОС поставить Arduino IDE с библиотеками исключительно под этот проект - без изменений.

            • Ну тогда может быть энкодер на пару кнопок (или какой нибудь переключатель) замените чтобы уж точно проверить что проблема заключается в дребезге контактов энкодера или в программе нужно что-нибудь поменять чтобы получше устранить данный эффект

  16. Не получается убавить температуру , в какую сторону энкодер не вращай значение заданной температуры увеличивается . В СОМ порту всё выглядит ровно до момента нажатия кнопки энкодера после нажатия значение идет на повышение

    • А что значит в СОМ порту всё выглядит ровно? Там значения температуры правильно меняются или нет? Попробуйте поэкспериментировать с константами ПИД алгоритма и его целевой точкой. Посмотрите меняется ли при этом что-нибудь в проекте?

    • Та же история, не могу регулировать температуру, постоянно доростает до максимальной

    • У кого неправильно работает энкодер - не уменьшает температуру при установке, смотрите комменты к исходной статье (https://circuitdigest.com/comment/35594#comment-35594). Там 31.08.2022 опубликовано решение этой проблемы. Нужно всего лишь внести небольшое исправление в исходный код.

      • ... проверил это - не работает ... установка температуры вообще перестает работать. Хотя в окне монитора COM-порта видно, что энкодер работает нормально до тех пор, пока на экране высвечивается начальная заставка "PID Termo-Control". Как только после нажатия кнопки происходит
        переход к показу текущей температуры или установке температуры, энкодер перестает работать. Видимо косяк какой то в самом алгоритме.
        Буду разбираться дальше.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *