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.
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:
Gate open – Connect and send the MQTT Open message
Wait for the gate to close
Gate closed – Connect and send the MQTT Closed message
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.
Problem Solved…. For now. 🙂