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 OLEDI2C 接口四针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.3VSTM32 的 USART 也是 3.3V所以直接交叉连接就行STM32 的 TX 接模组的 RXSTM32 的 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说明通信正常。然后依次发ATCPIN?查 SIM 卡、ATCSQ查信号、ATCMGF0设置 PDU 模式。这些指令在 STM32 代码里都会用到先在电脑上跑通心里有底。3. AT 指令与 PDU 编码核心解析3.1 Air780E 初始化指令序列Air780E 上电后不会自动进入我们想要的状态需要发一串指令把它驯服。我整理了一个最小可用的初始化序列每一步都有它的道理。第一步是AT就是握手确认串口通信正常。如果这一步没返回OK后面都不用看了先查接线和波特率。第二步是ATE0关闭回显。为什么要关因为如果不关你发的每一条指令都会被模组原样返回一遍解析响应的时候会混入多余字符增加代码复杂度。关了之后模组只返回执行结果清爽很多。第三步是ATCPIN?查 SIM 卡状态。正常返回是CPIN: READY。如果返回CPIN: SIM PIN说明卡被 PIN 码锁了需要先解锁。如果返回ERROR检查卡座接触是否良好。第四步是ATCSQ查信号质量。返回格式是CSQ: rssi,ber其中 rssi 是信号强度范围 0 到 31越大越好。一般来说rssi 低于 10 就基本没法发短信了需要检查天线或者换个位置。第五步是ATCMGF0设置短信模式为 PDU。为什么不用 Text 模式因为 Text 模式发中文会乱码这是很多新手踩的坑。PDU 模式虽然编码麻烦但支持 UCS2 编码中文短信必须走这条路。第六步是ATCSCSUCS2设置字符集为 UCS2。这一步和上一步配合确保中文内容能被正确编码。第七步是ATCNMI2,1,0,0,0设置新短信提示方式。这个不是发送必需的但如果你想让模组收到短信时主动通知 STM32就需要配置。我一般加上方便后续扩展。// Air780E 初始化指令序列基于常见实践整理 const char *init_cmds[] { AT\r\n, ATE0\r\n, ATCPIN?\r\n, ATCSQ\r\n, ATCMGF0\r\n, ATCSCS\UCS2\\r\n, ATCNMI2,1,0,0,0\r\n };每发一条指令后要等待模组返回OK或者预期响应再发下一条。中间加 200ms 到 500ms 的延时给模组一点处理时间。我试过连续快速发送结果模组直接不响应了后来加了延时才稳定。3.2 PDU 编码原理与中文短信构造PDU 编码是这套方案里最让人头疼的部分但理解了之后其实有规律可循。一条短信的 PDU 串包含几个关键字段短信中心号码、目标号码、协议标识、编码方式、有效期、用户数据长度、用户数据。短信中心号码一般存在 SIM 卡里可以用ATCSCA?查出来。如果查不到就需要手动设置格式是ATCSCA861380XXXX500具体号码问运营商。目标号码就是你要发给谁比如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。发送的时候先发ATCMGS长度这里的长度是 PDU 串去掉短信中心号码部分后的字符数除以 2。然后等模组返回提示符再把 PDU 串发过去最后发一个CtrlZ十六进制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 串发ATCMGS指令等提示符发 PDU 串和CtrlZ等最终响应。整个过程要加超时机制比如等提示符最多等 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有返回但后续指令报错重点查指令格式。比如ATCMGF0里的等号不能少ATCSCSUCS2里的引号必须是英文引号。我遇到过用中文引号导致报错的这种问题很隐蔽要仔细看。还有一种情况是模组进入了休眠模式不响应 AT 指令。这时候需要发一个唤醒信号比如拉低 PWRKEY 引脚一段时间或者发一个任意字符唤醒。Air780E 默认是不休眠的但如果你改过配置就要注意这一点。5.2 中文短信乱码或发送失败中文短信乱码九成是编码问题。确认ATCMGF0和ATCSCSUCS2都设置成功了。然后检查 PDU 串里的编码方式字段是不是08。如果写成00那就是 GSM 7-bit 编码中文会乱码。发送失败但返回OK这种情况也有。可能是短信中心号码不对或者目标号码格式不对。目标号码必须带国家码比如8613800138000不能只写13800138000。短信中心号码如果 SIM 卡里没有需要手动设置。还有一种失败是 PDU 串长度计算错误。ATCMGS后面的长度是 PDU 串去掉短信中心号码部分后的字符数除以 2。如果算错了模组会返回ERROR。我建议写个函数自动计算不要手算。问题现象可能原因排查方法模组无响应供电不足、接线错误、波特率不对量电压、查接线、确认波特率返回 ERROR指令格式错误、SIM 卡异常逐条指令测试、查 SIM 卡状态中文乱码编码方式不对确认 CMGF0、CSCSUCS2、DCS08发送失败号码格式错误、短信中心号码缺失检查号码带国家码、查 CSCAOLED 不显示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 编码和电源问题上。一旦跑通后面就是复制粘贴的事。如果你也在做类似的项目希望这些经验能帮你少走点弯路。