Connecting Aquastorozh to Smart Home on Z-Wave



    Last year, I bought equipment for emergency shutoff of cranes when a leak from Aquastorozh was detected. For a long time I could not put it. There was an idea to integrate it into the Z-Wave network and get a gidrolock analogue, but working on batteries. Finally, the hands reached ...

    Aquastorozh is a base with plug-in taps and leakage sensors. This complex can work both from a 220 V network through an adapter, and from batteries. The developers have provided the ability to connect to the systems of "smart home". By closing one pair of contacts in an Ethernet outlet, you can open the taps and the other close them. Relay contacts close for 1 second when a leak is detected. There is a non-soldered UART connector on the board, but in this article I will talk about the implementation of documented functions.

    Development tasks


    • Remote on / off taps.
    • Leakage information.
    • Two water meters.
    • Do not interrupt the work of "Aquastorozh".

    Decided to do on the basis of ZUNo shield. It is delivered in a sealed enclosure with a pressure seal, has terminal blocks on board and free space for installing the battery and additional electronic components. Arduino is similar.

    Ethernet connector




    One of the closable wires is the “earth” of the “aqua watchman”. You can combine the lands of ZUNo and Aqua-Watch and control the cranes directly through the GPIO ZUNo. So I did. But in the event of a ZUNo failure (for example, the battery has run out), the “Aquastorozh” control lines receive a “zero” and it starts to restart cyclically. Such a connection option greatly affects the reliability of the entire system, therefore, having slightly complicated the circuit, I switched to two reed relays, which provided galvanic isolation from Aquastorozh. Relays consume about 7 mA when on. To switch the cranes you need to turn on one relay for one second, which is quite acceptable. The battery charge was enough for several thousand switchings. (Now I have electromagnetic pulse single coil relays in my hands. To switch them, you need to give a pulse of 1 ms, which is much more energy efficient. But for control you need 4 transistors and two I / O legs on the relay).

    Sleep in Z-wave


    I’ll talk a little about how Z-Wave devices sleep and about the resulting problem.
    Z-wave devices can be dormant or often waking up. The sleeping device is the most energy-efficient, but it cannot be sent a command (in my case, to switch the cranes). The second type suits me. FLiRS device - Frequently Listening Routing Slaves. A device configured for this mode of operation wakes up every second, and if for a short period of time it does not receive a signal for complete waking up from the controller, it falls asleep. For example: I send a command to open cranes. The controller understands that my device is often listening and sends out a special short packet (wakeup beam) within a second so that all FLIRS devices on the network wake up. As soon as my device accepts this packet, it sends a report that it has woken up and is ready to accept the command. Gets a command to close the taps. Falling asleep again. And so every time when the device is being controlled. The disadvantage is that the device can receive a wakeup beam both at the end of the broadcast by the controller and at the beginning. The controller sends it for about a second. In the worst case, the device wakes up at the beginning of this newsletter, and will wait almost a second, until a command arrives. But since opening and closing taps is often not necessary, this is not a serious drawback.

    Implementation


    ZUNo Shield has a small breadboard on which you can place the necessary components.



    The circuit contains two relays and two transistors for their control. A simple little sketch.



    A few words about energy consumption.

    ZUNo shield contains a driver chip for the RS-485 protocol and a pull-up resistor for pin "11" on the bottom block, for the One Wire protocol. After removing these components, the main consumer remains ZUNo.



    Consumption in sleep mode is about 5-10 μA, and in active mode up to 60 mA (the relay is active and ZUNa is transmitting).

    Oscillograms of current consumption for different operating modes


    The direction of the current axis is from top to bottom.

    The device is waiting for a command:



    About every second, short peaks are visible during which the device wakes up and checks if a wakeUp beam has arrived.

    The device received the command:



    First, the device woke up, received a wakeUp beam, waited for a command (from 0 to 1 second), if the command to control the cranes, it turns on the corresponding relay for 1 second (at this stage you need to put the controller to sleep while keeping the legs in the current state, but I I was afraid and too lazy) and the rest of the time is spent on the internal operation of the chip, after which ZUNo falls asleep. A total of almost 3.5 seconds per one operation of opening or closing by taps. A terribly long time, but due to the fact that such operations will be performed extremely rarely, optimization can be neglected. Yes, and it will give little, because the sketch in the Arduino ide is only a small part of what is tossed and turned in this small microcontroller, and what is safely hidden by the manufacturer from the curious.

    Scheme of connection to "Aquastorozh"




    Conclusion


    It turned out to add “Aquastorozh” quite accurately to the existing Z-Wave network. The main disadvantage is the lack of feedback from Aquastorozh. At this stage I’m waiting for a new version of the ZUNo library, in which a bug will be fixed that prevents ZUNo from sleeping normally, so instead of a photo with Aquastorozh installed and connected, a picture with the debugging process.



    Thanks for attention!

    Sketch
    //#define _DEBUG
    #define OPEN_PIN 11
    #define CLOSE_PIN 12
    #define LEAK_PIN 19
    #define INT1 18
    uint8_t valve_action = 0;
    #ifdef _DEBUG
      uint8_t const METER1_PIN = 8;
    #else
      uint8_t const METER1_PIN = 7;
    #endif
    #define METER2_PIN 8
    #include "EEPROM.h"
    #define MAGIC_VALUE 42
    #define ADDR_ACTION 1
    #define CH_METER_1 4
    #define CH_METER_2 8
    #define CNT_ON_OFF_CICL 12
    // Global variables
    byte pin7SwitchBinaryState = 0;
    DWORD eeprom_buf = 0;
    #define LEAK_CHANNEL 3
    DWORD meter_cnt1;
    DWORD meter_cnt2;
    #define ADR_MET1 4
    #define ADR_MET2 5
    uint8_t alarm_clr = LOW;
    // Z-Wave channels
    ZUNO_SETUP_CHANNELS(
      ZUNO_SWITCH_BINARY(pin7SwitchBinaryGetter, pin7SwitchBinarySetter),
      ZUNO_SWITCH_BINARY(alarmGetter, alarmSetter),
      ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_WATER, getterSensorBinary),
      ZUNO_METER(ZUNO_METER_TYPE_WATER, METER_RESET_ENABLE , ZUNO_METER_WATER_SCALE_PULSECOUNT, 4, 0, getterMETER1, resetterMETER1),
      ZUNO_METER(ZUNO_METER_TYPE_WATER, METER_RESET_ENABLE , ZUNO_METER_WATER_SCALE_PULSECOUNT, 4, 0, getterMETER2, resetterMETER2)
    );
    ZUNO_SETUP_BATTERY_LEVELS(2700, 3300);
    ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_FREQUENTLY_AWAKE);
    void close_water()
    {
      #ifdef _DEBUG
        Serial1.println("close");
      #endif
      digitalWrite(CLOSE_PIN, HIGH); 
      delay(1000); 
      digitalWrite(CLOSE_PIN, LOW); 
      //delay(1000);
    }
    void open_water()
    {
      #ifdef _DEBUG
        Serial1.println("open");
      #endif
      digitalWrite(OPEN_PIN, HIGH); 
      delay(1000); 
      digitalWrite(OPEN_PIN, LOW); 
      //delay(1000);
    }
    #define LEAK_DETECTED LOW
    #define LEAK_END      HIGH
    #define ADDR_LEAK_ST_LAST     2
    #define ADR_B1_F              3
    #define ADR_B2_F              4
    #define NZ_ADR_LEAK           5
    uint8_t last_leak_st;
    #define EEPROM_MAGIC          0x11223342
    #define EEPROM_ADR_MAGIC      0
    void setup() 
    {
      #ifdef _DEBUG
        Serial1.begin(9600);
        Serial1.println("serial init");  
      #else
        pinMode(METER1_PIN,  INPUT);
        pinMode(METER2_PIN,  INPUT);
      #endif 
      pinMode(CLOSE_PIN, OUTPUT);
      pinMode(OPEN_PIN, OUTPUT);
      pinMode(LEAK_PIN,  INPUT_PULLUP);
      pinMode(INT1,  INPUT_PULLUP);
      digitalWrite(CLOSE_PIN, LOW);
      digitalWrite(OPEN_PIN, LOW);
      byte n;
      NZRAM.get(0x0, &n, 1);
      if (n == MAGIC_VALUE) 
      {
        // correct magic value after wake up from sleep mode
        // trust NZRAM data
      } 
      else 
      {
        // incorrect magic, first boot after battery insert ot rebooted due to low battery
        // initialize NZRAM magic
        n = MAGIC_VALUE;
        NZRAM.put(0x0, &n, 1);
        NZRAM.write(ADDR_ACTION, LOW);
        NZRAM.write(ADDR_LEAK_ST_LAST, LEAK_END);
        NZRAM.write(ADR_B1_F, HIGH);
        NZRAM.write(ADR_B2_F, HIGH);
      }
      EEPROM.get(EEPROM_ADR_MAGIC, &eeprom_buf, sizeof(DWORD));
      if(eeprom_buf != EEPROM_MAGIC)
      {
        eeprom_buf = EEPROM_MAGIC;
        EEPROM.put(EEPROM_ADR_MAGIC, &eeprom_buf, sizeof(DWORD));
        resetterMETER1(); 
        resetterMETER2(); 
        eeprom_buf = 0;
        EEPROM.put(CNT_ON_OFF_CICL, &eeprom_buf, sizeof(DWORD));
      }
    }
    uint8_t last_btn_st;
    void check_btn(uint8_t meter_pin, uint8_t NZ_adr_st)
    {
      last_btn_st = NZRAM.read(NZ_adr_st);
      if(digitalRead(meter_pin) == LOW)
      {
        if(last_btn_st != LOW)
        {
          for(uint8_t i=0; i<3; ++i)
          {
            if(digitalRead(meter_pin) == LOW)
              delay(5);
            else
              return;
          }
          last_btn_st = LOW;
          NZRAM.write(NZ_adr_st, last_btn_st);
        }
      }
      else
      {
        if(last_btn_st == LOW)
        {
          for(uint8_t i=0; i<3; ++i)
          {
            if(digitalRead(meter_pin) == HIGH)
              delay(5);
            else
              return;
          }
          last_btn_st = HIGH;
          NZRAM.write(NZ_adr_st, last_btn_st);
          if(NZ_adr_st == ADR_B1_F)
            inc_met(ADR_MET1);
          else
            inc_met(ADR_MET2);
        }
      }  
    }
    //=-----------------------------------------------------------
    void loop() 
    {
      if(digitalRead(LEAK_PIN) == LEAK_DETECTED)
      {
        if(NZRAM.read(ADDR_LEAK_ST_LAST) != LEAK_END)
        {
          zunoSendReport(LEAK_CHANNEL);
          NZRAM.write(ADDR_LEAK_ST_LAST, LEAK_END);
        }
      }
      check_btn(METER1_PIN, ADR_B1_F);
      check_btn(METER2_PIN, ADR_B2_F);
      if(zunoGetWakeReason() == ZUNO_WAKEUP_REASON_RADIO)
      {
        uint32_t start_time=0;
        #define ACTION_T_OUT 2000
        start_time = millis();
        //while(NZRAM.read(ADDR_ACTION)== 0) 
        while(valve_action== 0)
          if((millis() - start_time) >= ACTION_T_OUT)
          {
            #ifdef _DEBUG
              Serial1.println("T_OUT");
            #endif
            break;
          }
          else
            delay(50);
        #ifdef _DEBUG
          Serial1.println(millis() - start_time);
        #endif
        if(NZRAM.read(ADDR_ACTION))
        {
          valve_action = 0;
          NZRAM.write(ADDR_ACTION, LOW);
          if(pin7SwitchBinaryState == LOW)
            close_water();
          else
            open_water();
        }
        if(alarm_clr) // Если пришла команда сброса тревоги
        {
          alarm_clr == LOW;
          zunoSendReport(LEAK_CHANNEL);
        }   
      }
      if(digitalRead(INT1)==0)
      {
        zunoSetWUOptions(ZUNO_WUPFLAGS_INT1_HIGH);
      }
      else
      {
        zunoSetWUOptions(ZUNO_WUPFLAGS_INT1_LOW);
      }
      zunoSendDeviceToSleep();
    }
    //-----------------------------------------------------------
    // Getters and setters
    void inc_met(uint8_t num_chennel)
    {
      uint8_t eeprom_adr_met;
      if(num_chennel == ADR_MET1)
        eeprom_adr_met = CH_METER_1;
      else
        eeprom_adr_met = CH_METER_2;
      EEPROM.get(eeprom_adr_met, &eeprom_buf, sizeof(DWORD));
      eeprom_buf++;
      EEPROM.put(eeprom_adr_met, &eeprom_buf, sizeof(DWORD)); 
      zunoSendReport(num_chennel);
    }
    DWORD getterMETER1() 
    {
      EEPROM.get(CH_METER_1, &eeprom_buf, sizeof(DWORD));
      return eeprom_buf; 
    }
    DWORD getterMETER2() 
    {
      EEPROM.get(CH_METER_2, &eeprom_buf, sizeof(DWORD));
      return eeprom_buf;
    }
    void resetterMETER1() 
    {
        eeprom_buf = 0;
        EEPROM.put(CH_METER_1, &eeprom_buf, sizeof(DWORD));  
    }
    void resetterMETER2() 
    {
        eeprom_buf = 0;
        EEPROM.put(CH_METER_2, &eeprom_buf, sizeof(DWORD));  
    }
    void pin7SwitchBinarySetter(byte value) 
    {
      valve_action = 1;
      NZRAM.write(ADDR_ACTION, HIGH);
      pin7SwitchBinaryState = value;
      if(value == 255)   // if open valve, then off leak alarm
      {
        NZRAM.write(ADDR_LEAK_ST_LAST, LOW);
        zunoSendReport(LEAK_CHANNEL);
      }
    }
    byte pin7SwitchBinaryGetter() {
      return pin7SwitchBinaryState ? 0xFF : 0;
    }
    byte getterSensorBinary() {
        return digitalRead(LEAK_PIN) ? 0 : 0xFF;
    }
    byte alarmGetter() 
    {
      uint8_t ret;
      ret = NZRAM.read(ADDR_LEAK_ST_LAST);
      return ret ? 0xFF : 0;
    }
    void alarmSetter(byte value) 
    {
      alarm_clr = HIGH;
      NZRAM.write(ADDR_LEAK_ST_LAST, value);
    }
    


    Also popular now: