Reliable user-friendly management of the power loads of the smart home: Domoticz + manual switch

Hi, Geektimes!

I implement my smart home and I want to share a few solutions achieved.

As a central controller in my smart home version, I use the Raspberry Pi with Domoticz and MQTT brokers installed Mosqito. The central controller controls a variety of loads, often located far enough away from the main building, for example, in a garage or in a greenhouse. In such remote nodes I use inexpensive KMTronic UDP relays for switching loads and Arduino with Ethernet Shield for data acquisition and feedback.

Moreover, I prefer to use a switching circuit such that at any time, including when the elements of the infrastructure of a smart home fail, I can switch loads manually. For me, the requirement of reliability is secondary - the main thing is that my wife always maintains the usual management interface, which will keep the peace in the family and give me the opportunity to continue to pursue my favorite hobby.

Idea


The idea is simple: the application of the classical switching scheme from two places:



As the switch SA1, one of the relay outputs KMTronic (NO-C-NC) acts, as the switch SA2 - the usual one-button home switch. Everything works fine, only there is one nuance - the controller has no information about the actual state of the load. It does not matter - we add an intermediate relay or a KM contactor with a 24V coil to the circuit as a load:


So we kill several birds with one stone:
  • we will be able to use additional KM relay contacts for feedback;
  • we will increase electrical safety, which is especially important in wet rooms;
  • we will be able to switch more powerful loads than those for which KMTronic relays are designed.

As intermediate relays I like to use Hager ERxxxx on 24V with forced manual control (OFF, AUTO, ON).



This option helps a lot when debugging and in further use.

Implementation


Connect KMTronic UDP Relay to Domoticz on Raspberry Pi



  • Install socat to work with UDP:

    sudo apt-get install socat
    

  • In the ~ / domoticz / scripts directory we create a script for managing KMTronic via UDP. Let's call the script, for example, 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" ]; thenecho${CommandOFF} | socat - udp-datagram:192.168.100.${RelayIP}:12345
    elseecho${CommandON} | socat - udp-datagram:192.168.100.${RelayIP}:12345
    fi

  • Make the rudp.sh file executable:

    chmod +x rudp.sh
    

  • Check from the command line:

    rudp.sh 199 1
    


    This command should switch the relay # 1 of the KMTronic device with the address 192.168.100.199 using the default port 12345.

  • In Domoticz we create a Dummy switch, in the On Action and Off Action fields we specify:

    script:///home/pi/domoticz/scripts/rudp.sh 199 1
    


    Now when you click on a switch, the UDP Relay contact is switched.
    In parallel, we can switch the load using a manual switch.


Collecting information about the state of loads with the help of Arduino using the MQTT protocol


  • Fill in Arduino with a sketch for working with MQTT (note, in the sketch you need to link the inputs of Arduino and the Idx indices of our Dummy switches, filling in the domoticz_Idx [] array).
    Sketch
    #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) staticconstint ioPorts = 20; 
    // all pins                           0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19staticconstuint8_t arduinoPins[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,A0,A1,A2,A3,A4,A5};
    staticconstint 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 availablestaticconstint 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 datachar  statusBuffer[ioPorts+1] = "0";           
    char  prevStatusBuffer[ioPorts+1] = "0"; 
    // Instantiate a Bounce object
    Bounce debouncer[ioPorts] = Bounce();
    // Function headersvoidcallback(char* topic, byte* payload, unsignedint length);
    charpinToChar(int value);
    intcompareCharArrays(char  array1[ioPorts+1], char array2[ioPorts+1]);
    voidreadAllPins();
    voidpublishPins(boolean all);
    voidsetup();
    voidloop();
    EthernetClient ethClient;
    PubSubClient clientMQTT(server, 1883, callback, ethClient);
    voidcallback(char* topic, byte* payload, unsignedint length){
      byte* p = (byte*)malloc(length);
      memcpy(p,payload,length);
      publishPins(true); 
      free(p);
    }
    charpinToChar(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; 
    }
    intcompareCharArrays(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;    
    }
    voidreadAllPins(){
      for (int i =0; i < (ioPorts); i++)
      {
        if (availablePin[i]) 
        {
          debouncer[i].update();
          statusBuffer[i] =  pinToChar(debouncer[i].read());
        }  
      }  
    }
    voidpublishPins(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);
          }
        }  
      }  
    }
    voidsetup(){  
      // 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);
      }
    }
    voidloop(){
      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]; 
        }
      }    
    }
    


  • We connect the free contact of our intermediate relay to the Gnd and DIxx Arduino contacts.

  • To receive information through MQTT in Domoticz, install MQTTWorker.

  • Manually turn on and off the load and observe a change in the load state in the Domoticz interface.


Thank you for your attention, I hope someone my experience will be useful.

Also popular now: