You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Flask的树莓派3D圣诞树网页控制功能异常求解

问题原因
  • 所有效果路由中都包含while True无限循环或pause()阻塞调用,Flask默认单线程处理请求时会直接卡死请求线程,即使开启了多线程模式,旧效果的循环没有终止机制,会一直在后台运行,导致多个效果逻辑同时操作LED,出现效果混杂的情况
  • GPIO.cleanup()执行后没有重新初始化LEDBoard对象,导致后续调用效果时无法正常控制GPIO引脚
  • Flicker效果中设置的led.source没有主动清除机制,即使调用关灯逻辑,引脚的随机输出源仍会持续工作
修复方案

核心改动逻辑:

  1. 引入全局状态标记控制效果运行,每次切换效果前先终止上一个运行的效果,清理LED状态
  2. 所有效果逻辑放到独立后台线程运行,不阻塞Flask的请求处理流程
  3. 修复Reset逻辑,清理GPIO后重新初始化LEDBoard对象
  4. 把所有无限循环的终止条件绑定到全局停止标记,避免线程无法退出

修改后完整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 10:45:03