rebuilt the arduino code and website, since i had lost it like an idiot.

This commit is contained in:
Lucca Pirovano
2025-11-15 18:55:41 -05:00
parent 4ca73343a5
commit 81f4a6d1c0
4 changed files with 125 additions and 25 deletions
+75 -25
View File
@@ -1,13 +1,21 @@
#!/usr/bin/env python3
from flask import Flask, render_template, jsonify
"""
Flask web UI for ultra-responsive fan control.
Works with the Arduino sketch above (pin 11 PWM).
"""
from flask import Flask, render_template, jsonify, request
import serial
import serial.tools.list_ports
import threading
import time
import sys
app = Flask(__name__)
# --- Auto-find Arduino ---
# -------------------------------------------------
# Auto-detect Arduino
# -------------------------------------------------
def find_arduino():
for p in serial.tools.list_ports.comports():
if 'Arduino' in p.description or 'ACM' in p.device or 'USB' in p.device:
@@ -17,57 +25,99 @@ def find_arduino():
PORT = find_arduino()
BAUD = 115200
# Global serial object
# Global serial
ser = None
serial_lock = threading.Lock()
# -------------------------------------------------
# Serial init with retry on "busy"
# -------------------------------------------------
def init_serial():
global ser
try:
ser = serial.Serial(PORT, BAUD, timeout=0)
time.sleep(2) # wait for Arduino reset
print(f"Connected to {PORT}")
except Exception as e:
print(f"Serial error: {e}")
ser = None
for attempt in range(6):
try:
ser = serial.Serial(PORT, BAUD, timeout=0)
time.sleep(2) # Arduino reset
print(f"[OK] Connected to {PORT}")
return
except serial.SerialException as e:
if 'Device or resource busy' in str(e):
print(f"[BUSY] Port busy, retry {attempt+1}/6 in 1s...")
time.sleep(1)
else:
print(f"[ERROR] Serial: {e}")
sys.exit(1)
sys.exit("[FATAL] Could not open serial port after retries")
init_serial()
# --- Send command safely ---
def send_command(cmd):
# -------------------------------------------------
# Safe send
# -------------------------------------------------
def send_command(cmd: str):
with serial_lock:
if ser and ser.is_open:
try:
ser.write(cmd.encode())
ser.flush()
except:
print("Serial write failed")
else:
print("Serial not open")
except Exception as e:
print(f"[WRITE FAIL] {e}")
# -------------------------------------------------
# Background reader pushes feedback to browser via SSE
# -------------------------------------------------
from collections import deque
feedback_buffer = deque(maxlen=50) # last 50 messages
# --- Background reader (optional: show live feedback) ---
def serial_reader():
while True:
if ser and ser.in_waiting:
line = ser.readline().decode(errors='ignore').strip()
if line:
print(f"Arduino: {line}")
time.sleep(0.01)
try:
line = ser.readline().decode(errors='ignore').strip()
if line:
feedback_buffer.append(line)
except:
pass
time.sleep(0.005)
threading.Thread(target=serial_reader, daemon=True).start()
# --- Routes ---
# -------------------------------------------------
# Routes
# -------------------------------------------------
@app.route('/')
def index():
return render_template('index.html')
@app.route('/cmd/<key>')
def command(key):
if len(key) == 1 and key in '0123456789fFsS':
if len(key) == 1 and key.lower() in '0123456789fs':
send_command(key)
return jsonify(status="ok", key=key)
return jsonify(status="invalid"), 400
# -------------------------------------------------
# Server-Sent Events (SSE) live Arduino feedback
# -------------------------------------------------
from flask import Response
@app.route('/stream')
def stream():
def event_stream():
last_len = 0
while True:
if len(feedback_buffer) > last_len:
msg = feedback_buffer[-1]
last_len = len(feedback_buffer)
yield f"data: {msg}\n\n"
time.sleep(0.05)
return Response(event_stream(), mimetype="text/event-stream")
# -------------------------------------------------
# Run
# -------------------------------------------------
if __name__ == '__main__':
# Allow access from your phone/tablet on same WiFi
app.run(host='0.0.0.0', port=5000, debug=False)
# Accessible from phone/tablet on same WiFi
print(f"\nWeb UI: http://YOUR_IP:5000")
print(" (Find YOUR_IP with: ip addr show | grep inet)")
app.run(host='0.0.0.0', port=5000, debug=False, threaded=True)
@@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}
@@ -0,0 +1,42 @@
// Fan control via Serial - works with ultra-responsive Python script
// Pin 11 = PWM output to fan (use transistor/MOSFET/driver for high current!)
const int FAN_PIN = 11;
void setup() {
Serial.begin(115200);
pinMode(FAN_PIN, OUTPUT);
analogWrite(FAN_PIN, 0); // Start stopped
Serial.println("Fan controller ready. Use 0-9, f=full, s=stop");
}
void loop() {
if (Serial.available() > 0) {
char cmd = Serial.read();
int speed = 0; // 0 to 255 for analogWrite
if (cmd >= '0' && cmd <= '9') {
speed = map(cmd - '0', 0, 9, 0, 255); // 0-9 → 0-255
}
else if (cmd == 'f' || cmd == 'F') {
speed = 255;
}
else if (cmd == 's' || cmd == 'S') {
speed = 0;
}
else {
// Ignore invalid commands, but optionally echo
Serial.print("?");
return;
}
// Apply speed
analogWrite(FAN_PIN, speed);
// Send feedback: percentage
int percent = map(speed, 0, 255, 0, 100);
Serial.print(percent);
Serial.println("%");
}
}