Автоматизация снятия показаний со счетчиков воды

от автора

Все знают, что лень двигатель прогресса. Так случилось и в моем случае.

В квартире присутствует 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, замыкается геркон внутри счетчика. В таком состоянии он находится до того, пока значение последнего разряда не станет равным 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 сек, пусть будет :)   // Проверяем состояние всех счетчиков   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, которые заполняются автоматически, когда происходит вставка строки.

Вот, собственно, и все. Теперь в любой момент я могу узнать какое значение имеют все счетчики, просто зайдя браузером на домашний сервер.

Что дальше?
Дальше хочется ежемесячную автоматическую распечатку на заполненном бланке.
Так же хочется подключить счетчик электроэнергии с передачей данных в Мосэнергосбыт, а потом и с их оплатой.
Статистика, графики и прочие радости работы с данными.

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


Комментарии

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

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