first commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
from flask import Flask, render_template, jsonify
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import threading
|
||||
import time
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# --- Auto-find 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:
|
||||
return p.device
|
||||
return '/dev/ttyACM0'
|
||||
|
||||
PORT = find_arduino()
|
||||
BAUD = 115200
|
||||
|
||||
# Global serial object
|
||||
ser = None
|
||||
serial_lock = threading.Lock()
|
||||
|
||||
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
|
||||
|
||||
init_serial()
|
||||
|
||||
# --- Send command safely ---
|
||||
def send_command(cmd):
|
||||
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")
|
||||
|
||||
# --- 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)
|
||||
|
||||
threading.Thread(target=serial_reader, daemon=True).start()
|
||||
|
||||
# --- 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':
|
||||
send_command(key)
|
||||
return jsonify(status="ok", key=key)
|
||||
return jsonify(status="invalid"), 400
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Allow access from your phone/tablet on same WiFi
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ultra-responsive fan control – every key press is sent instantly.
|
||||
Uses raw stdin + select() with zero timeout.
|
||||
"""
|
||||
import serial
|
||||
import sys
|
||||
import select
|
||||
import termios
|
||||
import tty
|
||||
import serial.tools.list_ports
|
||||
|
||||
# -------------------------------------------------
|
||||
# Auto-detect Arduino port
|
||||
# -------------------------------------------------
|
||||
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:
|
||||
return p.device
|
||||
return None
|
||||
|
||||
PORT = find_arduino() or '/dev/ttyACM0'
|
||||
BAUD = 115200
|
||||
|
||||
# -------------------------------------------------
|
||||
def raw_stdin():
|
||||
"""Put stdin into raw mode and return old settings."""
|
||||
fd = sys.stdin.fileno()
|
||||
old = termios.tcgetattr(fd)
|
||||
tty.setraw(fd)
|
||||
return old
|
||||
|
||||
# -------------------------------------------------
|
||||
def main():
|
||||
# --- open serial -------------------------------------------------
|
||||
try:
|
||||
ser = serial.Serial(PORT, BAUD, timeout=0) # non-blocking
|
||||
except Exception as e:
|
||||
sys.exit(f"Cannot open {PORT}: {e}")
|
||||
|
||||
print(f"Connected to {PORT}")
|
||||
print("Keys: 0-9, f=full, s=stop, q=quit (every press is sent immediately)")
|
||||
|
||||
old_term = raw_stdin()
|
||||
try:
|
||||
while True:
|
||||
# ---- non-blocking read from keyboard (0-second timeout) ----
|
||||
rlist, _, _ = select.select([sys.stdin], [], [], 0)
|
||||
if rlist:
|
||||
key = sys.stdin.read(1) # exactly one char
|
||||
if key.lower() == 'q':
|
||||
print("\nBye!")
|
||||
break
|
||||
ser.write(key.encode()) # send raw byte
|
||||
print(f" → {key}", end='', flush=True)
|
||||
|
||||
# ---- echo any reply from Arduino (non-blocking) ----
|
||||
if ser.in_waiting:
|
||||
reply = ser.read(ser.in_waiting).decode(errors='ignore')
|
||||
print(reply, end='', flush=True)
|
||||
|
||||
finally:
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_term)
|
||||
ser.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,14 @@
|
||||
const int FAN_PIN = 9; // Pin connected to the FAN
|
||||
|
||||
void setup() {
|
||||
pinMode(FAN_PIN, OUTPUT); // Set the FAN pin as an output
|
||||
}
|
||||
|
||||
void loop() {
|
||||
digitalWrite(FAN_PIN, HIGH); // Turn the FAN on
|
||||
Serial.print("Fan-High");
|
||||
delay(2000); // Wait for 1 second
|
||||
digitalWrite(FAN_PIN, LOW); // Turn the FAN off
|
||||
Serial.print("Fan-Low");
|
||||
delay(2000); // Wait for 1 second
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Fan Speed Control</title>
|
||||
<style>
|
||||
body { font-family: Arial; text-align: center; padding: 2rem; background: #f4f4f4; }
|
||||
.btn {
|
||||
font-size: 1.5rem; padding: 1rem 1.5rem; margin: 0.5rem;
|
||||
background: #007bff; color: white; border: none; border-radius: 8px;
|
||||
cursor: pointer; width: 80px;
|
||||
}
|
||||
.btn:hover { background: #0056b3; }
|
||||
.btn.stop { background: #dc3545; }
|
||||
.btn.stop:hover { background: #b02a37; }
|
||||
.btn.full { background: #28a745; }
|
||||
.btn.full:hover { background: #218838; }
|
||||
#status { margin-top: 1rem; font-size: 1.2rem; color: #333; }
|
||||
.keys { margin-top: 2rem; font-size: 1rem; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Fan Speed Control</h1>
|
||||
<p>Click buttons or press keys: <strong>0–9, f, s</strong></p>
|
||||
|
||||
<div>
|
||||
<button class="btn stop" onclick="send('s')">S<br>Stop</button>
|
||||
<button class="btn" onclick="send('1')">1<br>~11%</button>
|
||||
<button class="btn" onclick="send('2')">2<br>~20%</button>
|
||||
<button class="btn" onclick="send('3')">3<br>~30%</button>
|
||||
<button class="btn" onclick="send('4')">4<br>~40%</button>
|
||||
<button class="btn" onclick="send('5')">5<br>50%</button>
|
||||
<button class="btn" onclick="send('6')">6<br>~60%</button>
|
||||
<button class="btn" onclick="send('7')">7<br>~70%</button>
|
||||
<button class="btn" onclick="send('8')">8<br>~80%</button>
|
||||
<button class="btn" onclick="send('9')">9<br>~90%</button>
|
||||
<button class="btn full" onclick="send('f')">F<br>Full</button>
|
||||
</div>
|
||||
|
||||
<div id="status">Ready. Press keys or click buttons.</div>
|
||||
<div class="keys">Keyboard: 0–9, f = full, s = stop</div>
|
||||
|
||||
<script>
|
||||
function send(key) {
|
||||
fetch(`/cmd/${key}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'ok') {
|
||||
document.getElementById('status').textContent = `Sent: ${key.toUpperCase()} → Fan updated`;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('status').textContent = 'Error: Is Arduino connected?';
|
||||
});
|
||||
}
|
||||
|
||||
// Keyboard control
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const k = e.key.toLowerCase();
|
||||
if ('0123456789fs'.includes(k)) {
|
||||
e.preventDefault();
|
||||
send(k);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user