树莓派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
相关产品推荐
相关产品推荐

