- PVSM.RU - https://www.pvsm.ru -
Я реализую свой умный дом и хочу поделиться несколькими выстраданными решениями.
В качестве центрального контроллера в моей версии умного дома я использую Raspberry Pi с установленными Domoticz и MQTT брокером Mosqito. Центральный контроллер управляет разнообразными нагрузками, зачастую расположенными достаточно далеко от основного здания, например, в гараже или в теплице. В таких удаленных узлах я использую недорогие UDP реле KMTronic для коммутации нагрузок и Ардуино с Ethernet Shield для сбора данных и обратной связи.
Причем схему коммутации я предпочитаю использовать такую, чтобы в любой момент, в том числе при выходе из строя элементов инфраструктуры умного дома, я мог коммутировать нагрузки вручную. Для меня требование надежности вторично — главное, чтобы у моей жены всегда сохранялся привычный интерфейс управления, что позволит сохранить мир в семье и даст мне возможность и далее заниматься любимым хобби.
Идея проста: применение классической схемы включения с двух мест:

В качестве переключателя SA1 выступает один из релейных выходов KMTronic (NO-C-NC), в качестве переключателя SA2 — обычный бытовой одноклавишный переключатель. Все отлично работает, вот только есть один нюанс — у контроллера нет информации о действительном состоянии нагрузки. Не беда — добавим в схему в качестве нагрузки промежуточное реле или контактор KM с катушкой на 24V:

Так мы убьем сразу несколько зайцев:
В качестве промежуточных реле мне нравится использовать Hager ERxxxx на 24V с принудительным ручным управлением (OFF, AUTO, ON).

Эта опция здорово помогает при отладке и в дальнейшей эксплуатации.
Кратко расскажу о подключении UDP реле KMTronic к Domoticz на Raspberry Pi:
sudo apt-get install socat
#!/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
chmod +x rudp.sh
rudp.sh 199 1
Эта команда должна переключить реле #1 устройства KMTronic с адресом 192.168.100.199, используя дефолтный порт 12345.
script:///home/pi/domoticz/scripts/rudp.sh 199 1
Теперь при клике по свичу переключается контакт UDP Relay.
Параллельно мы можем переключать нагрузку, используя ручной переключатель.
#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];
}
}
}
Спасибо за внимание, надеюсь, кому-то мой опыт окажется полезен.
Автор: himch
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/raspberry-pi/187432
Ссылки в тексте:
[1] Источник: https://geektimes.ru/post/280424/
Нажмите здесь для печати.