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

от автора

image

Обязательно прочитайте предыдущую тему.

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

Онлаин…

Температуру и влажность будет измерять 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.

Опрос 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 обнулится и автоматическая работа кондиционера будет выключена.

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

Управление

Скачайте архив (github) и распакуйте его в рабочую папку сервера – /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.

ссылка на оригинал статьи http://geektimes.ru/post/250858/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *