Привет, Geektimes!

Я реализую свой умный дом и хочу поделиться несколькими выстраданными решениями.

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


Сбор информации о состоянии нагрузок при помощи Ардуино по протоколу MQTT


  • Заливаем в Ардуино скетч для работы с MQTT (внимание, в скетче нужно связать входы Ардуино и индексы 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]='\0';      // EOL
      prevStatusBuffer[ioPorts]='\0';  // 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.

  • Вручную включаем и выключаем нагрузку и наблюдаем изменение состояния нагрузки в интерфейсе Domoticz.


Спасибо за внимание, надеюсь, кому-то мой опыт окажется полезен.
Поделиться с друзьями
-->

Комментарии (27)


  1. 4ebriking
    14.09.2016 17:03
    +3

    Поделитесь — в чём выстраданность решения рвать выключателями 0, а не фазу?


    1. Dark_Purple
      14.09.2016 17:28

      Просто за дело взялись «айцишники», а не электрик.


      1. A3a
        14.09.2016 18:08

        Да, есть такой момент. Я тоже наступал на эти грабли при составлении схемы для «умной» лампы. Проблема в том, что у большинства программистов, как правило, безопасность пользователей никак не фигурирует в повседневной работе, а вот ноль рвать на первый взгляд кажется полегче чем фазу чтоли. А потом я разобрал выключатель, увидел что там переключается фаза и «быстро» понял почему это сделанно именно так. Спасибо электрикам :)


        1. webkumo
          14.09.2016 19:37

          Хм...? При чём тут выключатель? Там рвётся один из контактов — да… вот только в зависимости от грамотности устанавливавшего/менявшего электрику там может оказаться и ноль…
          И спасибо не электрикам — как и прочие правила ТБ эти написаны «кровью». Электрики, же, по должности обязаны эти правила знать и соблюдать.

          PS а если вам очень «повезло» с распределительной станцией, то у вас и ноль небезопасен (хотя убить всё-таки не должен).


          1. A3a
            14.09.2016 20:07

            так в том то и дело, что электрика была проведена хорошо. И расцветка проводов правильная, и ноль на месте, и заземление есть.


      1. black_semargl
        15.09.2016 17:53

        Угу. Когда я поменял обычную люстру на светодиодную, внезапно выяснилось что выключатель рвёт ноль.
        Даже того слабенького тока утечки оказалось достаточно для «пепельного света»


        1. saege5b
          16.09.2016 18:29

          У нас после проведённого ремонта электрики, ноль 'внезапно' может стать фазой.
          А так как, в среднем, таких ремонтов по паре штук в год, то мы не заморачиваемся.


    1. himch
      14.09.2016 17:43

      Да, согласен, это моя ошибка, конечно, нужно рвать фазу


  1. dakiev
    14.09.2016 17:42

    В схеме я так понял есть импульсное реле… зачем тогда использование проходного выключателя SA2? Не легче ли SA2 заменить на импульсный выключатель? Схема станет проще.


    1. himch
      14.09.2016 18:10

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


      1. dakiev
        15.09.2016 13:06

        Ну смотрите… импульсное реле подключается непосредственно к лампочке, а все выключатели подключаются к управляющим входам на указанном реле, плюс контроллер который тоже подключается к одному из управляющих входов. Обычно на импульсных реле три комбинации входов — ON, OFF и импульсный контакт положение которого меняется на противоположное при подаче импульса (РИО-1, РИО-2 от Меандра к примеру). Выключатель используется — кнопка без фиксатора, то есть такой как на дверном звонке, таких в магазинах валом, есть даже сенсорные. Настенные выключатели можно подать на импульсный контакт, а контроллер будет подведен к контактам ON и OFF.


  1. x893
    14.09.2016 17:42

    Что бы не расслаблялись при замене лампочек.


    1. himch
      14.09.2016 18:08

      Пофиксил, спасибо!


  1. eldarmusin
    15.09.2016 08:39

    Данная схема исключает возможность использования н-количества выключателей. Как нипример для гостинной, или коридора.


    Во многих Z-Wave устройствах, используется параллельное включение нормально разомкнутых кнопок, или последовательное нормально замкнутых. Я использую нормально замкнутые, так как цепь всегда под напряжением, и наводками случайно ничего не коммутируется. Затем всё это добро идёт на вход логики, которая имеет 1 физический вход, для кнопок и 1 логический, для управления через планшет (или что-то в этом роде). Затем всё это подаётся на физический выход, который сам коммутирует уже реле управления накрузкой.


    Плюс такого подхода в том, что одно другому не мешает. А при использовании 1 выключателя, можно всё равно логикой отключить свет/бойлер, выставив приоритет на логический вход. То есть не вставая с постели.


    Минус в том, что если сам модуль выходит из строя, то кнопочками нагрузку уже не включишь.


    1. himch
      15.09.2016 10:20

      >Данная схема исключает возможность использования н-количества выключателей. Как например для гостиной, или коридора.
      Я в таких случаях использую схему управления с перекрестными выключателями. Их можно хоть десять поставить в цепочку, гуглите «Управление освещением с трех мест и более мест».

      >Минус в том, что если сам модуль выходит из строя, то кнопочками нагрузку уже не включишь.
      О том и речь. А еще пользователям привычнее переключатель, а не нажимная кнопка.


      1. eldarmusin
        15.09.2016 10:42

        О том и речь. А еще пользователям привычнее переключатель, а не нажимная кнопка.
        В подъездах по 1му выключателю на этаж, и он как раз таки кнопочный. В Европе используют latching relay.
        В Швейцарии поголовно кнопки, вместо выключателей, или переключателей.

        Это скорее дело привычки. У родителей дома как раз всё на кнопках. Сначала было не привычно, сейчас уже в порядке вещей.


  1. legioner
    15.09.2016 08:58

    Нужно иметь в виду, что подход с промежуточным реле подходит только для нагрузок без высоких стартовых токов, например, лампы накаливания. Большой стартовый ток, от тех же светодиодных светильников, со временем может привести к залипанию контактов реле в одном из положений. Выходов из этой ситуации два: 1) выбирать более мощное реле 2) выбирать «умное» реле, которое включается в момент перехода напряжения через 0. Оба варианта удорожают конструкцию.


    1. himch
      15.09.2016 10:24

      Ну да, удорожают. Например, хороший мощный четырехполюсный контактор на 63А может стоить 4000-6000 руб. плюс информационный допконтакт 1000 руб. А что делать?
      В любом случае, коммутировать мощную нагрузку напрямую с контроллера не получится.


    1. xxvy
      15.09.2016 10:40

      Немного не понял. Я правда не в курсе.

      для нагрузок без высоких стартовых токов, например, лампы накаливания

      Спираль лампы накаливания в холодном состоянии имеет на порядок (да, примерно 10 раз) меньшее сопротивление, чем, когда разгорится.

      Большой стартовый ток, от тех же светодиодных светильников, со временем может привести к залипанию контактов

      У светодиодных светильников большой стартовый ток? Во сколько раз больше номинального? (так-то светодиоды сами по себе раз в 5-7 меньше жрут чем лампы)


      1. himch
        15.09.2016 15:43

        У светодиодных светильников на самом деле большой стартовый ток. Нет, не так. У дешевых светильников и драйверов к ним на самом деле большой стартовый ток. Это реальная проблема, при большом количестве светильников выгорают контакты выключателей, отрубаются автоматы с характеристикой C и приходится ставить автоматы с характеристикой D.

        Но лучший способ решить проблему — использовать дорогие драйвера с плавным запуском.


  1. sisaenkov
    15.09.2016 09:53

    Как решили вопрос с утечкой памяти domoticz'а?


    image


    У меня он примерно за неделю отъедает всю доступную память и начинает люто лагать.
    Добавил костыль в виде перезапуска сервиса по крону.


    1. himch
      15.09.2016 15:45

      Пока никак, буду смотреть, спасибо за наводку.


  1. BurlakovSG
    15.09.2016 12:20

    Чтобы оставить возможность управлять светом, если контроллер/сервер накрылись, нужно использовать импульсные/бистабильные реле. К нему можно подключить сколько угодно кнопок (выключателей) и можно управлять контроллером.


    1. himch
      15.09.2016 15:45

      Да, можно и так.
      Два момента:
      1) импульсные реле дороже и не больше 16А, то есть на большие токи все равно нужно добавлять контактор (поправьте, если ошибаюсь),
      2) кнопки


      1. BurlakovSG
        15.09.2016 15:53

        1. Так речь идёт только про освещение. В освещении очень сложно забить все 16А.
        2. Ну и что, что кнопки. У используемого варианта всё равно нужно менять обычные выключатели на проходные, а проходные стоят дороже кнопок.


      1. BurlakovSG
        15.09.2016 16:03

        По цене — РИО-1 стоит 995 рублей, что дешевле реле Hager.


        1. himch
          15.09.2016 16:27

          Hager покупаю по 10,90 эуро/шт, примерно такая же цена получается.