Python LED Server
quart & LED lib? Or Raspi pin lib?
#!/usr/bin/env python3
"""
Quart web server with colour picker + ON/OFF toggle for an RGB LED strip
on a Raspberry Pi 2 B.
"""
import asyncio
from quart import Quart, render_template_string, request, jsonify
import RPi.GPIO as GPIO
# --------------------------- GPIO CONFIG ---------------------------
RED_PIN = 17 # Physical pin 11
GREEN_PIN = 27 # Physical pin 13
BLUE_PIN = 22 # Physical pin 15
PWM_FREQ = 1000 # Hz
GPIO.setmode(GPIO.BCM)
GPIO.setup(RED_PIN, GPIO.OUT)
GPIO.setup(GREEN_PIN, GPIO.OUT)
GPIO.setup(BLUE_PIN, GPIO.OUT)
red_pwm = GPIO.PWM(RED_PIN, PWM_FREQ)
green_pwm = GPIO.PWM(GREEN_PIN, PWM_FREQ)
blue_pwm = GPIO.PWM(BLUE_PIN, PWM_FREQ)
red_pwm.start(0)
green_pwm.start(0)
blue_pwm.start(0)
# --------------------------- GLOBAL STATE -------------------------
# Remember the last colour (as a hex string) and whether the strip is on.
last_colour = "#ff0000"
led_is_on = True # start powered on; change to False if you prefer off
# --------------------------- HELPERS ------------------------------
def hex_to_duty(hex_colour: str):
"""Convert '#RRGGBB' → (r,g,b) duty cycles 0‑100."""
hex_colour = hex_colour.lstrip('#')
r = int(hex_colour[0:2], 16)
g = int(hex_colour[2:4], 16)
b = int(hex_colour[4:6], 16)
return (r / 255 * 100, g / 255 * 100, b / 255 * 100)
def apply_colour(col_hex: str):
"""Write PWM duties – respects the global on/off flag."""
global led_is_on
if not led_is_on:
# Strip is forced off → set all duties to 0
red_pwm.ChangeDutyCycle(0)
green_pwm.ChangeDutyCycle(0)
blue_pwm.ChangeDutyCycle(0)
return
r, g, b = hex_to_duty(col_hex)
red_pwm.ChangeDutyCycle(r)
green_pwm.ChangeDutyCycle(g)
blue_pwm.ChangeDutyCycle(b)
# --------------------------- QUART APP ----------------------------
app = Quart(__name__)
@app.route('/')
async def index():
# Render template with current colour and power state
return await render_template(
"home.html",
colour=last_colour,
power=led_is_on
)
@app.route('/set', methods=['POST'])
async def set_colour():
global last_colour
payload = await request.get_json()
colour = payload.get('colour')
try:
last_colour = colour # remember for later “on” toggles
apply_colour(colour)
return jsonify(success=True)
except Exception as exc:
return jsonify(success=False, error=str(exc)), 400
@app.route('/power', methods=['POST'])
async def power():
"""Toggle the strip on/off."""
global led_is_on
payload = await request.get_json()
state = payload.get('state')
if state not in ('on', 'off'):
return jsonify(success=False, error='Invalid state'), 400
led_is_on = (state == 'on')
# Apply the appropriate duty cycles (either colour or all‑off)
apply_colour(last_colour)
return jsonify(success=True, state=('on' if led_is_on else 'off'))
# --------------------------- CLEANUP -----------------------------
@app.before_serving
async def startup():
print("RGB server started – strip is", "ON" if led_is_on else "OFF")
@app.after_serving
async def cleanup():
print("\nShutting down – turning LEDs off")
red_pwm.ChangeDutyCycle(0)
green_pwm.ChangeDutyCycle(0)
blue_pwm.ChangeDutyCycle(0)
red_pwm.stop()
green_pwm.stop()
blue_pwm.stop()
GPIO.cleanup()
# --------------------------- MAIN --------------------------------
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RGB LED Controller</title>
<style>
body{font-family:sans-serif;text-align:center;margin-top:2rem}
input[type=color]{width:150px;height:150px;border:none;cursor:pointer}
.status{margin-top:1rem;font-size:1.2rem}
button{padding:.6rem 1.2rem;font-size:1rem;margin-top:.8rem}
</style>
</head>
<body>
<h1>RGB LED Strip</h1>
<input type="color" id="picker" value="{{ colour }}">
<div class="status" id="msg">Current colour: {{ colour }}</div>
<button id="toggle">{{ 'Turn OFF' if power else 'Turn ON' }}</button>
<div class="status" id="powerMsg">
Strip is {{ 'ON' if power else 'OFF' }}
</div>
<script>
const picker = document.getElementById('picker');
const msg = document.getElementById('msg');
const toggle = document.getElementById('toggle');
const powerMsg = document.getElementById('powerMsg');
async function setColour(col){
const resp = await fetch('/set', {
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({colour:col})
});
const data = await resp.json();
if(data.success){ msg.textContent = `Current colour: ${col}`; }
else { msg.textContent = `Error: ${data.error}`; }
}
async function togglePower(){
const newState = toggle.textContent.includes('ON') ? 'on' : 'off';
const resp = await fetch('/power', {
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({state:newState})
});
const data = await resp.json();
if(data.success){
// Update UI according to returned state
const on = data.state === 'on';
toggle.textContent = on ? 'Turn OFF' : 'Turn ON';
powerMsg.textContent = `Strip is ${on ? 'ON' : 'OFF'}`;
// If we just turned it on, re‑apply the current colour
if(on){ setColour(picker.value); }
}else{
powerMsg.textContent = `Error: ${data.error}`;
}
}
// Event listeners
picker.addEventListener('input', e=> setColour(e.target.value));
toggle.addEventListener('click', togglePower);
// Initialise with the colour stored on the server
setColour(picker.value);
</script>
</body>
</html>
No comments to display
No comments to display