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

树莓派Zero W通过Flask网页控制LED灯带循环模式的问题

解决树莓派Flask控制LED模式循环运行的问题

嘿,很高兴看到你在树莓派Zero W上折腾LED灯带项目!我完全明白你的困扰——现在点击网页按钮只能让模式跑一次,没法持续循环直到你发出停止或者切换指令对吧?咱们一步步来搞定这个问题。

问题根源分析

你当前的代码里,用execnet调用Python2函数是同步阻塞的:每次调用callPython2,Flask都会等着那个Python2函数执行完才会返回网页响应。而如果你的runTheater这类模式函数是一次性执行完就结束的(没有循环逻辑),那自然只会跑一次就停了。另外,现在的架构也没法在后台持续运行模式,更没法中断正在运行的模式来切换新的。

解决方案步骤

1. 改造Python2的模式函数,支持循环和终止

首先要在你的lakeboot.py里给每个模式函数加上循环逻辑,并且用一个全局的“停止信号”来控制循环是否继续。推荐用threading.Event(比全局变量更安全):

# lakeboot.py (Python2)
import time
import threading
from neopixel import Adafruit_NeoPixel, Color

# 初始化LED灯带(根据你的硬件参数调整)
LED_COUNT = 16
LED_PIN = 18
LED_FREQ_HZ = 800000
LED_DMA = 10
LED_BRIGHTNESS = 255
LED_INVERT = False
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS)
strip.begin()

# 全局停止事件,用来控制所有模式的循环
stop_event = threading.Event()

def theaterChase(strip, color, wait_ms=50):
    # 你的Theater Chase模式单次执行逻辑,这里保留你原来的代码
    for q in range(3):
        if stop_event.is_set():
            return  # 如果收到停止信号,立即退出
        for i in range(0, strip.numPixels(), 3):
            strip.setPixelColor(i+q, color)
        strip.show()
        time.sleep(wait_ms/1000.0)
        for i in range(0, strip.numPixels(), 3):
            strip.setPixelColor(i+q, 0)

def runTheater():
    stop_event.clear()  # 启动前重置停止信号
    while not stop_event.is_set():
        theaterChase(strip, Color(127, 127, 127))
        time.sleep(0.1)  # 小延迟避免CPU占满

def startUp():
    stop_event.clear()
    # 你的BootUp模式循环逻辑
    while not stop_event.is_set():
        # 这里放原来的BootUp单次执行代码,比如渐变、闪烁等
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, Color(0, 255, 0))
        strip.show()
        time.sleep(0.5)
        if stop_event.is_set():
            break
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, Color(255, 0, 0))
        strip.show()
        time.sleep(0.5)

def turnOff():
    stop_event.set()  # 发出停止信号
    # 关闭所有LED
    for i in range(strip.numPixels()):
        strip.setPixelColor(i, Color(0,0,0))
    strip.show()

2. 修改Flask代码,支持后台运行和模式切换

原来的Flask代码是同步调用execnet,会阻塞请求,而且没法终止正在运行的模式。我们要改成后台线程调用,并且跟踪当前运行的Python2进程,确保切换/停止时能终止旧进程:

# webPage.py (Python3)
from flask import Flask, render_template, request
import execnet
import threading

CURRENT_PATTERN = "No Pattern Running"
current_gateway = None  # 保存当前的execnet网关,用来终止旧进程
lock = threading.Lock()  # 线程安全锁,避免多请求冲突

app = Flask(__name__)

def run_python2_task(version, module, func):
    """后台运行Python2函数的辅助函数"""
    global current_gateway
    # 先终止之前的Python2进程
    if current_gateway is not None:
        current_gateway.exit()
    # 创建新的网关并执行函数
    gw = execnet.makegateway(f"popen//python=python{version}")
    gw.remote_exec(f"""
from {module} import {func}
{func()}
""")
    # 保存当前网关
    with lock:
        current_gateway = gw

@app.route("/")
def index():
    global CURRENT_PATTERN
    CURRENT_PATTERN = 'BootUp'
    # 后台启动BootUp模式
    threading.Thread(target=run_python2_task, args=("2.7", "lakeboot", "startUp")).start()
    return render_template("index.html", 
                           title='LED Pattern Status',
                           pattern=CURRENT_PATTERN)

@app.route("/<patternName>/<action>")
def action(patternName, action):
    global CURRENT_PATTERN, current_gateway
    with lock:
        if action == 'stop':
            CURRENT_PATTERN = 'No Pattern Running'
            # 停止当前模式并关闭LED
            if current_gateway is not None:
                current_gateway.exit()
                current_gateway = None
            threading.Thread(target=run_python2_task, args=("2.7", "lakeboot", "turnOff")).start()
        else:  # 处理start动作
            # 先停止旧模式
            if current_gateway is not None:
                current_gateway.exit()
                current_gateway = None
            # 设置新模式并启动
            pattern_map = {
                'bootup': ('BootUp', 'startUp'),
                'TheaterChase': ('Theater Chase', 'runTheater'),
                'ledGrn': ('Pattern Three', 'runGreenPattern')  # 假设你的第三个模式函数叫runGreenPattern
            }
            if patternName in pattern_map:
                CURRENT_PATTERN, func_name = pattern_map[patternName]
                threading.Thread(target=run_python2_task, args=("2.7", "lakeboot", func_name)).start()
    return render_template("index.html",
                           title='LED Pattern Status',
                           pattern=CURRENT_PATTERN)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000, debug=True)

3. 修正原代码的语法错误

你原来的代码里有几个小bug,比如:

  • render_templates拼写错误,应该是render_template
  • 字符串引号不匹配(比如'No Pattern Running")
  • 路由里的模板变量引号错误,这些在上面的修正代码里都已经改好了。

额外小贴士

  • 权限问题:运行Flask的时候可能需要加sudo,因为控制LED灯带需要访问树莓派的硬件引脚。
  • 测试顺序:先单独运行lakeboot.py测试模式循环和停止功能,确保没问题再整合到Flask里。
  • 线程安全:如果以后要支持多用户访问,建议进一步优化锁的逻辑,或者用更健壮的进程管理方式(比如subprocess模块)。

内容的提问来源于stack exchange,提问作者zPassmore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:36:22