Эта статья показывает ещё один способ взаимодействия микроконтроллера из семейства Arduino с универсальной платформой для объединения всей домашней «умной» техники в единую систему управления openHAB. На Хабре уже представлены статьи про взаимодействия с помощью Serial и HTTP. Для своего нового проекта я выбрал MQTT, т.к. два предыдущих способа я уже пробовал и хотелось попробовать что-то ещё.
Приступим…
MQTT — протокол для обмена сообщениями между устройствами. Предполагается наличие одного сервера MQTT (так называемый MQTT-broker) и подключённых к нему клиентских устройств. Сообщения состоят из заголовка (topic) и текста сообщения. Подключение к серверу даёт нам две основные возможности: отправлять сообщения и подписываться на интересующие нас топики. При этом работает древовидная структура топиков. Это проще показать на примере. Для датчиков, передающих данные из спальни, можно использовать такие заголовки:
/myhome/bedroom/temperature
/myhome/bedroom/humidity
/myhome/bedroom/luminosity
Аналогично для кухни:
/myhome/kitchen/temperature
Теперь, подписавшись на любой из этих топиков, мы будем получать сообщения о состоянии каждого конкретного датчика. Но протокол также позволяет подписываться на всю ветку сразу. Чтобы получать в одной подписке данные со всех датчиков спальни, можно подписаться на топик
/myhome/bedroom/#
Такая структура удобна для понимания человеком, но дальнейшее использование MQTT показало, что значительно проще использовать всего две ветки: одну для обновления статуса элементов и другую — для отправки команд. Но об этом чуть позже.
В своём проекте я использовал Raspberry Pi model B+ с установленной Raspbian в качестве платформы для openHAB и Arduino MEGA 2560 с установленным на ней Ethernet Shield w5100. Также экспериментировал с Arduino Yun — всё тоже самое, только используем YunClient вместо EthernetClient.
Оставим за скобками процесс установки openHAB, т.к. этому посвящено множество статей. Перейдём сразу к установке MQTT. Здесь всё просто, открываем консоль и устанавливаем:
sudo apt-get install mosquitto
Перезагружаемся и проверяем работоспособность (у меня при первой установке mosquitto никак не хотел запускаться при старте системы):
sudo shutdown -r now
После перезагрузки:
sudo service mosquitto status
В ответ должны получить:
[ ok ] mosquitto is running.
Теперь необходимо установить binding для openHAB. Для этого выполняем:
sudo apt-get install openhab-addon-binding-mqtt
Или просто копируем файл org.openhab.binding.mqtt-1.6.2.jar (актуальная на сегодня версия) в папку /usr/share/openhab/addons/
Перейдём к настройке openHAB:
sudo nano /etc/openhab/configurations/openhab.cfg
Здесь добавляем следующие строки:
mqtt:mybroker.url=tcp://localhost:1883
mqtt-eventbus:broker=mybroker
mqtt-eventbus:commandPublishTopic=/myhome/in/${item}
mqtt-eventbus:stateSubscribeTopic=/myhome/out/${item}
Ещё нам потребуются два Item, отвечающих за включение/выключение света на кухне:
Switch Kitchen_light1 "Кухня. Свет 1" <light>
Switch Kitchen_light2 "Кухня. Свет 2" <light>
Не забываем добавить их в sitemap в любое удобное место. Перезапускаем openHAB:
sudo service openhab restart
Можно начинать экспериментировать! Подключаемся с любого устройства к серверу MQTT и подписываемся на топик /myhome/#. При каждом изменении статуса любого Item мы будем получать сообщение, в топике которого будет указано имя Item, а в тексте сообщения — его новый статус. Нам этого достаточно, перейдём к программированию микроконтроллера.
#include <SPI.h> // Ethernet shield
#include <Ethernet.h> // Ethernet shield
#include <PubSubClient.h> // MQTT
#define light1_pin 25
#define light2_pin 27
byte mac[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0x12 };
byte server[] = { 192, 168, 1, 11 };
byte ip[] = { 192, 168, 1, 12 };
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
unsigned long lastMqtt = 0;
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = '';
Serial.print(topic);
Serial.print(" ");
String strTopic = String(topic);
String strPayload = String((char*)payload);
Serial.println(strPayload);
if (strTopic == "/myhome/in/Kitchen_light1") {
if (strPayload == "OFF") digitalWrite(light1_pin, LOW);
else if (strPayload == "ON") digitalWrite(light1_pin, HIGH);
}
else if (strTopic == "/myhome/in/Kitchen_light2") {
if (strPayload == "OFF") digitalWrite(light2_pin, LOW);
else if (strPayload == "ON") digitalWrite(light2_pin, HIGH);
}
}
void setup() {
Serial.begin(57600);
Serial.println("start");
pinMode(light1_pin, OUTPUT);
digitalWrite(light1_pin, LOW);
pinMode(light2_pin, OUTPUT);
digitalWrite(light2_pin, LOW);
Ethernet.begin(mac, ip);
if (client.connect("myhome-kitchen")) {
client.publish("/myhome/out/Kitchen_light1", "OFF");
client.publish("/myhome/out/Kitchen_light2", "OFF");
client.subscribe("/myhome/in/#");
}
}
void loop() {
if (lastMqtt > millis()) lastMqtt = 0;
client.loop();
// здесь какой-то другой код по уравлению светом, например, с кнопок или ещё как
if (millis() > (lastMqtt + 60000)) {
boolean mqtt_connect;
if (!client.connected()) mqtt_connect = client.connect("myhome-kitchen");
if (mqtt_connect) {
if (digitalRead(light1_pin)) client.publish("/myhome/out/Kitchen_light1", "ON");
else client.publish("/myhome/out/Kitchen_light1", "OFF");
if (digitalRead(light2_pin)) client.publish("/myhome/out/Kitchen_light2", "ON");
else client.publish("/myhome/out/Kitchen_light2", "OFF");
}
lastMqtt = millis();
}
}
Вот и всё. Прокомментирую только последний кусочек кода. Если управление освещением происходит напрямую через микроконтроллер, то необходимо передать новое состояние в openHAB. Это можно сделать непосредственно сразу после изменения состояния, либо раз в минуту передавать состояние всех контролируемых нагрузок.
В завершении хочу дать ссылку на потрясающую статью, которая очень мне помогла во всём этом разобраться: «Uber Home Automation w/ Arduino & Pi»
Автор: swelld