Працюємо з 09:00 до 19: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 размітка не підтримується! Використовуйте звичайтий текст.
    Погано           Добре
Силіконовий браслет RFID 13,56 МГц

Силіконовий браслет RFID 13,56 МГц

Силіконовий браслет для дорослих для ідентифікації RFIDТакі браслети використовують в спортивних клу..

61.88грн.

Свердла 1,0 мм 10 шт.

Свердла 1,0 мм 10 шт.

Свердла для свердління фольгованого текстоліту діаметром 1,0 ммВ упаковці 10 шт...

33.55грн.

Перехідник живлення з крони на Arduino

Перехідник живлення з крони на Arduino

Перехідник живлення з батарейки крона на роз'єм  5,5х2,1ммПідходить для живлення плат конт..

12.47грн.

Модуль Bluetooth HC 05

Модуль Bluetooth HC 05

Стандарт bluetooth V2.0 Працює в режимі майстер та підлеглий (налаштовується АТ-командами)Напру..

150.26грн.

Датчик дотику ємнісний

Датчик дотику ємнісний

Датчик видає дискретний сигнал на виході, коли до нього доторкаються пальцем.Його можна використовув..

22.47грн.