Python Remote Gate Monitor

Here was the issue.. We have three sight hounds, a Whippet and two Silken Windhounds. When we bought our house a year ago, we had fencing put up to keep said hounds safe and so they wouldn’t bother the neighbors. We installed a ten foot gate in the back of the property so I could drive various vehicles through it. We cannot see the gate from the house and thus, can’t tell quickly if the gate is opened or closed. Sometime we have contractors come work on our shop building and we always tell them to make sure the back gate is closed, but sometimes they fail to close it.

I needed a way to monitor the gates state remotely and I am a nerd so I built something…

Currently all of my monitoring and home automation is in Home Assistant. I have Zigbee Window and Door sensors in the house that work really well. This particular gate is just out of range for the Zigbee hub, so I needed another solution. Add to the complexity, I need it to run on batteries and have a solar panel for charging. The ESP32 chip has a co-processor and supports a deep sleep mode which would be very handy…

I ordered up the cheapest ESP32 boards I could find, the ESP32-S2. This will prove to be a mistake in the long run. Long story short, I did get Micropython loaded on the board, but the power reading I was getting in Deep Sleep was not great. After further reading and research, I found that the ESP32-S2 chip does not really support DeepSleep.. Sigh.. So, I needed something else. I decided on the ESP32 Mini as it was a small footprint and said it was a normal ESP32 chip which would support Deep Sleep.

ESP32 – S2 No Go!
ESP32MINI for the win!

I ordered up an ESP32 Mini board and loaded Micropython on it. I also had some Reed switches already that I planned to use at the trigger. I started coding with the thought, this should be simple. Not much logic is needed, just tell me if the gate is open and go to sleep.. Well, not so fast there smarty pants. (I never claimed I was smart).  

Here was the problem… I needed the ESP32 to:

Deep Sleep

Gate open – Connect and send the MQTT Open message

Wait for the gate to close

Gate closed – Connect and send the MQTT Closed message

Deep Sleep

This is the part of the story where I admit that I am a novice Python programmer. I know enough to waste hours on a solution someone with skills could probably do in a few minutes. The Deep Sleep function seemed to be pretty straight forward. Set a trigger and tell it to go to sleep. When it’s triggered, the ESP32 resets and runs the boot.py program. Seems easy right? 

So, I soldered up the reed switch between the IO04 pin and ground. I planned on pulling up the IO04 pin and when the gate was closed, the reed switch would be closed which would result in a 0 or LOW reading. When the gate opened, the reed switch would separate the connection from ground and provide a 1 or HIGH reading. I also connected up a LIPO charging board and the solar panel. Here is what that looked like..

Now, about that Micropython code… I went in strong. Feeling like this was going to be a fun short little session using the skills I had learned from the Pool Controller project. That is still going strong by the way. Pretty happy with that. I digress, back to this fiasco. I loaded up some libraries,  setup the WiFi network connection, set a couple of variables, setup the MQTT server IP variable, setup my Pin for IO04, setup my secrets file. Everything was going smooth as silk.. I set the variables for the MQTT topic and connection. Whew, I was feeling like straight FIRE!

I then wrote a couple of functions, one for when the gate was opened and one for when the gate was closed. Each function called the variables for MQTT, Made the connection and sent the respective 0 or 1 to the topic variable. Yep, I got this Python stuff… Then I wrote the if statement and things started falling apart. The problem was, if the ESP32 was asleep and was triggered, it just does a soft reset and runs like a reboot running the code again. So if I just used an if statement, the gate would open, the trigger would happen and re-run boot.py and the if statement said, if pin = 1 (the gate was open) than connect to the MQTT server and send “open” to the topic. If the pin was 0 (gate closed) then connect to the MQTT server and send “open” to the topic. Then go to Deep Sleep. Sounds correct right? Yeah well, the issue is that you can only trigger the wake up with a HIGH or a LOW (0 or 1) not ANY change. So I figured out I had to wait when the gate was open.

It’s the wait that turned out to be trouble..

I will spare you the countless Google searches for ESP32 wait for pin input. Just know that I lost part of my life in that hell. Life I will never get back… EVER!

I found that some were using uasyncio which basically assigns tasks and allows you to wait until a task completes before moving forward. That did the trick and I tested everything out and I was good. No smoke and the messages were being sent. I did have to use some sleep statements to slow things down a bit. Without them, the MQTT commands were not being sent. Things were happening too fast.  So here it is.. If you would like to build your very own WiFi Gate/Door sensor and you have MQTT and Home Assistant.

import network
import esp32
import uasyncio
from secrets import secrets
from machine import Pin
from time import sleep
from machine import deepsleep
from umqtt.simple import MQTTClient

switch = Pin(04, Pin.IN, Pin.PULL_UP)
esp32.wake_on_ext0(pin = switch, level=1)
ssid = secrets['ssid']
pw = secrets['pw']

#Connecting to wifi
wlan = network.WLAN(network.STA_IF)
if not wlan.isconnected():
    print('connecting to network...')
    wlan.active(True)
    wlan.connect(ssid, pw)
    while not wlan.isconnected():
        pass
print('network config:', wlan.ifconfig())
sleep(2)

#MQTT variables
mqtt_server = "xxx.xxx.xxx.xxx" #<-Replace the x's with your mqtt server IP
topic_pub = b'homemade/gate_sensor'
client = MQTTClient("Gate_Keeper", mqtt_server)

async def main():
    if switch.value() == 0:
        sleep(4)
        gate_closed()
        print("Gate closed, sleeping")
    else:
        print("Gate Open")
        sleep(4)
        gate_open()
        print("Waiting")
        await check_gate()
        print("Gate Closed")
        sleep(4)
        gate_closed()
        sleep(2)
        
def gate_open():
    client.connect()
    client.publish(topic_pub, "1")
    print("Open Published")
    print(switch.value())
    client.disconnect()
    
def gate_closed():
    client.connect()
    client.publish(topic_pub, "0")
    print("Closed Published")
    print(switch.value())
    client.disconnect()
    
async def check_gate():
    while switch.value() == 1:
        sleep(2)

uasyncio.run(main())
print("sleeping")
deepsleep()

Note: I do not know if this is the most efficient way to achieve this. If someone smarter than this old hillbilly has a better way, I would love to see the code. I am always up for a code review.

Here it is all mounted up in a custom 3D printed enclosure I designed in Fusion 360 and printed.

And here it is all mounted on the gate. Note I added a part that extended the magnet out from the gate.

Setup in Home Assistant

Problem Solved…. For now. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.