Эта статья скорей не про автоматику, а про управление ТЭНом. Развитие темы в статье новая автоматика (версия 2).
Я думал что это будет самое простое, однако время показало обратное)))... Хотя наверено даже наоборот, я все усложнил и на практике оказалось что нужно относится к этому проще. Так как это не ратификационная колонна, а просто дистилятор, то нет необходимости поддерживать постоянную температуру, управляя тэном по милисекундам. Секундными включениями тэна можно добиться постоянного, но неравномерного (что с нашем случае не принципиально) потока пара из перегонного куба. /Впоследствии, когда управление тэном осуществляется порядком 200-700 милисекунд на вкл\выкл, по струе пара в сухопарнике видно что процесс идет равномерно, не сравнимо с управлением по секундам. Температура на входе в сухопарник достаточно живо откликается на изменение режима нагрева./
Про ТЭНы - только нержавейные ТЭНы. Ни оцинковку, ни латунь с пищевым покрытием (покрытие слезет после 4-5 перегонки) использовать крайне не рекомендуется. Есть еще медные ТЭНы, но смысла именно в ТЭНах из меди не вижу.
Итак, был заказан контроллер ARDUINO UNO R3, релюхи, светодиоды, набор сопротивлений разного номинала, и датчик температуры DALLAS DS18B20 (по моему так). Всего по 2 штуки, чтобы можно было оперативно заменить. После размышлений как доставить температурный датчик в зону пара в перегонный куб, неожиданно на глаза попался стержень от ручки паркер, он идеально подходил для этой цели: тонкие стенки, с одной стороны он цельный( не надо паять, сминать и тд), подходящий по длине и диаметру. Стержень был схвачен, отпилен со стороны пера и рапотрашен. Для того чтобы избавиться от остатков чернил, стержень был замочен в ацетоне на неделю (просто забыл про него))). Потом вытащил его, протер, после чего он опять валялся неделю. Далее датчик был помещен внутрь стержня и герметизирован силиконовыми прокладками. Форма для запекания из силикона очень выручила, хватило на все не экономя и осталось 70% от нее еще. Дальше стержень был "инсталлирован" )))... Ну не внедрен же)))... в корпус от выпускного клапана через силиконовые прокладки. Можно было конечно заказать у китайцев готовый термометр в герметичном корпусе, но не заказался как то.
Все было собрано, программулина написана, собран из коробки и лампы стенд для испытаний, отработана логика /уж какая была, исходя из моего тогдашнего опыта и представления о процессе/, но опять же спешка, подходила брага, да и дел кучу никто не отменял, найти и купить градусник для измерения реальной температуры не удосужился.
Схема первого варианта автоматики (во втором варианте убрал блок из 5 светодиодов и поставил экран от нокиа5510, купленный на aliexpress, по программке можно запросто разобраться что куда подключено, схема как пример подключения элементов):
После первой же перегонки был заказан электронный градусник и было обнаружено что: 1. Разность в показании градусника и датчика примерно в 2 градуса (так и не определился чему верить), причем инертность самого датчика на остывание достаточно большая, но это не принципиально. 2. Установленный киловатный тэн, в режиме работы 4/6, 4/7 (нагрев, сек/пауза, сек) практически до конца перегонки поддерживает очень плавное повышение температуры, 0.3-0.5 градусов минут за 20-30, при этом процесс не останавливается и идет постоянно. 3. Выявлен косяк с работой китайских реле. Реле от стандартного шилда для ардуины на 10А не подойдут. Было непонятно что происходит, какой то промежуток все работало нормально, потом температура начинала быстро расти и спасало только отключение аппарата. Стало понятно что реле не справляется с возложенной на нее функцией. Контакты залипали и в один прекрасный момент контакты реле просто приварились. Срочно реле была заменена для окончания перегонки на новую, после чего начались поиски более надежного варианта.
Было решено управлять симистором с помощью реле или оптопары. Сначала был установлен небольшой симистор, в последствии он был утерян и по маркировке ничего сказать и посмотреть уже не могу, но он достаточно сильно грелся. После этого был раздобыт монстрячий симистор BTA41-800B (40А по даташиту) и прикрутил его к достаточно большому радиатору. Но и эта связка при работе ТЭНа на полной мощности нагревается, без вентилятора как то страшновато, поставил вентилятор - проблем нет.
Схема:
Вердикт: работает прекрасно, без нареканий и неожиданностей. Пока включаю вентилятор на первичный нагрев, далее автоматизирую этот процесс (в дальнейших модификациях выполнено).
После того как я перестал думать что контакты релюхи могут залипнуть и процесс превратится в неуправляемый, пошла дальнейшая модернизация.
Вид автоматики:
Принцип модульности мне показался заманчивым, но в последствии я понял что меня ломает постоянно вытаскивать несколько компонентов, расставлять, соединять их, а после всего все действия производятся в обратном порядке, да и проводов куча - поэтому в след версии все скомпановано в одном корпусе.
Цель: насколько это возможно оптимизировать процесс и устраниться от контроля за ним (очно-визуальным по крайней мере).
Сейчас идея программки (скетча) такая:
вначале цикла делаем запрос температурному датчику, засекаем время обращения по прошествии секунды получаем от него температуру. Это делаю потому что иначе с кнопками траблы получаются, если delay (1000) выставить.
дальше кнопками определяем переменные, которые потом обрабатываются условиями.
Строго не судить, я понимаю что можно было сделать красивее, можно было применять функции... но так как я его кучу раз допиливал и писал иногда в автобусе на планшете - так получилось. Коментарии некоторые тоже старые и неактуальные - обещаю разобраться /не сделано - не вижу смысла, новый скетч хорошо комментирован и более функционален/.
К этому моменту проведено достаточно много перегонок и опытным путем было определено, что для отбора голов оптимален режим нагрева (тем же киловатным тэном, нагрев, сек/пауза, сек) 4/7 - это и есть мощность старта перегонки после начала процесса. Далее я определился что две прегонки - это минимальное количество. Соответственно первая перегонка может быть автоматизирована от окончания отбора голов и до достижения заданной, определенной мной температуры. Вторая перегонка по определению дробная, празничная и подлежащая дегустации)))... здесь автоматика лишняя (неправильно думал, вторую перегонку тоже можно автоматизировать и прекратить по достижению 45-50%), но необходимо в ручном режиме влиять на мощность нагрева, не часто, но нужно. Также было бы неплохо оповещать звуком о наполнении очередного поллитра (а то бегай смотри как там))) - это решается достаточно просто, если есть свободные пины на контроллере. В дальнейшем есть еще мысли по модернизации - внедрение веб-сервера для контроля с дивана за процессом, но это попозже)))
Что-то переделать (модернизировать) - сложней чем сделать заново. Установленный ЖК-экран, 4 кнопки, 3 светодиода, бипер, термодатчик и реле отъели все пины на контроллере, оставив только 2 аналоговых. Все это изначально не задумывалось и не планировалось, поэтому получился такой затык... 2 пина конечно хватит для внедрения контроля за наполнением емкости, но не спасет от полной переделки, т.к. есть задумка как это все сделать более компактно и с большим функционалом и комфортом опять же)))
#include <OneWire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
OneWire ds(10);
// D7 - Serial clock out (CLK oder SCLK)
// D6 - Serial data out (DIN)
// D5 - Data/Command select (DC oder D/C)
// D4 - LCD chip select (CE oder CS)
// D3 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);
int ReleDevice = 2; //реле подключено ко 2му пину
int LedReleDevice = A2; // светодиод индикации работы реле
int Beeper = 9; // BEEPER PIN
int StartShow = 0; // переменная для перебора светодиодов при старте
int LedPin1 = A0; // светодиод кнопки 1 на порту A0
int ButPin1 = 8; // Кнопка 1 на пине 8
int LedPin2 = A1; // светодиод кнопки 2 на порту A1
int ButPin2 = 13; // Кнопка 2 на пине 13
int FlipButMinus = 12; // Кнопка "Качелька минус"
int FlipButPlus = 11; // Кнопка "Качелька плюс"
int flag_B1 = 0; // флаг состояния кнопки 1 (используется для отслеживания срабатывания кнопки)
int flag_B2 = 0; // флаг состояния кнопки 2 (используется для отслеживания срабатывания кнопки)
int flag_FlipButMinus = 0; // флаг состояния "Качелька минус" (используется для отслеживания срабатывания кнопки)
int flag_FlipButPlus = 0; // флаг состояния "Качелька плюс" (используется для отслеживания срабатывания кнопки)
int OnOff = 0; // переменная вкл и выкл режима начального нагрева до 68 град (кнопка 1)
int HeadOff = 0; // переменная окончания режима отбора голов и начало нагрева до 78 град (кнопка 2)
int BasicModeU = 4; // режим переключения пошагового набора температуры (с помощью качельки)
int BasicModeD = 7; // режим переключения пошагового набора температуры (с помощью качельки)
int TempUpSlow = 0; // переменная отслеживания режима быстрого/плавного нагрева
// начальное соотношение нагрев/пауза
unsigned long tD = 7000; // (time down) время отключенного реле в режиме плавного нагрева (mсек)
unsigned long tU = 4000; // (time up) время включенного реле в режиме плавного нагрева (mсек)
unsigned long tDr = 0;
unsigned long tUr = 0;
// ***
unsigned long AllTime; // переменная для записи времени прошедшего со старта программы
unsigned long MemTime; // переменная для записи времени начала события относительно времени прошедшего с начала программы
unsigned long AllTempTime; // переменная для записи времени прошедшего со старта программы для опроса DALLAS DS18 (температуры)
unsigned long MemTempTime; // переменная для записи времени начала события относительно времени прошедшего с начала программы для опроса DALLAS DS18 (температуры)
int flagTemp = 0; // переменная отслеживания опроса датчика температуры
int tTempTemp = 0; // временная переменная для записи расчета времени с последнего опроса датчика, не совсем верно, но по коду можно разобраться зачем))))
int Temp = 0;
int data[10];
int TempBeepMode_1 = 0; // | переменные для бипера при вкл/откл реле
int TempBeepMode_2 = 0; // |
int TempBeepMode_3 = 0; // |
int TempBeepMode_4 = 0; // |
int TempBeepMode_5 = 0; // |
int TempBeepMode_6 = 0; // |
int TempBeepMode_7 = 0; // |
int Temp1 = 87;
int Temp2 = 89;
int Temp3 = 91;
int TempEnd = 96;
unsigned long tD1 = 6000;
unsigned long tD2 = 5000;
unsigned long tD3 = 4000;
void setup()
{
display.begin(); // инициализируем дисплей
display.setContrast(60); // устанавливаем контраст LCD
display.clearDisplay(); // очищаем экран
pinMode(LedPin1, OUTPUT);
pinMode(LedPin2, OUTPUT);
pinMode(ReleDevice, OUTPUT);
pinMode(Beeper, OUTPUT);
pinMode(LedReleDevice, OUTPUT);
digitalWrite(ReleDevice, 0);
digitalWrite(LedReleDevice, 0);
Serial.begin(9600);
// analogWrite(9, 20); // значение должно находится между 0 и 255
delay(100);
// analogWrite(9, 0);
delay(50);
// analogWrite(9, 20); // значение должно находится между 0 и 255
delay(100);
// analogWrite(9, 0);
delay(50);
// analogWrite(9, 20); // значение должно находится между 0 и 255
delay(100);
// analogWrite(9, 0);
delay(50);
}
void BeepFunction(){
analogWrite(9, 20); // +BEEP значение должно находится между 0 и 255
delay(30);
analogWrite(9, 0); // -BEEP
}
void loop()
{
// *** НАЧАЛО ТЕМПЕРАТУРА
if (flagTemp == 0){
byte data[2]; // | если включили основной режим (до 78 град) |
ds.reset(); // | измеряем температуру и разблокируем блок |
ds.write(0xCC); // | 5ти светодиодов |
ds.write(0x44);
flagTemp = 1; // выставляем флаг на пропуск этого блока, пока не пройдет 1000 мс
MemTempTime = millis(); // засекаем время
}else{
AllTempTime = millis(); //
tTempTemp = AllTempTime - MemTempTime;
if (tTempTemp < 1000){
flagTemp = 1;
}else{
flagTemp = 0;
ds.reset();
ds.write(0xCC);
ds.write(0xBE);
data[0] = ds.read();
data[1] = ds.read();
Temp = (data[1]<<8)+data[0];
Temp = Temp>>4;
}
}
// *** КОНЕЦ ТЕМПЕРАТУРЫ
// *** НАЧИЛО КНОПКИ
// кнопка 1 (OnOff)
if (digitalRead(ButPin1) == HIGH && flag_B1 == 0)
{
digitalWrite(LedPin1, !digitalRead(LedPin1)); //изменяем состояние LED1
flag_B1 = 1; //ставим флаг нажатия кнопки
BeepFunction();
if(OnOff == 0)
{ // OnOff = 1 - это постоянно включенное реле
OnOff = 1;
TempUpSlow = 0;
}else if(OnOff == 1){ // OnOff = 2 - это режим плавного нагрева вкл./выкл. (tD; tU)
OnOff = 2;
TempUpSlow = 0;
}else{
OnOff = 0;
HeadOff = 0;
TempUpSlow = 1;
digitalWrite(LedPin1, 0);
digitalWrite(LedPin2, 0);
}
}
if(digitalRead(ButPin1) == LOW && flag_B1 == 1)
{
flag_B1 = 0;
}
// End кнопка 1
// кнопка 2 (HeadOff)
if(digitalRead(ButPin2) == HIGH && flag_B2 == 0 && OnOff != 0)
{
digitalWrite(LedPin2, !digitalRead(LedPin2));
flag_B2 = 1;
BeepFunction();
if(OnOff != 0 && HeadOff == 0){
HeadOff = 1;
}else if(OnOff != 0 && HeadOff == 1){
HeadOff = 2;
}else{
HeadOff = 0;
}
}
if(digitalRead(ButPin2) == LOW && flag_B2 == 1)
{
flag_B2 = 0;
}
// End кнопка 2
// качелька ПЛЮС
if(digitalRead(FlipButPlus) == HIGH && flag_FlipButPlus == 0 && OnOff != 0)
{
flag_FlipButPlus = 1;
if(BasicModeU != 20 && HeadOff == 0){
BasicModeU = BasicModeU+1;
TempUpSlow = 0;
tU = (BasicModeU*1000);
BeepFunction();
}else if(BasicModeU == 20 && HeadOff == 0){
BasicModeU = 20;
TempUpSlow = 0;
BeepFunction();
}else if(BasicModeD != 20 && HeadOff == 1){
BasicModeD = BasicModeD+1;
TempUpSlow = 0;
tD = (BasicModeD*1000);
BeepFunction();
}else if(BasicModeD == 20 && HeadOff == 1){
BasicModeD = 20;
TempUpSlow = 0;
BeepFunction();
}
}
if(digitalRead(ButPin2) == LOW && flag_FlipButPlus == 1)
{
flag_FlipButPlus = 0;
}
// End качелька ПЛЮС
// качелька МИНУС
if(digitalRead(FlipButMinus) == HIGH && flag_FlipButMinus == 0 && OnOff != 0)
{
flag_FlipButMinus = 1;
if(BasicModeU != 1 && HeadOff == 0){
BasicModeU = BasicModeU-1;
TempUpSlow = 0;
tU = (BasicModeU*1000);
BeepFunction();
}else if(BasicModeD == 1 && HeadOff == 0){
BasicModeU = 1;
TempUpSlow = 0;
BeepFunction();
}
if(BasicModeD != 1 && HeadOff == 1){
BasicModeD = BasicModeD-1;
TempUpSlow = 0;
tD = (BasicModeD*1000);
BeepFunction();
}else if(BasicModeD == 1 && HeadOff == 1){
BasicModeD = 1;
TempUpSlow = 0;
BeepFunction();
}
}
if(digitalRead(ButPin2) == LOW && flag_FlipButMinus == 1)
{
flag_FlipButMinus = 0;
}
// End качелька МИНУС
// BODY
if(OnOff != 0 && HeadOff == 2 && Temp >= Temp1 && Temp < Temp2){
tD = tD1;
}else if (OnOff != 0 && HeadOff == 2 && Temp >= Temp2 && Temp < Temp3){
tD = tD2;
}else if (OnOff != 0 && HeadOff == 2 && Temp >= Temp3 && Temp < TempEnd){
tD = tD3;
}else if (OnOff != 0 && HeadOff == 2 && Temp == TempEnd){
OnOff = 0;
HeadOff = 0;
digitalWrite(LedPin1, 0);
digitalWrite(LedPin2, 0);
TempUpSlow = 0;
digitalWrite(ReleDevice, 0);
digitalWrite(LedReleDevice, 0);
}
if (OnOff == 0 && Temp > Temp1){
BeepFunction();
delay(1500);
}
else if (OnOff == 1)
{ // нажимаем кнопку OnOff 1 раз - постоянный нагрев (OnOff = 1)
digitalWrite(ReleDevice, 1);
digitalWrite(LedReleDevice, 1);
//###########################################
}
else if(OnOff == 2)
{ // нажимаем кнопку OnOff 2 раза - плавный-регулируемый нагрев (OnOff = 2)
//###########################################
if (HeadOff == 2){
digitalWrite(LedPin2, !digitalRead(LedPin2));
}
if (TempUpSlow == 0){ //цикл вкл\выкл не начался
TempUpSlow = 1; // говорим что запустили плавный режим
MemTime = millis(); // засекаем время
digitalWrite(ReleDevice, 0);
digitalWrite(LedReleDevice, 0);
//###########################################
}else{
AllTime = millis(); //
if(tD > AllTime-MemTime){
tUr = 0;
tDr = AllTime-MemTime;
digitalWrite(LedPin1, !digitalRead(LedPin1)); // моргаем светодиодом первой кнопки
digitalWrite(ReleDevice, 0); // выключаем реле
digitalWrite(LedReleDevice, 0); // выключаем светодиод индикации работы реле
//###########################################
}else if(tU > AllTime-MemTime-tD){
tDr = 0;
tUr = AllTime-MemTime-tD;
digitalWrite(LedPin1, !digitalRead(LedPin1));
digitalWrite(ReleDevice, 1);
digitalWrite(LedReleDevice, 1);
//###########################################
}else{
TempUpSlow = 0;
digitalWrite(ReleDevice, 0);
digitalWrite(LedReleDevice, 0);
}
}
}
// END BODY
display.clearDisplay();
display.setTextSize(1);
display.print("Temp = ");
display.println(Temp);
display.drawLine(0, 11, 84, 11, BLACK);
display.println(" ");
display.print("U> ");
if (tU/1000 < 10){
display.print(" ");
display.print(tU/1000);
display.print(" | ");
display.println(tUr);
}else{
display.print(tU/1000);
display.print(" | ");
display.println(tUr);
}
display.print("D> ");
if (tD/1000 < 10){
display.print(" ");
display.print(tD/1000);
display.print(" | ");
display.println(tDr);
}else{
display.print(tD/1000);
display.print(" | ");
display.println(tDr);
}
display.drawLine(0, 35, 84, 35, BLACK);
display.println(" ");
if (HeadOff == 0 && OnOff == 0){
display.println(" DISTSYS.RU ");
}else if (OnOff != 0 && HeadOff == 0){
display.println("EDIT UP Time");
}else if (OnOff != 0 && HeadOff == 1){
display.println("EDIT DOWN Time");
}else if (OnOff != 0 && HeadOff == 2){
display.println("AUTOMATIC MODE");
}
display.display();
delay(100);
}
void set_text(int x,int y,String text,int color){
display.setTextColor(color);
display.setCursor(x,y);
display.println(text);
display.display();
}
На этом с данной верстей закончено, собрана версия 2, о ней в другой статье, а этот " прототип" разобран.
automatoc2
Автоматика для любого самогонного аппарата с сухопарником на основе контроллера arduino/ардуино.
Случилось чудо и все основное по моему новому блоку управления самогонным аппаратом закончено. Основное - потому, что это далеко не конечный вариант.
В этот раз решил сразу все подробно изложить, чтобы было все подробно.
Что в настоящий момент реализовано в самогонном аппарате:
легко редактируемое меню с навигацией джойстиком.
управление венилятором охлаждения семистора в зависимости от режима нагрева
возможность ручного и автоматического управления нагревом
в автоматическом режиме аппарат может самостоятельно управлять нагревом на всем протяжении перегонки и закончить перегонку по достижении заданной температуры в перегонном кубе.
есть возможность включить звуковое предупреждение / оповещение для каких-либо событий.
Передача показаний температуры на смартфон/планшет через блютуз
Начало работы над автоматикой, ошибки, варианты управления ТЭНом.
Я думал что это будет самое простое, однако время показало обратное)))... Хотя наверено даже наоборот, я все усложнил и на практике оказалось что нужно относится к этому проще. Так как это не ратификационная колонна, а просто дистилятор, то нет необходимости поддерживать постоянную температуру, управляя тэном по милисекундам. Секундными включениями тэна можно добиться постоянного, но неравномерного (что с нашем случае не принципиально) потока пара из перегонного куба.
Итак, был заказан контроллер ARDUINO UNO R3, релюхи, светодиоды, набор сопротивлений разного номинала, и датчик температуры DALLAS 18DS (по моему так). Всего по 2 штуки, чтобы можно было оперативно заменить. После размышлений как доставить температурный датчик в зону пара в перегонный куб, неожиданно на глаза попался стержень от ручки паркер, он идеально подходил для этой цели: тонкие стенки, с одной стороны он цельный( не надо паять, сминать и тд), подходящий по длине и диаметру. Стержень был схвачен, отпилен со стороны пера и рапотрашен.