news 2026/10/2 15:58:54

STM32与Air780E实战:中文短信发送与OLED状态显示

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32与Air780E实战:中文短信发送与OLED状态显示

1. 项目缘起与整体方案拆解

按键一按,短信就发到手机上,而且内容还是中文——这个需求听起来简单,但真动手做过的朋友都知道,里面藏着好几个坑。我最近刚把一套基于 STM32 和 Air780E 的方案跑通,OLED 上能实时看到模块状态、信号强度和发送结果,整个过程从硬件连线到 PDU 编码踩了不少雷,今天就把完整思路和实操细节摊开讲一遍。

这套东西适合谁看?如果你手上有 STM32 开发板、想入门 4G 通信模组、又不想一上来就啃厚厚的 AT 指令手册,那这篇就是给你写的。核心链路其实就四块:STM32 做主控、Air780E 负责蜂窝通信、OLED 做本地状态显示、按键做触发输入。四者通过 UART 和 I2C 串起来,逻辑清晰,但每一环都有细节要抠。

先说为什么选 Air780E。市面上常见的 4G 模组不少,Air780E 的优势在于它把 AT 指令集做得比较规整,中文短信走 PDU 模式时对 UCS2 编码的支持很稳定,而且供电要求相对友好,3.7V 锂电池或者 5V 经 LDO 都能带得动。相比之下,有些模组在中文短信上要么编码转换麻烦,要么对串口波特率挑剔,调试起来很折磨人。Air780E 默认 115200 波特率,和 STM32 的 USART 配合起来基本不用改什么。

OLED 这边我选的是 0.96 寸 I2C 接口的 SSD1306。为什么不用 SPI 的?因为 I2C 只占两个引脚,接线简单,刷新率对于显示几行状态文字完全够用。这里有个热词里也提到的问题——0.9 寸 OLED 对 I2C 兼容性有时候会出幺蛾子,我实测下来 0.96 寸的 SSD1306 兼容性最好,驱动库成熟,HAL 库下移植也快。

整体方案的设计思路是这样的:STM32 上电后先初始化 OLED 和串口,然后给 Air780E 发一系列 AT 指令做初始化——关闭回显、查 SIM 卡状态、查信号质量、设置短信模式为 PDU、设置字符集为 UCS2。初始化完成后进入主循环,轮询按键状态。按键按下后,STM32 把预设的中文短信内容做 PDU 编码,通过串口发给模组,模组返回结果后再把状态刷到 OLED 上。

注意:Air780E 在发送短信的瞬间电流会突然拉高,峰值能到 2A 左右。如果你用 USB 口直接供电,很可能因为供电不足导致模组重启或者发送失败。我一开始就栽在这上面,后来换了一个能输出 2A 以上的独立电源才稳定下来。

这个方案最大的好处是把通信模组当做一个"黑盒"来处理,STM32 只管发 AT 指令和收响应,不需要深入理解 4G 协议栈。对于嵌入式入门来说,这是性价比最高的学习路径。而且 PDU 编码虽然看起来复杂,但一旦封装成函数,后面就是复制粘贴的事。

2. 硬件选型与连线细节

2.1 核心器件清单与选型理由

先把物料列清楚,免得你看到一半发现少东西。主控我用的是 STM32F103C8T6 最小系统板,也就是大家常说的"蓝板"。这颗芯片资源够用,USART 有两个,I2C 也有,Flash 64KB 跑这套逻辑绰绰有余。如果你手头是其他 STM32 型号,比如 F407 或者 G0 系列,代码逻辑一样,改一下 HAL 库的初始化就行。

通信模组就是 Air780E 开发板,注意买的时候要确认带 SIM 卡座和天线接口。天线千万别省,我试过不加天线,信号强度直接掉到 10 以下,短信根本发不出去。SIM 卡用普通的物联网卡或者手机副卡都行,但要注意有些物联网卡默认关闭了短信功能,需要找运营商开通。

显示部分用 0.96 寸 SSD1306 OLED,I2C 接口,四针:VCC、GND、SCL、SDA。按键就是一个普通的轻触开关,一端接 GPIO,另一端接地,配合内部上拉或者外部上拉电阻使用。

器件型号关键参数作用
主控STM32F103C8T672MHz, 64KB Flash, 20KB RAM逻辑控制与 AT 指令调度
通信模组Air780E4G Cat.1, 支持短信/语音/数据发送中文短信
显示屏SSD1306 0.96寸I2C, 128x64 分辨率显示状态与信号强度
按键轻触开关6x6mm触发发送动作
电源5V/2A 适配器峰值电流 2A给模组和主控供电

2.2 接线方案与电平匹配

接线这块看起来简单,但有几个地方容易翻车。Air780E 的串口电平是 3.3V,STM32 的 USART 也是 3.3V,所以直接交叉连接就行:STM32 的 TX 接模组的 RX,STM32 的 RX 接模组的 TX。千万别接反,接反了收不到任何响应,而且不会烧,只是干瞪眼。

OLED 的 I2C 接线要注意上拉电阻。很多 OLED 模块自带上拉,但如果你买的模块没有,就需要在 SCL 和 SDA 上各接一个 4.7K 到 10K 的上拉电阻到 3.3V。我遇到过一块模块没加上拉,结果 I2C 通信时好时坏,查了半天才发现是这个问题。

按键接在 PA0 上,配置为输入模式,启用内部上拉。这样按键未按下时读到高电平,按下时读到低电平。如果你用的是外部上拉,那就把内部上拉关掉,避免重复上拉导致电平异常。

电源部分要单独说。Air780E 的供电范围是 3.4V 到 4.2V,典型值 3.7V。你可以用锂电池直接供,也可以用 5V 经 LDO 降到 3.7V。但不管哪种方式,都要保证能提供 2A 的峰值电流。我建议在模组电源引脚旁边并一个 1000uF 的电解电容,再并一个 0.1uF 的陶瓷电容,这样能有效吸收发送瞬间的电流冲击。

提示:STM32 和 Air780E 最好分开供电,或者至少用独立的 LDO 给模组供电。我试过共用一个 LDO,结果发送短信时 STM32 会跟着复位,原因就是电流被模组拉垮了。

2.3 调试工具准备

在正式写代码之前,建议先准备一个 USB 转 TTL 模块,用来单独调试 Air780E。把模组通过 USB 转 TTL 接到电脑上,用串口助手手动发 AT 指令,确认模组能正常注册网络、能发短信。这一步非常关键,因为如果模组本身有问题,你在 STM32 上怎么调都是白费功夫。

串口助手我习惯用 SSCOM 或者 XCOM,设置 115200 波特率、8 数据位、1 停止位、无校验。先发一个AT,如果返回OK,说明通信正常。然后依次发AT+CPIN?查 SIM 卡、AT+CSQ查信号、AT+CMGF=0设置 PDU 模式。这些指令在 STM32 代码里都会用到,先在电脑上跑通,心里有底。

3. AT 指令与 PDU 编码核心解析

3.1 Air780E 初始化指令序列

Air780E 上电后不会自动进入我们想要的状态,需要发一串指令把它"驯服"。我整理了一个最小可用的初始化序列,每一步都有它的道理。

第一步是AT,就是握手,确认串口通信正常。如果这一步没返回OK,后面都不用看了,先查接线和波特率。

第二步是ATE0,关闭回显。为什么要关?因为如果不关,你发的每一条指令都会被模组原样返回一遍,解析响应的时候会混入多余字符,增加代码复杂度。关了之后,模组只返回执行结果,清爽很多。

第三步是AT+CPIN?,查 SIM 卡状态。正常返回是+CPIN: READY。如果返回+CPIN: SIM PIN,说明卡被 PIN 码锁了,需要先解锁。如果返回ERROR,检查卡座接触是否良好。

第四步是AT+CSQ,查信号质量。返回格式是+CSQ: rssi,ber,其中 rssi 是信号强度,范围 0 到 31,越大越好。一般来说,rssi 低于 10 就基本没法发短信了,需要检查天线或者换个位置。

第五步是AT+CMGF=0,设置短信模式为 PDU。为什么不用 Text 模式?因为 Text 模式发中文会乱码,这是很多新手踩的坑。PDU 模式虽然编码麻烦,但支持 UCS2 编码,中文短信必须走这条路。

第六步是AT+CSCS="UCS2",设置字符集为 UCS2。这一步和上一步配合,确保中文内容能被正确编码。

第七步是AT+CNMI=2,1,0,0,0,设置新短信提示方式。这个不是发送必需的,但如果你想让模组收到短信时主动通知 STM32,就需要配置。我一般加上,方便后续扩展。

// Air780E 初始化指令序列(基于常见实践整理) const char *init_cmds[] = { "AT\r\n", "ATE0\r\n", "AT+CPIN?\r\n", "AT+CSQ\r\n", "AT+CMGF=0\r\n", "AT+CSCS=\"UCS2\"\r\n", "AT+CNMI=2,1,0,0,0\r\n" };

每发一条指令后,要等待模组返回OK或者预期响应,再发下一条。中间加 200ms 到 500ms 的延时,给模组一点处理时间。我试过连续快速发送,结果模组直接不响应了,后来加了延时才稳定。

3.2 PDU 编码原理与中文短信构造

PDU 编码是这套方案里最让人头疼的部分,但理解了之后其实有规律可循。一条短信的 PDU 串包含几个关键字段:短信中心号码、目标号码、协议标识、编码方式、有效期、用户数据长度、用户数据。

短信中心号码一般存在 SIM 卡里,可以用AT+CSCA?查出来。如果查不到,就需要手动设置,格式是AT+CSCA="+861380XXXX500",具体号码问运营商。目标号码就是你要发给谁,比如+8613800138000。

编码方式这里选 UCS2,对应 PDU 里的 TP-DCS 字段值为08。用户数据就是短信内容,中文经过 UCS2 编码后,每个字符占两个字节,用十六进制表示。比如"你好"的 Unicode 是4F60 597D,在 PDU 里就写成4F60597D。

用户数据长度字段要注意,它表示的是用户数据的字节数,不是字符数。"你好"是两个字符,UCS2 编码后是 4 个字节,所以长度字段写04。

整个 PDU 串的构造逻辑是这样的:先拼短信中心号码部分,再拼目标号码部分,然后拼协议标识、编码方式、有效期,最后拼用户数据长度和用户数据。每一部分都有特定的编码规则,比如号码要用半字节交换的方式编码。

我举个完整的例子。假设短信中心号码是+8613800250500,目标号码是+8613800138000,内容是"你好"。

短信中心号码部分:0891683108200505F0。其中08是长度,91是国际格式,后面是号码的半字节交换编码,最后F0是补位。

目标号码部分:11000D91683108013800F0。11是目标号码长度,00是协议标识,0D是目标号码长度(13位),91是国际格式,后面是号码编码。

协议标识和编码方式:0008。00是协议标识,08是 UCS2 编码。

有效期:00。表示默认有效期。

用户数据长度:04。表示后面有 4 个字节的用户数据。

用户数据:4F60597D。"你好"的 UCS2 编码。

把这些拼起来,完整的 PDU 串就是:0891683108200505F011000D91683108013800F0000800044F60597D。

发送的时候,先发AT+CMGS=<长度>,这里的长度是 PDU 串去掉短信中心号码部分后的字符数除以 2。然后等模组返回>提示符,再把 PDU 串发过去,最后发一个Ctrl+Z(十六进制1A)表示结束。

// PDU 编码核心逻辑示意(基于常见实践整理) void build_pdu(char *pdu, const char *sca, const char *dest, const char *msg) { // 拼接短信中心号码部分 // 拼接目标号码部分 // 拼接协议标识与编码方式 // 拼接有效期 // 拼接用户数据长度 // 拼接 UCS2 编码后的用户数据 }

注意:PDU 串里的长度字段都是十六进制,不是十进制。比如长度 17 要写成11,长度 13 要写成0D。我一开始用十进制写,结果模组一直返回ERROR,查了好久才发现是这个问题。

3.3 串口收发与响应解析

STM32 和 Air780E 之间的串口通信,我建议用中断接收加空闲中断的方式。为什么?因为模组的响应长度不固定,用轮询方式容易丢数据。用 HAL 库的话,可以开启UART_IT_IDLE空闲中断,在中断里判断一帧数据接收完毕,然后置一个标志位,主循环里再处理。

接收缓冲区要开够大,我一般开 256 字节。因为 PDU 串加上响应前缀,有时候会超过 128 字节。如果缓冲区太小,数据会被截断,解析就会出错。

解析响应的逻辑要简单粗暴一点。不要试图做复杂的字符串匹配,就用strstr找关键字。比如发送短信后,如果响应里包含+CMGS:和OK,就认为发送成功。如果包含ERROR,就认为失败。然后把结果映射到 OLED 上显示。

// 串口接收与解析示意(基于常见实践整理) void parse_response(char *buf) { if (strstr(buf, "+CMGS:") && strstr(buf, "OK")) { sms_status = SMS_SUCCESS; } else if (strstr(buf, "ERROR")) { sms_status = SMS_FAIL; } }

这里有个细节:模组的响应有时候会分多次到达,比如先来+CMGS: 12,隔几十毫秒再来OK。如果你在第一次收到数据时就判断,可能会漏掉后面的OK。我的做法是收到数据后不立即判断,而是等空闲中断触发后再统一解析,这样能保证一帧数据完整。

4. OLED 状态显示与按键交互实现

4.1 HAL 库驱动 OLED 的关键步骤

OLED 驱动这块,网上有很多现成的 SSD1306 库,但很多是基于标准库的,移植到 HAL 库需要改 I2C 读写函数。我建议直接找 HAL 库版本的驱动,或者自己封装两个函数:OLED_WR_Byte和OLED_WR_Cmd,底层调用HAL_I2C_Mem_Write。

初始化流程是这样的:先给 OLED 上电,延时 100ms 等它稳定,然后发一系列初始化命令。这些命令包括关闭显示、设置时钟分频、设置多路复用率、设置显示偏移、设置起始行、设置对比度、设置预充电周期、设置 COM 引脚配置、设置 VCOMH 电压、开启电荷泵、开启显示。每一步的命令值在 SSD1306 数据手册里都有,照着写就行。

显示汉字需要取模。我一般用 PCtoLCD2002 这个软件,设置模式为阴码、逐列式、顺向,取模方式选 C51 格式。取出来的数组直接放到代码里,调用显示函数时指定起始坐标和汉字数组就行。

// OLED 显示汉字示意(基于常见实践整理) void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t *ch) { // 设置页地址和列地址 // 逐列写入汉字点阵数据 }

提示:0.96 寸 OLED 的 I2C 地址通常是 0x78 或 0x7A,取决于模块上的电阻配置。如果你发指令没反应,先用 I2C 扫描程序查一下地址,确认地址对了再调驱动。

4.2 状态显示界面设计

OLED 只有 128x64 的分辨率,能显示的内容有限,所以要精打细算。我设计了四行显示:第一行显示"STM32 SMS",第二行显示模组状态(初始化中/就绪/发送中/成功/失败),第三行显示信号强度(CSQ 值),第四行显示按键提示。

状态更新策略是"变化时才刷新"。因为 OLED 刷新需要时间,如果每轮循环都全屏刷新,按键响应会变慢。我的做法是用一个变量记录当前状态,只有状态变化时才调用显示函数。信号强度也是,只有 CSQ 值变化超过一定阈值才更新。

界面布局用坐标来管理。比如第一行从 (0, 0) 开始,第二行从 (0, 16) 开始,第三行从 (0, 32) 开始,第四行从 (0, 48) 开始。每个汉字占 16x16 像素,一行能显示 8 个汉字。英文和数字用 6x8 或 8x16 的字体,灵活搭配。

4.3 按键消抖与发送流程

按键处理看起来简单,但机械按键的抖动如果不处理,一次按下可能会触发多次发送。我用的是软件消抖:检测到低电平后延时 20ms 再检测,如果还是低电平,就确认按下。然后等按键释放,再执行发送动作。

发送流程是这样的:按键确认后,先把状态设为"发送中"并刷新 OLED,然后构造 PDU 串,发AT+CMGS指令,等>提示符,发 PDU 串和Ctrl+Z,等最终响应。整个过程要加超时机制,比如等>提示符最多等 5 秒,等最终响应最多等 10 秒。超时了就判失败,把状态设为"失败"并刷新 OLED。

// 按键处理与发送流程示意(基于常见实践整理) void key_handler(void) { if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { HAL_Delay(20); if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { sms_status = SMS_SENDING; OLED_Refresh(); send_sms(); while (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET); } } }

发送过程中,OLED 上会依次显示"发送中..."、"发送成功"或"发送失败"。如果失败,我还会把错误码显示出来,方便排查。比如ERROR对应模组返回错误,TIMEOUT对应超时,NO SIM对应 SIM 卡异常。

5. 常见问题与排查技巧实录

5.1 模组无响应或返回 ERROR

这是最常见的问题,原因可能有很多。先查供电,用万用表量模组电源引脚,发送瞬间电压不能低于 3.4V。如果电压掉得厉害,换电源或者加大电容。再查串口接线,TX 和 RX 是否交叉,波特率是否 115200。然后查 SIM 卡,是否插好,是否欠费,是否开通短信功能。

如果AT有返回但后续指令报错,重点查指令格式。比如AT+CMGF=0里的等号不能少,AT+CSCS="UCS2"里的引号必须是英文引号。我遇到过用中文引号导致报错的,这种问题很隐蔽,要仔细看。

还有一种情况是模组进入了休眠模式,不响应 AT 指令。这时候需要发一个唤醒信号,比如拉低 PWRKEY 引脚一段时间,或者发一个任意字符唤醒。Air780E 默认是不休眠的,但如果你改过配置,就要注意这一点。

5.2 中文短信乱码或发送失败

中文短信乱码,九成是编码问题。确认AT+CMGF=0和AT+CSCS="UCS2"都设置成功了。然后检查 PDU 串里的编码方式字段是不是08。如果写成00,那就是 GSM 7-bit 编码,中文会乱码。

发送失败但返回OK,这种情况也有。可能是短信中心号码不对,或者目标号码格式不对。目标号码必须带国家码,比如+8613800138000,不能只写13800138000。短信中心号码如果 SIM 卡里没有,需要手动设置。

还有一种失败是 PDU 串长度计算错误。AT+CMGS=后面的长度是 PDU 串去掉短信中心号码部分后的字符数除以 2。如果算错了,模组会返回ERROR。我建议写个函数自动计算,不要手算。

问题现象可能原因排查方法
模组无响应供电不足、接线错误、波特率不对量电压、查接线、确认波特率
返回 ERROR指令格式错误、SIM 卡异常逐条指令测试、查 SIM 卡状态
中文乱码编码方式不对确认 CMGF=0、CSCS=UCS2、DCS=08
发送失败号码格式错误、短信中心号码缺失检查号码带国家码、查 CSCA
OLED 不显示I2C 地址错误、上拉电阻缺失扫描 I2C 地址、加上拉电阻

5.3 OLED 显示异常与 I2C 通信故障

OLED 不显示,先查 I2C 地址。用 I2C 扫描程序扫一下,看看能不能找到设备。如果找不到,查接线和上拉电阻。如果找到了但显示花屏,可能是初始化命令不对,或者显示缓冲区没清空。

显示内容闪烁或者部分缺失,通常是刷新频率太高或者 I2C 速率太快。把 I2C 速率降到 100kHz 试试,或者减少刷新次数。我一般只在状态变化时刷新,这样既省时间又稳定。

还有一种情况是 OLED 供电不足。有些模块对 3.3V 要求比较严,如果电压偏低,显示会变暗或者不显示。量一下 VCC 引脚电压,确保在 3.3V 左右。

5.4 实操心得与避坑清单

第一个心得:先在电脑上把 AT 指令跑通,再移植到 STM32。这样能把模组问题和代码问题分开,排查效率高很多。

第二个心得:PDU 编码写个测试函数,在电脑上先用串口助手验证。把生成的 PDU 串手动发给模组,确认能发送成功,再把函数移植到 STM32。

第三个心得:OLED 显示不要频繁全屏刷新,用局部刷新或者变化刷新。这样能减少 I2C 占用时间,提高系统响应速度。

第四个心得:电源一定要给足。Air780E 发送瞬间的电流冲击很大,电源不行的话,什么代码都白搭。

第五个心得:串口接收缓冲区开大一点,256 字节起步。PDU 串加上响应前缀,很容易超过 128 字节。

第六个心得:加超时机制。等模组响应不能无限等,设个 5 到 10 秒的超时,超时就判失败,避免程序卡死。

第七个心得:按键消抖不能省。机械按键的抖动会导致一次按下触发多次发送,加 20ms 延时消抖就能解决。

第八个心得:调试信息要丰富。OLED 上显示的状态越详细,排查问题越容易。我一般会显示当前执行的指令和模组返回的关键字。

这套方案我前后调了大概一周,大部分时间花在 PDU 编码和电源问题上。一旦跑通,后面就是复制粘贴的事。如果你也在做类似的项目,希望这些经验能帮你少走点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 15:58:28

UE5蓝图真实能力边界:可视化脚本的工程实践与性能真相

1. 这不是“拖拽游戏”&#xff0c;而是用逻辑积木搭出真实交互——UE5蓝图系统的真实能力边界“我的规矩就是规矩&#xff1f;&#xff01;”这句话乍看像一句江湖气十足的调侃&#xff0c;但放在UE5蓝图语境里&#xff0c;它意外精准地戳中了核心&#xff1a;你定义的节点连接…

作者头像 李华
网站建设 2026/10/2 15:56:57

GradCIR在FashionIQ上突破0.6703:分级监督如何提升跨模态检索排序质量

1. 从0.6703这个数字说起&#xff1a;GradCIR在FashionIQ上到底做对了什么FashionIQ这个数据集在视觉搜索圈子里不算新面孔&#xff0c;但每次有人在它上面刷出有意义的涨幅&#xff0c;都值得停下来看看。沃尔玛团队这次把GradCIR推到0.6703的Recall10&#xff0c;同时NDCG比基…

作者头像 李华
网站建设 2026/10/2 15:55:53

单元测试在敏捷开发与持续交付中的关键作用与实战要点

1. 单元测试在敏捷迭代中的角色定位1.1 为什么说单元测试不是敏捷开发的选修课做敏捷做了七八年&#xff0c;我对单元测试的态度发生过一次很彻底的转变&#xff1a;从最早的“写它干嘛、纯浪费时间”&#xff0c;变成后来的“没它我根本不敢说这个迭代能交付”。促成这个转变的…

作者头像 李华
网站建设 2026/10/2 15:53:52

Linux服务器监控与进程守护:Monit轻量级开源工具实战指南

如果你运维过三五台Linux服务器&#xff0c;一定经历过这种场景&#xff1a;网站突然打不开&#xff0c;登录服务器一看&#xff0c;磁盘满了或者Nginx进程早没了&#xff1b;又或者你明明写了个定时脚本去守护服务&#xff0c;结果脚本自己崩了&#xff0c;服务也跟着一起出事…

作者头像 李华
网站建设 2026/10/2 15:53:34

微信开源知识库项目深度拆解:企业级RAG与混合检索实践指南

微信开源了一个知识库项目&#xff0c;这事在技术圈里炸开之后&#xff0c;我第一时间就去扒了代码和文档。说实话&#xff0c;刚看到标题的时候我以为又是哪个团队拿向量数据库套了个壳&#xff0c;结果翻完架构设计之后发现&#xff0c;微信这次开源的东西远不止“企业级 RAG…

作者头像 李华