- PVSM.RU - https://www.pvsm.ru -

Шаблон интерфейса для «умного дома» на Ардуино — третья часть

image

Для понимания, необходимо прочитать предыдущую тему [1].

Здравствуйте.

В этой части будет немного изменён внешний вид и добавлен датчик температуры (и влажности), чтоб в зависимости от температуры включалась печка или кондиционер, а так же реализована ещё одна очень полезная функция…
Поскольку наш веб-интерфейс непрерывно запрашивает данные у ардуины, то будет не лишним отправлять его в «спящий режим», для того чтоб не мешать другим клиентам.

Онлаин... [2]

Температуру и влажность будет измерять DHT22.

image
Ардуина
Прошиваем код:

#include <EEPROM.h>
#define MAXMILLIS 4294967295
#include "DHT.h"
#define DHTPIN 8 // пин для подключения датчика
#define DHTTYPE DHT22  
DHT dht(DHTPIN, DHTTYPE);

int hum = 110; // влажность
int thum = 110; // температура

byte d2 = EEPROM.read(2);     // флаги (состояние пинов) хранится в EEPROM, считываем их
byte d3 = EEPROM.read(3);
byte d4 = EEPROM.read(4);
int shim1 = EEPROM.read(5); // значение ШИМ хранится в EEPROM, считываем их
int shim2 = EEPROM.read(6);
byte d7 = 0; 
byte d8 = 0;
byte d11 = EEPROM.read(11);
byte d12 = EEPROM.read(12);
byte d13 = EEPROM.read(13);

int oxlagdenie = EEPROM.read(14); // заданая температура для включения кондиционера
int toxl = EEPROM.read(15); // флаг включения

int podogrev = EEPROM.read(16); // заданая температура для включения печки
int tpod = EEPROM.read(17); // флаг включения

byte descript[5]; // массив

unsigned long   time; // время

long timelapsed = 0;

byte count=0; // счетчик минут

void setup() 
{
  Serial.begin(57600);
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT); // ШИМ
  pinMode(6, OUTPUT); // ШИМ
  pinMode(7, OUTPUT); // охлаждение
  pinMode(8, OUTPUT); // подогрев 
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  
  thum = dht.readTemperature(); // считываем температуру, сначала делаем это в сетапе, так как следующее чтение будет только через минуту
  hum = dht.readHumidity(); // считываем влажность
  
  if(d2) digitalWrite(2, HIGH); else digitalWrite(2, LOW); // если до перезагрузки d2 была включена, то включаем, если нет, то нет 
  delay(500); // чтобы не включалось всё сразу, делаем паузы
  if(d3) digitalWrite(3, HIGH); else digitalWrite(3, LOW);
  delay(500);
  if(d4) digitalWrite(4, HIGH); else digitalWrite(4, LOW);
  delay(500);
  analogWrite(5, shim1 * 2.55); // включаем ШИМ d5
  delay(500);
  analogWrite(6, shim2 * 2.55); // включаем ШИМ d6
  delay(500);
  if(d11) digitalWrite(11, HIGH); else digitalWrite(11, LOW);
  delay(500);
  if(d12) digitalWrite(12, HIGH); else digitalWrite(12, LOW);
  delay(500);
  if(d13) digitalWrite(13, HIGH); else digitalWrite(13, LOW);
}
  
void loop() 
{  
  if (Serial.available()>4) // ждём дескриптор и нужный символ
   {
    if (Serial.read()=='Y') // проверяем первый символ, если это 'Y', то продолжаем принимать, если нет, то выходим из цикла чтения
     {
      for (byte i=0; i < 5; i++)
        {
           descript[i] = Serial.read(); // добавляем символы в массив   
        } 
        
    if((descript[0] =='+') && (descript[1] =='=') && (descript[2] =='Z')) // проверяем дескриптор
     {
      switch (descript[3])
       {
         case 'o': // обновление
         glavnaia(); // отправка ответа
         break;
         
         ////////////// Кнопки ///////////////////
         case 'A': // d2 вкл
         digitalWrite(2, HIGH); // вкл d2
         d2 = 1; // ставим флаг в единицу (вкл)
         EEPROM.write(2, d2); // записываем состояние d2 в ячейку №2 EEPROM 
         glavnaia(); // отправка ответа
         break;
         
         case 'a': // d2 откл
         digitalWrite(2, LOW); // откл d2
         d2 = 0; // ставим флаг в ноль (откл)
         EEPROM.write(2, d2); // записываем состояние d2 в ячейку №2 EEPROM 
         glavnaia(); // отправка ответа
         break; 
 
         case 'B': // d3
         digitalWrite(3, HIGH);
         d3 = 1;
         EEPROM.write(3, d3);
         glavnaia();
         break;
         
         case 'b': // d3
         digitalWrite(3, LOW);
         d3 = 0;
         EEPROM.write(3, d3);
         glavnaia();
         break;          
  
         case 'C': // d4
         digitalWrite(4, HIGH);
         d4 = 1;
         EEPROM.write(4, d4);
         glavnaia();
         break;
         
         case 'c': // d4
         digitalWrite(4, LOW);
         d4 = 0;
         EEPROM.write(4, d4);
         glavnaia();
         break;   
         
         /////////////// ШИМ ///////////////
         case 'D': // d5 прибавляем shim1
         shim1++;
         if(shim1 > 100) shim1 = 100;
         EEPROM.write(5, shim1);
         analogWrite(5, shim1 * 2.55);
         glavnaia();
         break;
         
         case 'd': // d5 убавляем shim1
         shim1--;
         if(shim1 < 1) shim1 = 0;
         EEPROM.write(5, shim1);
         analogWrite(5, shim1 * 2.55);
         glavnaia();
         break; 
  
         case 'E': // d6 прибавляем shim2
         shim2++;
         if(shim2 > 100) shim2 = 100;
         EEPROM.write(6, shim2);
         analogWrite(6, shim2 * 2.55);
         glavnaia();
         break;
         
         case 'e': // d6 убавляем shim2
         shim2--;
         if(shim2 < 1) shim2 = 0;
         EEPROM.write(6, shim2);
         analogWrite(6, shim2 * 2.55);
         glavnaia();
         break;   
  
         case 'F': // мгновенное включение ШИМ на D5
         shim1 = EEPROM.read(5); // считываем значение ШИМ из EEPROM
         analogWrite(5, shim1 * 2.55); // включаем ШИМ D5
         glavnaia();
         break;
         
         case 'f': // мгновенное отключение ШИМ на D5
         shim1 = 0;
         analogWrite(5, shim1); // отключаем ШИМ D5, но НЕ записываем в EEPROM
         glavnaia();
         break;  
 
         case 'G': // мгновенное включение ШИМ на D6
         shim2 = EEPROM.read(6); // считываем значение ШИМ из EEPROM
         analogWrite(6, shim2 * 2.55); // включаем ШИМ D6
         glavnaia();
         break;
         
         case 'g': // мгновенное отключение ШИМ на D6
         shim2 = 0;
         analogWrite(6, shim2); // отключаем ШИМ D6, но НЕ записываем в EEPROM
         glavnaia();
         break;  
         
         //////////////// Кнопки//////////////////
         case 'J': // d11
         digitalWrite(11, HIGH);
         d11 = 1;
         EEPROM.write(11, d11);
         glavnaia();
         break;
         
         case 'j': // d11
         digitalWrite(11, LOW);
         d11 = 0;
         EEPROM.write(11, d11);
         glavnaia();
         break;  
        
         case 'K': // d12
         digitalWrite(12, HIGH);
         d12 = 1;
         EEPROM.write(12, d12);
         glavnaia();
         break;
         
         case 'k': // d12
         digitalWrite(12, LOW);
         d12 = 0;
         EEPROM.write(12, d12);
         glavnaia();
         break;         
      
         case 'M': // d13
         digitalWrite(13, HIGH);
         d13 = 1;
         EEPROM.write(13, d13);
         glavnaia();
         break;
         
         case 'm': // d13
         digitalWrite(13, LOW);
         d13 = 0;
         EEPROM.write(13, d13);
         glavnaia();
         break;
         
         ////////////// ОХЛАЖДЕНИЕ /////////////////
         case 'N': // увеличение "температуры включения охлаждения"
         oxlagdenie++; // если "температура включения охлаждения" больше нуля, то охлаждение будет работать в автоматическом режиме
         EEPROM.write(14, oxlagdenie); // запишем значение в память
         toxl = 1; // включаем флаг
         EEPROM.write(15, toxl); // запишем флаг в память
         glavnaia();
         break;
         
         case 'n': // уменьшение "температуры включения охлаждения"
         oxlagdenie--;
         if(oxlagdenie < 1) // если "температура включения охлаждения" = 0, то охлаждение отключится
            {
               oxlagdenie = 0; // меньше нуля, у нас не будет
               toxl = 0; // отключаем флаг
               EEPROM.write(14, oxlagdenie); // запишем значение в память
               EEPROM.write(15, toxl); // запишем флаг в память
               digitalWrite(7, LOW); // отключаем
               d7 = 0; 
            }
         glavnaia();
         break;  
  
         ////////////// ПОДОГРЕВ /////////////////
         case 'P': // увеличение "температуры включения печки"
         podogrev++; // если "температура включения печки" больше нуля, то печка будет работать в автоматическом режиме
         EEPROM.write(16, podogrev); // запишем значение в память
         tpod = 1; // включаем флаг
         EEPROM.write(17, tpod); // запишем флаг в память
         glavnaia();
         break;
         
         case 'p': // уменьшение "температуры включения печки"
         podogrev--;
         if(podogrev < 1) // если "температура включения печки" = 0, то печка отключится
            {
               podogrev = 0; // меньше нуля, у нас не будет
               tpod = 0; // отключаем флаг
               EEPROM.write(16, podogrev); // запишем значение в память
               EEPROM.write(17, tpod); // запишем флаг в память
               digitalWrite(8, LOW); // отключаем
               d8 = 0; 
            }
         glavnaia();
         break;  
  
         default:
         glavnaia();
       }
     }
   
    else // если символ был не 'Y', то очищаем буфер
      {
        for(byte i=0; i < 255; i++) 
         {
           Serial.read();    
         } 
      } 
     }    // конец if (Serial.read()=='Y')
   }    // конец чтение порта
 
 
 unsigned long currtime = millis(); 
 
 if(currtime > time) timelapsed = (currtime - time); 
 
 else timelapsed = (MAXMILLIS - time + currtime);

 if(timelapsed >= 120000)  // функция будет выполняться раз в 2 минуты
   { 
     time = currtime;
     count++;
     // поскольку время чтения каждого датчика ~ 250 мс (программа вешается на это время), то будем читать их по отдельности, в разное время  
     if(count == 1)  thum = dht.readTemperature(); // считываем температуру. Время чтения = 250 мс.
     if(count == 2)  hum = dht.readHumidity(); // считываем влажность, если не нужна, то лучше отключить. Время чтения = 250 мс
     if(count > 1) count=0;
 
   /////////////////////// Охлаждение ///////////////////////   
       if((thum > oxlagdenie) && (toxl == 1)) // если тепература больше заданого значения и флаг = 1, тогда включаем кондиционер (если флаг = 0, тогда функция не работает)
         {                                  
           digitalWrite(7, HIGH); // включили охлаждение                        
           d7 = 1;                          
         }
   
       else if(toxl == 1) // иначе если меньше и флаг = 1, тогда отключаем охлаждение                                   
         {
           digitalWrite(7, LOW); // отключили охлаждение  
           d7 = 0;
         }  
        
   /////////////////////// Подогрев ////////////////////////
       if((thum < podogrev) && (tpod == 1)) // если тепература меньше заданого значения и флаг = 1, тогда включаем печку (если флаг = 0, тогда функция не работает)  
         {                                
           digitalWrite(8, HIGH); // включили печку                      
           d8 = 1;                       
         }
   
       else if(tpod == 1) // иначе если больше и флаг = 1, тогда отключаем печку
         {
           digitalWrite(8, LOW); // отключили печку 
           d8 = 0;
         }
         
   } // конец включения по температуре
   
 } // конец loop

void glavnaia() // отправка данных
 {
      Serial.print(d2);//0
      Serial.print(",");
      Serial.print(d3);//1
      Serial.print(",");
      Serial.print(d4);//2
      Serial.print(",");
      Serial.print(0);//3  //  пока отключаем, потом пригодится 
      Serial.print(",");
      Serial.print(0);//4  //  пока отключаем, потом пригодится 
      Serial.print(",");
      Serial.print(d7);//5 охлаждение
      Serial.print(",");
      Serial.print(d8);//6 подогрев
      Serial.print(",");
      Serial.print(0);//7  //  пока отключаем, потом пригодится 
      Serial.print(",");
      Serial.print(0);//8 //  пока отключаем, потом пригодится 
      Serial.print(",");
      Serial.print(d11);//9
      Serial.print(",");
      Serial.print(d12);//10
      Serial.print(",");
      Serial.print(d13);//11 
      Serial.print(",");
      Serial.print(shim1); // 12 
      Serial.print(",");
      Serial.print(shim2); // 13 
      Serial.print(",");
      Serial.print(thum); // 14 //температура
      Serial.print(",");
      Serial.print(hum); // 15 // влажность
      Serial.print(",");
      Serial.print(oxlagdenie);//16 
      Serial.print(",");
      Serial.println(podogrev);//17 , отсылается 18 значений разделённых запятой
 }


Библиотека DHT22 [3].

Опрос DHT22

Внутри DHT22 стоят два датчика, температуры и влажности. Время чтения каждого составляет 250 мс, то есть во время опроса (dht.readTemperature) вся программа «вешается» и обмен данными с ардуиной невозможен.
Исходя из этого, мы будем опрашивать датчики поочереди, с интервалом 2 мин.

...
 if(timelapsed >= 120000) // функция будет выполняться раз в 2 минуты
   {  
     time = currtime;
     count++;
     // поскольку время чтения каждого датчика ~ 250 мс (программа вешается на это время), то будем читать их по отдельности, в разное время  
     if(count == 1)  thum = dht.readTemperature(); // считываем температуру. Время чтения = 250 мс.
     if(count == 2)  hum = dht.readHumidity(); // считываем влажность, если не нужна, то лучше отключить. Время чтения = 250 мс
     if(count > 1) count=0;
... 

Опрос датчиков произойдёт только через две минуты после старта программы, поэтому необходимо первый раз получить данные в функции void setup()

...
  thum = dht.readTemperature(); // считываем температуру, сначала делаем это в сетапе, так как следующее чтение будет только через две минуты
  hum = dht.readHumidity(); // считываем влажность
...  

Включение устройств по температуре

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

Проверка необходимости включения/отключения происходит (тоже раз в две минуты) сразу после опроса датчика.

...
 if(timelapsed >= 120000) // функция будет выполняться раз в 2 минуты
   {  
     time = currtime;
     count++;
     // поскольку время чтения каждого датчика ~ 250 мс (программа вешается на это время), то будем читать их по отдельности, в разное время  
     if(count == 1)  thum = dht.readTemperature(); // считываем температуру. Время чтения = 250 мс.
     if(count == 2)  hum = dht.readHumidity(); // считываем влажность, если не нужна, то лучше отключить. Время чтения = 250 мс
     if(count > 1) count=0;
 
   /////////////////////// Охлаждение ///////////////////////   
       if((thum > oxlagdenie) && (toxl == 1)) // если тепература больше заданого значения и флаг = 1, тогда включаем кондиционер (если флаг = 0, тогда функция не работает)
         {                                  
           digitalWrite(7, HIGH); // включили охлаждение                        
           d7 = 1;                          
         }
   
       else if(toxl == 1) // иначе если меньше и флаг = 1, тогда отключаем охлаждение                                   
         {
           digitalWrite(7, LOW); // отключили охлаждение  
           d7 = 0;
         }  
        
   /////////////////////// Подогрев /////////////////////////
       if((thum < podogrev) && (tpod == 1)) // если тепература меньше заданого значения и флаг = 1, тогда включаем печку (если флаг = 0, тогда функция не работает)  
         {                                
           digitalWrite(8, HIGH); // включили печку                      
           d8 = 1;                       
         }
   
       else if(tpod == 1) // иначе если больше и флаг = 1, тогда отключаем печку
         {
           digitalWrite(8, LOW); // отключили печку 
           d8 = 0;
         }
         
   } // конец включения по температуре  

Если температура окружающей среды (thum), поднимется выше заданой (oxlagdenie), то кондиционер включится, а когда опустится ниже, то отключится.

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


         ////////////// ОХЛАЖДЕНИЕ /////////////////
         case 'N': // увеличение "температуры включения охлаждения"
         oxlagdenie++; // если "температура включения охлаждения" больше нуля, то охлаждение будет работать в автоматическом режиме
         EEPROM.write(14, oxlagdenie); // запишем значение в память
         toxl = 1; // включаем флаг
         EEPROM.write(15, toxl); // запишем флаг в память
         glavnaia();
         break;
         
         case 'n': // уменьшение "температуры включения охлаждения"
         oxlagdenie--;
         if(oxlagdenie < 1) // если "температура включения охлаждения" = 0, то охлаждение отключится
            {
               oxlagdenie = 0; // меньше нуля, у нас не будет
               toxl = 0; // отключаем флаг
               EEPROM.write(14, oxlagdenie); // запишем значение в память
               EEPROM.write(15, toxl); // запишем флаг в память
               digitalWrite(7, LOW); // отключаем
               d7 = 0; 
            }
         glavnaia();
         break;  

При получении символа N, ардуина увеличивает значение oxlagdenie на единицу, устанавливает флаг работы toxl = 1; и записывает эту инфу в EEPROM.
После обесточивания и последующего включения, система вернётся в рабочее состояние.

При получении символа n, ардуина уменьшает значение oxlagdenie на единицу.
Если oxlagdenie будет равно нулю, то флаг toxl обнулится и автоматическая работа кондиционера будет выключена.

Подогрев устроен так же.

Управление

Скачайте архив [4] (github [5]) и распакуйте его в рабочую папку сервера – /var/www/knoppolztemp/

Для наглядности, откройте файл index.html из архива:

Нажатие на «Кнопки», «Диммер» и «Темп» открывают/закрывают соответствующие панели с элементами управления.

image

Зайдите по адресу ваш_роутер/knoppolztemp/ и нажмите кнопку "Темп".

image

Левые кнопки + и - отвечают за «охлаждение», правые за «подогрев».

Установите температуру «включения охлаждения» (19`C) ниже температуры окружающей среды (20`C) и подождите.
Когда сработает функция:

if(timelapsed >= 120000) // функция будет выполняться раз в 2 минуты  

Охлаждение включится и «плюсик» на кнопке станет красным.

С подогревом всё аналогично, только температуру «включения подогрева» (17`C) надо установить выше.

Спящий режим

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

В файле index.html, в конце функции обновления (function show()) есть строки:

...
slmode++;
if(slmode > 60) { /* спящий режим */
$(".pansl").show();
$("st").hide();
flagobnov = 0;
slmode = 0;
...  

Каждый раз, при срабатывании функции show(), переменная slmode увеличивается на единицу. По достижении указаного значения if(slmode > 60), обновление отключится и экран закроется полупрозрачной панелью с кнопкой «ПУСК».

image

Нажатие на Пуск, включит обновление и опять начнётся отсчёт.


$(".slip").click(function(){ /* кнопка пуск  */

        $(".pansl").hide();
        flagobnov = 1;
        show();
}); 

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

Добавил исходники на github [5].

Автор: dimastd

Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/umny-j-dom/90933

Ссылки в тексте:

[1] тему: http://geektimes.ru/post/250362/

[2] Онлаин...: http://istarik.ru/primer/knoppolztemp_em/

[3] DHT22: http://istarik.ru/file/DHT22.zip

[4] архив: http://istarik.ru/file/knoppolztemp.zip

[5] github: https://github.com/stdgithub/ymdom

[6] Источник: http://geektimes.ru/post/250858/