Давно наблюдаю за этим проектом, недавно решил таки влиться.
С точки зрения домашней автоматизации считаю для себя идеальной связку:
- Центральный сервер с Linux Debian и MajorDoMo. Пока запущен на Raspberry Pi.
Вариант конечно не идеальный - заметно как притормаживает, да и флешка под MySQL быстро помрет.
В будущем думаю пересесть на cubieboard - он помощнее, тем более есть возможность подключить HDD через SATA. - На уровне ниже планирую применить связку Arduino+W5100, пока для опытов это Arduino Uno R3 и Arduino Ethernet Shield.
В будущем можно применить Arduino Mega 2560 для увеличения кол-ва входов-выходов.
Либо для компактности Arduino Nano с неофициальным шилдом ENC28J60.
Состояние дискретных входов Ардуина будет передавать в MDM как клиент при изменении их состояния.
Почему на напрямую на GPIO, а через Ардуину? Просто планирую ардуину сделать т.н. умным датчиком (исполнительным механизмом), к тому же на нее можно переложить часть вычислений/преобразований/проверок и пр., тем самым немного разгрузить малину.
На данный момент пробовал подключать через USB, работа связки MR3020 на OpenWRT и Arduino Nano через виртуальный com-порт в OpenWRT мне не понравилась. Как минимум раз в месяц стопорится обмен данными, иногда данные приходят "битыми". Пробовал все - и питание более надежное и различные программные заплатки и костыли, и различные преобразователи интерфейса (PL2303, FTDI232, CP102), все равно система не надежна. Да и не просто так в промышленной автоматики USB редко используется для работы 365/12/7/24... HTTP после всех этих заморочек мне показался просто детской сказкой)) Хотя может пока нет статистики такой, чтобы усомниться в этом...
Пример №1.1. Раз в минуту система с MajorDoMo (в качестве клиента) запрашивает у WEB-сервера (Arduino Uno R3 + Ethernet Shield W5100) температуру (датчики DS18B20).
Используется библиотека WebServer.h Webduino (https://github.com/sirleech/Webduino)
Итак, начнем снизу - Arduino Uno R3 + Arduino Ethernet Shield. К входу №2 ардуины подключены по 1-wire c подтяжкой через резистор 4,7К 3 датчика DS18B20. Для удобства работы с датчиками я заранее прочитал их адреса скетчем из примеров среды IDE. В моем случае:
- Т1 - 284AEB340500007D
- Т2 - 28459634050000D8
- Т3 - 280F7234050000A7
Прошу прощения за быдло-код, я не программист, писал не сам. Куски кода взяты из различных источников, в т.ч. с этого форума.
Код Ардуины:
Код: Выделить всё
/**
* Контроллер-исполнительное устройство (к проекту http://smartliving.ru/)
* Platform: Arduino UNO R3 + EthernetShield W5100
* IDE: Arduino 1.0.5-r2
*
* обращение по http://xx.xx.xx.xx/ выдаст справочную информацию по этому устройству (нужно для того, чтобы когда обращаешься
* по IP к устройству понять что это за контроллер и пр.)
*
* обращение по http://xx.xx.xx.xx/sensors - вывод значений всех аналоговых датчиков DS18B20
* формат вывода:T1=xx,xx&T2=xx,xx&T3=xx,xx для удобства потом в php распарсить.
**/
#include <Ethernet.h>
#include <SPI.h>
#include "WebServer.h" // Webduino (https://github.com/sirleech/Webduino)
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE4, 0xDE, 0x35 }; // MAC-адрес нашего устройства
byte ip[] = { 192, 168, 69, 52 };
byte subnet[] = { 255, 255, 255, 0 };
#define VERSION_STRING "0.1"
#define COMPILE_DATE_STRING "2014-01-23"
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress T1 = { 0x28, 0x4A, 0xEB, 0x34, 0x05, 0x00, 0x00, 0x7D };
DeviceAddress T2 = { 0x28, 0x45, 0x96, 0x34, 0x05, 0x00, 0x00, 0xD8 };
DeviceAddress T3 = { 0x28, 0x0F, 0x72, 0x34, 0x05, 0x00, 0x00, 0xA7 };
P(Page_info) = "<html><head><title>smartliving.ru controller " VERSION_STRING "</title></head><body>\n";
P(location_info) = "underground server room";
P(pin_info) = "D2 - 1-wire (many DS18S20)";
P(version_info) = VERSION_STRING ". Compile date: " COMPILE_DATE_STRING;
#define PREFIX ""
WebServer webserver(PREFIX, 80);
/**********************************************************************************************************************/
//Генерация и вывод информации об устройстве
void infoRequest(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
server.printP(Page_info);
server.print("IP:");
server.print(Ethernet.localIP());
server.print("<br>Location:");
server.printP(location_info);
server.print("<hr>Pin info:<br>");
server.printP(pin_info);
server.print("<hr>Pin current state: ");
server.print("<hr><a href='/getdev'>1-wire devices</a>");
server.print("<hr>Commands:<br>");
server.print("<hr><br>Version info: ");
server.printP(version_info);
}
//Вывод значений всех сенсоров. Парсинг в коде php клиента - в MDM.
void getSensors(WebServer &server, WebServer::ConnectionType type, char *, bool)
{
server.httpSuccess();
sensors.requestTemperatures();
float t1 = sensors.getTempC(T1);
float t2 = sensors.getTempC(T2);
float t3 = sensors.getTempC(T3);
if (isnan(t1) || isnan(t2) || isnan(t3)) {
server.print("NOT FOUND");
} else {
server.print("T1=");
server.print(t1);
server.print("&T2=");
server.print(t2);
server.print("&T3=");
server.print(t3);
Serial.print("T1=");
Serial.print(t1);
Serial.print("&T2=");
Serial.print(t2);
Serial.print("&T3=");
Serial.println(t3);
}
}
/**********************************************************************************************************************/
void setup() {
Serial.begin(9600);
Serial.println("Start");
sensors.begin();
sensors.setResolution(T1, 12);
sensors.setResolution(T2, 12);
sensors.setResolution(T3, 12);
Ethernet.begin(mac, ip, subnet); // Инициализируем Ethernet Shield
webserver.setDefaultCommand(&infoRequest); // дефолтная страница вывода (информация о контроллере)
webserver.addCommand("sensors", &getSensors); // отправка строки с результатами измерения
webserver.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
/**********************************************************************************************************************/
void loop() {
char buff[64];
int len = 64;
webserver.processConnection(buff, &len); // process incoming connections one at a time forever
}
Если у вас все так, то переходим далее.
Сверху у нас MDM, тут надо отправить запрос на сервер-Ардуину в виде "http://ip-адрес/sensors", считать строчку значений, затем её можно "распарсить" в php функцией parse_str($строка);. Есть несколько вариантов сделать запрос на php из MajorDoMo: wget, curl, socket, но я воспользовался file_get_contents, он самый простой мне показался... Правда надо будет его настроить - добавить тайм-аут хотя бы, пока не читал как это сделать. Итак, переходим в панель управления MDM -> Объекты, создаем там новый класс "tempSensors"
Добавляем классу tempSensors свойства: direction, temp, updated и updatedTime.
Так же на вкладке "методы" добавляем метод "tempChanged", используем "Выполнить код" и вставляем следующий текст:
Код: Выделить всё
//$params['t']
$old_temp=$this->getProperty('temp');
$t=round($params['t']*2)/2;
$this->setProperty('temp',$t);
if ($t>$old_temp) {
$d=1;
} elseif ($t<$old_temp) {
$d=-1;
} else {
$d=0;
}
$this->setProperty('direction',$d);
$this->setProperty("updated",time());
$this->setProperty("updatedTime",date("H:i",time()));
Теперь можно создавать наши объекты - температуры. Переходим на вкладку "Объекты" и создадим 3 объекта: "sensorT1", "sensorT2" и "sensorT3".
Итак. Теперь нам надо в определенное время, например раз в минуту, записывать значения в "tempSensors.sensorT1" запуская метод "tempSensors.tempChanged".
Для этого создадим сценарий, нажимаем в меню слева "Сценарии", добавим новый сценарий с названием, например, " read_ds18b20", для реализации выбираем "php".
Вводим следующий текст:
Код: Выделить всё
$read = file_get_contents('http://192.168.69.52/sensors');
if ($read)
{
parse_str($read);
}
if ($T1<>"" and $T1<>0 and $T1<>127 and $T1<>-127)
{
$T1 = round($T1,1);
callMethod('sensorT1.tempChanged',array('t'=>$T1));
}
if ($T2<>"" and $T2<>0 and $T2<>127 and $T2<>-127)
{
$T2 = round($T2,1);
callMethod('sensorT2.tempChanged',array('t'=>$T2));
}
if ($T3<>"" and $T3<>0 and $T3<>127 and $T3<>-127)
{
$T3 = round($T3,1);
callMethod('sensorT3.tempChanged',array('t'=>$T3));
Так, нам осталось теперь раз в минуту выполнять этот скрипт. Вызов сделать очень просто, достаточно в "Объектах" найти класс "Timer", объект "ClockChime", в нем метод "onNewMinute".
Там уже есть некоторый текст, просто в конце добавляем строчку RunScript('read_ds18b20');
Вот собственно и все. Можно добавить табличку в меню, чтобы отображались данные.
Далее будет описание небольшой проблемы, с которой я столкнулся, пока не разобрался...