Умный дом на Arduino и TL-MR3020 без Lighttpd и PHP


Умный дом на Arduino




Это новая версия шаблона для построения простого «умного дома», сделаного на базе ардуино и роутера mr3020.

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


В прежней реализации использовался сервер Lighttpd, скриптовый язык PHP, утилита stty для настройки порта, а так же небольшой прокси-сервер ser2net, служивший посредником между сервером и ардуиной.

Если у Вас это установленно, то ничего удалять не нужно, просто пропустите установку uHTTPd.

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

Теперь для работы системы, достаточно легкого сервера uHTTPd (используется в OpenWrt для работы Web-интерфейса) и двух маленьких программ. Благодаря этому нет необходимости использовать флешку.



Как это работает?


Ардуина два раза в секунду отправляет данные роутеру (по USB или UART), на роутере программа arduread получает эти данные и записывает их в файл. Клиент подключённый к роутеру, раз в секунду считывает данные из этого файла.

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

Функционально, система может включать/отключать устройства подключённые к ардуине и считавать информацию с датчиков.



Терминология:

Сервер — роутер или любой другой компьютер.
Клиент — устройство (телефон, планшет или компьютер), с которого Вы подключаетесь к серверу.

Soft-сервера — uHTTPd и arduserver.




Ардуина


Всё что делает ардуина — это принимает команды и два раза в секунду отсылает серверу информацию.

Заливаем скетч:


#include <DHT.h>

#include "CyberLib.h"

#define MAXMILLIS 4294967295

#define DHTPIN 9 // пин для подключения датчика

#define DHTTYPE DHT22  
DHT dht(DHTPIN, DHTTYPE);


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

byte d2 = 0; // флаг состояния    
byte d3 = 0;
byte d4 = 0;
byte d5 = 0; 
byte d6 = 0;
byte d7 = 0; 
byte d8 = 0;
byte d9 = 0; 
byte d10 = 0;
byte d11 = 0;
byte d12 = 0;
byte d13 = 0;
byte pir1 = 0;
byte knop1 = 0;
byte knop2 = 0;
byte knop3 = 0;

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

unsigned long   time; // время

long timelapsed = 0;

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

void setup() 
{
  Serial.begin(57600);
  D2_Out; 
  D3_Out;
  D4_Out;
  D5_Out; 
  D6_Out; 
  D7_Out; 
  D8_Out; 
  //D9_Out; // DHT
  D10_Out; 
  D11_Out;
  D12_Out;
  D13_Out;

  thum = dht.readTemperature(); // считываем температуру, сначала делаем это в сетапе, так как следующее чтение будет только через минуту
  hum = dht.readHumidity(); // считываем влажность
}
  
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 вкл
         D2_High; 
         d2 = 1; // флаг вкл
         break;
         
         case 'a': 
         D2_Low; 
         d2 = 0; // флаг откл
         break; 
 
         case 'B': // d3
         D3_High;
         d3 = 1;
         break;
         
         case 'b': // d3
         D3_Low;
         d3 = 0;
         break;          
  
         case 'C': // d4
         D4_High;
         d4 = 1;
         break;
         
         case 'c': // d4
         D4_Low;
         d4 = 0;
         break;   

         case 'D': // d5
         D5_High;
         d5 = 1;
         break;
         
         case 'd': // d5
         D5_Low;
         d5 = 0;
         break;  
  
         case 'E': // d6
         D6_High;
         d6 = 1;
         break;
         
         case 'e': // d6
         D6_Low;
         d6 = 0;
         break;   
  
         case 'F': // d7
         D7_High;
         d7 = 1;
         break;
         
         case 'f': // d7
         D7_Low;
         d7 = 0;
         break;  
 
         case 'G': // d8
         D8_High;
         d8 = 1;
         break;
         
         case 'g': // d8
         D8_Low;
         d8 = 0;
         break;  
 
         case 'H': // d9
         //D9_High; // DHT
         //d9 = 1;
         break;
         
         case 'h': // d9
         //D9_Low; // DHT
         //d9 = 0;
         break; 
         
         case 'I': // d10
         D10_High;
         d10 = 1;
         break;
         
         case 'i': // d10
         D10_Low;
         d10 = 0;
         break;         
       
         case 'J': // d11
         D11_High;
         d11 = 1;
         break;
         
         case 'j': // d11
         D11_Low;
         d11 = 0;
         break;  
        
         case 'K': // d12
         D12_High;
         d12 = 1;
         break;
         
         case 'k': // d12
         D12_Low;
         d12 = 0;
         break;         
      
         case 'M': // d13
         D13_High;
         d13 = 1;
         break;
         
         case 'm': // d13
         D13_Low;
         d13 = 0;
         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 >= 440)  // функция будет выполняться раз в 2 минуты
   { 
     time = currtime;
     count++;
     if(count == 120)  thum = dht.readTemperature(); 
     if(count == 240)  hum = dht.readHumidity(); 
     if(count > 239) count=0;
     glavnaia(); // отправка данных серверу   
     
   } // конец включения по температуре
   
 } // конец loop

void glavnaia() // отправка данных серверу 
 {
      // сумма проверяется на сервере, на предмет целостности пакета
      int summa = d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + thum + hum + pir1 + knop1 + knop2;
      Serial.print('A');
      Serial.print(" "); 
      Serial.print(summa);
      Serial.print(" ");   
      Serial.print(d2);
      Serial.print(" ");
      Serial.print(d3);
      Serial.print(" ");
      Serial.print(d4);
      Serial.print(" ");
      Serial.print(d5);  
      Serial.print(" ");
      Serial.print(d6);  
      Serial.print(" ");
      Serial.print(d7); 
      Serial.print(" ");
      Serial.print(d8);
      Serial.print(" ");
      Serial.print(d9); 
      Serial.print(" ");
      Serial.print(d10);
      Serial.print(" ");
      Serial.print(d11);
      Serial.print(" ");
      Serial.print(d12);
      Serial.print(" ");
      Serial.print(d13);
      Serial.print(" ");
      Serial.print(thum); 
      Serial.print(" ");
      Serial.print(hum); 
      Serial.print(" ");
      Serial.print(pir1); // не используется
      Serial.print(" ");
      Serial.print(knop1); // не используется
      Serial.print(" ");
      Serial.print(knop2); // не используется
      Serial.print(" ");
      Serial.print(knop3); // не используется
      Serial.print(" ");
      Serial.print(0); // не используется
      Serial.print(" ");
      Serial.print(0); // не используется
      Serial.print(" ");
      Serial.println('Z');    
 }

В скетче используются две библиотеки: (устанавливаются стандартным способом.)

CyberLib — для ускорения ардуины.
DHT — для датчика влажности и температуры (подключение).

Ардуина получает команду включить/вы