http://habrahabr.ru/post/232903/
Все знают, что лень двигатель прогресса. Так случилось и в моем случае.
В квартире присутствует 6 точек раздачи воды (3 холодные и 3 горячие). На каждой из точек стоит счетчик.
Каждые 2 счетчика спрятаны за люками скрытого монтажа, один из люков находится за зеркалом, которое нужно снять, чтобы до него добраться.
Раз в месяц с 20 по 25 число необходимо снимать показания со всех счетчиков и отправлять данные в Управляющую Компанию на бланке определенного образца.
В какой-то момент мне надоело открывать люки, снимать зеркало и было решено автоматизировать снятие показаний.
Вот, для примера, пара люков (открытый и закрытый):
Сначала перерыл интернет на предмет существующих устройств автоматизации. Нашел только один для меня подходящий —
Счетчик импульсов-регистратор «Пульсар» 6-ти канальный. Надо сказать, что стоит он почти 6000 рублей! На самом деле в розницу нигде я его не видел, так как слишком специфический продукт и предполагается, что закупать их будут ТСЖ на все квартиры в доме. Попытался его заказать через интернет в разных местах, но каждый раз, как только доходило до доставки, продавец пропадал. Как я понял, они не любят работать с «физиками», либо был не слишком настойчив.
Ну, нет, так нет — сделаем сами, да еще и дешевле.
Тут то и пригодилась Arduino Mega 2580 с Ethernet модулем, которая была когда-то куплена для различных экспериментов.
Когда делали ремонт в квартире, от каждой точки, где имеются счетчики, до щитка на лестничной клетке, были проложены кабели типа UTP cat 5e. Это было одно из требований контролирующей организации, чтобы в будущем снимать все показания централизованно. Будущее все никак не наступает, а провода пригодились.
Дополнительно из слаботочного щитка квартиры до щитка на лестничной клетке, было проложено много витых пар (для нескольких каналов интернета, телефон, домофон, резерв и прочее), и как раз нашлась парочка свободных, чтобы сигналы от счетчиков завести в назад в квартиру, а оттуда в шкаф с домашним сетевым оборудованием.
В итоге, что мы имеем:
- Счетчики воды
- Arduino Mega 2580
- Arduino Ethernet 3.0
- Бокс для Arduino
- Блок питания
- Шлейф для протягивания из слаботочного щитка в шкаф к Arduino.
- Домашний сервер на Debian с Lighttpd и Mysql
Сами счетчики такие:
Экспериментальным путем было определено, что счетчики работают не просто, а очень просто. Когда последний разряд меняет свое значение с 9 на 0, замыкается геркон внутри счетчика и это значит, что утекло еще 10 литров воды. В таком состоянии он находится до того, пока значение последнего разряда не станет равным 3. Т.е. фактически нам надо фиксировать момент перехода из состояния «разомкнуто» в состояние «замкнуто». Заострю внимание, что мы фиксируем ТОЛЬКО факт перехода из одного состояния в другое, потому что система может обесточиться, да и вообще, мало ли какие могут быть коллизии.
В момент замыкания геркона, Arduino по HTTP вызывает простенький perl-скрипт на сервере, где крутится lighttpd. Скрипт записывает в базу данных этот момент. Другой скрипт позволяет смотреть текущее состояние счетчиков.
Скетч Arduino с комментариями:#include <Ethernet.h>
#include <SPI.h>
#include <Bounce2.h> // Эту библиотеку необходимо скачать тут: https://github.com/thomasfredericks/Bounce-Arduino-Wiring
byte mac[] = {0x90,0xA2,0xDA,0x0E,0xF1,0x92}; // MAC-адрес нашего устройства (написан на наклейке платы Ethernet shield)
IPAddress ip(192,168,1,11); // IP адрес, если вдруг не получится получить его через DHCP
//IPAddress server(192,168,1,10); // ip-адрес удалённого сервера (использовался, пока не было имени)
char server[] = "smarthome.mydomain.ru"; // Имя удалённого сервера
char request[40]; // Переменная для формирования ссылок
int CounterPin[6] = {22,23,24,25,26,27}; // Объявляем массив пинов, на которых висят счетчики
char *CounterName[6] = {"0300181","0293594","0300125","0295451","0301008","0293848"}; // Объявляем массив имен счетчиков, которые мы будем передавать на сервер
Bounce CounterBouncer[6] = {}; // Формируем для счетчиков Bounce объекты
EthernetClient rclient; // Объект для соединения с сервером
void setup() {
//Serial.begin(9600);
for (int i=0; i<6; i++) {
pinMode(CounterPin[i], INPUT); // Инициализируем пин
digitalWrite(CounterPin[i], HIGH); // Включаем подтягивающий резистор
CounterBouncer[i].attach(CounterPin[i]); // Настраиваем Bouncer
CounterBouncer[i].interval(10); // и прописываем ему интервал дребезга
}
// Инициализируем сеть
if (Ethernet.begin(mac) == 0) {
Ethernet.begin(mac, ip); // Если не получилось подключиться по DHCP, пробуем еще раз с явно указанным IP адресом
}
delay(1000); // даем время для инициализации Ethernet shield
}
void loop() {
delay(1000); // Задержка в 1 сек, пусть будет. Мы уверены, что два раза в секунду счетчик не может сработать ни при каких обстоятельствах, потому что одно срабатывание - 10 литров.
// Проверяем состояние всех счетчиков
for (int i=0; i<6; i++) {
boolean changed = CounterBouncer[i].update();
if ( changed ) {
int value = CounterBouncer[i].read();
// Если значение датчика стало ЗАМКНУТО
if ( value == LOW) {
//Serial.println(CounterPin[i]);
sprintf(request, "GET /input.pl?object=%s HTTP/1.0", CounterName[i]); // Формируем ссылку запроса, куда вставляем имя счетчика
sendHTTPRequest(); // Отправляем HTTP запрос
}
}
}
}
// Функция отправки HTTP-запроса на сервер
void sendHTTPRequest() {
if (rclient.connect(server,80)) {
rclient.println(request);
rclient.print("Host: ");
rclient.println(server);
rclient.println("Authorization: Basic UmI9dlPnaJI2S0f="); // Base64 строка, полученная со значения "user:password"
rclient.println("User-Agent: Arduino Sketch/1.0");
rclient.println();
rclient.stop();
}
}
На сервере крутится: Debian, Lighttpd, Mysql. В свою очередь на нем имеется два perl-скрипта: один для записи состояний счетчиков в базу, второй для вывода текущих показаний.
input.pl#!/usr/bin/perl -w
use strict;
use CGI::Fast;
use DBI;
while(my $q = CGI::Fast->new)
{
main($q);
}
sub main
{
my $q = shift;
my $dbh = DBI->connect( "dbi:mysql:database=smart_home;mysql_client_found_rows=1;mysql_enable_utf8=1;mysql_socket=/var/run/mysqld/mysqld.sock", 'dbname', 'password',
{
RaiseError => 1,
AutoCommit => 1,
mysql_multi_statements => 1,
mysql_init_command => q{SET NAMES 'utf8';SET CHARACTER SET 'utf8'}
} ) or die "Cannot connect";
$dbh->{mysql_auto_reconnect} = 1;
print "Content-Type: text/html; charset=UTF-8\n\n";
print "OK\n";
my $object = $q->param("object");
if ($object)
{
$dbh->do(q{INSERT INTO water_count (object) VALUES(?)},undef,$object) or die $dbh->errstr;
}
}
result.pl#!/usr/bin/perl -w
use strict;
use CGI::Fast;
use DBI;
# массив стартовых показаний счетчиков
my $start = {
"0300125" => 102.53,
"0301008" => 75.31,
"0300181" => 65.92,
"0293594" => 54.51,
"0293848" => 55.04,
"0295451" => 87.43
};
while(my $q = CGI::Fast->new)
{
main($q);
}
sub main
{
my $dbh = DBI->connect( "dbi:mysql:database=smart_home;mysql_client_found_rows=1;mysql_enable_utf8=1;mysql_socket=/var/run/mysqld/mysqld.sock", 'dbname', 'password',
{
RaiseError => 1,
AutoCommit => 1,
mysql_multi_statements => 1,
mysql_init_command => q{SET NAMES 'utf8';SET CHARACTER SET 'utf8'}
} ) or die "Cannot connect";
$dbh->{mysql_auto_reconnect} = 1;
print "Content-Type: text/html; charset=UTF-8\n\n";
print "Текущие показания счетчиков:<br>";
my $sql = "SELECT count(*) as c,object FROM water_count group by object";
my $sth = $dbh->prepare($sql);
$sth->execute;
while (my ($count, $object) = $sth->fetchrow_array())
{
$start->{$object} = sprintf("%.2f",$start->{$object}+$count/100);
}
$sth->finish;
foreach my $object (keys $start) {
my ($intcurrent,$fine) = split(/\./,$start->{$object});
print "$object <b>$intcurrent</b>.$fine<br>\n";
}
}
Mysql база с одной таблицей:CREATE TABLE `water_count` (
`object` varchar(20) NOT NULL DEFAULT '',
`datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8
В таблице есть только два поля. Первое — название объекта (в нашем случае это номер счетчика). Второе — дата и время в формате TIMESTAMP, которые заполняются автоматически, когда происходит вставка строки.
Вот, собственно, и все. Теперь в любой момент я могу узнать какое значение имеют все счетчики, просто зайдя браузером на домашний сервер.
Что дальше?
Дальше хочется ежемесячную автоматическую распечатку на заполненном бланке.
Так же хочется подключить счетчик электроэнергии с передачей данных в Мосэнергосбыт, а потом и с их оплатой.
Статистика, графики и прочие радости работы с данными.