基于Flask的树莓派3D圣诞树网页控制功能异常求解
问题原因
- 所有效果路由中都包含
while True无限循环或pause()阻塞调用,Flask默认单线程处理请求时会直接卡死请求线程,即使开启了多线程模式,旧效果的循环没有终止机制,会一直在后台运行,导致多个效果逻辑同时操作LED,出现效果混杂的情况 GPIO.cleanup()执行后没有重新初始化LEDBoard对象,导致后续调用效果时无法正常控制GPIO引脚- Flicker效果中设置的
led.source没有主动清除机制,即使调用关灯逻辑,引脚的随机输出源仍会持续工作
修复方案
核心改动逻辑:
- 引入全局状态标记控制效果运行,每次切换效果前先终止上一个运行的效果,清理LED状态
- 所有效果逻辑放到独立后台线程运行,不阻塞Flask的请求处理流程
- 修复Reset逻辑,清理GPIO后重新初始化LEDBoard对象
- 把所有无限循环的终止条件绑定到全局停止标记,避免线程无法退出
修改后完整app2.py代码
import RPi.GPIO as GPIO import random import threading from random import randint from gpiozero.tools import random_values from flask import Flask, render_template, request from gpiozero import LEDBoard from time import sleep app = Flask(__name__) GPIO.setmode(GPIO.BCM) tree = LEDBoard(*range(2,28),pwm=True) # 全局状态控制 current_effect_thread = None stop_flag = False thread_lock = threading.Lock() def stop_current_effect(): global stop_flag, current_effect_thread with thread_lock: if current_effect_thread and current_effect_thread.is_alive(): # 发送停止信号 stop_flag = True # 等待旧线程退出 current_effect_thread.join(timeout=2) # 重置状态 stop_flag = False current_effect_thread = None # 清理LED状态 tree.off() # 清除flicker设置的输出源 for led in tree: led.source = None @app.route("/") def home2(): return render_template("home2.html") @app.route("/flicker/", methods=['POST', 'GET']) def flicker(): global current_effect_thread stop_current_effect() def run_flicker(): for led in tree: if stop_flag: return led.source_delay = random.uniform(0.1, 0.9) led.source = random_values() sleep(1) # 替换原pause(),增加停止检测 while not stop_flag: sleep(0.1) with thread_lock: current_effect_thread = threading.Thread(target=run_flicker, daemon=True) current_effect_thread.start() return render_template("home2.html") @app.route("/randomOnOff/", methods=['POST', 'GET']) def randomOnOff(): global current_effect_thread stop_current_effect() def run_random(): while not stop_flag: for led in tree: if stop_flag: return s = randint(0,1) led.on() if s == 1 else led.off() sleep(1) with thread_lock: current_effect_thread = threading.Thread(target=run_random, daemon=True) current_effect_thread.start() return render_template("home2.html") @app.route("/allFlash/", methods=['POST', 'GET']) def allFlash(): global current_effect_thread stop_current_effect() def run_flash(): while not stop_flag: if stop_flag: return tree.on() sleep(1) tree.off() sleep(1) with thread_lock: current_effect_thread = threading.Thread(target=run_flash, daemon=True) current_effect_thread.start() return render_template("home2.html") @app.route("/upAndDown/", methods=['POST', 'GET']) def upAndDown(): global current_effect_thread stop_current_effect() def run_updown(): while not stop_flag: if stop_flag: return tree.off() order = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0] for item in order: if stop_flag: return tree[item].on() sleep(0.1) order.reverse() sleep(0.5) for item in order: if stop_flag: return tree[item].off() sleep(0.1) with thread_lock: current_effect_thread = threading.Thread(target=run_updown, daemon=True) current_effect_thread.start() return render_template("home2.html") @app.route("/reset/", methods=['POST', 'GET']) def tree_reset(): global tree stop_current_effect() GPIO.cleanup() # 重新初始化LEDBoard tree = LEDBoard(*range(2,28),pwm=True) return render_template("home2.html") @app.route("/off/", methods=['POST', 'GET']) def tree_off(): stop_current_effect() tree.off() return render_template("home2.html") if __name__ == "__main__": app.run(host='0.0.0.0', port=5000, debug=True)
前端说明
原有前端模板不需要修改即可正常运行,若不需要点击按钮后刷新页面,可以自行添加AJAX请求逻辑替换表单提交即可。
内容的提问来源于stack exchange,提问作者Babeeshka
相关产品推荐
相关产品推荐

