"Умный дом" на Arduino - часть третья
1
Для понимания того, о чём пойдёт речь ниже, необходимо прочесть две предыдущие части, здесь и здесь.
Итак, мы можем управлять дискретными устройствами (вкл/откл), плавно регулировать свет и сохранять значения в энергонезависимую память (EEPROM).
Теперь пришло время немного изменить внешний вид, добавить датчик температуры (и влажности) и сделать так, чтоб в зависимости от температуры включалась печка или кондиционер, а так же реализуем ещё одну очень полезную функцию…
Поскольку наш веб-интерфейс непрерывно запрашивает данные у ардуины, то будет не лишним отправлять его в «спящий режим», для того чтоб не мешать другим клиентам.
Попробовать онлайн.
Температуру и влажность будет измерять датчик DHT22.
За дело…
Ардуина
Прошиваем ниже приведённый код:
#include <EEPROM.h>
#define MAXMILLIS 4294967295
#include "DHT.h"
#define DHTPIN 9 // пин для подключения датчика
#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 // если дескриптор ложный, то очищаем буфер
{
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 значений разделённых запятой
}
Потребуется библиотека DHTsensor.
Нажатие кнопок и диммер описаны в предыдущей статье.
Опрос 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 обнулится и автоматическая работа кондиционера будет выключена.
Подогрев устроен так же.
Управление
Скачайте архив и распакуйте его в рабочую папку сервера – /var/www/knoppolztemp/
Для наглядности, откройте файл index.html из архива:
Нажатие на «Кнопки», «Диммер» и «Темп» открывают/закрывают соответствующие панели с элементами управления.
Зайдите по адресу ваш_роутер/knoppolztemp/ и нажмите кнопку "Темп".
Левые кнопки + и - отвечают за «охлаждение», правые за «подогрев».
Установите температуру «включения охлаждения» (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), обновление отключится и экран закроется полупрозрачной панелью с кнопкой «ПУСК».
Нажатие на Пуск, включит обновление и опять начнётся отсчёт.
$(".slip").click(function(){ /* кнопка пуск */
$(".pansl").hide();
flagobnov = 1;
show();
});
На этом пока всё, теперь Вы можете доверить ардуине управлять дачей или теплицей.
В дальнейшем будет наращиваться функционал и создаваться конструктор для отрисовки помещений.
Телеграм-чат istarik
- +126
- stD
29542
Поддержать автора
Комментарии (21)
Я рекомендую Вам воспользоваться вот этим проектом.
swState=digitalRead(swPin);
if (swState == LOW) {
delay (100);
fl = fl + 1;
if (fl == 1){
if(digitalRead(4)) // пин к которому подключёна лампочка (если включена, то гасим лампочку)
{
digitalWrite(4, LOW);
d4 = 0;
EEPROM.write(4, d4);
glavnaia();
}
else // иначе включаем лампочку
{
digitalWrite(4, HIGH);
d4 = 1;
EEPROM.write(4, d4);
glavnaia();
}
}
fl = 100;
}
if (swState == HIGH) {fl=0;}
Второй вопрос: что изменить, чтобы при нажатии на вкладку на странице — темп или диммер или кнопки, предыдущая открытая сама закрывалась, а то получается что они друг на друга накладываются.
istarik.ru/blog/arduino/32.html
А по поводу автозакрытия предыдущей вкладки при нажатии на другую что-нибудь можно сделать?
Сейчас буду пробывать установку по этой статье…
Увеличьте таймаут в файле index.html до 500мс.
…
Вы имеете в виду кнопки, а не вкладки? Можно переделать, вот тут:
…
Ставьте Lighttpd.
По поводу таймингов, то я пробовал и 300 и 500 и 750 — результат одинаковый.
За файлики для ББ заранее спасибо. Жду.
Так и было задумано.