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

树莓派上如何通过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)

注意事项

  1. 按钮接线:代码默认按钮接GPIO16和GND,实际使用时请根据你的硬件接线修改BUTTON_PIN的值。
  2. 灯光电平逻辑:若你的电路是高电平点亮灯光,只需将代码中GPIO.HIGH和GPIO.LOW的位置互换即可。
  3. 音频分块大小:chunk_size控制停止响应的灵敏度,值越小响应越快,但可能增加播放卡顿,可根据音频文件大小调整。
  4. GPIO清理:无论是按钮中断还是手动终止脚本,都会执行GPIO.cleanup(),避免引脚状态异常影响下次运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:35:22