Я реализую свой умный дом и хочу поделиться несколькими выстраданными решениями.
В качестве центрального контроллера в моей версии умного дома я использую Raspberry Pi с установленными Domoticz и MQTT брокером Mosqito. Центральный контроллер управляет разнообразными нагрузками, зачастую расположенными достаточно далеко от основного здания, например, в гараже или в теплице. В таких удаленных узлах я использую недорогие UDP реле KMTronic для коммутации нагрузок и Ардуино с Ethernet Shield для сбора данных и обратной связи.
Причем схему коммутации я предпочитаю использовать такую, чтобы в любой момент, в том числе при выходе из строя элементов инфраструктуры умного дома, я мог коммутировать нагрузки вручную. Для меня требование надежности вторично — главное, чтобы у моей жены всегда сохранялся привычный интерфейс управления, что позволит сохранить мир в семье и даст мне возможность и далее заниматься любимым хобби.
Идея
Идея проста: применение классической схемы включения с двух мест:
В качестве переключателя SA1 выступает один из релейных выходов KMTronic (NO-C-NC), в качестве переключателя SA2 — обычный бытовой одноклавишный переключатель. Все отлично работает, вот только есть один нюанс — у контроллера нет информации о действительном состоянии нагрузки. Не беда — добавим в схему в качестве нагрузки промежуточное реле или контактор KM с катушкой на 24V:
Так мы убьем сразу несколько зайцев:
- получим возможность использовать дополнительные контакты реле KM для обратной связи;
- повысим электробезопасность, что особенно важно во влажных помещениях;
- получим возможность коммутировать более мощные нагрузки, чем те, на которые рассчитаны реле KMTronic.
В качестве промежуточных реле мне нравится использовать Hager ERxxxx на 24V с принудительным ручным управлением (OFF, AUTO, ON).
Эта опция здорово помогает при отладке и в дальнейшей эксплуатации.
Реализация
Кратко расскажу о подключении UDP реле KMTronic к Domoticz на Raspberry Pi:
- Устанавливаем socat для работы с UDP:
sudo apt-get install socat
- В каталоге ~/domoticz/scripts создаем скрипт для управления KMTronic через UDP. Назовем скрипт, например, rudp.sh:
#!/bin/bash RelayIP="$1" RelayNumber="$2" Status=$(echo FF0000 | socat - udp-datagram:192.168.100.${RelayIP}:12345) StatusBit=${Status:RelayNumber-1:1} CommandON=FF0${RelayNumber}01 CommandOFF=FF0${RelayNumber}00 if [ "$StatusBit" = "1" ]; then echo ${CommandOFF} | socat - udp-datagram:192.168.100.${RelayIP}:12345 else echo ${CommandON} | socat - udp-datagram:192.168.100.${RelayIP}:12345 fi
- Делаем файл rudp.sh исполняемым:
chmod +x rudp.sh
- Проверяем из командной строки:
rudp.sh 199 1
Эта команда должна переключить реле #1 устройства KMTronic с адресом 192.168.100.199, используя дефолтный порт 12345.
- В Domoticz cоздаем Dummy switch, в полях On Action и Off Action указываем:
script:///home/pi/domoticz/scripts/rudp.sh 199 1
Теперь при клике по свичу переключается контакт UDP Relay.
Параллельно мы можем переключать нагрузку, используя ручной переключатель. - Заливаем в Ардуино такой скетч (внимание, в скетче нужно связать входы Ардуино и индексы Idx наших Dummy switches, заполнив массив domoticz_Idx[]):
#include <SPI.h> #include <Ethernet.h> #include <Bounce2.h> #include <PubSubClient.h> byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; byte server[] = { 192, 168, 100, 250}; byte ip[] = { 192, 168, 100, 199}; // I/O ports on board, 20 for UNO and Leonardo (14 DI + 6 AI) static const int ioPorts = 20; // all pins 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static const uint8_t arduinoPins[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,A0,A1,A2,A3,A4,A5}; static const int availablePin[] = {1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; // pins available for general I/O. 1 - available, 0 - not available static const int domoticz_Idx[] = {5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // // Buffers for sending data char statusBuffer[ioPorts+1] = "0"; char prevStatusBuffer[ioPorts+1] = "0"; // Instantiate a Bounce object Bounce debouncer[ioPorts] = Bounce(); // Function headers void callback(char* topic, byte* payload, unsigned int length); char pinToChar(int value); int compareCharArrays(char array1[ioPorts+1], char array2[ioPorts+1]); void readAllPins(); void publishPins(boolean all); void setup(); void loop(); EthernetClient ethClient; PubSubClient clientMQTT(server, 1883, callback, ethClient); void callback(char* topic, byte* payload, unsigned int length) { byte* p = (byte*)malloc(length); memcpy(p,payload,length); publishPins(true); free(p); } char pinToChar(int value) { char result = 'X'; if (value==HIGH) { result = '0'; // if pin opened, send 0 } if (value==LOW) { result = '1'; // if pin closed to GND, send 1 } return result; } int compareCharArrays(char array1[ioPorts+1], char array2[ioPorts+1]) { int result = 0; for (int i =0; i <= (ioPorts); i++) { if (array1[i]!=array2[i]) { result = 1; break; } } return result; } void readAllPins() { for (int i =0; i < (ioPorts); i++) { if (availablePin[i]) { debouncer[i].update(); statusBuffer[i] = pinToChar(debouncer[i].read()); } } } void publishPins(boolean all) { char topic[]="domoticz/in"; String data; char jsonStr[200]; for (int i =0; i < (ioPorts); i++) { if ((all) || (prevStatusBuffer[i]!=statusBuffer[i])) { if ((availablePin[i]) && (domoticz_Idx[i])) { data="{"idx":"; data+=(int)domoticz_Idx[i]; data+=","nvalue":"; data+=(char)statusBuffer[i]; data+="}"; data.toCharArray(jsonStr,200); clientMQTT.publish(topic,jsonStr); Serial.print(topic); Serial.print(" "); Serial.println(jsonStr); } } } } void setup() { // initialize serial port over USB Serial.begin(9600); Ethernet.begin(mac, ip); // initialize the digital pins as an input. for (int i =0; i < ioPorts; i++) { if (availablePin[i]) { pinMode(i, INPUT_PULLUP); debouncer[i].attach(arduinoPins[i]); // setup the Bounce instance debouncer[i].interval(100); // interval in ms } statusBuffer[i]='0'; prevStatusBuffer[i]='0'; } statusBuffer[ioPorts]=''; // EOL prevStatusBuffer[ioPorts]=''; // EOL readAllPins(); if (clientMQTT.connect("myhome-ino-id1")) { clientMQTT.subscribe("myhome/ino/id1/in/#"); publishPins(true); } } void loop(){ clientMQTT.loop(); readAllPins(); if (compareCharArrays(statusBuffer,prevStatusBuffer)==1) // time for send information to the server { if (!clientMQTT.connected()){ if (clientMQTT.connect("myhome-ino-id1")) { clientMQTT.subscribe("myhome/ino/id1/in/#"); } } if (clientMQTT.connected()) { publishPins(false); } for (int i =0; i < (ioPorts); i++) { prevStatusBuffer[i]=statusBuffer[i]; } } }
- Подключаем свободный контакт нашего промежуточного реле к контактам Gnd и DIxx Ардуино.
- Для приема информации через MQTT в Domoticz устанавливаем MQTTWorker.
Спасибо за внимание, надеюсь, кому-то мой опыт окажется полезен.
Автор: himch