Домашняя погодная станция

12.12.2013 - 14:37

В этой заметке хочу рассказать о маленькой погодной станции для Вашего дома. Основным заданием которой было:
Температура
* вывод на сайт и на LCD экран температуры с 3-х комнат и улицы
* уличные показания температуры отправлять на проект Народный мониторинг
Сигнализация
* постановка/снятие с охраны
* датчик открытия/закрытия двери, при срабатывании, возможность отправлять уведомления на Jabber.

Что нам для этого нужно:)

* Маленький карманный роутер TP-LINK3020 (OpenWRT)
* USB хаб D-Link DUB-1040 4port
* USB Flash на 8Gb
* Среда разработки Arduino UNO
* Датчики температуры DS18B20 - 4шт.
* Геркон - 2 шт.
* LCD экран 4X20

О прошивке роутера TP-LINK3020 в OpenWRT и монтирование флеши в качестве системного раздела думаю писать не стоит.
Для взаимодействия Arduino с OpenWRT по USB, необходимо установить дополнительно USB-Serial:

#opkg update
#opkg install kmod-usb-acm

Также устанавливаем lighttpd, php5, rrdtool.

Перейдем к аппаратной части Arduino + LCD + DS18B20.

LCD_WH1602B_Arduino_big.gif

Подключаем экран по данной схеме.( Регулируемый резистор на 10к, на подсветку 6-8 Ом. ). Заливаем скетч, недостающие библиотеки берем в аттаче к заметке).
bash-4.2$ cat sketch_temor.ino
#include <Bounce.h> //подавляем дребезг контактов
#include <LiquidCrystalRus.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define SOMK 7 //геркон двери
#define BUTTON 8 //кнопка снятия с охраны
#define ONE_WIRE_BUS 6 // DS18B20 подключены к контакту 6 на Arduino

int n = 1;
boolean stat_alarm = true ;
boolean delay_alarm_on = false;
long prev_time = 0;
long interval_alarm = 30000; //задержка выхода до постановки на охрану 30сек.
long temp_prev_time = 0;
long temp_interval_alarm = 20000; //интервал между опросами датчиков температуры 20сек.

float out_temp = 200; //Улица
float zal_temp = 200; //Зал
float spalnya_temp = 200; //Спальня
float detskaya_temp = 200; //Детская

float ex_out_temp = 200; //Предыдущее улица
float ex_zal_temp = 200; //Предыдущее Зал
float ex_spalnya_temp = 200; //Предыдущее Спальня
float ex_detskaya_temp = 200;//Предыдущее Детская

LiquidCrystalRus lcd(12, 11, 10, 5, 4, 3, 2);

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire); // Передаем наш OneWire ссылкой Dallas Temperature.

//создаем объект класса Bounce. Указываем пин, к которому подключена кнопка, и время дребезга в мс.
Bounce somk = Bounce(SOMK,5);
Bounce button = Bounce(BUTTON,5);                                                                                                          
                                                                                                                                           
//дверь открыта                                                                                                                            
byte open_d[8] =                                                                                                                            
{                                                                                                                                          
  B11111,                                                                                                                                  
  B10001,                                                                                                                                  
  B10001,                                                                                                                                  
  B10001,                                                                                                                                  
  B10001,                                                                                                                                  
  B10001,                                                                                                                                  
  B10001,                                                                                                                                  
  B11111,                                                                                                                                  
};                                                                                                                                          
                                                                                                                                           
//дверь закрыта                                                                                                                            
byte close_d[8] =                                                                                                                          
{                                                                                                                                          
  B11111,                                                                                                                                  
  B11111,                                                                                                                                  
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
};

//знак температуры
byte temp_c[8] =
{
  B00010,
  B00101,
  B00010,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
};

//стрелка вверх
byte up[8] =
{
  B00100,
  B01110,
  B10101,
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
};

//стрелка вниз
byte down[8] =
{
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
  B10101,
  B01110,
  B00100,
};

//знак звонка
byte alarm_on[8] =
{
  B00100,
  B01110,
  B01110,
  B01110,
  B11111,
  B00100,
  B00000,
  B00000,
};

void setup() {
  Serial.begin(9600);
  sensors.begin();
  sensors.setResolution(12); //9-bit 0.5 93.75 ms 10-bit 0.25 187.5 ms 0.12 11-bit 375 ms 12-bit 0.06 750 ms
  pinMode(BUTTON, INPUT);
  pinMode(SOMK, INPUT);
  Serial.println ("system:start");
  lcd.begin(20, 4);
  lcd.setCursor(2, 0);
  lcd.print("Погодная станция");
  lcd.setCursor(2, 2);
  lcd.print("vlymar");
  lcd.setCursor(0, 3);
  lcd.print ("Датчиков темп.: ");
  lcd.print (sensors.getDeviceCount());
  lcd.print ("шт.");
  delay (2000);
  lcd.clear();
  lcd.createChar(1, open_d);
  lcd.createChar(2, close_d);
  lcd.createChar(3, temp_c);
  lcd.createChar(4, up);
  lcd.createChar(5, down);
  lcd.createChar(6, alarm_on);
  /*заголовки*/
  lcd.setCursor(0, 0);
  lcd.print("Улица:");
  lcd.setCursor(0, 1);
  lcd.print("Зал:");
  lcd.setCursor(0, 2);
  lcd.print("Спальня:");
  lcd.setCursor(0, 3);
  lcd.print("Детская:");    
  lcd.setCursor(19, 3);
  lcd.print("\6");  
  /*конец заголовки*/
}

void loop() {
  if (millis() - temp_prev_time > temp_interval_alarm) {
    temp_prev_time = millis();

    switch (n) {
    case 1:
      sensors.requestTemperaturesByIndex(0);
      out_temp = sensors.getTempCByIndex(0);
      n++;
      break;

    case 2:
      sensors.requestTemperaturesByIndex(1);
      zal_temp = sensors.getTempCByIndex(1);
      n++;
      break;  

    case 3:
      sensors.requestTemperaturesByIndex(2);
      spalnya_temp = sensors.getTempCByIndex(2);
      n++;
      break;  

    case 4:
      sensors.requestTemperaturesByIndex(3);
      detskaya_temp = sensors.getTempCByIndex(3);
      n=1;
      break;  
    }

  }


  /* Состояние двери */
  if ( somk.update() ) {
    if ( somk.read() == HIGH) {
      if (stat_alarm == true) {
        Serial.println ("door:open");
      }
      lcd.setCursor(19, 0);
      lcd.print("\1");
    }
    else {
      if (stat_alarm == true) {
        Serial.println ("door:close");
      }
      lcd.setCursor(19, 0);
      lcd.print("\2");
    }
  }
  /* Конец Состояние двери */


  /* Вкл/Выл сигнализации */
  if ( button.update() ) {
    if (button.read() == HIGH) {
      if ( stat_alarm == true ) {
        Serial.println ("alarm:off");
        lcd.setCursor(19, 3);
        lcd.print(char(47));
        stat_alarm = false;
      }
      else {
        lcd.setCursor(19, 3);
        lcd.print(char(34));
        prev_time = millis(); //запоминаем время
        delay_alarm_on = true;
        Serial.println ("alarm:delay");
      }
    }
  }
  /* Конец Вкл/Выл сигнализации */


  /* Вывод Температура - Улица */
  if (out_temp != ex_out_temp) {
    if (out_temp >= 0) {
      lcd.setCursor(9, 0);
    }
    else {
      lcd.setCursor(8, 0);
    }
    lcd.print(out_temp);
    lcd.print("\3C");
    if (out_temp > ex_out_temp) {
      lcd.print(" \4 ");
    }
    else {
      lcd.print(" \5 ");
    }

    Serial.print("temp:out:");
    Serial.println(out_temp);
    ex_out_temp = out_temp;
  }
  /* Конец Вывод Температура - Улица */


  /* Вывод Температура - Зал */
  if (zal_temp != ex_zal_temp) {
    if (zal_temp >= 0) {
      lcd.setCursor(9, 1);
    }
    else {
      lcd.setCursor(8, 1);
    }
    lcd.print(zal_temp);
    lcd.print("\3C");
    if (zal_temp > ex_zal_temp) {
      lcd.print(" \4 ");
    }
    else {
      lcd.print(" \5 ");
    }
    Serial.print("temp:zal:");
    Serial.println(zal_temp);
    ex_zal_temp = zal_temp;
  }
  /* Конец Вывод Температура - Зал */


  /* Вывод Температура - Спальня */
  if (spalnya_temp != ex_spalnya_temp) {
    if (spalnya_temp >= 0) {
      lcd.setCursor(9, 2);
    }
    else {
      lcd.setCursor(8, 2);
    }
    lcd.print(spalnya_temp);
    lcd.print("\3C");
    if (spalnya_temp > ex_spalnya_temp) {
      lcd.print(" \4 ");
    }
    else {
      lcd.print(" \5 ");
    }
    Serial.print("temp:spalnya:");
    Serial.println(spalnya_temp);
    ex_spalnya_temp = spalnya_temp;
  }

  /* Конец Вывод Температура - Спальня */


  /* Вывод Температура - Детская */
  if (detskaya_temp != ex_detskaya_temp) {
    if (detskaya_temp >= 0) {
      lcd.setCursor(9, 3);
    }
    else {
      lcd.setCursor(8, 3);
    }
    lcd.print(detskaya_temp);
    lcd.print("\3C");
    if (detskaya_temp > ex_detskaya_temp) {
      lcd.print(" \4 ");
    }
    else {
      lcd.print(" \5 ");
    }
    Serial.print("temp:detskaya:");
    Serial.println(detskaya_temp);
    ex_detskaya_temp = detskaya_temp;  
  }

  /* Конец Вывод Температура - Детская */


  if (delay_alarm_on == true) {
    if (millis() - prev_time > interval_alarm) {
      Serial.println ("alarm:on");
      lcd.setCursor(19, 3);
      lcd.print("\6");
      stat_alarm = true;
      delay_alarm_on = false;
    }
  }
}

По скетчу, на 7-ю ногу arduino вешаем геркон открытия/закрытия двери, на 8-ю ногу вместо кнопки постановки/снятия с охраны я также повестил геркон, который скрыл от посторонних глаз, включение-выключение сигнализации производится с помощью неодимового магнитика на брелке ключей, на 6-ю ногу вешаем наши 4-ре датчика температуры по 1-wire.

Проверяем работоспособность, в консоль должны идти данные вида:

door:open  - дверь открыта
door:close - дверь закрыта
alarm:off - снято с охраны
alarm:on - взято под охрану
alarm:delay - у вас есть 30сек. для выхода с дома
temp:out: - значение температуры "Улица"
temp:zal: - значение температуры "Зал"
temp:spalnya: - значение температуры "Спальня"
temp:detskaya: - значение температуры "Детская"

При подключении Adruino к роутеру в OpenWRT появится устройство /dev/ttyACM0.
Забирать и парсить поток данных с /dev/ttyACM0 будем с помощью php:

root@OpenWrt:/narodmon# cat serial_to_pc.php
#!/usr/bin/php-cgi

<?php

$ch='';
$bufer = '';
$filename = "/dev/ttyACM0";
$fp = fopen($filename, "r+");
if ($fp)
{

while (1) {
while ($ch != chr(0xA))
{
    $ch = fgetc($fp);
    $bufer.=$ch;
}

 $curl = curl_init();
 curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1:8080/receiver.php?a='.$bufer);
 curl_setopt($curl, CURLOPT_RETURNTRANSFER,true);
 $out = curl_exec($curl);
 echo $out;
 curl_close($curl);


 echo $bufer;

$ch='';
$bufer = '';

}
}

?>

Передаем буфер с помощью GET-запроса в receiver.php.

root@OpenWrt:/www# cat /www/receiver.php
#!/usr/bin/php-cgi
<?php

$a = $_GET['a'];
$n=1;

$count = substr_count("$a",":");

if ($count == 1)
{
list($n1, $n2) = explode(":", $a);

switch($n) {

case 1:

if ($n1 == system & $n2 == start ) {


include '/narodmon/j_send/XMPPHP/XMPP.php';

$conn = new XMPPHP_XMPP('jabber.org', 5222, 'пользователь', 'пароль', 'xmpphp', 'jabber.org', $printlog=false, $loglevel=XMPPHP_Log::LEVEL_INFO);

try {
    $conn->useEncryption(false);
    $conn->connect();
    $conn->processUntil('session_start');
    $conn->presence();
    $conn->message('пользователь@jabber.org', 'Arduino Start System');
    $conn->disconnect();
        } catch(XMPPHP_Exception $e) {
    die($e->getMessage());
    }

}


case 2:

if ($n1 == door & $n2 == open ) {

include '/narodmon/j_send/XMPPHP/XMPP.php';

$conn = new XMPPHP_XMPP('jabber.org', 5222, 'пользователь', 'пароль', 'xmpphp', 'jabber.org', $printlog=false, $loglevel=XMPPHP_Log::LEVEL_INFO);

try {
    $conn->useEncryption(false);
    $conn->connect();
    $conn->processUntil('session_start');
    $conn->presence();
    $conn->message('пользователь@jabber.org', 'Door Open!');
    $conn->disconnect();
        } catch(XMPPHP_Exception $e) {
    die($e->getMessage());
    }
}

case 3:

if ($n1 == door & $n2 == close ) {

include '/narodmon/j_send/XMPPHP/XMPP.php';

$conn = new XMPPHP_XMPP('jabber.org', 5222, 'пользователь', 'пароль', 'xmpphp', 'jabber.org', $printlog=false, $loglevel=XMPPHP_Log::LEVEL_INFO);

try {
    $conn->useEncryption(false);
    $conn->connect();
    $conn->processUntil('session_start');
    $conn->presence();
    $conn->message('пользователь@jabber.org', 'Door Close!');
    $conn->disconnect();
        } catch(XMPPHP_Exception $e) {
    die($e->getMessage());
    }
}
}
}

if ($count == 2 ){

list($t1, $t2, $t3) = explode(":", $a);

switch($n) {

case 1:

if ($t1 == temp & $t2 == out ) {
$f_write = fopen("/tmp/temp_out.txt", "w");
fwrite($f_write, "$t3");
fclose($f_write);
}



case 2:

if ($t1 == temp & $t2 == zal ) {
$f2_write = fopen("/tmp/temp_zal.txt", "w");
fwrite($f2_write, "$t3");
fclose($f2_write);
}

case 3:

if ($t1 == temp & $t2 == spalnya ) {
$f3_write = fopen("/tmp/temp_spalnya.txt", "w");
fwrite($f3_write, "$t3");
fclose($f3_write);
}

case 4:
if ($t1 == temp & $t2 == detskaya ) {
$f4_write = fopen("/tmp/temp_detskaya.txt", "w");
fwrite($f4_write, "$t3");
fclose($f4_write);
}
}
}

?>

С помощью скрипта to_narod_m.php, отпарвляем данные о температуре на народмон.

root@OpenWrt:/narodmon# cat to_narod_m.php
<?php

$f0 = fopen("/tmp/temp_out.txt", "r");
$out = fgets($f0);
fclose($f0);

$f1 = fopen("/tmp/temp_zal.txt", "r");
$zal = fgets($f1);
fclose($f1);

$f2 = fopen("/tmp/temp_spalnya.txt", "r");
$spalnya = fgets($f2);
fclose($f2);

$f3 = fopen("/tmp/temp_detskaya.txt", "r");
$detskaya = fgets($f3);
fclose($f3);

$fp = @fsockopen("tcp://narodmon.ru", 8283, $errno, $errstr);
if(!$fp) exit("ERROR(".$errno."): ".$errstr);
fwrite($fp, "#90-В6-12-7B-13-2C\n#1AB00000142ADE3828#$out\n#940000040876AFC218#$zal\n#12000004F564C428#$spalnya\n#12000004F61D1451#$detskaya\n##");
fclose($fp);

?>

так же дублируем температуру себе на страничку роутера:

weath.png

Ставим задания в системный крон

#cat /etc/crontabs/root
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

#RRDTOOLS
*/5 * * * * /bin/bash /narodmon/rrd2/src.sh

#SEND_TO_N_MON
*/6 * * * * /usr/bin/php-cgi /narodmon/to_narod_m.php

sh /etc/init.d/cron start
sh /etc/init.d/cron enable

IMG_3721.JPG

Прикрепленный файлРазмер
lib.tar_.bz221.55 кб
Ваша оценка: Нет Средняя: 3.3 (11 votes)

Комментарии:


Мой сайт погоды в городе Вознесенске, Украина. http://vozmeteo.com.ua



Приветствую! Уважаемый автор, а вы бы не могли выложить код страницы которая у вас на скрине, куда выводятся данные о температуре, просто в сети не нашел подобных примеров. Спасибо!



Отправил



не увидел, как это
"так же дублируем температуру себе на страничку роутера:"
?
судя по коду значения записываются в файлы в папке tmp
а как выводится график?



Вывод страницы и графиков расписан в другой заметке http://www.unix.ck.ua/content/monitoring-temperatury-s-pomoshchyu-rrdtool с помощью rrdtool.