Project by Michael; WiFi Garage Door Controller with Raspberry Pi Pico W

I started the project and it worked for a while. Note that it has undergone significant changes. Since my garage motor is too powerful (or I don’t know how to wire it up), I bought a small remote and connected the servo motor to the pico so I could always open the garage door. on the phone. Why? Well, I don’t like carrying keys or key chains, so…

1 Like

I added these two lines in the main() function’s while True loop:

        if wlan_connected and wlan.status() != network.STAT_GOT_IP:
            machine.reset()

You also need to set up the variable in the global space:

wlan_connected = False

And set it at the end of the connect_to_wifi() function (just after the print('ip = ' + status[0]) line):

        wlan_connected = True

Now each time it loops (four times per second unless you changed that) it’ll check if the Wi-Fi is still connected and if it’s disconnected it’ll reboot the Pico. Theoretically you could just change that variable to False and call connect_to_wifi() again, but I work in IT and I feel it’s safer to do a full reboot.

2 Likes

Can I get code explanation and on which device should we load this code,I mean on raspberry pi or on phone or on both and how? I’m a beginner so pls don’t mind for this basic questions and give a brief explanation on all these questions.

1 Like

Hi Karri, Welcome to the forum!

In this case, all the code runs on the Pico. The code includes a webserver to host a small webpage within your WiFi that your phone can access through a web browser, like you would any other site.

You can find a guide with less assumed knowledge here to get you started:

Feel free to hit us up with any followup questions!

1 Like

Love this Project! finally configured and soldered it all together…but this got me thinking. Has anyone tried to make this lil device work within Home Assistant ? Im super new to all of this but very interested in learning more

Hey there made a 3d model to hold the board …couldn’t find the exact one you used in this project. Posted on Prusa Printables and thingyverse. I made sure to give your project and page a shout out.

1 Like

Great tutorial! Has anyone adapted this to work with a garage door that only has a single “activate” button (which uses only two wires - I assume for 12v and ground)?

I assume both the circuit and python code would need to be altered; presumably only one of the Q output drivers and one GPIO pin on the Pico W would be needed, but with only the 12v and ground wires, does that mean the ground wire would be connected to pins 1 and three on the MOSFET? I’m certainly no engineer, but that seems odd/wrong.

As for altering the code, I’d need to delete (or comment out) the parts pertaining to the 2 unused MOSFETS. Any advice would be greatly appreciated!

Hey @David248820, welcome to the forums :smiley:

Each mosfet controls a single channel and for a controller with a single input you will only need one copy of the MOSFET output circuit.

What will change is that on the webpage you will not be able to have 3x buttons (up/stop/down) but rather a single button which will work like a normal remote. Press to activate up/stop/down depending on the door’s current state.

That makes sense. But won’t I still have 3 wires to connect from J1 (12v, ground, and let’s say UP/Q1) but only 2 places to connect those 3 wires on my garage door opener?

If your existing switch has only two wires then it’s probably just a normally open switch which operates the door by closing the circuit. The circuit could be closed in the positive (powered) side of the motor or on the negative (ground) side. It will be difficult to tell just by measuring, because the wires will measure 12V and ground in either case - 12V via the motor to ground, or 12V to ground via the motor. You need to examine the wiring at the motor to determine exactly how it is being switched.

In either case you will need an arrangement to power to remote controller - a simple push button doesn’t need any power, but the controller does. It may be possible to arrange that with just two wires, but how you would do that, or whether it’s possible, depends on how the motor is switched.

You also need to consider whether the switch is momentary, triggering some mechanism at the motor that moves the door until a stop switch turns it off, or requires the button to be pressed until the door finishes opening or closing, because that will affect the code you use.

Would it be possible to use this existing code and convert it such that it uses a POST versus a GET method? I would prefer a POST just because browsers tend to cache frequently used sites, and it may auto populate with the query at the end resulting in it doing some other action rather than the one you are visiting the site to do. I have tried simply adding "method=“POST” to the form with the buttons, but it seems like the way a POST request is parsed is different from a GET. How could I go about this?

Can this code be used on a esp32S3 with micropython… what libraries or changes would be needed. -Mike

1 Like

Welcome to the forum Michael!

Since the example uses all common Micropython libraries the examples should work on the S3 in the same way that the Pico does.

If you run into any complications though feel free to make another post and we’ll take a look!

1 Like

I have it running on a Pi Pico W when I save it as main.py and then disconnect from the computer and reconnect. It won’t connect to Thonny. If I connect it just to power it runs fine. I have to nuke it to get to work on the computer again.

1 Like

Hi Michael,

The main loop doesn’t use any sleep functions so it’s quite hard to get a keyboard interrupt in there.

If you spam control+C or Control + F2(I think) you’ll be able to interrupt the Pico and launch the REPL.

Ok I’ll give it a try.

I was wandering would this work on any garage door. I have a Merlin mt60p.

i could have given a try if I had a pico W. Unfortunately, I have Pico. That has no wifi.

I am having this issue constantly. Having to reset it is a pain.

Genius idea!

Could you paste the whole modified code you have made? I’m hopeless and haven’t been able to successfully integrate it into mine.

1 Like

Oops, I forgot to mention “import machine” being required.

"""
Garage Door Controller code
Written by Michael Ruppe @ Core Electronics
    - Version 1.0 July 2022

Hosts a static webpage with three garage door control buttons (Up, Stop, Down)
Outputs: Open Drain channels to pull-down garage door controller channels.

Adapted from examples in: https://datasheets.raspberrypi.com/picow/connecting-to-the-internet-with-pico-w.pdf

######################
Modified by Peter McAndrew
    - Version 1.1 October 2022
"""

import time
import network
import uasyncio as asyncio
from machine import Pin
import machine

# Hardware definitions
led = Pin("LED", Pin.OUT, value=1)
pin_up = Pin(20, Pin.OUT, value=0)
pin_down = Pin(17, Pin.OUT, value=0)
pin_stop = Pin(18, Pin.OUT, value=0)

# Configure your WiFi SSID and password
ssid = 'My Network'
password = 'Password'

check_interval_sec = 0.25

wlan = network.WLAN(network.STA_IF)


# The following HTML defines the webpage that is served
html = """<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
.button { background-color: #4CAF50; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
.buttonRed { background-color: #d11d53; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
</style></head>
<body><center><h1>Garage Door Controller</h1></center><br><br>
<form><center>
<center> <button class="button" name="DOOR" value="UP" type="submit">Door UP</button>
<br><br>
<center> <button class="buttonRed" name="DOOR" value="STOP" type="submit">STOP</button>
<br><br>
<center> <button class="button" name="DOOR" value="DOWN" type="submit">Door DOWN</button></center>
</center></form>
<br><br>
<br><br>
<p>Last command issued was %s<p></body></html>
"""

def blink_led(frequency = 0.5, num_blinks = 3):
    for _ in range(num_blinks):
        led.on()
        time.sleep(frequency)
        led.off()
        time.sleep(frequency)

def control_door(cmd):
    if cmd == 'stop':
        pin_stop.on()
        blink_led(0.1, 1)
        pin_stop.off()
        
    if cmd == 'up':
        pin_up.on()
        blink_led(0.1, 1)
        pin_up.off()
    
    if cmd == 'down':
        pin_down.on()
        blink_led(0.1, 1)
        pin_down.off()
        
        
async def connect_to_wifi():
    wlan.active(True)
    wlan.config(pm = 0xa11140)  # Diable powersave mode
    wlan.connect(ssid, password)

    # Wait for connect or fail
    max_wait = 10
    while max_wait > 0:
        if wlan.status() < 0 or wlan.status() >= 3:
            break
        max_wait -= 1
        print('waiting for connection...')
        time.sleep(1)

    # Handle connection error
    if wlan.status() != 3:
        blink_led(0.1, 10)
        raise RuntimeError('WiFi connection failed')
    else:
        blink_led(0.5, 2)
        print('connected')
        status = wlan.ifconfig()
        print('ip = ' + status[0])
        wlan_connected = True


async def serve_client(reader, writer):
    print("Client connected")
    request_line = await reader.readline()
    print("Request:", request_line)
    # We are not interested in HTTP request headers, skip them
    while await reader.readline() != b"\r\n":
        pass
    
    # find() valid garage-door commands within the request
    request = str(request_line)
    cmd_up = request.find('DOOR=UP')
    cmd_down = request.find('DOOR=DOWN')
    cmd_stop = request.find('DOOR=STOP')
    print ('DOOR=UP => ' + str(cmd_up)) # show where the commands were found (-1 means not found)
    print ('DOOR=DOWN => ' + str(cmd_down))
    print ('DOOR=STOP => ' + str(cmd_stop))

    stateis = "" # Keeps track of the last command issued
    
    # Carry out a command if it is found (found at index: 8)
    if cmd_stop == 8:
        stateis = "Door: STOP"
        print(stateis)
        control_door('stop')
        
    elif cmd_up == 8:
        stateis = "Door: UP"
        print(stateis)
        control_door('up')
        
    elif cmd_down == 8:
        stateis = "Door: DOWN"
        print(stateis)
        control_door('down')
    
    response = html % stateis
    writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    writer.write(response)

    await writer.drain()
    await writer.wait_closed()


async def main():
    print('Connecting to WiFi...')
    asyncio.create_task(connect_to_wifi())

    print('Setting up webserver...')
    asyncio.create_task(asyncio.start_server(serve_client, "0.0.0.0", 80))

    wlan_connected = False
    while True:
        if wlan_connected and wlan.status() != network.STAT_GOT_IP:
            machine.reset()
        await asyncio.sleep(check_interval_sec)


try:
    asyncio.run(main())
finally:
    asyncio.new_event_loop()
2 Likes