STM32智能手表DIY:GPS定位与GSM短信上报全链路实战

发布时间:2026/10/27 23:52:57
STM32智能手表DIY:GPS定位与GSM短信上报全链路实战 简介这份资源面向嵌入式硬件与单片机方向的初学者及课程设计、毕业设计开发者提供一套基于STM32的智能手表完整实现方案重点解决GPS定位与GSM短信上报的联动问题。包内共233个文件以h头文件、c源码、jpg实物图、d依赖文件、doc与pdf文档为主另含mp4演示视频、schdoc原理图及hex固件压缩包约77.97MB覆盖从源码到图纸的完整链路。功能上LCD1602可切换显示时钟日期与经纬度GPS获取定位信息SIM800A模块支持按键发送短信、指示灯提示发送状态并可通过回复GET主动查询位置同时具备手电筒与电位器调屏功能。资源还附带任务书、开题报告、参考论文、系统框图、程序流程图、芯片资料与器件清单便于快速理解整体架构与答辩准备。目前已有1771人学习下载适合需要完整软硬件参考的嵌入式学习者。1. 智能手表短信上报GPS定位从一块STM32裸板到能发坐标的完整链路你手上可能有一块STM32最小系统板、一个SIM800C模块、一个ATGM336H GPS模块外加一块小尺寸TFT屏。把它们拼成一块“智能手表”核心诉求其实就一个按下按键手表把当前经纬度通过GSM短信发到指定手机号。听起来简单但真正动手时你会发现GPS冷启动要等几十秒、GSM注册网络要等十几秒、串口互相抢资源、短信中文编码还容易翻车。这套方案适合做毕业设计、课程设计或者物联网入门的第一个完整项目也适合已经玩过51单片机、想升级到STM32做点“能拿出手”的东西的人。下面按硬件选型、GPS解析、GSM短信、主循环调度、避坑、进阶验证六步走每一步都给出可复现的代码和参数。2. 硬件选型与接线为什么选STM32F103C8T6 SIM800C ATGM336H2.1 主控、通信、定位三件套的取舍逻辑STM32F103C8T6是绕不开的起点。72MHz主频、64KB Flash、20KB RAM跑GPS解析和GSM AT指令绰绰有余关键是资料多、价格低、HAL库和标准库都能用。如果你手头是STC或51单片机也能做但多串口同时收发的压力会很大GPS和GSM各占一个串口51很难同时伺候好。GSM模块选SIM800C而不是SIM900A因为SIM800C体积小、功耗低、支持2G频段短信和GPRS都够用。注意SIM800C只支持移动和联通2G电信卡不能用。GPS模块选ATGM336H而不是NEO-6M因为ATGM336H支持北斗GPS双模冷启动时间更短价格也差不多。接线原则GPS和GSM都用UARTSTM32F103C8T6有3个USARTUSART1接GPSUSART2接GSMUSART3留给调试打印。TFT屏用SPI接口ILI9341驱动读ID是0xA1A1的那个版本。供电要特别注意SIM800C发射瞬间电流能到2A必须单独用锂电池或者大电流LDO供电不能直接从STM32的3.3V取电否则一发短信就复位。2.2 最小系统接线表与供电注意事项模块STM32引脚说明ATGM336H TXPA10 (USART1_RX)GPS发STM32收ATGM336H RXPA9 (USART1_TX)STM32发GPS收一般不用SIM800C TXPA3 (USART2_RX)GSM发STM32收SIM800C RXPA2 (USART2_TX)STM32发GSM收SIM800C VCC外部4.2V锂电池必须独立供电共地ILI9341 SCKPA5 (SPI1_SCK)屏幕时钟ILI9341 MOSIPA7 (SPI1_MOSI)屏幕数据ILI9341 CSPA4片选ILI9341 DCPB0数据/命令切换ILI9341 RSTPB1复位按键PB12按下触发发短信供电部分单独说SIM800C的VCC范围是3.4V~4.4V典型4.0V。用3.7V锂电池直接供或者用MP1584降压到4.0V。STM32和GPS用3.3V LDO供。所有模块共地。如果发现SIM800C注册不上网先量VCC在发射瞬间有没有掉到3.4V以下。3. GPS数据解析从NMEA报文里抠出经纬度3.1 NMEA-0183协议里哪些字段真正有用ATGM336H上电后默认输出NMEA-0183报文波特率9600。你会在串口看到一堆$GNRMC、$GNGGA、$GPGSV。真正有用的只有两条$GNRMC包含经纬度、速度、日期$GNGGA包含经纬度、海拔、定位质量。其他$GPGSV是卫星信息调试时看看就行解析时可以忽略。$GNRMC格式如下$GNRMC,123519.00,A,3958.12345,N,11618.54321,E,0.0,0.0,230324,,,A*7C字段含义第1字段时间UTC第2字段A有效定位V无效第3字段纬度ddmm.mmmmm第4字段N/S第5字段经度dddmm.mmmmm第6字段E/W第7字段速度节第9字段日期ddmmyy。注意纬度是度分格式要转成十进制度度 整数部分分 小数部分十进制 度 分/60。3.2 用状态机解析RMC语句的完整代码不要用strtok在中断里切字符串容易翻车。我一般用状态机逐字节收收到\n后置标志主循环里解析。下面是一个可复现的解析函数// gps_parse.c #include string.h #include stdlib.h #include gps_parse.h static char rx_buf[128]; static uint8_t rx_idx 0; static uint8_t line_ready 0; // 在USART1中断里调用逐字节存入 void GPS_IRQHandler(uint8_t byte) { if (byte \n) { rx_buf[rx_idx] \0; line_ready 1; rx_idx 0; } else if (byte ! \r rx_idx sizeof(rx_buf) - 1) { rx_buf[rx_idx] byte; } } // 主循环调用返回1表示解析到有效定位 uint8_t GPS_Parse(GPS_Data_t *out) { if (!line_ready) return 0; line_ready 0; if (strncmp(rx_buf, $GNRMC, 6) ! 0) return 0; char *p rx_buf; char *fields[13]; uint8_t n 0; // 按逗号切分最多13个字段 fields[n] p; while (*p n 13) { if (*p ,) { *p \0; fields[n] p 1; } p; } if (n 10) return 0; if (fields[2][0] ! A) return 0; // 无效定位 // 纬度 ddmm.mmmmm - 十进制度 float lat_raw atof(fields[3]); int lat_deg (int)(lat_raw / 100); float lat_min lat_raw - lat_deg * 100; out-latitude lat_deg lat_min / 60.0f; if (fields[4][0] S) out-latitude -out-latitude; // 经度 dddmm.mmmmm - 十进制度 float lon_raw atof(fields[5]); int lon_deg (int)(lon_raw / 100); float lon_min lon_raw - lon_deg * 100; out-longitude lon_deg lon_min / 60.0f; if (fields[6][0] W) out-longitude -out-longitude; return 1; }逻辑说明中断只负责收字节和置标志解析放主循环避免在中断里做浮点运算和字符串操作。fields[2][0] ! A判断定位是否有效无效直接返回0。经纬度转换是重点ddmm.mmmmm格式里前两位或三位是度剩下的是分。参数说明rx_buf大小128够用RMC语句一般不超过80字节atof需要stdlib.h浮点运算在F103上默认用软件浮点如果嫌慢可以改成整数运算但短信上报对实时性要求不高浮点够用。3.3 冷启动、热启动与定位有效性的判断ATGM336H冷启动完全断电后上电需要30~60秒才能定位热启动有备份电池3~5秒。判断定位是否有效除了看RMC的A字段还要看GGA里的定位质量位0无效1单点定位2差分定位。实际使用中如果RMC的A字段为A但经纬度是0说明模块还没真正定位需要继续等。我一般会在主循环里加一个超时如果60秒内没拿到有效定位就在屏幕上显示“GPS未定位”而不是傻等。4. GSM短信上报AT指令发中文短信的完整流程4.1 SIM800C注册网络与短信模式设置SIM800C上电后先等它自动注册网络。用AT指令查ATCREG? // 返回CREG: 0,1 表示已注册 ATCSQ // 信号质量第一个值0-31越大越好99表示无信号注册成功后设置短信模式为文本模式ATCMGF1 // 1文本模式0PDU模式 ATCSCSGSM // 字符集发英文用GSM发中文用UCS2如果要发中文短信必须用UCS2编码并且ATCSCSUCS2然后把中文转成Unicode十六进制字符串。这就是为什么很多人发中文短信翻车——直接发中文模块不认。4.2 发英文短信的最小代码与AT指令时序先看英文短信简单直接// gsm_sms.c #include usart2.h #include string.h #include stdio.h void GSM_SendSMS_En(const char *phone, const char *text) { char cmd[64]; // 设置文本模式 USART2_SendString(ATCMGF1\r\n); HAL_Delay(500); // 设置目标号码 sprintf(cmd, ATCMGS\%s\\r\n, phone); USART2_SendString(cmd); HAL_Delay(500); // 发送内容以CtrlZ(0x1A)结束 USART2_SendString(text); USART2_SendByte(0x1A); HAL_Delay(3000); // 等短信发出 }逻辑说明ATCMGF1设置文本模式ATCMGS号码后模块返回提示符然后发内容最后发0x1ACtrlZ触发发送。参数说明HAL_Delay(500)是给模块反应时间实际调试时可以用串口助手看返回如果返回说明可以发内容了。HAL_Delay(3000)是等短信发出SIM800C发短信一般2~5秒3秒是保守值。注意号码要带国家码比如8613800138000。4.3 中文短信UCS2编码转换与发送中文短信需要把GBK或UTF-8转成UCS2。STM32上资源有限我一般用查表法或者只支持常用汉字。下面是一个简化的UCS2转换函数假设输入是GBK编码的中文字符串// 将GBK中文转UCS2十六进制字符串 // 输入: 北京 (GBK: B1B1 BE A9) // 输出: 53174EAC void GBK_To_UCS2(const char *gbk, char *ucs2_out) { // 这里用简化映射实际项目建议用完整GBK-UCS2表 // 仅示例北0x5317, 京0x4EAC const struct { uint16_t gbk; uint16_t ucs2; } table[] { {0xB1B1, 0x5317}, // 北 {0xBE A9, 0x4EAC}, // 京 }; // 实际代码需要遍历gbk字符串查表拼接 // 省略完整实现重点在流程 }更可靠的做法在PC端把中文转成UCS2十六进制字符串直接硬编码到STM32里。比如“北京”的UCS2是53174EAC发送时void GSM_SendSMS_CN(const char *phone, const char *ucs2_hex) { USART2_SendString(ATCSCS\UCS2\\r\n); HAL_Delay(500); USART2_SendString(ATCMGF1\r\n); HAL_Delay(500); char cmd[64]; sprintf(cmd, ATCMGS\%s\\r\n, phone); USART2_SendString(cmd); HAL_Delay(500); USART2_SendString(ucs2_hex); // 例如 53174EAC USART2_SendByte(0x1A); HAL_Delay(3000); }参数说明ATCSCSUCS2必须在ATCMGF1之前或之后都行但发内容前必须设置好。UCS2字符串是纯十六进制不带0x前缀。如果发出去是乱码检查UCS2字节序——SIM800C默认大端5317对应“北”。5. 主循环调度与低功耗按键触发、屏幕刷新、GPS与GSM互不打架5.1 用状态机替代delay的主循环框架新手最容易犯的错在GPS解析里HAL_Delay(1000)等定位在GSM发送里HAL_Delay(3000)等短信结果屏幕卡死、按键没反应。正确做法是用状态机定时器主循环里非阻塞地轮询。下面是一个可复现的框架// main.c typedef enum { STATE_IDLE, STATE_GPS_WAIT, STATE_SMS_SEND } SysState_t; int main(void) { HAL_Init(); SystemClock_Config(); USART1_Init(); // GPS USART2_Init(); // GSM SPI1_Init(); // TFT TFT_Init(); GPS_Init(); GSM_Init(); SysState_t state STATE_IDLE; GPS_Data_t gps; uint32_t gps_start 0; while (1) { // 按键触发 if (Key_Pressed() state STATE_IDLE) { state STATE_GPS_WAIT; gps_start HAL_GetTick(); TFT_ShowString(Locating...); } // GPS解析非阻塞 if (state STATE_GPS_WAIT) { if (GPS_Parse(gps)) { char msg[64]; sprintf(msg, Lat:%.6f Lon:%.6f, gps.latitude, gps.longitude); TFT_ShowString(msg); state STATE_SMS_SEND; } else if (HAL_GetTick() - gps_start 60000) { TFT_ShowString(GPS Timeout); state STATE_IDLE; } } // 短信发送 if (state STATE_SMS_SEND) { char sms[80]; sprintf(sms, GPS:%.6f,%.6f, gps.latitude, gps.longitude); GSM_SendSMS_En(8613800138000, sms); TFT_ShowString(SMS Sent); state STATE_IDLE; } HAL_Delay(10); // 小延时让出CPU } }逻辑说明状态机三个状态IDLE等按键GPS_WAIT等定位SMS_SEND发短信。HAL_GetTick()做超时60秒没定位就回IDLE。参数说明HAL_Delay(10)是主循环节拍10ms一次既不太快也不太慢。GPS_Parse和GSM_SendSMS内部如果有delay要改成非阻塞或者放到状态里这里为了简洁保留了GSM的delay实际项目建议也改成状态机。5.2 屏幕刷新与GPS/GSM串口冲突的处理ILI9341用SPIGPS和GSM用UART硬件上不冲突。但软件上如果GPS中断和GSM中断同时来要注意优先级。我一般把GPS的USART1中断优先级设得比GSM的USART2高因为GPS数据量大丢一个字节可能整句就废了。屏幕刷新不要放在中断里放主循环每次刷一小块避免长时间占用SPI。如果屏幕花屏检查SPI时钟是不是太快ILI9341一般不超过10MHz先降到4MHz试试。6. 避坑与排查GPS定位慢、短信发不出、屏幕花屏的5个血泪经验6.1 坑一GPS冷启动半小时都定不了位现象上电后串口有NMEA数据但RMC的A字段一直是V经纬度是0。原因GPS天线没接好或者天线放在室内。ATGM336H必须接有源天线而且天线要放在窗边或室外。解决先确认天线供电3.3V或5V看模块要求再把天线吸在窗外。如果还不行用ATGNSS指令查卫星数少于4颗基本定不了。6.2 坑二SIM800C发短信返回ERROR现象发ATCMGS后返回ERROR或者一直不返回。原因号码格式不对或者短信模式没设对。解决号码必须带国家码比如8613800138000不能只写13800138000。另外ATCMGF1和ATCSCS的顺序要对先设模式再设字符集。如果返回CMS ERROR: 500说明模块没注册上网等ATCREG?返回0,1再发。6.3 坑三发中文短信对方收到乱码现象英文短信正常中文短信收到一堆问号或方块。原因没有用UCS2编码或者UCS2字符串字节序反了。解决ATCSCSUCS2必须设内容必须是十六进制字符串。如果收到乱码把UCS2字符串每两个字符交换一下比如5317改成1753试试。另外SIM800C对UCS2长度有限制一条短信最多70个汉字超了要分条。6.4 坑四一按按键STM32就复位现象按键触发发短信SIM800C一开始发射STM32就重启。原因SIM800C发射瞬间电流2A把电源拉垮了。解决SIM800C单独用锂电池供电或者用大电流LDO至少3A并在VCC和GND之间并一个1000uF电解电容。STM32的3.3V和SIM800C的4.0V要共地但电源要分开。6.5 坑五ILI9341读ID是0xA1A1但屏幕不亮现象SPI能读到ID 0xA1A1说明通信正常但屏幕背光亮、没内容。原因初始化序列不对或者复位时序不对。解决ILI9341的RST引脚要先拉低10ms再拉高然后等120ms再发初始化命令。初始化序列里0x11Sleep Out后必须等120ms0x29Display ON后等50ms。如果还不行检查DC引脚在发命令和发数据时是否正确切换。7. 进阶验证用串口助手模拟GPS和GSM做半实物测试7.1 为什么要在没有硬件的情况下先跑通逻辑GPS和GSM模块都受环境影响调试时经常分不清是代码问题还是模块问题。我一般先用串口助手模拟GPS发NMEA语句模拟GSM返回OK和把STM32的逻辑跑通再接实物。这样能把代码bug和环境问题分开。具体做法用PC的串口助手接STM32的USART1手动发$GNRMC,123519.00,A,3958.12345,N,11618.54321,E,0.0,0.0,230324,,,A*7C看STM32能不能解析出经纬度并显示在屏幕上。7.2 用Python脚本模拟GPS数据流下面是一个Python脚本通过串口持续发NMEA语句模拟GPS模块# gps_sim.py import serial import time ser serial.Serial(COM3, 9600, timeout1) # 改成你的串口号 rmc $GNRMC,123519.00,A,3958.12345,N,11618.54321,E,0.0,0.0,230324,,,A*7C\r\n while True: ser.write(rmc.encode()) time.sleep(1)逻辑说明每秒发一次RMC语句STM32的GPS解析函数应该能稳定解析出纬度39.9687经度116.3090。参数说明COM3改成实际串口波特率9600和STM32的USART1一致。如果STM32解析不出来先检查串口线是不是TX-RX交叉接了。7.3 验证短信发送成功的三个检查点接上SIM800C后怎么确认短信真的发出去了三个检查点第一ATCMGS后模块返回说明号码和模式都对第二发完0x1A后模块返回CMGS: 15说明短信已提交给网络第三目标手机收到短信。如果卡在第一步查号码和ATCMGF卡在第二步查信号质量和SIM卡余额卡在第三步查目标号码是不是写错了。我习惯在发短信前先发一条ATCSQ信号值低于10就先等等别硬发。最后说个习惯每次改完代码先用串口助手看AT指令的返回别急着接屏幕和GPS。STM32的串口打印是你的黑匣子printf重定向到USART3所有关键步骤都打一行日志出问题时一眼就能看出卡在哪。这个项目我前后做了三版第一版用delay阻塞第二版改状态机第三版才把中文短信和低功耗加上。别想着一次成功先把英文短信发出去再搞中文再搞屏幕一步一步来。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询