如何从Node-RED向树莓派终端发送CTRL+C终止Python脚本?
我明白你的需求——给高中生做教程,得让Python脚本在终止时优雅清理GPIO,而不是硬杀导致引脚状态异常。你之前用kill/killall的问题在于默认信号不会触发KeyboardInterrupt,而管道发$'\cc'又有权限问题,下面给你两个可靠的解决方案:
方法一:通过PID发送SIGINT信号(完全模拟CTRL+C,推荐)
这个方法直接对应你想要的“按下CTRL+C”的行为,步骤很清晰:
在Node-RED中记录脚本PID
配置EXEC节点运行你的脚本时,找到并勾选Append PID to msg.payload选项。这样脚本启动后,msg.payload会返回进程的PID(比如类似1234的数字)。存储PID供后续使用
把这个PID存到Node-RED的Flow/Global变量里(用Change节点就能实现),或者用Context节点暂存,方便后面终止脚本时调用。发送SIGINT信号终止脚本
当需要停止脚本时,用另一个EXEC节点执行命令:sudo kill -SIGINT <你存储的PID>这里的
-SIGINT是关键——它就是CTRL+C对应的信号,会触发你脚本里的except KeyboardInterrupt块,自动执行pwm.stop()和GPIO.cleanup()。
方法二:修改Python脚本,兼容更多终止信号
如果觉得管理PID有点麻烦,可以修改你的Python脚本,让它不仅响应SIGINT,还能捕获默认的kill信号(SIGTERM),这样即使普通的kill/killall命令也能触发清理:
修改后的脚本示例:
import RPi.GPIO as GPIO import time import signal import sys pwmPin = 18 ledPin = 23 butPin = 17 dc = 95 # 统一的清理函数 def cleanup(signal_num, frame): pwm.stop() GPIO.cleanup() print("GPIO cleaned up, exiting gracefully") sys.exit(0) # 注册信号处理:同时监听SIGINT(CTRL+C)和SIGTERM(默认kill信号) signal.signal(signal.SIGINT, cleanup) signal.signal(signal.SIGTERM, cleanup) GPIO.setmode(GPIO.BCM) GPIO.setup(ledPin, GPIO.OUT) GPIO.setup(pwmPin, GPIO.OUT) pwm = GPIO.PWM(pwmPin, 50) GPIO.setup(butPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.output(ledPin, GPIO.LOW) pwm.start(dc) print("Press CTRL+C to exit, or use 'kill <PID>' to stop") try: while 1: if GPIO.input(butPin): pwm.ChangeDutyCycle(dc) GPIO.output(ledPin, GPIO.LOW) else: pwm.ChangeDutyCycle(100-dc) GPIO.output(ledPin, GPIO.HIGH) time.sleep(0.075) GPIO.output(ledPin, GPIO.LOW) time.sleep(0.075) except Exception as e: # 捕获意外错误也执行清理 print(f"Unexpected error: {str(e)}") cleanup(None, None)
改完之后,你用sudo kill <PID>或者sudo killall python就能触发清理逻辑了,不用特意指定信号。
为什么你之前的尝试失败了?
kill/killall默认发送的是SIGTERM信号,你的原脚本没有捕获这个信号,所以直接终止进程,不会执行清理代码。echo $'\cc' | sudo python Documents/python/blinker.py的权限错误是因为脚本没有可执行权限(可以用chmod +x Documents/python/blinker.py解决),但即使解决了权限问题,这种方式是给脚本的标准输入发EOF,不是真正的SIGINT,可靠性不如直接发送信号。
内容的提问来源于stack exchange,提问作者Nile Ellis

