Cheap 4 valve irrigation controlled via MQTT

Upgrade of an inexpensive (Aliexpress) low-power 4 valve controller to a 4 valve MQTT/HA controlled irrigation system by adding an ESP8266 and some more stuff. I use it as a distributor after a Link-Tap valve, hence I wrote the Tasmota script specifically for that purpose. (safety built in)

Change the script for your purpose.

my Setup :

The Components

The picture shown it open, all fits into the original case, the only outside difference is the feed for the 3.3V.
Controller and Valves Automatic 4-zone Lcd Display Watering Timer Irrigation System Garden Water Timer Controller With 2 Solenoid Valves - Garden Water Timers - AliExpress
Plus an 74HC238E 3-to-8 decoder, a stepup converter 3.3V to 19V, some resistors and a 3.3V power supply.

The wiring (updated after Nickrout’s question, thx)

ESP8266 74HC238E
gnd GND 8
GPIO 5 6
gnd 5
gnd 4
GPIO 4 3
GPIO 12 2
GPIO 14 1

I cut the battery compartment out which gave me room to wedge in the ESP8266.

Tasmota compiled with the script option (thank you
I have the main valve (a Link-Tap) inside with solid pipes (and no more room for more pipes). The 4-valves are outside. Hence my Tasmota script “only” distributes that single on/off water feed onto the 4 separated irrigation segments. Default is set that all 4 segments are open and safeguarding within the Tasmota code prevents all 4 valves to be closed at the same time. (in order to prevent static pressure on the pipes if Link-Tap yet no segment would be open) Easy to see in the code, change as needed. (code at the end of this post as it is lengthy)

Template Config

- binary_sensor:      
    - name: "Irrigation4 Valve 1"
      unique_id: "irrigation4_valve_1"
      state: > 
        {{ 'on' if states('sensor.irrigation4_valves').split(',')[0] | int == 0 else 'off' }}
    - name: "Irrigation4 Valve 2"
      unique_id: "irrigation4_valve_2"
      state: > 
        {{ 'on' if states('sensor.irrigation4_valves').split(',')[1] | int == 0 else 'off' }}  
    - name: "Irrigation4 Valve 3"
      unique_id: "irrigation4_valve_3"
      state: > 
        {{ 'on' if states('sensor.irrigation4_valves').split(',')[2] | int == 0 else 'off' }}  
    - name: "Irrigation4 Valve 4"
      unique_id: "irrigation4_valve_4"
      state: > 
        {{ 'on' if states('sensor.irrigation4_valves').split(',')[3] | int == 0 else 'off' }} 

Script to open / close valves

  alias: irrigation_valve_open
  - service: mqtt.publish
      topic: cmnd/irrigation4_/VOPEN
      qos: '2'
      payload_template: '{{valve_number}}'
  mode: single
  icon: mdi:water-pump
  alias: irrigation_valve_close
  - condition: state
    entity_id: input_boolean.irrigation4_mqtt
    state: 'on'
  - service: mqtt.publish
      topic: cmnd/irrigation4_/VCLOSE
      qos: '2'
      payload_template: '{{valve_number}}'
  mode: single
  icon: mdi:water-pump-off

and the automation to open a valve (the close is analogue)
I did not find a more elegant way for extracting the valve number from the entity_id, if you know, please tell me

- id: Irrigation_valve_open
  alias: Irrigation_valve_open
  description: ''
  - platform: state
    - input_boolean.irrigation4_valve1
    - input_boolean.irrigation4_valve2
    - input_boolean.irrigation4_valve3
    - input_boolean.irrigation4_valve4
    to: 'on'
      hours: 0
      minutes: 0
      seconds: 10
  condition: []
  - service: script.irrigation_valve_open
      valve_number: '{{trigger.entity_id.removeprefix(''input_boolean.irrigation4_valve'')}}'
  mode: queued

“my” trigger to open all valves at the end of an irrigation cycle (again, prevent static pressure, hence a VOPENALL)

- id: '1660032786486'
  alias: irrigation_stop
  description: 'stop irrigation altogether '
  - platform: state
    - switch.29a75f25004b1200_water_switch
    to: 'off'
  condition: []
  - service: mqtt.publish
      topic: cmnd/irrigation4_/VOPENALL
      qos: '2'
      payload: ' '
  mode: single

plus an automation to reset the input_binaries if the controller resets or otherwise opens all valves. (the explicit OPENALL is added to the MQTT-msg to force a status change no matter what it was before, even if it already was 0,0,0,0)

- id: '1659980142774'
  alias: irrigation4_reset
  description: set all input_binary to open when 0,0,0,0 is sent from irrigation4
  - platform: state
    - sensor.irrigation4_valves
    to: 0.00,0.00,0.00,0.00
  - platform: state
    - sensor.irrigation4_valves
    to: 0.00,0.00,0.00,0.00,OPENALL
  condition: []
  - service: input_boolean.turn_on
    data: {}
      - input_boolean.irrigation4_valve1
      - input_boolean.irrigation4_valve2
      - input_boolean.irrigation4_valve3
      - input_boolean.irrigation4_valve4
  mode: single

Below the Tasmota script code

; 30 July 22  Bruno Initial Version with init
; 30 July 22  Bruno permanent array
; 31 July 22  Bruno publish valves...
; 6 Aug 22    Bruno v6 plain text string
; 8 Aug 22    Bruno v7 openall and only change if different --> crash if all have to be set to closed -
; 8 Aug 22    Bruno    what's making all reset crashing ? moved MQTT outside of STCH so it publishes only once
; 9 Aug 22    Bruno    force open in VOPENALL, irrespective of previous status
; Pulse via 3-to-8 decoder
; gpio 14 = 0 open 1 = close
; gpio 12 = 0/1
; gpio  4 = 0/2
; gpio  5 = pulse 100ms

; Y0/Y1 = Valve 4
; Y2/Y3 = Valve 3
; Y4/Y4 = Valve 2
; Y6/Y7 = Valve 1

m:p:va=0 4

;;; cmnd/irrigation4_/VPULSE 1 

string=hello+"I booted"
print BOOT executed
print %string%

;set GPIO to output
print gpio to out
spinm(4 1)
spinm(5 1)
spinm(12 1)
spinm(14 1)
spin (5 0)  ;; pulse to 0
; ensure all valve values are set to close so openall opens them for sure



; subroutines
#STCH(v io)
;pulse valve v  for io=0=open; 1=close
print valve %v% %io%
spin(14 io)   ;; set open / close
;set binary address for 3-to-8 decoder
if v>2 ; binary 0/2
then spin(4 0)
else spin(4 1)
if v==1 
or v==3 ;; binary 0/1
then spin(12 1)
else spin(12 0)
;only pulse if change
if va[v]!=io
then spin(5 1)     ;pulse
va[v]=io      ;; store valve status
spin(5 0)
delay(1000) ; to recharge the capacitor

;safety check whether this is not the last valve closing. If it would then open all
if vchk==3
and va[v]==0  ;;v is the only valve still 0=open
else =#STCH(v 1)
=>Publish tele/irrigation4_/VALVES %va[1]%,%va[2]%,%va[3]%,%va[4]%

=#STCH(v 0)
=>Publish tele/irrigation4_/VALVES %va[1]%,%va[2]%,%va[3]%,%va[4]%

spin(v 1)
=>Publish tele/irrigation4_/VALVES %va[1]%,%va[2]%,%va[3]%,%va[4]%

for cnt 1 4 1
va[cnt]=1 ; force open
=>Publish tele/irrigation4_/VALVES %va[1]%,%va[2]%,%va[3]%,%va[4]%,OPENALL

=>Publish tele/irrigation4_/VALVES %va[1]%,%va[2]%,%va[3]%,%va[4]%

print MQTT event!

;print restarting now

A couple of queries:

What does the 19v supply? Am I right in thinking it is for the solenoids?

Correct. Original has a step-up converter controlled by the CPU that’s charging the capacitor before triggering the valve. I have not figured out how to trick that, so I simply wedged in a permanent step-up converter. Does not need much power and it needs a powersupply anyway to keep WiFi going.

1 Like

The output voltage seems to be generated by L1 controlled through R5 from P57/IROUT (PWM) on the Elan EM78P469AL64J microcontroller. You probably need to toggle that get some switch-mode action going. Feedback from the voltage divider R10/R11 coming from the Zener diode D2 is connected to P56/TCC (timer/counter) on the Elan. I still need to determine frequency and duty cycle. I might put a scope on it to see what the Elan does.

Are the valves really actuated by solenoids, and if so - how is that done, because surely they are not powered for the duration of the on-period? I would have suspected small DC motors.

How did you determine that the output voltage should be 19V? I’m reading 7.5V when my device is idle.

Fortunately charging the capacitor once for each valve is enough to toggle the valve, so one period is enough. I measured the 19V on the RZ7889 just before the valve triggers. (hence charging the capacitor by the step-up converter before triggering)