The why
There are products on the market to automate gates. I especially like Remootio. I have however been yearning for some DIY electronics action so I took it upon myself to DIY something similar that could not only open and close my sliding gate, but what would tell me if the gate is open or closed.
The how
I have a Centurion gate motor; they are a popular gate motor supplier in South Africa and they also have their own lineup of home automation products.
The content :)
Initial concept
I've had a simple eWeLink relay in the gate box for a while now, which can help me open and close it remotely. This is very useful when a courier decides to stop by as soon as I go out to the shops. The problem with this is that I have no way of verifying that the gate is actually closed after they leave, other than asking them. Two weeks ago, I noticed that my gate control board has a status LED output, which mirrors the Status LED on the control board itself. Upon some nerdy doc reading, I discovered that this light (and thus output), except for the obvious rudimentary error checking, also indicates the gate status as follows:
Open: Solid on
Opening: flashing
Closing: flashing
Closed: off
I immediately started looking for solutions as to how to use this somehow.
And action
I recently flashed my first Sonoff board with ESPHome and I fell in love instantly. First off, it integrates into Home Assistant (I have it running in a cloud instance) with a single click and the YAML OTA is extremely convenient for updating the configuration.
When dealing with the status light, one of my primary concerns was regarding how to check if the light is just flashing or whether it's staying on or off. It's not ideal to watch a gate icon flash between open and closed for 30 seconds, wondering whether the gate is actually closing or not. I trawled ESPHome's documentation and eventually saw a line that made my heart skip a beat.
For example, you can measure if a status LED of a pool controller is permanently active (indicating that the pump is on) or blinking.
It raised a couple of questions for me though, mostly regarding the time frame of the state checks. I decided to power through anyway and headed out to buy a NodeMCU dev board.
The proof of concept worked flawlessly. I built a simple voltage divider with some resistors I had lying around and it worked. The final voltage was actually around 1V (3.3V logic) but it still seems to trigger it just fine.
The installation
I headed out to my gate, armed with a NodeMCU hanging onto a breadboard for dear life, and started connecting it up. The dreaded moment of truth finally arrived, but instead of confirming my genius by spluttering to life, nothing happened. After a lot of troubleshooting, I finally figured out that my gate controller outputs about 16V on the 12V port! Not ideal for the cheap YwRobot board I had repurposed to deliver safe 3.3V to my NodeMCU.
Back to the drawing board
Not to be outdone by a simple voltage spike, I headed back to the interwebs in search of a better solution and there it was! A Sonoff SV.
The Sonoff SV is a simple board which is designed to handle 5-24V; perfect for my rebellious gate controller. As a bonus, it exposes 3 GPIO pins right on the board!
The installation v2
It turns out that the Sonoff SV is intended to be used to power DC devices, but luckily, this specific board is extremely hacker-friendly.
Pop out two relays and pretend to remove the jumper that wasn't included with my specific board and you now have a relay that is unpowered, perfect for triggering something. Of course, there is a but though; there is always a but. The relay is unpowered but one of the two sides needs to be connected to complete the gate trigger circuit.
Also, because I like doing things the right way, I busted out my soldering iron. Now, even calling my soldering skills decent is a pretty long shot, but I managed to bridge the gap and while I was at it I soldered a jumper wire onto the board (the relay inputs do not have headers.
The aftermath
At the time of writing this, ESPHome does not have the "gate" device class as per Home Assistant's documentation, but I have just submitted a PR to fix it :) https://github.com/esphome/esphome/pull/1175 (also my first contribution to ESPHome).
Depending on how well this post does, I will show off my horrible soldering skills and I'll add some more information regarding how I installed it in the motor enclosure.
Thanks for reading :)
The esphome YAML file
esphome:
name: gate
platform: ESP8266
board: esp8285
wifi:
ssid: "*** IoT"
password: "****"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Gate Fallback Hotspot"
password: "supersecure"
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
web_server:
port: 80
switch:
- platform: gpio
pin: GPIO12
id: relay1
internal: true
- platform: template
name: "Gate Trigger"
id: rTrg
internal: true
lambda: |-
return false;
turn_on_action:
- switch.turn_on: relay1
- delay: 100ms
- switch.turn_off: relay1
binary_sensor:
- platform: gpio
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: True
name: "On-PCB Trigger"
internal: true
on_press:
- switch.turn_on: rTrg
sensor:
- platform: duty_cycle
pin: 14 #same as 4
name: "Status Duty Cycle"
id: status_duty
update_interval: 1s
filters:
- delta: 2.0
- platform: pulse_counter
pin: 4 # same as 14
name: "Status Pulse Counter"
update_interval: 2s
id: status_pulse
filters:
- delta: 2.0
- platform: wifi_signal
name: "WiFi Signal Sensor"
update_interval: 60s
filters:
- sliding_window_moving_average:
window_size: 10
send_every: 15
cover:
- platform: template
device_class: gate
id: gateCover
name: "Gate"
lambda: |-
if (id(status_duty).state > 80) {
id(gateCover).current_operation = COVER_OPERATION_IDLE;
return COVER_OPEN;
} else if (id(status_duty).state < 5) {
id(gateCover).current_operation = COVER_OPERATION_IDLE;
return COVER_CLOSED;
} else {
if (id(status_pulse).state > 60 && id(status_pulse).state < 90) {
id(gateCover).current_operation = COVER_OPERATION_OPENING;
} else if (id(status_pulse).state > 230 && id(status_pulse).state < 290) {
id(gateCover).current_operation = COVER_OPERATION_CLOSING;
}
return {};
}
open_action:
- if:
condition:
lambda: |-
return id(gateCover).state != COVER_OPEN;
then:
- switch.turn_on: rTrg
- cover.template.publish:
id: gateCover
current_operation: OPENING
else:
- logger.log: "Gate is already open!"
close_action:
- if:
condition:
lambda: |-
return id(gateCover).state != COVER_CLOSED;
then:
- switch.turn_on: rTrg
- cover.template.publish:
id: gateCover
current_operation: CLOSING
else:
- logger.log:
"Gate is in secure state. Cannot perform unsecure
op(close_action)"
stop_action:
- if:
condition:
lambda: |-
return id(gateCover).current_operation != COVER_OPERATION_IDLE;
then:
- switch.turn_on: rTrg
- cover.template.publish:
id: gateCover
current_operation: IDLE
else:
- logger.log:
"Gate is in secure state. Cannot perform unsecure
op(stop_action)"
text_sensor:
- platform: template
id: gateText
name: "Gate State"
lambda: |-
if (id(status_duty).state > 80) {
if (id(gateText).state == "Open!")
return {};
return {"Open!"};
} else if (id(status_duty).state < 5) {
if (id(gateText).state == "Closed")
return {};
return {"Closed"};
} else if (id(status_pulse).state > 60 && id(status_pulse).state < 90) {
return {"Opening..."};
} else if (id(status_pulse).state > 230 && id(status_pulse).state < 290) {
return {"Closing..."};
}
return {"Unknown"};
update_interval: 1s
status_led:
pin:
number: GPIO13
inverted: yes