尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32+LoRa GPS定位数据回传实战:从NMEA解析到低功耗设计

STM32+LoRa GPS定位数据回传实战:从NMEA解析到低功耗设计 做定位数据回传这个需求的时候我第一反应是这块 ST 官方出的 B-L072Z-LRWAN1 评估板到底能不能扛住整个链路——从 GPS 模块读 NMEA 报文到 LoRaWAN 入网再到把经纬度数据打到网关。实测下来这套方案完全能作为产品原型来跑而且低功耗特性比用 4G 模组实在太多了。如果你也在做户外资产追踪、野外环境监测或者车队管理这类项目这篇文章基本可以帮你把GPS 数据采集 LoRa 传输这条路一次走通。1. 硬件的底牌B-L072Z-LRWAN1到底能做什么1.1 不是一块普通的STM32板子B-L072Z-LRWAN1 是 ST 针对 LoRa 物联网场景推出的评估套件核心是一颗 STM32L072CZ 超低功耗 MCUCortex-M0 内核192KB Flash、20KB RAM跑 LoRaWAN 协议栈绰绰有余。板上真正干活的是那颗 SX1276 LoRa 收发器最大发射功率可以到 14dBm 左右接收灵敏度在低速率下能到 -137dBm这意味着什么在开阔环境下用默认的 EU868 频段、SF12 速率点对点通信几公里是家常便饭。我刚开始拿到这块板子的时候其实没太把它当回事觉得就是块普通的开发板。后来看原理图才发现ST 把射频前端、Balun 匹配网络、天线接口全都画好了SMA 天线座直接引出你不需要自己去画射频电路这在大规模产品打样阶段能省掉一大笔学费。板上还带了 ST-LINK/V2-1 调试器一根 USB 线就能烧录、调试、串口打印三合一很省桌面空间。1.2 GPSLoRa这种组合适合什么场景先说清楚为什么偏偏是 LoRa 而不是 Wi-Fi、蓝牙或者 4GGPS 数据本身很小一条经纬度加时间戳加卫星数压缩完也就十几个字节这种低频小数据量恰好是 LoRa 的舒适区。Wi-Fi 覆盖范围几十米野外根本没有基础设施蓝牙更不用说了那位拿手机当网关的蓝牙 GPS 数据传输方案只能在视线范围内玩玩4G 模组功耗高、资费贵一年光流量费就能买好几块开发板。LoRa 的价值在于用极低的功耗换取极高的接收灵敏度在城市里穿墙效果好在野外可以传几公里。B-L072Z-LRWAN1 这套板子典型的应用场景包括冷链物流车的温湿度位置回传、牛群羊群的电子围栏项圈、光伏电站的组件巡检定位、共享设备防丢追踪。这些场景共同点是电池供电、数据量小、上报频率低一颗 18650 电池撑几个月是基本要求。1.3 上电之前的检查清单拿到板子别急着插电先花两分钟做检查免得后面对着不亮灯的板子瞎折腾确认板卡频段版本和你的网关一致。B-L072Z-LRWAN1 有 EU868、US915 等不同 SKU 版本我在国内用 470-510MHz 的场景则需要先确认 SX1276 频段设置是否覆盖功率配置也需要按当地法规调整后再做空口测试。把 SMA 天线接好。LoRa 射频前端没有天线的时候长时间发射容易让 PA 处于不理想的负载状态调试可以别作死长时间空载发射。用 USB 线连接板子的 ST-LINK USB 口电脑上应该能识别出一个串口设备这个口同时承担虚拟串口和调试器功能。首次上电板载红色 LED 会闪烁通过 USB 虚拟串口可以看到默认固件打出的日志。我实际遇到过好几次板子不工作的情况最后发现是 USB 线只有充电没有数据。调试物联网板子一定找一根正经的数据线。2. GPS模块选型与四线接线最容易被忽略的细节2.1 选模块NEO-8M还是M9NGPS 模块是整个系统里最容易出幺蛾子的部分选型不能只看价格。我在这个项目里对比过三种给大家一个简单的参考表模块型号定位精度冷启动时间工作电流特色u-blox NEO-6M2.5m27s约90mA老款资料多便宜u-blox NEO-8M2.5m26s约67mA性价比高市面常见u-blox NEO-M9N1.5m25s约50mA支持多星座含北斗功耗低如果是做产品我建议直接上 NEO-M9N 或者国产的 ATGM336H多模多星座GPS 北斗 GLONASS定位速度和抗干扰能力都强很多。如果只是学习验证NEO-6M/8M 完全够用十来块钱一块模块串口直接输出 NMEA坏了大不了换一块。选购的时候注意模块有没有带 LNA板载陶瓷天线还是外接有源天线。室内测试强烈建议选带 IPEX 座、可以外接有源天线的不然你把 GPS 模块放窗边信号也不一定稳定。2.2 接线和电平匹配GPS 模块和 STM32L072 之间就是标准的 UART 通信四条线干净利落GPS模块引脚B-L072Z-LRWAN1引脚说明VCC3V3供电3.3VGNDGND共地TXDPA2 (USART2_RX)GPS发送、MCU接收RXDPA3 (USART2_TX)MCU发送、GPS接收PPS可选时间同步脉冲有一个细节必须强调很多 GPS 模块标注支持 3.3V 或 5V 供电但输出电平是 3.3V TTL这个直接接 STM32 没问题。反倒是某些老模块输出电平是 5V TTL要加电平转换或者分压电阻不然会打坏 MCU 引脚。接线前先看一眼模块规格书别信店家标题里的无需电平转换。把模块的 TXD 接到板子的 PA2 上模块的 RXD 接 PA3。用 STM32CubeMX 把 USART2 配置成 9600 波特率、8 位数据、无校验、1 位停止位这是绝大多数 GPS 模块的默认配置。别自作聪明改成 115200模块不一定认。2.3 怎么快速确认GPS模块在工作接完线先不写任何代码直接用串口助手看。打开电脑上的串口工具连上 B-L072Z-LRWAN1 的虚拟串口注意不是直连 GPS 模块那根线9600 波特率如果 GPS 模块的 TXD 接到了 PA2而 USART2 的 RX 被 CubeMX 映射到了虚拟串口的某个通道上硬件上还得看你代码里怎么把 USART2 的数据转发到 USB 虚拟串口这个后面软件部分会讲。最简单粗暴的验证方法把 GPS 模块的 TXD 单独接一个 USB-TTL 小板到电脑上如果串口助手哗哗地吐 $GPRMC、$GPGGA、$GPGSV 这些报文说明模块供电、天线、波特率都正常。如果一直没输出大概率是模块被设在低功耗模式某些模块默认开了 Power Save 模式没定位时静默或者天线没接好、没搜到卫星。模块正常输出 NMEA 报文但报文里的定位字段是 0比如 $GPGGA 逗号后面连续两个 0说明模块还没定位成功。你需要把它放到窗边或者楼下空地等上几十秒看到定位状态从 0 变成 1 才说明真的搜到星了。GPS 这东西室内跑不出来的原因大多是卫星信噪比太低这个我们后面专门说。3. NMEA数据解析从字符串里抠出经纬度3.1 GGA和RMC报文怎么读GPS 模块输出的 NMEA 0183 协议报文是一堆 ASCII 字符串以 $ 开头以回车换行结尾。最常用的两条是 $GPGGA 和 $GPRMC。前者最直接一条报文里把经纬度、定位状态、卫星数、海拔全给齐了后者多了速度和航向做移动轨迹回放很好用。我截一条实际抓到的 GGA 报文来拆解$GPGGA,093507.00,3102.45678,N,12124.78901,E,1,08,1.2,10.5,M,0.0,M,*5E按照逗号位置从左到右分别是字段示例值含义0$GPGGA报文标识1093507.00UTC时间09:35:0723102.45678纬度度分格式3N北纬412124.78901经度度分格式5E东经61定位状态0无定位1单点定位2差分定位708可见卫星数81.2HDOP水平精度因子910.5海拔注意 3102.45678 这个字段不是 31.02 度它表示的是 31 度 02.45678 分。这个格式特别容易踩坑后面专门说换算。3.2 解析逻辑和代码实践NMEA 解析没必要从零造轮子但如果你不想引入第三方库自己写一个轻量的解析器也就几十行。思路很清晰按行接收报文找到 $GPGGA 开头的那行按逗号切分提取需要的字段。我用的方式是 USART2 接收中断把数据往环形缓冲区里塞主循环里从缓冲区一行一行抠字符串。核心解析函数长这样typedef struct { uint8_t fix_valid; double latitude; // 十进制纬度单位度 double longitude; // 十进制经度单位度 uint8_t satellites; float hdop; } gga_info_t; static uint8_t nmea_parse_gga(const char *line, gga_info_t *out) { const char *p[15] {0}; int idx 0; int field 0; if (line[0] ! $ || memcmp(line, $GPGGA, 6) ! 0) return 0; // 简单按逗号切分字段 char buf[128]; strncpy(buf, line, sizeof(buf) - 1); buf[sizeof(buf) - 1] \0; p[field] buf; for (char *c buf; *c; c) { if (*c ,) { *c \0; if (field 15) p[field] c 1; } } // 字段6是定位状态非0才算有效 out-fix_valid (p[6] atoi(p[6]) ! 0) ? 1 : 0; if (!out-fix_valid) return 0; // 纬度度分转十进制 if (p[2] p[3]) { double raw atof(p[2]); int deg (int)(raw / 100); double min raw - deg * 100; out-latitude deg min / 60.0; if (p[3][0] S) out-latitude -out-latitude; } // 经度同样处理 if (p[4] p[5]) { double raw atof(p[4]); int deg (int)(raw / 100); double min raw - deg * 100; out-longitude deg min / 60.0; if (p[5][0] W) out-longitude -out-longitude; } out-satellites p[7] ? atoi(p[7]) : 0; out-hdop p[8] ? atof(p[8]) : 99.0f; return 1; }这段代码没有做校验和验证实际产品里建议加上。校验规则也很简单$ 和 * 之间的所有字符按位异或结果转成两位十六进制等于 * 后面的值。3.3 度分转十进制的坑这个坑我必须单独拎出来讲。把 3102.45678 当成 31.0245678 度的人不在少数算出来的位置直接偏出去几十公里。正确算法是前面的两位31是度后面所有小数02.45678是分然后按 度 分/60 转换纬度 31 02.45678 / 60 31.0409463经度同理121 24.78901 / 60 121.4131502。你要是把度分当十进制直接用了传回去的位置在地图上会偏到海里。这个我在实际项目里见过太多次了连不少做过几年的工程师都会在这栽跟头。3.4 GPS数据质量的判断指标做定位数据回传不能只传经纬度还要带质量指标。数据采集系统里GPS 数据需要专属的质量评估字段这和相机、激光雷达、IMU 做传感器融合时评估数据可靠性的逻辑是一样的。GPS 这边我习惯带上三组指标定位状态字段1 是单点定位2 是差分定位0 是无效这个是硬开关。可见卫星数正常情况下至少 4 颗星才有 3D 定位低于 6 颗在城市峡谷里信号漂移会很明显。HDOP 水平精度因子小于 1 定位精度很高1 到 2 属于正常2 到 5 还能用但误差变大大于 5 基本只能当方向参考。卫星信噪比SNR也是好用的诊断指标要读 $GPGSV 报文里的信号强度值正常开阔环境 30 到 50 dBHz 都有室内或者高楼密集区往往会掉到 20 以下。我这里给一个经验判断表格指标良好一般差HDOP 1.51.5 - 3 3卫星数 106 - 10 6平均SNR 40 dBHz30 - 40 30数据帧里加一个字节的状态字节把这个质量等级打成 0、1、2后续处理就非常方便了。4. LoRaWAN入网与数据帧设计数据怎么传才算合格4.1 LoRaWAN协议栈怎么接进来ST 官方提供了一套完整的 LoRaWAN 软件扩展包 I-CUBE-LRWAN和 STM32CubeMX 配合使用。你在 CubeMX 里选好 B-L072Z-LRWAN1 板型软件包里选择 LoRaWAN 示例协议栈的底层驱动、MAC 层、应用层框架就全都生成好了。用官方大厂的协议栈好处是省心频段配置、LoRa 调制参数这些细节都已经调过你只需要关注应用层的传感器数据读取和上报。如果你的场景没有现成的 LoRaWAN 网关和服务器也可以用 SX1276 的裸驱动做点对点模式一个板子发一个板子收类似无线串口。这种模式对于验证 GPS 数据链路很有用一台电脑挂一个接收板就能看到完整的数据流适合没有网关的开发环境。4.2 OTAA入网配置要点LoRaWAN 入网有两种方式OTAA 和 ABP。OTAA 每次上电通过 Join 流程动态获取网络地址和会话密钥安全性高ABP 是预先写死地址和密钥开机直接用省去入网流程但密钥容易泄露。我推荐用 OTAA代码里只需要配置三组参数参数说明获取方式DevEUI设备唯一ID从LoRaWAN服务器生成或按芯片唯一ID生成JoinEUIAppEUI应用标识服务器端配置AppKey应用密钥服务器端配置入网时派生会话密钥实际生成工程后代码里会有一个LoRaWAN_Confirm之类的头文件把这些参数填进去。注意 DevEUI 的字节序LoRaWAN 协议里有些字段是大端存储有些是小端填错导致入网失败是新手最常见的错误之一。ST 的例程里一般会有注释说明照着填就行。入网成功后协议栈会回调LoRaMacOnJoinRequest之类的函数在里面打一条日志确认设备真的上线了。此时再看网关那边的服务器后台应该能看到一个在线设备。4.3 把GPS数据压缩成最小帧GPS 数据传到服务器没必要发 NMEA 原始字符串十几个字段一大串文本带宽浪费大而且 LoRaWAN 在低数据率下的负载有限制比如 EU868 DR0 只能用 51 字节的 payload。我的习惯是把关键信息压缩成二进制结构体再以十六进制字节发出去。一个实用的数据帧设计字段类型字节数说明帧序号uint162本地自增用于发现数据丢包纬度int324十进制纬度 × 10^7取整经度int324十进制经度 × 10^7取整UTC时间uint324简化成自当天0点起的秒数速度uint162单位 cm/s航向uint162单位 0.1 度状态卫星数uint81高4位质量等级低4位卫星数电池电压uint162单位 mV合计17打包代码很简单uint8_t frame[32]; uint16_t idx 0; int32_t lat_i (int32_t)(lat * 10000000); int32_t lon_i (int32_t)(lon * 10000000); frame[idx] (seq 8) 0xFF; frame[idx] seq 0xFF; memcpy(frame[idx], lat_i, 4); idx 4; memcpy(frame[idx], lon_i, 4); idx 4; uint32_t utc_sec (hh * 3600 mm * 60 ss); memcpy(frame[idx], utc_sec, 4); idx 4; // ... 后续字段服务器端按同样规则拆开就能还原出经纬度、时间、速度。注意经纬度在传输和存储时要约定好采用相同字节序我统一用大端为什么因为 LoRaWAN 里面很多字段就是大端风格统一之后少踩坑。很多人在这个点上栽跟头数据传上去了一解析发现数字完全对不上十有八九是字节序问题。5. 实测记录公园跑测、信号强度与问题排查5.1 跑测环境与结果硬件组装完成、代码编译通过后我在一个工作日下午带着板子和 GPS 模块到了附近公园做实测。网关放在办公室窗边直线距离不到 500 米中间隔着一栋楼和一片树。GPS 模块放在头顶的帽子上保证天空视野板子挂在背包带上电池用的是一节 3.7V 18650。测试参数LoRaWAN 用 SF7 速率上报DR5每 10 秒一条 GPS 数据GPS 模块保持连续工作。实测下来从开始走到网关对侧的小湖边约 1 公里范围内封包成功率 95% 以上偶尔有一两条丢了应该是中间有建筑挡住导致信号衰减。服务器后台能稳定收到经纬度在地图上连出来的轨迹和实际行走路线吻合偏差基本在几米内符合预期。信号强度方面网关侧 RSSI 在 -100 到 -115 dBm 之间波动SNR 在 5 到 10 dB 浮动。这个数值虽然不算强但对于 SF7 速率来说已经足够稳定。如果你测出来 SNR 是负数就要小心了说明链路余量已经不够了要么换低数据率比如 SF10要么调整网关位置。5.2 常见问题排查思路我把从零调试这块板子过程中遇到的高频问题列成了一张表按排查优先级排列现象可能原因排查和处理GPS 模块无输出天线没接好、模块未搜到星用USB-TTL单独测试模块确认报文输出换到窗边等待串口打印乱码波特率不匹配、USB虚拟串口驱动异常确认GPS和调试串口波特率不同步重装驱动LoRaWAN 入网失败DevEUI/AppKey填错、频段与网关不一致核对参数字节序检查频段配置抓取入网日志上报数据解析不对字节序不一致、字段偏移错误用Hex格式抓原始帧手工对照结构体低数据率下发不出去payload超长、占用冲突压缩帧长降低上报频率检查占空比限制走远了掉线严重天线方向不对、SF太低换低速率如SF10保证天线垂直这里说一个我当时费了挺大劲才搞明白的问题LoRaWAN 是有占空比限制的EU868 频段规定每 24 小时每个子频段的发送时间不能超过一定时长。测试时我为了观察方便设成每 5 秒发一条跑了一个多小时之后模块突然发不出数据了日志里全是发送超时。后来查协议栈才发现是被占空比限制卡住了改成 10 秒以上一条再没出现过。实际产品如果上报频率很密一定要算好占空比余量。5.3 GPS室内的SNR困境很多人在室内测 GPS 发现永远没定位我在测试过程中也踩过。GPS 的 L1 信号到了室内信噪比会从室外的 40 dBHz 骤降到 20 dBHz 以下根本达不到模块的捕获门限。这不是模块问题也不是天馈问题而是物理环境决定的。所以调试 GPS 类项目一定要做好心理预期代码逻辑在室内可以先看 NMEA 报文是否正常输出但真正验证定位和轨迹必须到开阔环境。如果非要室内定位就得考虑加惯性传感器做航迹推算或者用蓝牙/ Wi-Fi 辅助定位方案GPS 本身在室内真的无能为力。6. 低功耗调优一块电池能撑多久6.1 功耗构成分析定位设备的续航核心就是三个耗电单元的博弈MCU、LoRa 射频、GPS 模块。STM32L072 在 Stop 模式下待机电流只有 3.4uA 左右几乎可以忽略SX1276 睡眠模式也不到 1uA真正的耗电大户是那个 GPS 模块连续定位时普遍要 50 到 90mA比另外两者加起来高好几个数量级。我开始没做任何优化的时候GPS 模块 24 小时连续工作整机平均电流大约 70mA拿一颗 2000mAh 的 18650 来算续航只有 2000/70 ≈ 28 小时这显然是没法用的。6.2 实用优化策略优化思路不是去压模块参数而是从系统调度上想办法。我做了一套简单的占空比控制逻辑平时整个系统睡死RTC 每隔 N 分钟唤醒一次唤醒后给 GPS 模块上电等待定位有效后立刻采集数据、经 LoRa 上报然后立即关闭 GPS 模块电源、MCU 重新进入 Stop 模式。用这种方式假设每 5 分钟唤醒一次GPS 模块每次工作 30 秒冷启动到定位有效需要的时间则 GPS 的平均电流大约是 70mA × 30s / 300s ≈ 7mA。加上 LoRa 上报瞬间的几十毫安电流折算整机平均电流能压到 8mA 左右2000mAh 电池理论续航约 250 小时也就是 10 天多一点。如果你想往月级别冲还要再叠加两个策略一是根据速度判断静止状态如果设备检测到连续 5 分钟位置没变化就自动把上报周期从 5 分钟拉长到 30 分钟二是利用 RMC 报文里的速度字段做运动触发静止时干脆连 GPS 都不开机等检测到运动再唤醒 GPS。我们实测过这种策略在停车场车辆追踪这种半静态场景里平均电流能再降一半以上。6.3 低功耗模式下的小技巧最后分享两个项目里实测有效的小经验第一GPS 模块的 PPS 引脚接到 MCU 的外部中断引脚上可以用来做高精度时间同步。定位成功后 PPS 会输出秒脉冲MCU 可以在脉冲沿校准自己的 RTC保证长时间离线记录的数据时间戳不漂移。第二LoRa 上报不一定每次都带完整 GPS 数据帧。如果服务器只需要低成本监测可以在设备端做简单的变化判断位置变化小于设定阈值比如 10 米就只发一个 1 字节的心跳包位置变化超过阈值才发完整 17 字节的数据帧。这在设备数量很多、网关下行带宽有限的时候特别有用网络容量压力小很多。实际部署这套系统之后我最深的体会是GPS 加 LoRa 这对组合本质上是把两个各自成熟的生态串起来中间最花时间的从来不是单点技术而是数据打包方式、状态切换逻辑、和各种奇奇怪怪的字节序问题。把这些基础问题都趟平了整套设备就稳了。
返回列表