Контроллер управления влажностью на Atmega328

в 13:38, , рубрики: arduino, DHT11, diy или сделай сам, Программинг микроконтроллеров, метки: ,

Недавно столкнулся с тривиальной задачей — управление вытяжным вентилятором дома в ванной комнате.

Казалось бы чего проще, подключил его к выключателю света и готово. Но, время работы света непостоянно и может быть недостаточно для уменьшения влажности, хотя данную проблему можно решить установкой таймера. К тому же, моим близким очень не нравится работающий вентилятор при принятии водных процедур, так как он «создает холодный ветер».

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

Пришлось подключить к делу свое увлечение Arduino и несложными микроконтроллерами.

Пораскинув мозгами сформулировал

Требования к устройству управления

  1. устройство управления должно работать в автоматическом режиме;
  2. вентилятор должен включаться от повышения влажности;
  3. включение вентилятора не должно зависеть от текущего уровня влажности в квартире;
  4. вентилятор должен работать, когда в ванной комнате никого нет;
  5. устройство управление должно быть максимально простым и дешевым;

Выбор элементной базы

Прототип данного устройства создавался на отладочной плате Arduino Uno китайского производства:
Контроллер управления влажностью на Atmega328

Потом все переносилось в микроконтроллер Atmega328, прошитый стандартным ардуиновским загрузчиком.

Конечное устройство создавалось по принципу «я тебя слепила из того что было». Все элементы были приобретены ранее на просторах интернета под различные проектики или выдраны из неработающих устройств:

В качестве источника питания подошел LED driver 3x1вт от светильника, который вполне справился с питанием импульсного стабилизатора LM2596.
В качестве корпуса была применена кроссовая коробочка от старой АТС.

Подключение

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

  • Pin 1 (Reset) через резистор 10К к + (для избежания помех на этой ноге);
  • Pin 2 (D0) — не используется;
  • Pin 3 (D1) — не используется;
  • Pin 4 (D2) — катод «G» семисегментного индикатора;
  • Pin 5 (D3) — катод "." семисегментного индикатора;
  • Pin 6 (D4) — катод «A» семисегментного индикатора;
  • Pin 7 (+) — питание 3.5В;
  • Pin 8 (-) — земля;
  • Pin 9 (Clock) — не используется;
  • Pin 10 (Clock) — не используется;
  • Pin 11 (D5) — катод «F» семисегментного индикатора;
  • Pin 12 (D6) — катод «D» семисегментного индикатора;
  • Pin 13 (D7) — катод «E» семисегментного индикатора;
  • Pin 14 (D8) — катод «C» семисегментного индикатора;
  • Pin 15 (D9) — катод «B» семисегментного индикатора;
  • Pin 16 (D10) — не используется;
  • Pin 17 (D11) — не используется;
  • Pin 18 (D12) — не используется;
  • Pin 19 (D13) — управление схемой включения вентилятора;
  • Pin 20 (+) — питание 3.5В (дубль);
  • Pin 21 (Aref) — не используется;
  • Pin 22 (-) — земля (дубль);
  • Pin 23 (A0/D14) — фоторезистор с подтягивающим резистором 27К на землю;
  • Pin 24 (A1/D15) — вход с датчика DHT11;
  • Pin 25 (A2/D16) — общий анод 2-го разряда семисегментного индикатора через резистор 4.7К;
  • Pin 26 (A3/D17) — общий анод 1-го разряда семисегментного индикатора через резистор 4.7К;
  • Pin 27 (A4/D18) — кнопка с подтягивающим резистором 10К на +;
  • Pin 28 (A5/D19) — не используется;

На индикатор CPS03621BR даташит найден не был, поэтому выводы находились при помощи батарейки методом тыка. Индикатор оказался с общим анодом. Схема расположения катодов:
image

Вентилятор управляется при помощи симистора BT137.
Схему подключения взял с сайта avr.ru
Контроллер управления влажностью на Atmega328
Если кто-то вздумает повторять — ОСТОРОЖНО, на корпусе симистора напряжение 220В.

Контроллер управления влажностью на Atmega328

Алгоритм работы

Микроконтроллер с периодичностью раз в 10 секунд меряет влажность и температуру.
Влажность циклически накапливается в архиве из 6 значений. Если текущая влажность выше первой из архива более чем на 3% либо абсолютное значение влажности выше 85%, значит нужно включать вентилятор.
Вентилятор включается на 20 минут при отсутствии света на фоторезисторе.
Кнопка принудительно включает вентилятор на 20 минут (если он не работает) или выключает (если работает).
Все константы в алгоритме подбирались эксперементальным путем.

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

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

  • нажата кнопка ручного режима;
  • сработал датчик влажности;
  • горит свет;
  • не горит свет;
  • сработал таймер работы вентилятора.

Множество состояний:

  1. режим ожидания, вентилятор не работает, тайме отключен;
  2. требуется включение вентилятора, вентилятор не работает, таймер (при)остановлен;
  3. вентилятор работает в автоматическом режиме, таймер включен;
  4. вентилятор работает в ручном режиме, таймер включен;

Ну и таблица переходов состояний автомата:
Контроллер управления влажностью на Atmega328

Программирование

AVR-studio и прочих монстров я устанавливать не стал, а обошелся опять же тем, что было — IDE Arduino.

Создаю в файле board.txt новый контроллер, работающий на внутреннем кварце 8МГц:

atmega328_8.name=Atmega328 (5V, 8 MHz internal)

atmega328_8.upload.protocol=arduino
atmega328_8.upload.maximum_size=30720
atmega328_8.upload.speed=57600

atmega328_8.bootloader.low_fuses=0xE2
atmega328_8.bootloader.high_fuses=0xDE
atmega328_8.bootloader.extended_fuses=0x05
atmega328_8.bootloader.path=optiboot
atmega328_8.bootloader.file=optiboot_atmega328.hex
atmega328_8.bootloader.unlock_bits=0x3F
atmega328_8.bootloader.lock_bits=0x0F

atmega328_8.build.mcu=atmega328p
atmega328_8.build.f_cpu=8000000L
atmega328_8.build.core=arduino
atmega328_8.build.variant=standard

Подключаю дешевый программатор USBASP к ISCP разъему платы Arduino UNO и, вставив в панельку свой Atmega328P, прошиваю загрузчик.

Теперь мой микроконтроллер можно отлаживать и программировать через стандартный загрузчик на плате UNO, выбрав в среде соответствующую плату.

В данном проекте использовались готовые библиотеки для Arduino:

Полный код скетча

#include <DHT.h>
#include <SevenSegmentDisplay.h>



#define DEBUG        1
#define TIMER_PERIOD 2400

#define ctrPIN   13 //Дискретный вывод для вентелятора
#define dhtPIN   15 //Дискретный вход для датчика температуры
#define btnPIN   18 //Кнопка

// Указатель на функцию перезагрузки контроллера
void(* resetFunc) (void) = 0; // Reset MC function

// Инициируем светодиодную матрицу
SevenSegmentDisplay<true, BiDigit<17, 16> > ss(4, 9, 8, 6, 7, 5, 2, 3);

// Инициируем DHT11 на контроллере с частотой 8МГц
DHT dht(dhtPIN, DHT11, 3);// 8Мгц
//DHT dht(dhtPIN, DHT11); //16МГц

// Состояния FSM
enum TMode
  {
  tmWait,       //Режим ожидания
  tmNeedPower,  //Требуется включение вентилятора
  tmAutoPower,  //Вентилятор работает в автоматическом режиме
  tmManualPower //Вентилятор работает в ручном режиме
};
  
// Состояния отображения
enum TDisplayMode
  {
  tdmTemp       , //Отображение температуры
  tdmHum        , //Отображение влажности
  tdmTimer        //Отображение таймера  
};
  
  
//int h_prev;
int t,h,a0;

int h_arr[6];



void setup()
{
// Инициируем порт отладки
#ifdef DEBUG
    Serial.begin(9600);
    Serial.println("Humidity controller start ...");
#endif
// Инициируем выход управления 
    pinMode(ctrPIN, OUTPUT); // будем мигать лампочкой при передаче
    digitalWrite(ctrPIN, LOW); 
// Инициируем вход с кнопкой
    pinMode(btnPIN, INPUT);     
//Включаем подтягивающий резистор    
    digitalWrite(btnPIN, HIGH); 

    
// Инициируем датчик температуры DHT11    
    dht.begin();
// Считываем первоначальное значение влажности
    h = dht.readHumidity();
    for( int i=0; i<6; i++)h_arr[i] = h;

}

// Счетчик 0.5 секудных тактов
unsigned long cnt05 = 0;
unsigned long ms1   = 0;
// Флаг включения света
boolean flag_light = false;
// Флаг нажатия кнопки
boolean flag_btn   = false;
// Флаг влажерсти
boolean flag_hum   = false;
// Считчик таймера работы вентилятора
unsigned int timer = 0;
TMode mode         = tmWait;
TDisplayMode dmode = tdmTemp;
boolean blink_stat = false;   

void loop ()
{ 
   unsigned long ms = millis();
   int p = ms - ms1;

// Считываем состояние кнопки
   
   if( digitalRead(btnPIN) == LOW ){
      int n = 1;
      for( int i=0; i<9; i++ ){
         if( digitalRead(btnPIN) == LOW )n++;
         delay(10);
      }
      if( n > 9 )flag_btn = true;   
      delay(400);
#ifdef DEBUG
      Serial.println("Button is press");
#endif
   }//end if     

// Проверка, что прошло не менее 0.5 сек
   if( p < 0 || p > 500 ){
      cnt05++;
      ms1 = ms;
// Считываем состояние фоторезистора
      a0 = analogRead(A0);
      if( a0 > 1000 )flag_light = false;
      else flag_light = true;
// Каждые 10 секунд опрашиваем датчик DHT11 и меняем отображение дисплея     
      if( cnt05%20 == 0 ){    
         h = dht.readHumidity();
         t = dht.readTemperature();
// Если влажность изменилась на 3% или влажность превысила 85%   
         
         if( h - h_arr[5] > 3 || h > 85 )flag_hum = true;
         for( int i=5; i>0; i--)h_arr[i-1] = h_arr[i];
         h_arr[0] = h;
#ifdef DEBUG
         Serial.print("VAL: Temp=");
         Serial.print(t);
         Serial.print(" H=");
         Serial.print(h);
         Serial.print(" A0=");
         Serial.print(a0);
         Serial.print(" X=");
         Serial.print(cnt05);
         Serial.print(" TM=");
         Serial.print(timer);
         Serial.print(" MODE=");
         Serial.print(mode);
         Serial.print(" DMODE=");
         Serial.print(dmode);
         Serial.println("");
#endif
// Переключаем дисплей
         switch( dmode ){
            case tdmTemp : dmode = tdmHum; break;
            case tdmHum  : dmode = tdmTimer; break;
            default:       dmode = tdmTemp;
         }//end switch
      }//end if( cnt05%20 == 0 )   
      blink_stat = !blink_stat;
      SetStatusFSM(); 
   }//end if( p < 0 || p > 500 ){   
   DisplayStatus(); 

}//end loop()     

/**
* Функция отображение разных параметров
*/
void DisplayStatus(){
// Настройка отображения режима в    
   int point = -1;
   switch( mode ){
      case tmNeedPower:
         point = 0;
         break;
      case tmAutoPower:
      case tmManualPower:
         if( blink_stat )point = 0;
         break;   
   }  
   switch( dmode ){
      case tdmTemp :
         ss.print((unsigned)t,point,50);
         break;
      case tdmHum  :
         ss.print((unsigned)h,point,50);
         break;
      case tdmTimer:
// Показываем минуты      
         if( timer > 120 )ss.print((unsigned)(timer/120),point,50);
// Показываем секунды
         else if( timer > 0 )ss.print((unsigned)(timer/2),point,50);
// Показываем 0
         else ss.print(0,point,50);    
//         ss.print((unsigned)(a0/100),point,50);
         break;
   }//end switch
   
}


/**
* Функция перехода автомата состояний
*/
void SetStatusFSM(){
   switch(mode){
// Режим ожидания     
      case tmWait :
         digitalWrite(ctrPIN, LOW);
// Нажата кнопка      
         if( flag_btn ){
            timer = TIMER_PERIOD;
            mode  = tmManualPower;
         }
// Если сработал датчик влажности
         else if( flag_hum ){
            timer = TIMER_PERIOD;
            mode  = tmNeedPower;
         }   
         break;
// Состояние ожилания работы вентилятора
      case tmNeedPower:
         digitalWrite(ctrPIN, LOW);
// Нажата кнопка      
         if( flag_btn ){
            mode  = tmManualPower;
         }
// Свет выключен
         else if( !flag_light ){
            mode  = tmAutoPower;
         }
         break;
// Состояние "Вентилятор работает в автомате"
      case tmAutoPower:
// Включить вентилятор
         digitalWrite(ctrPIN, HIGH);
// Таймер считает
         if( timer > 0 )timer--;
// Нажата кнопка      
         if( flag_btn ){
            mode  = tmWait;
            timer = 0;
         }
// Включился чвет      
         else if( flag_light ){
            mode  = tmNeedPower;
         }
// Таймер сработал
         else if( timer <= 0 ){
            timer = 0;
            mode  = tmWait;
         }
         break;
// Состояние "Вентилятор работает в ручном режиме"
      case tmManualPower:
// Включить вентилятор
         digitalWrite(ctrPIN, HIGH);
// Таймер считает
         if( timer > 0 )timer--;
// Нажата кнопка      
         if( flag_btn ){
            mode  = tmWait;
            timer = 0;
         }
// Таймер сработал
         else if( timer <= 0 ){
            timer = 0;
            mode  = tmWait;
         }
         break;
   } 
// Сбросить флани кнопка и влажность   
   flag_btn = false;
   flag_hum = false;

}

Проблемы

Первой проблемой, с которой столкнулся в реализации — не работал датчик DHT11. На Arduino UNO все нормально, а на голом микроконтроллер не работает. Проблема оказалась в частоте работы контроллера и таймингам протокола опроса DHT.
В контроллерах, работающих на частоте 8МГц в библиотеке DHT нужно обязательно указывать задержку «3» (третий параметр в конструкторе класса) DHT dht(dhtPIN, DHT11, 3);

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

Готовое устройство выглядит так:
Контроллер управления влажностью на Atmega328

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

Список полезных ссылок:

Автор: sav13

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js