树莓派上如何通过GPIO按钮中途终止带声光报警的Python脚本?
解决方案
要实现按钮中断声光报警并终止脚本,核心思路是用GPIO事件监听配合线程处理音频播放,避免阻塞主程序导致按钮事件无法响应。以下是具体修改方案:
关键修改点
- 配置按钮GPIO:将按钮引脚设为输入模式,启用内部上拉电阻,防止引脚浮空引发误触发。
- 线程化音频播放:用线程运行音频播放逻辑,让主程序能同时监听按钮事件。
- 按钮事件回调:注册GPIO事件触发的回调函数,在按钮按下时停止音频、关闭灯光并终止整个脚本。
- 全局控制标志:用一个全局变量标记是否需要停止音频播放,线程内循环检查该标志。
修改后的完整代码
import requests import time import RPi.GPIO as GPIO from pydub import AudioSegment from pydub.playback import play import threading import sys # 全局控制标志:标记是否停止音频播放 stop_audio = False # 音频线程对象,用于在回调中终止线程 audio_thread = None def stop_alarm(): """按钮按下时的回调函数:停止音频、关闭灯光、终止脚本""" global stop_audio, audio_thread # 设置停止标志,让音频线程退出 stop_audio = True # 等待音频线程结束(超时1秒强制跳过) if audio_thread and audio_thread.is_alive(): audio_thread.join(timeout=1) # 关闭所有灯光 GPIO.output(26, GPIO.HIGH) GPIO.output(21, GPIO.HIGH) GPIO.output(20, GPIO.HIGH) # 清理GPIO并退出脚本 GPIO.cleanup() sys.exit(0) def play_audio(audio_segment): """线程内运行的音频播放函数,支持中途停止""" global stop_audio stop_audio = False # 逐块播放音频,每100ms检查一次停止标志 chunk_size = 100 for i in range(0, len(audio_segment), chunk_size): if stop_audio: break chunk = audio_segment[i:i+chunk_size] play(chunk) # GPIO初始化 GPIO.setmode(GPIO.BCM) # 灯光引脚设为输出,初始高电平(默认关闭) GPIO.setup(26, GPIO.OUT, initial=GPIO.HIGH) GPIO.setup(20, GPIO.OUT, initial=GPIO.HIGH) GPIO.setup(21, GPIO.OUT, initial=GPIO.HIGH) # 按钮引脚设为输入,启用内部上拉电阻(按下时为低电平) # 请根据实际接线修改引脚号 BUTTON_PIN = 16 GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # 注册按钮按下事件:低电平触发,弹跳时间200ms防误触 GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callback=stop_alarm, bouncetime=200) # 获取初始消息ID response = requests.get('api.call?params') messageID = response['result'][0]['id'] try: while True: time.sleep(30) response = requests.get('api.call?params') data = response['result'][0] if data['id'] != messageID: messageID = data['id'] fanfare = AudioSegment.from_mp3('fanfare.mp3') # 打开灯光(低电平点亮,根据电路逻辑调整) GPIO.output(26, GPIO.LOW) GPIO.output(21, GPIO.LOW) GPIO.output(20, GPIO.LOW) # 启动线程播放音频 audio_thread = threading.Thread(target=play_audio, args=(fanfare,)) audio_thread.start() # 等待音频线程结束(或被按钮中断) audio_thread.join() # 播放结束/中断后关闭灯光 GPIO.output(26, GPIO.HIGH) GPIO.output(21, GPIO.HIGH) GPIO.output(20, GPIO.HIGH) except KeyboardInterrupt: # 捕获Ctrl+C,清理GPIO后退出 GPIO.cleanup() sys.exit(0)
注意事项
- 按钮接线:代码默认按钮接GPIO16和GND,实际使用时请根据你的硬件接线修改
BUTTON_PIN的值。 - 灯光电平逻辑:若你的电路是高电平点亮灯光,只需将代码中
GPIO.HIGH和GPIO.LOW的位置互换即可。 - 音频分块大小:
chunk_size控制停止响应的灵敏度,值越小响应越快,但可能增加播放卡顿,可根据音频文件大小调整。 - GPIO清理:无论是按钮中断还是手动终止脚本,都会执行
GPIO.cleanup(),避免引脚状态异常影响下次运行。
内容的提问来源于stack exchange,提问作者Tom Davies
相关产品推荐
相关产品推荐

