Initial import
This commit is contained in:
commit
20aaa77701
3 changed files with 475 additions and 0 deletions
216
WIRING.txt
Normal file
216
WIRING.txt
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
================================================================================
|
||||
GARAGE DOOR CONTROLLER - WIRING DIAGRAM
|
||||
Raspberry Pi Pico 2
|
||||
================================================================================
|
||||
|
||||
HARDWARE REQUIRED
|
||||
-----------------
|
||||
- Raspberry Pi Pico 2
|
||||
- SunFounder 2-Channel 5V Relay Module (Amazon: B00E0NTPP4)
|
||||
- 2x Door position sensors (optional, for status detection)
|
||||
- Jumper wires (female-to-male recommended)
|
||||
- Power supply (5V, 1A minimum)
|
||||
|
||||
================================================================================
|
||||
PIN CONFIGURATION
|
||||
================================================================================
|
||||
|
||||
Pico 2 Pin Layout (Top View)
|
||||
=============================
|
||||
| 3V3(1) (2) VBUS |
|
||||
| GND (3) (4) 3V3_EN|
|
||||
| GP28 (5) (6) ADC2 |
|
||||
| GP27 (7) (8) ADC1 |
|
||||
| GP26 (9) (10) ADC0 |
|
||||
| RUN (11) (12) GP22 |
|
||||
| GP21 (13) (14) GP20 |
|
||||
| GP19 (15) (16) GP18 |
|
||||
| GP17 (17) (18) GP16 |
|
||||
| GP15 (19) (20) GP14 |
|
||||
| GP13 (21) (22) GP12 |
|
||||
| GND (23) (24) GP11 |
|
||||
| VSYS (25) (26) GP10 |
|
||||
| GND (27) (28) GP9 |
|
||||
| 3V3 (29) (30) GP8 |
|
||||
| 3V3 (31) (32) GP7 |
|
||||
| ADC3 (33) (34) GP6 |
|
||||
| GP4 (35) (36) GP5 |
|
||||
| GP3 (37) (38) GP2 |
|
||||
| GP1 (39) (40) GP0 |
|
||||
|_____(41) (42)________|
|
||||
|
||||
================================================================================
|
||||
RELAY MODULE WIRING
|
||||
================================================================================
|
||||
|
||||
Relay Module Pins (Top View)
|
||||
=============================
|
||||
+--------+ +--------+
|
||||
| IN1 | | IN2 |
|
||||
| VCC | | GND |
|
||||
| GND | | COM2 |
|
||||
| COM1 | | NO2 |
|
||||
| NO1 | | NC2 |
|
||||
| NC1 | | |
|
||||
+--------+ +--------+
|
||||
|
||||
Connect Relay Module to Pico 2:
|
||||
-------------------------------
|
||||
Relay Module Pico 2
|
||||
----------- -------
|
||||
VCC ----> 5V (Pin 39)
|
||||
GND ----> GND (Pin 38)
|
||||
IN1 ----> GP15 (Pin 20) - Door 1
|
||||
IN2 ----> GP16 (Pin 21) - Door 2
|
||||
|
||||
================================================================================
|
||||
GARAGE DOOR OPENER WIRING
|
||||
================================================================================
|
||||
|
||||
OVERHEAD DOOR ODYSSEY 1000 TERMINALS
|
||||
------------------------------------
|
||||
Locate the wall control terminals on your garage door opener motor unit.
|
||||
These are typically labeled "WALL CONTROL" or have two screw terminals
|
||||
(usually red and white wires).
|
||||
|
||||
WIRING DIAGRAM FOR EACH DOOR:
|
||||
-----------------------------
|
||||
|
||||
+-------------------+ +-------------------+
|
||||
| Garage Door | | Garage Door |
|
||||
| Opener #1 | | Opener #2 |
|
||||
| | | |
|
||||
| Red Wire ----+--+---------+--+--> Relay 1 COM |
|
||||
| | | | | |
|
||||
| White Wire --+--+---------+--+--> Relay 1 NO |
|
||||
| (parallel w/ | |
|
||||
| wall button) | |
|
||||
+-------------------+ +-------------------+
|
||||
|
|
||||
v
|
||||
+-------------------+ +-------------------+
|
||||
| | | |
|
||||
| Red Wire ----+--+---------+--+--> Relay 2 COM |
|
||||
| | | | | |
|
||||
| White Wire --+--+---------+--+--> Relay 2 NO |
|
||||
| (parallel w/ | |
|
||||
| wall button) | |
|
||||
+-------------------+ +-------------------+
|
||||
|
||||
IMPORTANT NOTES:
|
||||
- Wire in PARALLEL with existing wall buttons (keep them functional)
|
||||
- Use NO (Normally Open) terminals, NOT NC
|
||||
- Garage door button circuit is low voltage (~12-24V), safe to work with
|
||||
- Always disconnect power before working with opener wiring
|
||||
|
||||
================================================================================
|
||||
DOOR SENSOR WIRING (OPTIONAL)
|
||||
================================================================================
|
||||
|
||||
If using door position sensors to detect open/closed status:
|
||||
|
||||
Sensor 1 (Door 1) Pico 2
|
||||
--------------- -------
|
||||
VCC (if powered) --> 3V3 (Pin 36)
|
||||
GND --> GND (Pin 38)
|
||||
Signal --> GP14 (Pin 19) - Configured in config.py
|
||||
|
||||
Sensor 2 (Door 2) Pico 2
|
||||
--------------- -------
|
||||
VCC (if powered) --> 3V3 (Pin 36)
|
||||
GND --> GND (Pin 38)
|
||||
Signal --> GP13 (Pin 18) - Configured in config.py
|
||||
|
||||
Note: Config uses internal pull-up resistors, so sensors should be
|
||||
configured as normally-open (closed = door open).
|
||||
|
||||
================================================================================
|
||||
COMPLETE WIRING SUMMARY
|
||||
================================================================================
|
||||
|
||||
+------------------+
|
||||
| Pico 2 W |
|
||||
+------------------+
|
||||
| |
|
||||
5V (Pin 39) ----+--> VCC (Relay) |
|
||||
| |
|
||||
GND (Pin 38) ---+--> GND (Relay) |
|
||||
| |
|
||||
GP15 (Pin 20)-->--> IN1 (Relay) |---- Door 1 Control
|
||||
| |
|
||||
GP16 (Pin 21)-->--> IN2 (Relay) |---- Door 2 Control
|
||||
| |
|
||||
GP14 (Pin 19)-->--> Door Sensor 1 |---- (Optional)
|
||||
| |
|
||||
GP13 (Pin 18)-->--> Door Sensor 2 |---- (Optional)
|
||||
| |
|
||||
+------------------+
|
||||
|
||||
================================================================================
|
||||
CONFIGURATION
|
||||
================================================================================
|
||||
|
||||
Edit config.py to customize:
|
||||
|
||||
WIFI_SSID - Your WiFi network name
|
||||
WIFI_PASSWORD - Your WiFi password
|
||||
STATIC_IP - Fixed IP address for the Pico
|
||||
SUBNET - Subnet mask
|
||||
GATEWAY - Router IP address
|
||||
DNS - DNS server IP
|
||||
|
||||
RELAY1_PIN - GPIO pin for door 1 relay (default: 15)
|
||||
RELAY2_PIN - GPIO pin for door 2 relay (default: 16)
|
||||
|
||||
DOOR1_SENSOR_PIN - GPIO pin for door 1 sensor (default: 14)
|
||||
DOOR2_SENSOR_PIN - GPIO pin for door 2 sensor (default: 13)
|
||||
|
||||
PRESS_COOLDOWN - Seconds between button presses (default: 2)
|
||||
|
||||
================================================================================
|
||||
SAFETY FEATURES
|
||||
================================================================================
|
||||
|
||||
1. PRESS COOLDOWN
|
||||
- Prevents rapid repeated presses
|
||||
- 2-second cooldown between toggles
|
||||
- Displays warning message if pressed too quickly
|
||||
|
||||
2. PARALLEL WIRING
|
||||
- Original wall buttons remain functional
|
||||
- No loss of manual control
|
||||
|
||||
3. LOW-VOLTAGE OPERATION
|
||||
- Relay contacts handle the garage door opener's low-voltage circuit
|
||||
- No mains voltage involved in control circuit
|
||||
|
||||
================================================================================
|
||||
TESTING PROCEDURE
|
||||
================================================================================
|
||||
|
||||
1. Upload config.py and main.py to Pico 2
|
||||
2. Wire relay module to Pico (5V, GND, GP15, GP16)
|
||||
3. Connect relay to one garage door opener (test with door 1 first)
|
||||
4. Power on Pico and check serial output for IP address
|
||||
5. Navigate to http://<STATIC_IP> in browser
|
||||
6. Test toggle button - door should respond
|
||||
7. Verify wall button still works
|
||||
8. Repeat for door 2
|
||||
|
||||
================================================================================
|
||||
FAQ
|
||||
================================================================================
|
||||
|
||||
Q: The relay clicks but the door doesn't respond
|
||||
A: Check wiring - ensure COM and NO terminals are used, not NC
|
||||
|
||||
Q: Door status shows wrong
|
||||
A: Check sensor wiring or disable sensors in code (remove sensor pins)
|
||||
|
||||
Q: Can't connect to web interface
|
||||
A: Verify STATIC_IP is in your network's subnet and not conflicting
|
||||
|
||||
Q: Button press has no effect
|
||||
A: Verify RELAY_ON/RELAY_OFF values match your relay (try swapping 0/1)
|
||||
|
||||
================================================================================
|
||||
21
config.py
Normal file
21
config.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
WIFI_SSID = "24isenough"
|
||||
WIFI_PASSWORD = "UltimateDrums!"
|
||||
|
||||
STATIC_IP = "192.168.1.63"
|
||||
SUBNET = "255.255.255.0"
|
||||
GATEWAY = "192.168.1.254"
|
||||
DNS = "192.168.1.254"
|
||||
|
||||
RELAY1_PIN = 15
|
||||
RELAY2_PIN = 16
|
||||
|
||||
DOOR1_SENSOR_PIN = 14
|
||||
DOOR2_SENSOR_PIN = 13
|
||||
|
||||
RELAY_ON = 0
|
||||
RELAY_OFF = 1
|
||||
|
||||
DOOR_OPEN = 1
|
||||
DOOR_CLOSED = 0
|
||||
|
||||
PRESS_COOLDOWN = 2
|
||||
238
main.py
Normal file
238
main.py
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import network
|
||||
import socket
|
||||
import time
|
||||
import machine
|
||||
import config
|
||||
from machine import Pin
|
||||
|
||||
relay1 = Pin(config.RELAY1_PIN, Pin.OUT)
|
||||
relay2 = Pin(config.RELAY2_PIN, Pin.OUT)
|
||||
door_sensor1 = Pin(config.DOOR1_SENSOR_PIN, Pin.IN, Pin.PULL_UP)
|
||||
door_sensor2 = Pin(config.DOOR2_SENSOR_PIN, Pin.IN, Pin.PULL_UP)
|
||||
|
||||
relay1.value(config.RELAY_OFF)
|
||||
relay2.value(config.RELAY_OFF)
|
||||
|
||||
last_press_time = {"door1": 0, "door2": 0}
|
||||
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
wlan.connect(config.WIFI_SSID, config.WIFI_PASSWORD)
|
||||
|
||||
max_retries = 30
|
||||
while not wlan.isconnected() and max_retries > 0:
|
||||
time.sleep(1)
|
||||
max_retries -= 1
|
||||
|
||||
if wlan.isconnected():
|
||||
wlan.ifconfig((config.STATIC_IP, config.SUBNET, config.GATEWAY, config.DNS))
|
||||
ip_address = config.STATIC_IP
|
||||
else:
|
||||
ip_address = None
|
||||
|
||||
|
||||
def toggle_door(door_num):
|
||||
current_time = int(time.time())
|
||||
door_key = f"door{door_num}"
|
||||
|
||||
if current_time - last_press_time[door_key] < config.PRESS_COOLDOWN:
|
||||
return False
|
||||
|
||||
last_press_time[door_key] = current_time
|
||||
|
||||
if door_num == 1:
|
||||
relay1.value(config.RELAY_ON)
|
||||
else:
|
||||
relay2.value(config.RELAY_ON)
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
if door_num == 1:
|
||||
relay1.value(config.RELAY_OFF)
|
||||
else:
|
||||
relay2.value(config.RELAY_OFF)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_door_status(door_num):
|
||||
if door_num == 1:
|
||||
sensor = door_sensor1
|
||||
else:
|
||||
sensor = door_sensor2
|
||||
return "open" if sensor.value() == config.DOOR_OPEN else "closed"
|
||||
|
||||
|
||||
def get_html(status1, status2, msg="", client_ip=""):
|
||||
return (
|
||||
"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Garage Door Control</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; text-align: center; padding: 30px; background-color: #f5f5f5; }
|
||||
.door-section { background: white; padding: 20px; margin: 20px auto; max-width: 400px; border-radius: 10px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
||||
.status { font-size: 20px; margin: 15px; padding: 15px; border-radius: 8px; }
|
||||
.open { background-color: #90EE90; }
|
||||
.closed { background-color: #FFB6C1; }
|
||||
button { font-size: 18px; padding: 15px 30px; cursor: pointer; background-color: #4CAF50; color: white; border: none; border-radius: 5px; }
|
||||
button:hover { background-color: #45a049; }
|
||||
button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
||||
.ip { margin-top: 30px; color: #666; }
|
||||
.message { color: #ff6600; font-size: 16px; margin: 10px; }
|
||||
.log-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 30px auto;
|
||||
max-width: 600px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
height: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.log-section h2 { margin-top: 0; }
|
||||
.log-box {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
resize: none;
|
||||
background-color: #1e1e1e;
|
||||
color: #00ff00;
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
text-align: left;
|
||||
}
|
||||
.log-entry { margin: 2px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Garage Door Control</h1>
|
||||
<div class="door-section">
|
||||
<h2>Door 1</h2>
|
||||
<div class="status """
|
||||
+ status1
|
||||
+ """">
|
||||
Door is """
|
||||
+ status1.upper()
|
||||
+ """
|
||||
</div>
|
||||
<form method="POST">
|
||||
<button type="submit" name="action" value="toggle1">Toggle Door 1</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="door-section">
|
||||
<h2>Door 2</h2>
|
||||
<div class="status """
|
||||
+ status2
|
||||
+ """">
|
||||
Door is """
|
||||
+ status2.upper()
|
||||
+ """
|
||||
</div>
|
||||
<form method="POST">
|
||||
<button type="submit" name="action" value="toggle2">Toggle Door 2</button>
|
||||
</form>
|
||||
</div>
|
||||
"""
|
||||
+ ('<div class="message">' + msg + "</div>" if msg else "")
|
||||
+ """
|
||||
<div class="log-section">
|
||||
<h2>Activity Log</h2>
|
||||
<textarea id="logBox" class="log-box" readonly></textarea>
|
||||
</div>
|
||||
<div class="ip">IP: """
|
||||
+ (ip_address or "Not connected")
|
||||
+ """</div>
|
||||
<script>
|
||||
var clientIP = \""""
|
||||
+ client_ip
|
||||
+ """\";
|
||||
|
||||
function loadLog() {
|
||||
var log = localStorage.getItem('garageDoorLog') || '';
|
||||
document.getElementById('logBox').value = log;
|
||||
document.getElementById('logBox').scrollTop = document.getElementById('logBox').scrollHeight;
|
||||
}
|
||||
|
||||
function addLogEntry(door, ip) {
|
||||
var now = new Date();
|
||||
var timestamp = now.toLocaleString();
|
||||
var entry = '[' + timestamp + '] Door ' + door + ' toggled from ' + ip + '\\n';
|
||||
|
||||
var log = localStorage.getItem('garageDoorLog') || '';
|
||||
log = entry + log;
|
||||
|
||||
var lines = log.split('\\n').filter(function(line) { return line.trim(); });
|
||||
if (lines.length > 100) {
|
||||
log = lines.slice(0, 100).join('\\n') + '\\n';
|
||||
}
|
||||
|
||||
localStorage.setItem('garageDoorLog', log);
|
||||
loadLog();
|
||||
}
|
||||
|
||||
loadLog();
|
||||
|
||||
document.querySelectorAll('form').forEach(function(form) {
|
||||
form.addEventListener('submit', function() {
|
||||
var action = form.querySelector('button').value;
|
||||
var doorNum = action.replace('toggle', '');
|
||||
addLogEntry(doorNum, clientIP);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
)
|
||||
|
||||
|
||||
def web_server():
|
||||
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
|
||||
s = socket.socket()
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(addr)
|
||||
s.listen(1)
|
||||
|
||||
while True:
|
||||
try:
|
||||
cl, addr = s.accept()
|
||||
client_ip = addr[0]
|
||||
request = cl.recv(1024)
|
||||
request_str = request.decode("utf-8")
|
||||
|
||||
message = ""
|
||||
if "POST" in request_str:
|
||||
if "toggle1" in request_str:
|
||||
if toggle_door(1):
|
||||
message = ""
|
||||
else:
|
||||
message = "Please wait before pressing again"
|
||||
elif "toggle2" in request_str:
|
||||
if toggle_door(2):
|
||||
message = ""
|
||||
else:
|
||||
message = "Please wait before pressing again"
|
||||
|
||||
status1 = get_door_status(1)
|
||||
status2 = get_door_status(2)
|
||||
response = get_html(status1, status2, message, client_ip)
|
||||
|
||||
cl.send("HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n")
|
||||
cl.send(response)
|
||||
cl.close()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if ip_address:
|
||||
print("Connected, IP:", ip_address)
|
||||
web_server()
|
||||
else:
|
||||
print("Failed to connect to WiFi")
|
||||
Loading…
Add table
Reference in a new issue