Працюємо з 09:00 до 20:00 без вихідних.
Самовивіз - Київ біля ТЦ Квадрат бул.Перова

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

Технологічна схема пристрою озонування питної води зі скважини.

Технологічна схема представлена на зображенні нижче. Вода зі свердловини, що містить в своєму складі занадто багато заліза, закачується насосом у збірник. Там вона озонується циркулюванням через електро-озонатор. Після цього очищена вода повністю зливається через відкритий клапан KL1. Та процес повторюється знову і знову.

Потужність насосу 1,5 кВт, клапан на 12 В, озонатор на 220 В потужністю 24 Вт.

Алгоритм автоматичної системи

При увімкненні пристрою в автоматичний режим перевіряється рівень води в баку. Якщо рівень менше нижнього датчика L1 (жодний датчик рівня не спрацював), зачиняється клапан KL1, вмикається насос та озонатор. При досягненні рівня води до датчика L2, вимикається насос та витримується час озонування. Після цього вимикається озонатор та витримується час осадження. Після цього відкривається клапан KL1 та алгоритм повторюється.

У випадку, коли по яким-небуть причинам не спрацьовує верхній рівень, було передбачено аварійний датчик рівня. При його спрацьовуванні алгоритм переривається. Вимикається озонатор та насос, а також закривається клапан KL1.

Інтерфейс

Для відображення інформації використаємо двохрядковий рідкокристалічний індикатор, а для введення даних та керування пристроєм використаємо простий енкодер з кнопкою. Я такі пристрої введення даних юзав на буржуйських заводських частотних перетворювачах та був у захваті - давно хотів повторити такий функціонал у своїх пристроях.

Обертанням енкодера в різні сторони можна переходити по меню керування, а також змінювати значення налаштувань. Натисканням на ручку енкодера будемо вибирати пункти меню, в які хочемо зайти, а також підтверджувати уведені дані.

Вибір елементної бази

Для реалізації озвученої задачі просто ідеально підійде плата контролера Arduino Nano, що встановлена на монтажний шилд наступного вигляду


Даний шилд максимально спрощує монтажні роботи та економить місце на клемніках. Він дозволяє підключати усю периферію контролера без необхідності у перетинках, що поєднують плюси та мінуси живлення.

Насос, клапан та озонатор в рамках цієї статті розглядати не будемо - у всіх будуть свої. 

Датчики рівня виберемо механічні буйкові горизонтального монтажу.

Символьний індикатор стандартний для любительської автоматики LCD 1602 з модулем I2C.

Для комутації живлення насосу візьмемо твердотільне реле SSR-40DA.

Для комутації живлення озонатора використаємо одноканальний модуль твердотільного реле G3MB-202P.

Для управління клапаном використаємо модуль MOSFET транзистора IRF520, котрий буде комутувати постійну напругу 12 В.

В якості енкодера беремо KY-040.

Для живлення контролера та клапану візьмемо блок живлення 12 В 2 А.

Усі ці елементи змонтуємо в електро-щитку з прозорою передньою панеллю. А усі зовнішні кабелі будемо саджати на дві клемні колодки. На одній будуть підключені виконавчі механізми та уведення 220 В, а на другій датчики та уведення 12 В.

Схема підключення

На схеме было очень сложно понятным образом показать, как периферия подключается к шилду Arduino Nano, поэтому попробую описать это текстом.

Символьный индикатор подключаем к специальным выводам слева на шилде SCL SDA 5V GND.

Реле SSR-40DA подключаем к третьему дискретному входу - минус на пин G, а плюс на пин S (signal).

Модуль реле G3MB-202P подключаем к 4-му дискретному входу - минус на пин G, плюс на пин V, а сигнальный вывод на пин S.

Модуль MOSFET иранзистора подключаем ко 2-му дискретному входу - минус на пин G, плюс на пин V, а сигнальный вывод на пин S.

Датчик L1 подключаем к 5-му дискретному каналу контроллера - один провод к G, а другой к сигнальному S.

Датчик L2 подключаем к 8-му дискретному каналу контроллера - один провод к G, а другой к сигнальному S.

Датчик L3 подключаем к 9-му дискретному каналу контроллера - один провод к G, а другой к сигнальному S.

Энкодер нужно подключить следующим образом: вывод CLK к выводу S 7-го канала контроллера; вывод DT к выводу S 6-го канала шилда; вывод кнопки SW  к выводу S 11-го канала шилда; вывод + на вывод V 7-го канала шилда; вывод GND на вывод G 7-го канала монтажного шилда.

Разъем питания 12 В подключаем не к контроллеру, а на шилд. И эти же 12 В в разрыве кабеля блока питания заводим на клеммы питания модуля MOSFET транзистора.

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


Программа контроллера


#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h> //библиотека для запоминания настроечных данных в постоянную память контроллера

LiquidCrystal_I2C lcd(0x27, 16, 2);

const int L1_pin = 5;
const int L2_pin = 8;
const int L3_pin = 9;
const int M1_pin = 3;
const int ozon_pin = 4;
const int KL1_pin = 2;

boolean L1, L2, L3;
int L1_count = 0;
int L2_count = 0;
int L3_count = 0;

const int button = 11;
int button_old = 1;

int astep = 0;
int menu = 0;
int menu_ = 0;
int t_ozona;
int t_osad;

int avto = 0; //rezhim avto
boolean nasos = 0; //nasos on/off
boolean ozon = 0; //ozonator on/off
boolean klapan = 0; //klapan on/off

int pinA = 7;  // номер вывода, подключенный к CLK енкодера
int pinB = 6;  // номер вывода контроллера, подключенный к DT енкодера
 int encoderPosCount = 0; 
 int pinALast;  
 int aVal;
 boolean bCW;

unsigned long previousMillis = 0;
unsigned long currentMillis;
const long interval = 500;
unsigned long prevMillis_ozona = 0;
unsigned long curMillis_ozona;

unsigned long prevMillis_osad = 0;
unsigned long curMillis_osad;


void setup()
{
  t_ozona = EEPROM.read(9);
  t_osad = EEPROM.read(10);
  pinMode(button, INPUT_PULLUP);
  pinMode (pinA,INPUT_PULLUP);
  pinMode (pinB,INPUT_PULLUP);
  pinMode(L1_pin, INPUT_PULLUP);
  pinMode(L2_pin, INPUT_PULLUP);
  pinMode(L3_pin, INPUT_PULLUP);

  pinMode(M1_pin, OUTPUT);
  pinMode(ozon_pin, OUTPUT);
  digitalWrite(ozon_pin, !ozon); //zaschita ot promigivaniya pri puske
  pinMode(KL1_pin, OUTPUT);
	
  lcd.begin();
  Serial.begin(9600);
  // включение подсветки и вывод приветствия
  lcd.backlight();
  lcd.print("Pryvit,");
  lcd.setCursor(7, 1);
  lcd.print("gospodar!");
  delay(2000);
  pinALast = digitalRead(pinA);
  
  currentMillis = millis();
  previousMillis = currentMillis;
}

void loop()
{
  if (!digitalRead(L1_pin)) {if (L1_count<100) L1_count ++;} else {if (L1_count>0) L1_count --;} //filtracia din
  if (L1_count==100) L1 = 1;
  if (L1_count==0) L1 = 0;
  if (!digitalRead(L2_pin)) {if (L2_count<100) L2_count ++;} else {if (L2_count>0) L2_count --;} //filtracia din
  if (L2_count==100) L2 = 1;
  if (L2_count==0) L2 = 0;
  if (!digitalRead(L3_pin)) {if (L3_count<100) L3_count ++;} else {if (L3_count>0) L3_count --;} //filtracia din
  if (L3_count==100) L3 = 1;
  if (L3_count==0) L3 = 0;
  
  currentMillis = millis();

  func_encoder();

  if (avto==1) func_auto(); else {prevMillis_ozona = 0; curMillis_ozona = 0; prevMillis_osad = 0; curMillis_osad=0; astep=0;}

  func_menu();

  
  func_button();
  func_outs();
  
}
void func_outs()
{
  button_old = digitalRead(button); 
  
  digitalWrite(M1_pin, nasos);
  digitalWrite(ozon_pin, !ozon);
  digitalWrite(KL1_pin, klapan);
  delay(10);
}
void func_auto()
{
  if (astep==0){
    nasos = 0; 
    ozon = 0; 
    klapan = 1;
    if (!L1&&!L2&&!L3) astep = 100;
  }
  if (astep==100){
    nasos = 1; 
    ozon = 1; 
    klapan = 0;
    if (L1&&L2) {
      astep = 200;
      prevMillis_ozona = millis();
    }
  }
  if (astep==200){
    nasos = 0; 
    ozon = 1; 
    klapan = 0;
    curMillis_ozona = millis();
    if (curMillis_ozona - prevMillis_ozona >= t_ozona*60000){
      astep = 300; 
      prevMillis_osad = millis();
    }
  }
  if (astep==300){
    nasos = 0; 
    ozon = 0; 
    klapan = 0;
    curMillis_osad = millis();
    if (curMillis_osad - prevMillis_osad >= t_osad*60000) astep = 0;
  }
  
  if (L3) {
    avto = 0;
    nasos = 0; 
    ozon = 0; 
    klapan = 0;
  }
}
void func_button()
{
  if ((digitalRead(button)==LOW)&&(button_old==1))
    { 
      if ((menu == 1)and(button_old==1)) {
        switch (menu_) {
          case 0: menu = 100; encoderPosCount=0;
          break;
          case 1: if (avto==0){ menu = 200; encoderPosCount=0;}
          break;
          case 2: menu = 0; encoderPosCount=0;
          break;
        }
        button_old=0;
      }

      if ((menu == 100)and(button_old==1)) {
        switch (menu_) {
          case 0: if (avto==0) avto = 1; else {avto = 0; nasos = 0; ozon = 0; klapan = 0;}
          break;
          case 1: menu = 120; encoderPosCount = t_ozona;
          break;
          case 2: menu = 130; encoderPosCount = t_osad;
          break;
          case 3: menu = 0; encoderPosCount=0;
          break;
        }
        button_old=0;
        
      }

      if ((menu == 120)and(button_old==1)) {
        menu = 100;
        encoderPosCount = 1;
        button_old=0;
      }

      if ((menu == 130)and(button_old==1)) {
        menu = 100;
        encoderPosCount = 2;
        button_old=0;
      }

      if ((menu == 200)and(button_old==1)) {
        switch (menu_) {
          case 0: if (nasos==0) nasos = 1; else nasos = 0;
          break;
          case 1: if (ozon==0) ozon = 1; else ozon = 0;
          break;
          case 2: if (klapan==0) klapan = 1; else klapan = 0;
          break;
          case 3: menu = 0; encoderPosCount=0;
          break;
        }
        button_old=0;
      }
      
      if ((menu == 0)and(button_old==1)) {
        menu = 1;
        menu_ = 0;
        button_old=0;
        encoderPosCount=0;
      }
      
      
      menu_ = 0;
    }
}
void func_encoder()
{
  //encoder
   aVal = digitalRead(pinA);
   if (aVal != pinALast){ // проверка на изменение значения на выводе А по сравнению с предыдущим запомненным, что означает, что вал повернулся
     // а чтобы определить направление вращения, нам понадобится вывод В.
     
     if (digitalRead(pinB) != aVal) {  // Если вывод A изменился первым - вращение по часовой стрелке
       encoderPosCount ++;
       bCW = true;
     } else {// иначе B изменил свое состояние первым - вращение против часовой стрелки
       bCW = false;
       encoderPosCount--;
     }              
   }
   pinALast = aVal;
   //encoder
}
void func_menu()
{
  if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
  if (menu == 1){
    
    if (encoderPosCount>2) encoderPosCount = 0;
    if (encoderPosCount<0) encoderPosCount = 2;
    menu_ = encoderPosCount;
    lcd.clear();
       lcd.setCursor(0, 0);
        lcd.print(">");
        lcd.setCursor(2, 0);
        switch (menu_) {
          case 0: lcd.print("AUTO");
          break;
          case 1: if (avto==0) lcd.print("RUCHNIY"); else lcd.print("ruchniy");
          break;
          case 2: lcd.print("VIHID");
          break;
        }
        
        lcd.setCursor(1, 1);
        switch (menu_) {
          case 0: if (avto==0) lcd.print("RUCHNIY"); else lcd.print("ruchniy");
          break;
          case 1: lcd.print("VIHID");
          break;
          case 2: lcd.print("AUTO");
          break;
        }
     
  }

  if (menu == 0){
      lcd.setCursor(0, 0);
      if (L1) lcd.print("L1 "); else lcd.print("l1 ");
      lcd.setCursor(3, 0);
      if (L2) lcd.print("L2 "); else lcd.print("l2 ");
      lcd.setCursor(6, 0);
      if (L3) lcd.print("L3 "); else lcd.print("l3 ");
      lcd.setCursor(9, 0);
      if (nasos) lcd.print("M1 "); else lcd.print("m1 ");
      lcd.setCursor(12, 0);
      if (avto == 0) lcd.print("RUCH"); else lcd.print("AUTO");
      lcd.setCursor(0, 1);
      if (klapan) lcd.print("KL1 "); else lcd.print("kl1 ");
      
      lcd.setCursor(4, 1);
      if (ozon) lcd.print("OZON  "); else lcd.print("ozon  ");
      lcd.setCursor(10, 1);
      if (avto==1) {
        switch (astep) {
          case 0: lcd.print("VYPUSK");
          break;
          case 100: lcd.print("NABOR ");
          break;
          case 200: lcd.print("OZONUV");
          break;
          case 300: lcd.print("OSADZH");
          break;
        } 
      }else lcd.print("      ");
       
  }

  if (menu == 100){
   
    if (encoderPosCount>3) encoderPosCount = 0;
    if (encoderPosCount<0) encoderPosCount = 3;
    menu_ = encoderPosCount;
    lcd.clear();
       lcd.setCursor(0, 0);
        lcd.print(">");
        lcd.setCursor(2, 0);
        switch (menu_) {
          case 0: if (avto==0) lcd.print("RUCHNIY"); else lcd.print("AUTO");
          break;
          case 1: lcd.print("CHAS OZONU");
          break;
          case 2: lcd.print("CHAS OSADU");
          break;
          case 3: lcd.print("VIHID");
          break;
        }
        
        lcd.setCursor(1, 1);
        switch (menu_) {
          case 0: lcd.print("CHAS OZONU");
          break;
          case 1: lcd.print("CHAS OSADU");
          break;
          case 2: lcd.print("VIHID");
          break;
          case 3: if (avto==0) lcd.print("RUCHNIY"); else lcd.print("AUTO");
          break;
        }
    
  }

    if (menu == 200){
      
        if (encoderPosCount>3) encoderPosCount = 0;
        if (encoderPosCount<0) encoderPosCount = 3;
        menu_ = encoderPosCount;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(">");
        lcd.setCursor(2, 0);
        switch (menu_) {
          case 0: if (nasos==0) lcd.print("nasos m1"); else lcd.print("NASOS M1");
          break;
          case 1: if (ozon==0) lcd.print("ozonator"); else lcd.print("OZONATOR");
          break;
          case 2: if (klapan==0) lcd.print("klapan kl1"); else lcd.print("KLAPAN KL1");
          break;
          case 3: lcd.print("VIHID");
          break;
        }
        
        lcd.setCursor(1, 1);
        switch (menu_) {
          case 0: if (ozon==0) lcd.print("ozonator"); else lcd.print("OZONATOR");
          break;
          case 1: if (klapan==0) lcd.print("klapan kl1"); else lcd.print("KLAPAN KL1");
          break;
          case 2: lcd.print("VIHID");
          break;
          case 3: if (nasos==0) lcd.print("nasos m1"); else lcd.print("NASOS M1");
          break;
        }
      
   }

   if (menu == 120){
      
        if (encoderPosCount>255) encoderPosCount = 255;
        if (encoderPosCount<0) encoderPosCount = 0;
        t_ozona = encoderPosCount;
        EEPROM.write(9, t_ozona);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Chas ozonu");
        lcd.setCursor(0, 1);
        lcd.print(t_ozona); lcd.print(" hvilin");
      
   }

   if (menu == 130){
     
        if (encoderPosCount>255) encoderPosCount = 255;
        if (encoderPosCount<0) encoderPosCount = 0;
        t_osad = encoderPosCount;
        EEPROM.write(10, t_osad);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Chas osadzhennia");
        lcd.setCursor(0, 1);
        lcd.print(t_osad); lcd.print(" hvilin");
   }
 }
}

Выводы

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

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

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

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

Подобные проекты можно заказать по телефонам, указанным сверху на сайте.

Написати відгук

Примітка: HTML размітка не підтримується! Використовуйте звичайтий текст.
    Погано           Добре
Гвинтовий конектор на три дроти

Гвинтовий конектор на три дроти

Монтується на друковану плату або макетну платуВідстань між виводами 5 мм..

4.14грн.

Модуль SPI i2c для LCD індикаторів

Модуль SPI i2c для LCD індикаторів

Модуль припаюється на LCD-індикатор типу LCD 2004, LCD 1602.Забезпечує зв'язок рідкокристалічного ін..

21.99грн.

Термінальний роз'ємний конектор кутовий 2д3.81mm

Термінальний роз'ємний конектор кутовий 2д3.81mm

Роз'ємний двохдротовий конектор для пайки на друковану платуДроти підводяться паралельно платі та з'..

17.79грн.

Модуль звукового сигналізатора

Модуль звукового сигналізатора

Пасивний звуковий сигналізатор для підключення до дискретного виходу контролераЖивлення 5 ВПриклад п..

37.18грн.

Модуль MP3-плеєра з управлінням від контролера

Модуль MP3-плеєра з управлінням від контролера

Модуль MP3-плеєра з управлінням по UARTПідтримує наступні частоти дискретизації (кГц): 8 / 11.0..

135.45грн.