Тема: Простая автоматика управления клапаном отбора
Доброго всем всего!
Будучи вдохновлён прекрасным TMAS и оторванным (временно!) от возможности заниматься винокурением собрал на досуге вариант, который предлагаю вашему вниманию.
Концепт: максимально просто, максимально автономно, управление одной рукой.
Функционал:
- DS18b20 раз в секунду читает температуру пара в голове колонны;
- BMP180 раз в полминуты читает атмосферное давление;
- вращением энкодера (таки да – одной рукой) оператор задаёт температуру закрытия клапана (справочную, для нормальных условий), шаг изменения 0,05 ºC;
- программа вносит поправку к заданной температуре с учётом атм. давления и,;
- сравнивая результат с показаниями DS18b20, закрывает или открывает клапан;
На дисплей выводятся: заданная температура, расчётная температура, измеренная температура, атмосферное давление.
Важный момент (забыл сразу написать) – для изменения температуры энкодер надо вращать в нажатом положении. Дело в том, что пока кнопка энкодера не нажата, приоритет отдан другим задачам. Если вращать не нажатый энкодер, поворот не считывается (либо несёт ахинею).
Состав:
- плата Arduino (у меня была Nano под руками)
- датчик BMP180
- датчик DS18b20 и резистор 4.7 кОм к нему
- дисплей 16х2 I2C
- реле для работы клапана
- энкодер
За код просьба больно не пинать, это в принципе первая моя попытка. Всё работает, но буду благодарен за советы по оптимизации:
#include <OneWire.h> // Библиотеки для DS18b20
#include <DallasTemperature.h> // ----------------------
#define ONE_WIRE_BUS 10 // D10 пин для DS18b20
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire); //Создаём объект sensors для работы с ds18b20
#include <Adafruit_BMP085.h> // Подключаем библиотеку для работы с датчиками BMP180 или BMP280
Adafruit_BMP085 bmp; // Создаём объект bmp для работы с датчиком адрес которого на шине I2C установлен по умолчанию.
#include <Wire.h> // библиотека для управления устройствами по I2C
#include <LCD_1602_RUS.h> // подключаем русскую библиотеку для LCD,
// LiquidCrystal_I2C.h должна быть установлена в системе
LCD_1602_RUS lcd(0x3F,16,2); // присваиваем имя lcd для дисплея 16х2 на адресе 0x3F
//--ПИНЫ ЭНКОДЕРА--
#define CLK 5 // D5
#define DT 6 // D6
#define SW 7 // D7
//--ПИНЫ ЭНКОДЕРА--
#define VLV 2 //--ПИН КЛАПАНА-- // D2
#define INTERVAL 1000UL // 1 секунда, базовый интервал
float actual; // расчётная температура с учётом атмосферного давления
float press; // атмосферное давление
float vape; // температура пара
float counter = 78.40; // переменная заданной (табличной, для Н.У.) темературы, °C
boolean DT_now, DT_last, SW_now; // состояния энкодера
void setup() {
sensors.begin();
pinMode (CLK, INPUT);
pinMode (DT, INPUT);
pinMode (SW, INPUT);
pinMode (VLV, OUTPUT);
DT_last = digitalRead(CLK); // читаем начальное положение CLK
if (!bmp.begin()) { // инициализация датчика BMP180
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
while (1) {}
}
lcd.init(); // инициализация LCD дисплея
lcd.backlight(); // включение подсветки дисплея
lcd.setCursor(5,0); // ставим курсор на 5 символ первой строки
lcd.print("° <="); // печатаем
lcd.setCursor(10,0); // ставим курсор на 10 символ первой строки
lcd.print(counter); // печатаем
lcd.setCursor(15,0); // ставим курсор на 15 символ первой строки
lcd.print("°"); // печатаем
lcd.setCursor(5,1); // ставим курсор на 5 символ второй строки
lcd.print("°C"); // печатаем
lcd.setCursor(8,1);
lcd.print(bmp.readPressure()*0.007500616827042);
lcd.setCursor(14,1); // ставим курсор на 14 символ второй строки
lcd.print("мм"); // печатаем
press = bmp.readPressure(); // считываем атм. давление на старте
}
void loop() {
SW_now = digitalRead(SW); // проверяем, не нажата ли кнопка энкодера
if (SW_now == HIGH) { // если нажата, то
encoderTick(); // считаем щелчки поворота
} else {
static unsigned long previousMillis = 0; // храним время последнего измерения
if(millis() - previousMillis > 30*INTERVAL) { // проверяем, не истек ли интервал. если да, то
previousMillis = millis(); // сохраняем время последнего измерения и
psensorMeasure(); // слушаем датчик атмосферы
} else {
dallas(); // все остальное время выполняем отработку температуры
}
}
}
void encoderTick() {
DT_now = digitalRead(CLK); // читаем текущее положение CLK
if (DT_now != DT_last) { // если предыдущее и текущее положение CLK разные, значит был поворот
if (digitalRead(DT) != DT_now) { // если состояние DT отличается от CLK, значит крутим по часовой стрелке
counter += 0.05; // прибавить счётчик
} else { // если совпадают, значит против часовой
counter -= 0.05; // убавить счётчик
}
lcd.setCursor(10,0); // ставим курсор на 10 символ первой строки
lcd.print(counter); // печатаем заданную температуру
}
DT_last = DT_now; // обновить значение
}
void psensorMeasure() {
press = bmp.readPressure();
lcd.setCursor(8,1);
lcd.print(press*0.007500616827042);
}
void dallas() {
actual = (press*0.007500616827042-760)*0.034+counter;
static unsigned long previousMillis = 0; // храним время последнего измерения
if(millis() - previousMillis > INTERVAL) { // проверяем, не истек ли интервал. если да, то
previousMillis = millis(); // сохраняем время последнего измерения и
sensors.requestTemperatures(); // опрашиваем датчик температуры
vape = sensors.getTempCByIndex(0); // обновляем значение переменной температуры пара
lcd.setCursor(0,1);
lcd.print(vape); // печатаем расчётную температуру
lcd.setCursor(0,0);
lcd.print(actual); // печатаем текущую температуру пара
if (vape > actual) {
digitalWrite (VLV, HIGH); // закрываем клапан при температуре пара больше расчётной
} else {
digitalWrite (VLV, LOW);
}
}
}