React Native Expo App如何触发Raspberry Pi运行Python采集数据存入Firebase
实现方案
你现有技术栈完全可以直接实现需求,不需要额外加第三方服务、做内网穿透,核心直接用Firebase实时数据库做信令通道就行,整套逻辑跑通很简单,分三部分配:
树莓派端配置
- 不要做“收到请求再启动Python脚本”的逻辑,延迟高还容易出环境、权限问题,直接写一个Python常驻脚本,配systemd开机自启,后台一直跑着。
- 脚本里接Firebase Admin SDK,初始化后长期监听一个固定的信令节点,比如
/commands/trigger_collect。 - 监听到节点值变为
True时,直接调用你已经写好的串口驱动Arduino采集传感器的逻辑就行,不用改原来的采集代码。 - 采集完成后做三个操作:
- 把带时间戳的传感器数据写入
/sensor_data节点 - 把
/commands/trigger_collect的值重置为False,避免重复触发 - 往
/commands/last_run_status节点写入本次执行结果(成功/失败、错误信息、执行时间)
- 把带时间戳的传感器数据写入
- 注意给串口操作加异常捕获和超时,别因为Arduino响应异常把整个常驻脚本卡挂。
核心代码参考:
import firebase_admin from firebase_admin import db import serial import time from datetime import datetime # 初始化Firebase服务账号 cred = firebase_admin.credentials.Certificate("你的服务账号密钥文件路径.json") firebase_admin.initialize_app(cred, {'databaseURL': '你的Firebase实时数据库地址'}) # 初始化串口连接,端口按你实际的改 ser = serial.Serial('/dev/ttyACM0', 9600, timeout=2) time.sleep(2) # 等串口握手完成 def run_collect(): # 这里直接放你原来写的驱动Arduino读传感器的代码就行 ser.write(b'start_collect\n') time.sleep(1) raw_data = ser.readline().decode().strip() return {"sensor_value": raw_data, "timestamp": int(time.time()*1000)} def trigger_callback(event): if event.data == True: try: data = run_collect() # 写入采集到的传感器数据 db.reference('/sensor_data').push(data) # 重置触发位 db.reference('/commands/trigger_collect').set(False) # 写入成功状态 db.reference('/commands/last_run_status').set({ "code": 0, "msg": "collect success", "run_time": datetime.now().isoformat() }) except Exception as e: # 异常时也要重置触发位,写错误状态 db.reference('/commands/trigger_collect').set(False) db.reference('/commands/last_run_status').set({ "code": -1, "msg": str(e), "run_time": datetime.now().isoformat() }) # 绑定信令节点监听 trigger_ref = db.reference('/commands/trigger_collect') trigger_ref.listen(trigger_callback) # 保持进程常驻 while True: time.sleep(1)
- 脚本写完用systemd配成系统服务,设置异常自动重启,比rc.local、crontab稳很多,记得把运行脚本的用户加到
dialout组,不然会报串口权限错误,不用拿root跑。
Expo移动端配置
- 直接用你项目里已经集成的Firebase JS SDK就行,不需要加额外的原生依赖,不用改Expo的配置。
- 采集按钮的点击事件里,直接把
/commands/trigger_collect节点的值设为True,就完成了触发指令下发。 - 页面挂载的时候监听
/commands/last_run_status节点,收到执行成功的状态就提示用户,同时拉取最新的传感器数据展示就行;收到失败状态就把错误信息弹给用户。
核心代码参考:
import { getDatabase, ref, set, onValue } from "firebase/database"; import { Alert, Button, View } from "react-native"; import { useEffect } from "react"; const db = getDatabase(); export default function CollectPage() { const handleTrigger = async () => { // 触发前可以先查下当前触发位状态,防止重复点击 try { await set(ref(db, 'commands/trigger_collect'), true); Alert.alert("提示", "采集指令已发送,等待结果") } catch (err) { Alert.alert("错误", "指令发送失败:" + err.message) } } useEffect(() => { const statusRef = ref(db, 'commands/last_run_status'); const unsubscribe = onValue(statusRef, (snapshot) => { const status = snapshot.val(); if(!status) return; if(status.code === 0) { Alert.alert("成功", "传感器数据采集完成") // 这里加逻辑拉取/sensor_data下的最新数据渲染 } else if(status.code === -1) { Alert.alert("失败", "采集出错:" + status.msg) } }) return () => unsubscribe(); }, []) return <View><Button title="开始采集" onPress={handleTrigger} /></View> }
- 记得配Firebase数据库安全规则,不要开公开读写,移动端用项目里的Firebase鉴权做权限控制,限制只有登录的合法用户能写触发节点,树莓派端用Admin SDK走服务账号权限,不受安全规则限制。
可选优化
- 采集任务多的话可以给
/sensor_data节点配置数据保留策略,自动清理过期数据,省存储。 - 可以给触发逻辑加锁,树莓派端收到触发信号后先判断当前是不是在采集中,避免并发操作串口报错。
- 如果需要更高的可靠性,可以给信令加任务ID,移动端发指令的时候带唯一ID,树莓派端返回结果的时候带上对应ID,避免状态错乱。
这套方案完全复用你现有的三个组件,不需要额外买服务器、配公网穿透,Firebase的长连接本身稳定性足够,延迟一般在几百毫秒,完全满足远程触发的需求,同类树莓派远程采集项目实测连续跑半年没出过信令丢失的问题。
内容的提问来源于stack exchange,提问作者Panda
相关产品推荐
相关产品推荐

