上个月接了这个老人跌倒手环的活我一开始觉得这事不难传感器、定位模块、GSM模块全是现成货拼一块板、写几行代码能发短信就行。真正把样机戴在假人手腕上测了两周我才发现标题里那串词——MSP430单片机手环、老人跌倒、GSM短信、GPS北斗定位、地图设计——没有一个字是白给的。跌倒判断的误报率、GSM发射瞬间的电源崩塌、GPS室内无信号、还有经纬度怎么变成家属能看懂的地图链接每一样都在实际调试里卡过我。这篇就把这个项目的完整设计过程和踩坑记录写出来适合正在做类似可穿戴报警项目、或者想用 MSP430 做低功耗物联网设备的朋友可以直接照着搭也能提前绕开我走过的弯路。1. 整体链路拆解从传感器采集到家属收到地图短信需要经过哪些环节这套手环本质上是一条数据链路运动传感器不停采集姿态数据单片机实时判断有没有发生跌倒一旦判定跌倒立刻唤醒定位模块读取坐标再通过GSM模块向外发送一条包含经纬度和地图链接的短信家属手机上点一下链接就能看到老人当前位置。1.1 系统组成与模块选型整个系统的核心器件我列在下面这不是随手的配置每一项都是根据功耗、接口、协议和采购便利性反复比过的。功能模块选型思路接口备注主控MSP430F1493路UART、I2C、多路GPIO低功耗是选它的首要原因后面专门讲运动传感器六轴姿态传感器MPU6050这类I2C测加速度和角速度跌倒检测的主力定位模块GPS/北斗双模定位模块UART输出NMEA协议数据冷启动需要时间通信模块四频GSM/GPRS模块UART发短信峰值电流大供电要专门处理电池单节18650锂电池—容量大、驱动GSM发射够力、可充电充电管理单节锂电池充电管理IC—充电电流可调带过充保护电源稳压3.3V LDO稳压器—给MSP430和传感器供电稳定优先为什么主控选 MSP430 而不是 STM32或者干脆用 Arduino理由很简单这是一个要戴在手腕上的电池设备续航是体验的底线。MSP430系列的超低功耗模式确实做得极致LPM3模式下主控自身电流是微安级别配合中断唤醒可以在“休眠-采集-判断-再休眠”的节奏里把平均电流压得很低。STM32当然性能更强但对手环这种单一任务场景属于杀鸡用牛刀功耗还压不下来Arduino开发快但板载硬件本来就不是为超低功耗场景设计的待机就有十几毫安到几十毫安戴在手上基本一天一充。MSP430的另外两个优势是内部时钟够用、IO口耐折腾跌打损伤中不至于因为一个电平问题烧掉整块板。1.2 串口资源分配与接口接线这个项目里最容易在初期翻车的是串口分配。MSP430F149带两个硬件UART但系统里GSM模块和GPS模块各占一个UART调试又需要一个串口这就出现了资源紧张。我的做法是UART1给GSM模块常驻UART0给GPS模块调试输出用LED指示灯加一个复用拨码开关平时UART0接GPS调试模式下把GPS的TX断开让UART0接USB转串口板。实际测试时我在板子上留了一组杜邦线插针方便切换。如果你手里的主控型号没有两个UART也可以用一个UART做软串口模拟但GSM模块AT指令通信对时序比较敏感软件模拟串口容易丢字节建议硬件UART优先给GSMGPS用软件串口这个优先级不要弄反。原因后面会说GPS数据是连续不间断往外吐的NMEA语句靠帧头区分就算中间丢一两个字符下一帧还是能重新对齐而GSM的AT指令是一问一答的交互式协议时序错乱整个指令就会失效。信号链路整体是这样走的传感器数据通过I2C进MSP430判断逻辑在主控内完成GPS模块开机后UART连续输出定位数据主控按帧解析GSM模块收到主控发来的短信指令后执行AT指令。三个模块之间没有复杂的互相握手主控是唯一的调度中心。2. 跌倒检测算法合加速度阈值只是开始姿态角二次确认才靠谱跌倒检测是整个项目的灵魂。把阈值设低了老人弯腰捡个东西就触发报警家属一天收十条短信最后直接把手环关了设高了真摔了又没反应。这一章我把自己的三段式算法完整展开可以直接抄。2.1 第一步合加速度冲击检测MPU6050输出的三个轴加速度数据不能用单轴直接判断因为手环佩戴方向会变抬手、甩手、翻身都会让某个轴的读数大幅变化。我取的组合值是三轴合加速度即向量模长acc_mag sqrt(ax*ax ay*ay az*az)静止状态下合加速度约等于 1g9.8m/s²无论手环怎么翻转这个值都稳定在1附近。跌倒的时候身体先加速下落再撞击地面合加速度会在短时间内冲破2.5g甚至3g。我实际测试过把佩戴手环的假人从1米高摔到海绵垫上峰值能到3.5g左右而正常走路、摆臂、上下楼梯合加速度峰值基本在1.8g以内。所以第一道门槛设成 2.5g持续至少100ms 才认为是一次冲击事件。这里有个关键细节只看峰值不够还要看持续时间。有人猛地抬手、拍桌子瞬时峰值也可能超过2.5g但持续时间只有几十毫秒。真正的跌倒是自由落体加撞击撞击持续时间通常在100到200ms以上。所以代码里要做窗口统计不能一超过阈值就立刻触发而是要在时间窗内连续采样判断。2.2 第二步姿态角二次确认冲击事件只能说明“身体剧烈运动了一下”不能说明“人倒地了”。跌倒之后人的姿态会从直立变成平躺或侧躺而这个姿态在跌倒后通常会维持一段时间。所以第二步是把冲击事件后一段时间的姿态角拉出来确认。用加速度计的静态倾角计算公式angle acos(az / acc_mag) * 180 / PI意思是重力方向与手环Z轴的夹角。手环佩戴在手腕上正常站立时手腕自然下垂或微曲这个夹角有一个相对固定的基准值跌倒平躺后手臂和重力方向的关系会明显改变夹角会偏向 0° 或 180° 附近。注意不同老人佩戴手环的松紧、手臂自然姿态差异很大所以设备开机后要做一个“静止3秒标定”把这支手臂的基准夹角记下来后续判断都基于相对角度变化而不是绝对值。这个标定步骤在量产或给老人戴上前必须做否则误报率会直线上升。算法上没有那么玄乎不需要上机器学习。我用的是合加速度超阈值后开始记录接下来2秒内的姿态角如果相对基准角变化超过45°并且这个姿态保持超过5秒就视为高度疑似跌倒。两个条件同时满足才进入报警流程。2.3 第三步连续触发与防抖延时即使有两步确认还是可能误报。我遇到最多的误报场景是老人坐椅子时猛起猛坐合加速度也能短暂飙到2.5g以上姿态角也有变化如果立刻报警就太烦人了。解决方案是加入“连续3次确认”机制每次确认间隔200ms三次都满足才触发。这样短促的起坐动作、弯腰捡东西状态很快恢复直立基本都会被过滤掉。触发报警后还有一个300ms到500ms的延时再真正发短信这个延时不是为了等什么数据而是给老人一个“虚惊一场”的人工取消机会。现实一点的解释是如果老人只是摔倒后迅速自己爬了起来姿态已经恢复正常系统自动取消报警不打扰家属。这个功能看起来小但对家属体验的改善比任何算法优化都大。样机测试时我甚至把取消窗口放到10秒配合手环上的按键长按3秒取消报警实际使用中确实收到了不少好评。有人可能问既然这么复杂为什么不直接把陀螺仪数据一起融合算欧拉角我的回答是MPU6050的陀螺仪有温漂和零偏融合姿态角需要卡尔曼或互补滤波运算量上来了代码复杂度也上来了。对可穿戴设备来说加速度计数据在跌倒冲击这种剧烈场景下已经能提供足够信息量用最简单的数学表达把可靠率做到95%以上比追求理论上99%却让工程师在调试中累死更重要。3. GPS/北斗解析与GSM短信串口上流动的每一帧数据都要读懂跌倒判定一旦通过系统要做的事情就变成了“报告位置”。这一章里我会说清楚定位模块输出的数据怎么解析以及怎么通过AT指令把它变成一条短信发出去。3.1 NMEA帧格式里最容易搞错的GNGGA/GNRMCGPS/北斗双模定位模块上电后UART会以默认波特率通常是9600持续输出NMEA 0183协议语句。这里有个绝大多数新手会踩的坑双模模块输出的句头是$GNGGA和$GNRMC不是传统GPS单模模块的$GPGGA和$GPRMC。GN代表GPS与北斗双系统的定位结果GP只代表GPS系统。如果你写解析程序时光盯着GP开头的语句找会发现串口数据明明在动却怎么也解析不出坐标。我实际关心的只有两条语句其中$GNRMC最关键包含了经纬度、速度、UTC时间和定位状态。一帧示例$GNRMC,083559.00,A,3129.41528,N,12020.51305,E,0.0,0.0,...字段含义分别是UTC时间时分秒、定位状态A有效V无效、纬度、北纬/南纬、经度、东经/西经、速度、航向。代码解析时要做字符分割取到经纬度字段后先判定位状态状态不是A的视频说明还没定位成功这时候的坐标不能用。3.2 度分坐标转十进制度的计算示例NMEA协议里的经纬度格式是“度分”格式不是我们在地图链接里常用的十进制度。比如3129.41528 N意思是31度29.41528分。要转成十进制公式是度 取整数部分即31 分 29.41528 十进制度 31 29.41528 / 60 31.4902547我写的转换函数是这样double convert_nmea_to_decimal(double raw) { int degree (int)(raw / 100); double minute raw - degree * 100; return degree minute / 60.0; }这个函数在调试时陪我跑了无数遍建议先用电脑串口助手抓一段真实定位数据先从终端里确认格式再写解析函数不要直接把网上找的代码贴进去因为不同模块的NMEA字段细节可能有细微差异比如有的没有速度字段有的带校验和不带校验位先把原始文本打印出来比什么都靠谱。3.3 GSM短信发送AT指令流程与失败重试坐标解析出来之后主控通过另一个UART向GSM模块发送AT指令。发送短信的指令序列非常简单真正考验人的是各种失败场景。基础流程ATCMGF1 // 短信设为文本模式 ATCMGS13xxxxxxxxx // 设置接收号码 FALL ALERT! ... // 短信内容 0x1A // 发送十六进制1A相当于CtrlZ注意文本模式下短信内容一般建议用英文或数字中文短信需要切到PDU模式并做Unicode编码代码复杂度会高很多。我的方案是短信内容用英文加链接避免中文在GSM模块内部的编码坑家属收到后照样一眼看懂。发送失败的三个高频原因SIM卡没插好或没欠费模块返回CMS错误信号差返回ERROR或发送超时GSM模块启动初始化未完成就发了指令。我的处理是每次开机先发ATCPIN?查询SIM卡状态返回READY才进主流程发短信前发ATCSQ查信号强度返回数字低于10就默认网络不可用先尝试三次每次间隔5秒短信发送后收到OK才置为成功收到ERROR就等待10秒重发整个指令序列最多5轮。这套流程在测试里把短信发送成功率从最初的70%提到了99%。4. 报警短信里的坐标与地图链接设计让家属点一下就能看到位置定位数据进了短信其实还有很多产品层面的细节。如果短信里只发“东经120.206866 北纬31.490254”这种裸坐标绝大多数家属根本看不懂也不会去搜索报警短信等于白发。所以我在设计短信内容时加入了地图链接。4.1 短信内容模板与链接生成实际发送的短信模板长这样FALL ALERT! Time: 2024-05-20 14:30:25 Pos: 31.490254,120.206866 Map: https://xxx.map/location?lat31.490254lng120.206866地图链接的生成逻辑不复杂主控把经纬度拼到固定前缀的URL参数里就行。不同地图服务商的链接格式有区别但核心参数都是lat和lng。这里给一个通用建议不要直接拼长链接因为短信有长度限制英文短信单条上限约160字符链接太长会被拆成两条甚至三条体验很差。我使用的是短链服务把URL压缩到30个字符以内这样短信总长度控制在120字符左右稳定一条发完。4.2 经纬度精度与最后1米问题手持定位模块的典型精度是3到10米即使在开阔地定位点也可能偏离真实位置几个车位。所以短信里再怎么写精确到6位小数也代表不了精确到1厘米。经纬度保留6位小数已经给出了约0.1米的精度这对地图显示来说绰绰有余更多小数位只是增加短信长度没有实际意义。这里分享一个实用技巧我在地图链接生成前做了坐标抖动过滤。定位模块静止时由于卫星信号和环境影响经纬度会在几米范围内漂移。如果不加处理理论上同一位置的报警链接会显示在不同坐标上。解决方法是拿最近两次有效定位做比较如果距离小于10米就沿用上一次坐标防止短时间内的随机漂移把位置画偏。这个逻辑用经纬度平面距离公式算就行不需要调用高精度定位服务几行代码就能实现。4.3 没有网络环境下基站定位兜底GPS/北斗模块在室内、地下通道、电梯里经常拿不到有效定位。作为报警设备不能因为没卫星信号就不发报警所以必须设计兜底方案GSM模块本身可以通过基站信息获取粗定位。主控在GPS模块连续30秒无有效定位时切换策略发送AT指令让GSM模块返回当前注册基站的三网信息或经纬度粗定位结果。基站定位精度大概是百米到公里级比卫星定位差很多但至少能告诉家属“老人大概在这个城市这个片区”比完全没位置强得多。我做的短信在兜底定位时会额外加一个“ACCURACY: LOW”字段提醒家属位置可能不准不要按着精准坐标找。这种诚实的设计反而比假装精确更能赢得家属信任。5. 低功耗设计把待机电流从几十毫安压到2毫安以内手环项目的成败有一半在续航上。第一版样机我犯了几乎所有新手都会犯的错所有模块上电常供主控跑在一个8MHz主频的死循环里。结果整个板待机电流轻松超过30mA一块2000mAh的电池撑不到两天戴在假人手腕上都能感觉到发热。后面做了一轮彻底的功耗改造才真正敢说能戴一个月。5.1 把GPS和GSM的电源切掉待机电流降一个数量级改造的核心思路只有一个平时不该供电的模块全部断电。GPS模块工作电流约20到30mAGSM模块待机也不低登录网络时候更是有几百毫安的瞬时电流。这两个模块如果常驻功耗任何低功耗主控都救不回来。我加了两路MOS管开关做电源控制单片机的两个GPIO分别控制GPS模块和GSM模块的电源端。平时两路都是关闭状态只有跌倒判定触发后主控才先给GPS模块上电等定位数据有效后再给GSM模块上电发短信。短信发完再按顺序断电回到休眠状态。这个开关不能随便用三极管就能通杀选择MOS管时要注意导通电阻不要太大否则模块满载工作时压降会触发欠压复位。我选的是低内阻N沟道MOS管GSM模块工作电流峰值接近2A开关的连续电流能力余量要留到3A以上不然一发射就开始发热抖动。5.2 电池容量、真实续航与充电策略改造后的待机电流分配是这样的MSP430进入LPM3休眠模式电流约1微安级别六轴传感器放在低功耗测量模式约几十微安两个无线模块断电后电流为零。主控每隔500ms醒来一次读一次加速度传感器判断是否需要进入高采样模式平时绝大多数时间都在休眠。综合下来整机待机电流可以压到1.5mA以内。用一块1000mAh的18650锂电池来做简单估算可用容量按80%计算800mAh 待机时间 800mAh / 1.5mA 533小时 ≈ 22天考虑到电池自放电、LDO静态电流、传感器唤醒采样开销实际使用我测下来大概20天左右。如果手环触发一次报警整个过程GPS上电约40秒、GSM上电约20秒额外消耗约30mAh对比1000mAh总容量可以忽略不计基本不影响整体续航。充电部分用的单节锂电池充电管理IC恒流恒压模式充电电流设在500mA既不伤电池也能在两三个小时内充满。6. 实测阶段踩过的坑电源、天线、模拟跌倒、串口电平最后这部分是项目里最值钱的经验全部来自真实测试比看一百篇原理分析都有用。6.1 模拟跌倒测试的正确打开方式我最初测试用的方法是把手环绑在鼠标垫假人上往沙发上扔结果数据波动大且不稳定误报率极高。后来改进成找一个1.5米高的桌沿让假人侧倒到海绵垫上并且保证自由落体方向与手环佩戴轴垂直这才模拟出了接近真实跌倒的冲击波形。这提醒我们用假人模拟跌倒必须控制跌落高度、落点软硬、撞击角度三个变量否则测试数据无法复现调出来的阈值也是碰运气。测试数据里最典型的是正常走路合加速度峰值约1.6g上下楼梯约1.9g快速坐起约2.2g而假人跌倒峰值稳定在3.5g左右。如果某次测试的峰值低于2.8g我就认为那次跌倒模拟不合格重新调整跌落姿势再测。阈值设置要有余量但又不能离正常运动峰值太近2.5g是我测试后确定的甜点值。6.2 GSM发射瞬间导致单片机重启第一次把样机装进壳子里做整机联调时一触发报警就白屏重启疑似原地无限复位。查了很久最后用示波器抓GSM模块发射瞬间的电源轨发现电压跌落超过600mV直接把LDO输出拉到单片机复位电压以下。问题根源是GSM模块发射时瞬态电流可达2A而我的供电线又细又长电池内阻加线路电阻扛不住这个冲击。解决方法是在GSM模块电源引脚旁并联两个470uF的低ESR法拉电容和一个小容量的MLCC同时把给GSM模块供电的导线换成短而粗的线径。这个改动之后GSM发射瞬间的电压跌落到150mV以内整机稳定通过测试。教训是无线模块的电源设计永远不要只看平均电流要看的是峰值电流和冲击响应。6.3 室内定位失败与冷启动时间GPS/北斗模块在室内的表现堪忧几乎拿不到有效定位。第一次整机测试我在写字楼里触发报警等了40秒定位状态始终是V。后来把测试搬到了窗边才在35秒左右拿到第一个有效点。冷启动时间受电离层、卫星几何分布和视野遮挡影响很大30到60秒是正常范围。这给产品的启示是手环跌倒报警时如果GPS长时间没有定位不能干等。我的做法是到了30秒就启用GSM基站定位兜底先发出一条低精度位置短信同时保留GPS继续搜索如果随后GPS定位成功再补发一条精确位置的短信。两条短信虽然有些重复但家属能前后对比反而是个负责任的设计。6.4 手腕佩戴角度的标定问题姿态角二次确认算法里基准角的标定是整个系统中唯一需要人工介入的步骤。刚开始我把基准角写死成固定值结果每个测试者的手腕静止姿态都不同有的人自然下垂有的人微屈还有人习惯性地握拳同一个阈值下误报率差异巨大。改成开机3秒静止标定后误报率立刻降了下来。标定逻辑很简单设备上电后显示LED慢闪3秒提示老人保持静止主控取100组加速度数据的平均值作为基准。这个步骤虽然简单但必须写进用户说明书里并且最好在配网或初始设置时做个引导否则老人不知道要静止标定出来的基准就是错的。调试佩带角度时还有一个重要提示六轴传感器最好设计在固定的PCB位置不要用飞线接出来让它在壳体内乱晃否则传感器坐标系整机会变。量产时传感器应该做在固定安装位的板载焊盘上手环外壳内部也最好设计定位槽保证每一次装配的传感器方向一致。否则即使做了开机标定加速度坐标轴方向不一致也会让算法结果飘掉。这套系统做完之后我再也不觉得“单片机手环”是个简单项目了它把低功耗设计、传感器算法、串口协议解析、移动通信、定位导航、产品交互全部串在了一条链上。如果下一步还有扩展计划我建议往两个方向走一是把跌倒判定的姿态判断进一步优化加入心电图或心率异常作为辅助确认条件减少老人心脏病发作伴随摔倒的漏报二是加一个蓝牙低功耗模块平时把数据同步到家里的网关老人不出门也能有人盯着状态。做这种东西真正值钱的地方不在模块有多新而在于把每个环节都调得比其他方案更稳一些。