发布时间:2026/7/5 14:00:52
【ESP32S3 + ATGM332D GPS模块实战二:SSD1306交互显示】
ESP32S3 ATGM332D GPS模块实战二SSD1306交互显示一、项目背景与目标上一篇博客《ESP32S3 ATGM332D GPS模块实战一TinyGPSPlus解析与本地墨卡托投影》中我们已经实现了GPS数据的解析、经纬度到本地ENU坐标系的转换以及速度和航向的计算。**本文在此基础上新增SSD1306 OLED显示屏的集成实现实时经纬度显示距离参考点距离显示GPS信号状态指示平滑的数据可视化交互二、硬件连接图ESP32S3开发板 ────────── ATGM332D GPS模块 GPIO18 (TX) ── RX GPIO17 (RX) ── TX 3.3V ── VCC GND ── GND ESP32S3开发板 ────────── SSD1306 OLED模块 (I2C) GPIO12 (SCL) ── SCL GPIO13 (SDA) ── SDA 3.3V ── VCC GND ── GNDESP32S3开发板ATGM332D GPS模块SSD1306 OLED模块三、核心代码实现完整代码#includeArduino.h#includeTinyGPS.h#includemath.h#includeU8g2lib.h// ATGM332D GPS模块配置#defineGPS_UARTSerial1// 使用硬件串口1#defineGPS_BAUD_RATE9600// ATGM332D默认波特率为9600#defineGPS_TX_PIN18// UART1_TX#defineGPS_RX_PIN17// UART1_RX// SSD1306 OLED配置#defineOLED_SCK_PIN12// SCL#defineOLED_SDA_PIN13// SDA// 输出频率配置#definePRINT_INTERVAL_MS500// 每秒5次 (1000/5200ms)// TinyGPSPlus对象TinyGPSPlus gps;// U8g2对象I2C接口SSD1306 128x64U8G2_SSD1306_128X64_NONAME_F_HW_I2Cu8g2(U8G2_R0,/* reset*/U8X8_PIN_NONE,/* clock*/OLED_SCK_PIN,/* data*/OLED_SDA_PIN);// 参考点初始位置作为本地坐标系原点doublerefLat0.0;doublerefLng0.0;boolrefSetfalse;// 上一时刻的位置和时间doublelastEast0.0;doublelastNorth0.0;unsignedlonglastTimeMs0;// 计算得到的速度和航向floatcalcSpeed0.0;floatcalcHeading0.0;// 函数声明voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,doubleeast,doublenorth);voidupdateOLED(doublelat,doublelng,doubledistance);voidsetup(){// 初始化USB串口用于调试输出Serial.begin(115200);delay(500);Serial.println(ATGM332D GPS Module Test Demo);Serial.println(Using TinyGPSPlus Library with Local Mercator Projection);Serial.println(Initializing...);// 配置并初始化GPS串口GPS_UART.begin(GPS_BAUD_RATE,SERIAL_8N1,GPS_RX_PIN,GPS_TX_PIN);// 等待GPS模块启动delay(1000);// 初始化OLED屏幕u8g2.begin();u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);u8g2.drawStr(0,20,GPS Initializing...);u8g2.sendBuffer();Serial.println(GPS module initialized!);Serial.printf(GPS UART: Serial1, TX%d, RX%d, Baud%d\n,GPS_TX_PIN,GPS_RX_PIN,GPS_BAUD_RATE);Serial.printf(OLED: SCK%d, SDA%d\n,OLED_SCK_PIN,OLED_SDA_PIN);Serial.printf(Output frequency: %d times per second\n,1000/PRINT_INTERVAL_MS);Serial.println(Listening for GPS NMEA sentences...);Serial.println(----------------------------------------);}voidloop(){// 持续读取并解析GPS数据while(GPS_UART.available()0){gps.encode(GPS_UART.read());}// 当接收到新的定位信息时输出staticunsignedlonglastPrintTime0;if(millis()-lastPrintTimePRINT_INTERVAL_MS){// 每秒5次lastPrintTimemillis();Serial.println();// 检查是否有有效的定位数据if(gps.location.isValid()){doublelatgps.location.lat();doublelnggps.location.lng();// 设置参考点首次有效定位if(!refSet){refLatlat;refLnglng;refSettrue;Serial.println(参考点已设置);}// 将经纬度转换为本地ENU坐标doubleeast,north;latLngToENU(lat,lng,refLat,refLng,east,north);// 计算速度和航向基于位移unsignedlongcurrentTimeMsmillis();if(lastTimeMs0refSet){doubledt(currentTimeMs-lastTimeMs)/1000.0;// 时间差秒if(dt0.01){// 避免除零doubledxeast-lastEast;doubledynorth-lastNorth;// 计算速度 (m/s - km/h)calcSpeedsqrt(dx*dxdy*dy)/dt*3.6;// 计算航向角度 (弧度转角度)calcHeadingatan2(dx,dy)*180.0/M_PI;if(calcHeading0)calcHeading360.0;// 转换为0-360度}}// 更新上一时刻数据lastEasteast;lastNorthnorth;lastTimeMscurrentTimeMs;// 输出定位状态Serial.println(定位状态: 有效);// 输出时间if(gps.time.isValid()){Serial.printf(当前时间 (UTC): %02d:%02d:%06.3f\n,gps.time.hour(),gps.time.minute(),gps.time.second()gps.time.centisecond()/100.0);}else{Serial.println(当前时间: 无效);}// 输出日期if(gps.date.isValid()){Serial.printf(日期: %04d-%02d-%02d\n,gps.date.year(),gps.date.month(),gps.date.day());}// 输出经纬度Serial.printf(纬度: %.6f°\n,lat);Serial.printf(经度: %.6f°\n,lng);// 输出本地ENU坐标相对于参考点Serial.printf(东向偏移: %.3f m\n,east);Serial.printf(北向偏移: %.3f m\n,north);// 计算到参考点的距离doubledistancesqrt(east*eastnorth*north);Serial.printf(距参考点距离: %.3f m\n,distance);// 更新OLED显示updateOLED(lat,lng,distance);// 输出计算得到的速度和航向基于本地墨卡托投影Serial.printf(对地航速 (计算): %.6f km/h\n,calcSpeed);Serial.printf(航向角度 (计算): %.6f°\n,calcHeading);// 输出原始GPS数据对比Serial.printf(原始航速: %.6f km/h\n,gps.speed.kmph());Serial.printf(原始航向: %.6f°\n,gps.course.deg());// 输出卫星数量Serial.printf(卫星数量: %d\n,gps.satellites.value());// 输出HDOP (水平精度因子)if(gps.hdop.isValid()){Serial.printf(HDOP: %.2f\n,gps.hdop.hdop());}// 输出海拔高度if(gps.altitude.isValid()){Serial.printf(海拔高度: %.2f m\n,gps.altitude.meters());}}else{Serial.println(定位状态: 无效 (请移至开阔地带));Serial.println(当前时间: --:--:--.---);Serial.println(纬度: ---.------°);Serial.println(经度: ---.------°);Serial.println(对地航速: ---.------ km/h);Serial.println(航向角度: ---.------°);// 显示接收到的字符数Serial.printf(已接收字符数: %lu\n,gps.charsProcessed());// 更新OLED显示无有效定位u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);u8g2.drawStr(0,20,No GPS Signal);u8g2.drawStr(0,40,Move to open area);u8g2.sendBuffer();}Serial.println();Serial.println();}// 检查是否有解析错误if(gps.charsProcessed()10){Serial.println(等待GPS数据...);delay(500);}}// 将经纬度转换为本地ENU坐标东-北-天坐标系voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,doubleeast,doublenorth){// 地球半径米constdoubleR6378137.0;// 将角度转换为弧度doublelatRadlat*M_PI/180.0;doublelngRadlng*M_PI/180.0;doublerefLatRadrefLat*M_PI/180.0;doublerefLngRadrefLng*M_PI/180.0;// 计算cos(lat)用于缩放doublecosLatcos(refLatRad);// 计算ENU坐标eastR*(lngRad-refLngRad)*cosLat;northR*(latRad-refLatRad);}// 更新OLED显示voidupdateOLED(doublelat,doublelng,doubledistance){charbuf[32];u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);// 显示纬度sprintf(buf,Lat: %.6f,lat);u8g2.drawStr(0,12,buf);// 显示经度sprintf(buf,Lng: %.6f,lng);u8g2.drawStr(0,28,buf);// 显示距离参考点距离sprintf(buf,Dist: %.2fm,distance);u8g2.drawStr(0,44,buf);// 显示定位状态u8g2.drawStr(0,60,GPS: OK);u8g2.sendBuffer();}3.1 依赖库配置在platformio.ini中添加U8g2库lib_deps mikalhart/TinyGPSPlus^1.1.0 olikraus/U8g2^2.35.143.2 OLED初始化与配置#includeU8g2lib.h// SSD1306 OLED配置#defineOLED_SCK_PIN12// SCL#defineOLED_SDA_PIN13// SDA// U8g2对象I2C接口SSD1306 128x64U8G2_SSD1306_128X64_NONAME_F_HW_I2Cu8g2(U8G2_R0,/* reset*/U8X8_PIN_NONE,/* clock*/OLED_SCK_PIN,/* data*/OLED_SDA_PIN);voidsetup(){// 初始化OLED屏幕u8g2.begin();u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);u8g2.drawStr(0,20,GPS Initializing...);u8g2.sendBuffer();// ... GPS串口初始化 ...}代码解析**U8G2_SSD1306_128X64_NONAME_F_HW_I2C使用硬件I2C驱动128x64分辨率的SSD1306屏幕**u8g2_font_ncenB08_tr选择8像素高度的字体适合在小屏幕上显示清晰**drawStr(x, y, text)在指定坐标绘制字符串OLED坐标系原点在左上角3.3 距离参考点距离计算// 将经纬度转换为本地ENU坐标东-北-天坐标系voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,doubleeast,doublenorth){constdoubleR6378137.0;// 地球半径米doublelatRadlat*M_PI/180.0;doublelngRadlng*M_PI/180.0;doublerefLatRadrefLat*M_PI/180.0;doublerefLngRadrefLng*M_PI/180.0;doublecosLatcos(refLatRad);eastR*(lngRad-refLngRad)*cosLat;northR*(latRad-refLatRad);}// 在loop中计算距离doubleeast,north;latLngToENU(lat,lng,refLat,refLng,east,north);doubledistancesqrt(east*eastnorth*north);原理说明参考点首次有效定位时的位置作为本地坐标系原点ENU坐标系East-North-Up东-北-天局部平面坐标系距离公式distance √(east² north²)3.4 OLED显示更新函数voidupdateOLED(doublelat,doublelng,doubledistance){charbuf[32];u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);// 显示纬度sprintf(buf,Lat: %.6f,lat);u8g2.drawStr(0,12,buf);// 显示经度sprintf(buf,Lng: %.6f,lng);u8g2.drawStr(0,28,buf);// 显示距离参考点距离sprintf(buf,Dist: %.2fm,distance);u8g2.drawStr(0,44,buf);// 显示定位状态u8g2.drawStr(0,60,GPS: OK);u8g2.sendBuffer();}显示布局128x64像素┌────────────────────────────┐ │ Lat: 38.869190 │ ← y12 │ Lng: 121.532056 │ ← y28 │ Dist: 9.71m │ ← y44 │ GPS: OK │ ← y60 └────────────────────────────┘3.5 信号状态处理if(gps.location.isValid()){// 有有效定位 → 显示详细数据updateOLED(lat,lng,distance);}else{// 无有效定位 → 显示提示信息u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB08_tr);u8g2.drawStr(0,20,No GPS Signal);u8g2.drawStr(0,40,Move to open area);u8g2.sendBuffer();}四、实战日志分析下面是从串口监视器中截取的实际运行数据让我们分析其中的关键信息4.1 初始定位阶段 定位状态: 有效 纬度: 38.869190° 经度: 121.532056° 东向偏移: -9.086 m 北向偏移: -3.432 m 距参考点距离: 9.713 m 卫星数量: 0 HDOP: 25.50 分析要点✅ 虽然显示卫星数量: 0但竟然有定位结果 → 这说明模块实际上还处于冷启动阶段模块可能使用了上次的星历数据⚠️ HDOP25.50非常高正常应小于2.0以下才是高精度定位 参考点已设置后续数据都以此为原点4.2 定位稳定阶段 定位状态: 有效 纬度: 38.869191° 经度: 121.531948° 东向偏移: -18.461 m 北向偏移: -3.303 m 距参考点距离: 18.754 m 卫星数量: 4 HDOP: 4.00 分析要点✅ 卫星数量增加到4颗HDOP降低到4.00 位置在距离参考点约18米处说明设备在移动 移动方向向西东向偏移-18m表示向西移动4.3 高精度定位阶段 定位状态: 有效 纬度: 38.869194° 经度: 121.531933° 东向偏移: -19.747 m 北向偏移: -2.969 m 距参考点距离: 19.968 m 卫星数量: 4 HDOP: 4.00 海拔高度: 104.10 m 分析要点✅ HDOP4.00定位精度尚可 距离参考点距离稳定在20米左右 经纬度小数点后第6位开始稳定4.4 信号改善阶段 定位状态: 有效 纬度: 38.869193° 经度: 121.531955° 东向偏移: -17.912 m 北向偏移: -3.080 m 距参考点距离: 18.175 m 卫星数量: 5 HDOP: 2.30 分析要点✅ 卫星数量增加到5颗✅ HDOP降低到2.30定位精度显著提升 这是一组比较理想的数据五、OLED屏幕实际显示效果有信号时显示Lat: 38.869190 Lng: 121.532056 Dist: 9.71m GPS: OK无信号时显示No GPS Signal Move to open area六、技术要点总结6.1 U8g2库的使用技巧缓冲区机制clearBuffer()→ 绘制 →sendBuffer()避免屏幕闪烁字体选择ncenB08_tr是8像素高的字体适合128x64屏幕坐标系统左上角为原点(0,0)向右向下递增硬件I2C比软件I2C更稳定速度更快6.2 GPS数据精度分析指标指标含义推荐值卫星数量用于定位的卫星颗数≥ 4颗HDOP水平精度因子 2.0 (优秀)海拔高度海拔高度数据-6.3 距离参考点距离的应用场景步行导航实时显示距离目标点的距离车辆追踪记录车辆驶离起点的距离位置记录记录设备移动轨迹运动测距简单的运动距离计算七、完整代码完整代码已在项目中实现核心逻辑包括GPS串口数据读取与解析本地ENU坐标转换距离参考点距离计算SSD1306 OLED显示更新八、下一步计划在接下来的博客中我们将继续扩展功能 **计划三添加LED状态指示与按键交互 **计划四SD卡数据记录与轨迹回放 **计划五Web服务器远程监控 **计划六MQTT数据上传到云平台参考资源TinyGPSPlus库文档U8g2库文档ATGM332D模块手册总结通过集成SSD1306 OLED显示屏我们将原本只能在串口监视器中看到的数据以更直观的方式展示出来。这为后续的便携式GPS追踪器、导航设备等项目打下了良好的基础。

相关新闻

分布式事务尝试取消确认模式的具体实现步骤
2026/7/5 13:00:52

分布式事务尝试取消确认模式的具体实现步骤

分布式事务尝试取消确认模式的具体实现步骤在分布式系统架构中,事务一致性是核心挑战之一。传统的两阶段提交协议(2PC)虽然提供了强一致性保证,但其同步阻塞和协调者单点故障问题限制了高并发场景下的可用性。尝试取消确认模式&am…

阅读更多
openeuler/os-compat-analyzer架构解密:Golang后端+Vue3前端如何实现极速兼容性分析
2026/7/5 13:00:52

openeuler/os-compat-analyzer架构解密:Golang后端+Vue3前端如何实现极速兼容性分析

openeuler/os-compat-analyzer架构解密:Golang后端Vue3前端如何实现极速兼容性分析 【免费下载链接】os-compat-analyzer The os-compat-analyzer is a compatibility analysis tool for operating systems and upper-layer software, supporting OS migration and …

阅读更多
边缘计算中MoE模型的SSD I/O优化与FlashMoE架构
2026/7/5 13:00:52

边缘计算中MoE模型的SSD I/O优化与FlashMoE架构

1. 边缘计算中的MoE推理挑战与SSD I/O瓶颈 在边缘设备上部署混合专家模型(Mixture-of-Experts, MoE)时,存储访问效率成为关键瓶颈。与传统DNN模型不同,MoE模型的动态专家激活特性导致其内存访问模式具有显著的不规则性——每次推理…

阅读更多
5个理由告诉你为什么Altium Designer元件库能让你告别设计焦虑
2026/7/5 15:00:52

5个理由告诉你为什么Altium Designer元件库能让你告别设计焦虑

5个理由告诉你为什么Altium Designer元件库能让你告别设计焦虑 【免费下载链接】AltiumDesigner-Libraries Personal schematic symbol and footprint libraries for Altium Designer. 项目地址: https://gitcode.com/gh_mirrors/al/AltiumDesigner-Libraries 作为一名电…

阅读更多
AI 导出鸭实操指南:智谱清言生成 word 文档指令落地使用技巧
2026/7/5 15:00:52

AI 导出鸭实操指南:智谱清言生成 word 文档指令落地使用技巧

智谱清言生成word文档指令|AI导出鸭简化大模型文档导出全流程AI导出鸭实操指南:智谱清言生成word文档指令落地使用技巧智谱清言生成word文档指令实操优化,AI导出鸭一站式搞定文档导出 引言 当下借助大模型产出文本内容已成办公常态&#xff0…

阅读更多
CloudSSH 开源项目:借助 Cloudflare Workers 打造免费 Web SSH 终端,用浏览器丝滑远程服务器,连接信息云端同步,一键部署还不花一分钱
2026/7/5 15:00:52

CloudSSH 开源项目:借助 Cloudflare Workers 打造免费 Web SSH 终端,用浏览器丝滑远程服务器,连接信息云端同步,一键部署还不花一分钱

一个基于 Cloudflare Workers 的开源 Web SSH 终端——打开浏览器,连上服务器,开干。 起因 不知道你有没有过这种经历: 出差在外,手机收到服务器告警,手边没有电脑,或者电脑上没装 SSH 客户端。你急得四处…

阅读更多
FinalBurn Neo完整指南:打造完美街机游戏模拟体验的终极教程
2026/7/5 15:00:52

FinalBurn Neo完整指南:打造完美街机游戏模拟体验的终极教程

FinalBurn Neo完整指南:打造完美街机游戏模拟体验的终极教程 【免费下载链接】FBNeo FinalBurn Neo - We are Team FBNeo. 项目地址: https://gitcode.com/gh_mirrors/fb/FBNeo FinalBurn Neo(简称FBNeo)是一款专注于经典街机游戏模拟…

阅读更多
C语言 冒泡排序
2026/7/5 15:00:52

C语言 冒泡排序

冒泡排序:是一种简单直观的排序算法,核心思想是通过多次遍历数组,将较大的元素逐步“冒泡”到数组的末尾,最终实现排序。它的名字来源于排序过程中较大的元素像气泡一样逐渐上浮的过程。算法原理:冒泡排序通过比较相邻的两个元素,…

阅读更多
D2 select/poll/epoll
2026/7/5 14:00:52

D2 select/poll/epoll

注意两个点客户端断开fd的值0:stdin;1:stdout;2:stderr;递增,回收后复用限制1024个,ulimit -n 65536:提升到65536个IO多复用复用一个线程,去同时检测多路是否有IO事件就绪select通过传入位图,内核遍历位图,…

阅读更多
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御
2026/7/5 0:00:50

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

阅读更多
3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略
2026/7/5 0:00:50

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否曾为Windows右键菜单中那些…

阅读更多
GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复
2026/7/5 0:00:50

GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复

如果你正在用 GXDE OS 或者任何基于 Deepin 的发行版,并且遇到了“检测到窗口系统采用 Wayland 协议,程序即将退出”这类弹窗,或者发现 VMware Tools 在 Ubuntu 24.04 这类默认 Wayland 的系统上启动失败,那这篇文章就是为你准备的…

阅读更多
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御
2026/7/5 0:00:50

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

阅读更多
3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略
2026/7/5 0:00:50

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否曾为Windows右键菜单中那些…

阅读更多
GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复
2026/7/5 0:00:50

GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复

如果你正在用 GXDE OS 或者任何基于 Deepin 的发行版,并且遇到了“检测到窗口系统采用 Wayland 协议,程序即将退出”这类弹窗,或者发现 VMware Tools 在 Ubuntu 24.04 这类默认 Wayland 的系统上启动失败,那这篇文章就是为你准备的…

阅读更多
基于Dify与DeepSeek构建私有知识库问答系统实战指南
2026/7/4 11:17:16

基于Dify与DeepSeek构建私有知识库问答系统实战指南

在业务中快速构建一个能理解私有文档、准确回答专业问题的智能助手,是很多开发团队面临的共同挑战。传统方案往往需要从零开始搭建复杂的 RAG(检索增强生成)系统,涉及文档解析、向量化、检索、大模型调用等多个环节,整…

阅读更多
FAE放射组学分析工具:医学影像特征探索的完整解决方案
2026/7/4 5:24:16

FAE放射组学分析工具:医学影像特征探索的完整解决方案

FAE放射组学分析工具:医学影像特征探索的完整解决方案 【免费下载链接】FAE FeAture Explorer 项目地址: https://gitcode.com/gh_mirrors/fae/FAE 你是否曾经面对海量医学影像数据感到无从下手?想要从CT、MRI等影像中提取有价值的定量特征&#…

阅读更多
DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!
2026/7/4 15:20:35

DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!

DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖! 【免费下载链接】DesktopNaotu 桌面版脑图 (百度脑图离线版,思维导图) 跨平台支持 Windows/Linux/Mac OS. (A cross-platform multilingual Mind Map Tool) 项目地址:…

阅读更多