树莓派后台运行的Python温感上传InfluxDB脚本随机停止问题
问题根因分析
- 脚本异常捕获缺失:现有代码仅捕获了键盘中断信号,网络波动、串口临时断开、InfluxDB服务不可用等场景都会触发未捕获异常,直接导致进程终止
- cron配置错误:你填写的
* * * * *是每分钟执行一次,并非每小时执行;且配置中未做重复运行校验,多次启动会导致串口被占用,同时cron默认环境变量可能缺失screen、Python的执行路径,也会导致命令执行失败 - screen不是可靠的后台托管方案:screen会话会被系统资源限制、会话回收机制意外终止,本身没有自动重启能力,不适合长期运行的后台服务
修复步骤
1. 优化Python脚本稳定性
在原有代码基础上补充通用异常捕获,避免偶发错误直接终止进程,修改后的核心逻辑如下:
#!/usr/bin/python # 原有导入部分新增time模块 import ConfigParser import serial import os, sys import requests import datetime import optparse import time # 新增导入 if __name__ == "__main__": parser = optparse.OptionParser(usage="%prog [-d] [--version]", version = "prog 1.2.0") parser.add_option('-d', '--debug', dest='debug', default=False, action='store_true',) options, remainder = parser.parse_args() c = ConfigParser.ConfigParser() c.read("/etc/gateway.conf") baud = c.getint('system','baud') serial_port = c.get('system', 'port') if not os.path.exists(serial_port): print 'Serial port %s not found' % serial_port sys.exit() ser = serial.Serial(serial_port, baud) DayOfMonth = datetime.datetime.utcnow().day ls_fileopen = False while True: try: data_in = ser.readline() utcnow = datetime.datetime.utcnow() timestamp = utcnow.strftime("%s") if options.debug: print data_in while data_in[-1] in ['\r', '\n']: data_in = data_in[:-1] z = data_in.split(' ') csv = ','.join(z[1:]) for sect in c.sections(): if sect=='emoncms' and c.getboolean(sect,'enabled'): #EMONCMS hostname = c.get(sect, 'hostname') node = c.get(sect, 'node') apikey = c.get(sect, 'apikey') url = "http://%s/input/post?apikey=%s&node=%s&csv=%s" % (hostname, apikey, node, csv) if options.debug: print url r = requests.post(url, timeout=10) # 新增超时设置避免永久挂起 if options.debug: print r if sect=='influxdb' and c.getboolean(sect,'enabled'): #INFLUXDB t = timestamp + '000000000' url = c.get(sect, 'url') dbname = c.get(sect, 'dbname') user = c.get(sect, 'user') passwd = c.get(sect, 'passwd') measurement = c.get(sect, 'measurement') params = {'db': dbname, 'u': user, 'p': passwd} i = 0 payload = "" for zz in z[1:]: i += 1 if zz!="": payload += "%s,channel=%02d value=%s %s\n" % (measurement, i, zz,t) if options.debug: print payload r = requests.post(url, params=params, data=payload, timeout=10) # 新增超时设置 if options.debug: print r.text if sect=='localsave' and c.getboolean(sect,'enabled'): # LOCALSAVE ls_dir = c.get(sect, 'directory') filename = ls_dir+'/'+timestamp+'.csv' if not ls_fileopen: f = open(filename,'w') ls_fileopen = True if DayOfMonth != utcnow.day: DayOfMonth = utcnow.day if ls_fileopen: f.close() f = open(filename,'w') ls_fileopen = True f.write(timestamp+','+csv+'\n') except KeyboardInterrupt: if ls_fileopen: f.close() print "Terminating." break # 新增通用异常捕获,出错后自动重试 except Exception as e: print "[%s] Run error: %s" % (datetime.datetime.utcnow(), str(e)) time.sleep(10) # 尝试重置串口连接 try: ser.close() ser = serial.Serial(serial_port, baud) except: pass
修改完成后给脚本加执行权限:chmod +x /home/pi/lcl-gateway.py
2. 用systemd托管后台服务(推荐方案)
systemd是树莓派默认自带的进程管理工具,支持崩溃自动重启、开机自启,比screen更稳定可靠。
执行sudo nano /etc/systemd/system/lcl-gateway.service创建服务配置文件,写入以下内容:
[Unit] Description=LCL Temperature Gateway Service After=network.target [Service] User=pi WorkingDirectory=/home/pi ExecStart=/usr/bin/python /home/pi/lcl-gateway.py Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=lcl-gateway [Install] WantedBy=multi-user.target
保存退出后执行以下命令启用服务:
sudo systemctl daemon-reloadsudo systemctl enable lcl-gatewaysudo systemctl start lcl-gateway
后续可以用systemctl status lcl-gateway查看服务状态,journalctl -u lcl-gateway -f查看运行日志。
3. 若坚持使用cron的正确配置
每小时执行的cron表达式为0 * * * *,需要先校验进程是否存在,避免重复启动,执行sudo crontab -e写入以下配置:
0 * * * * /usr/bin/pgrep -f "lcl-gateway.py" > /dev/null || /usr/bin/screen -d -m /usr/bin/python /home/pi/lcl-gateway.py
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

