1. 4G模块短信发送模式选择:Text vs PDU
第一次用4G模块发中文短信时,我踩了个大坑:用Text模式发了条"设备告警!温度过高",结果对方收到的是乱码。后来才发现,Text模式对中文支持就是个"薛定谔的猫"——理论上可以,实际全靠运气。经过多次实测,总结出两条铁律:
Text模式适合纯英文/数字场景,指令简单直观。比如发个验证码"Your code: 123456",用Text模式三行AT指令搞定:
AT+CMGF=1 AT+CMGS="13800138000" > Hello World\x1APDU模式才是中文短信的"真命天子"。虽然编码过程像玩魔方,但兼容性绝对靠谱。同样的中文内容,PDU模式需要经过:
- 短信中心号编码(奇偶位交换)
- 手机号编码(补F+奇偶位交换)
- 中文转Unicode(比如"测试"→6D4B8BD5)
- 组合成PDU串
实测对比数据:
| 指标 | Text模式 | PDU模式 |
|---|---|---|
| 中文支持 | 部分运营商不支持 | 100%兼容 |
| 指令复杂度 | ★☆☆☆☆ (简单) | ★★★★☆ (复杂) |
| 数据量 | 原始文本 | 编码后体积增加30% |
| 典型响应时间 | 2-3秒 | 3-5秒 |
特别提醒:有些模块(比如SIMCOM的SIM7600)在Text模式下发中文,会偷偷转成PDU格式发送。这种"伪Text模式"反而容易导致混乱,建议统一用PDU模式。
2. PDU模式编码避坑实战
2.1 短信中心号处理:90%新手的第一道坎
去年给某智能水表项目调试时,连续20条短信发送失败,最后发现是短信中心号少了个"F"。PDU编码的第一个"拦路虎"就是短信中心号处理,分三步走:
- 获取本地号码:用
AT+CSCA?查询(注意问号别漏),比如返回"+8613800100500" - 奇偶位交换:
# Python示例代码 sca = "+8613800100500" sca = sca[1:] + 'F' # 去掉+号补F → 8613800100500F pdu_sca = ''.join([sca[i+1]+sca[i] for i in range(0,len(sca),2)]) # 得到 683108100005F0 - 添加长度字节:长度=原始号码字符数(去掉+号后),十六进制表示。上例中8613800100500是13位,补F后14位,即0x0E→"0E"
常见错误:
- 漏掉国家代码(86开头)
- 忘记补F凑偶数
- 长度计算错误(应该用交换前的字符数)
2.2 手机号编码:小心国际标志位
给"+8613812345678"发短信时,如果直接编码会提示"invalid number"。PDU格式要求:
- 类型字节必须为91(国际号码)
- 号码要去掉+号并补F:
# 原始号码:+8613812345678 1. 去+ → 8613812345678 2. 补F → 8613812345678F 3. 奇偶交换 → 683118325476F8 - 长度字节是号码数字位数(不含+),十六进制。上例11位→0x0B
2.3 中文编码:Unicode的坑
"温度告警"四个字,用在线工具转Unicode得到"6E29→5EA6→544A→8B66",但直接发送会乱码。必须注意:
- 字节序问题:模块要求大端序(高位在前),而某些工具输出小端序
- 长度计算:
AT+CMGS参数是字节数,中文长度=字符数×2 - DCS设置:必须为08(UCS2编码)
推荐用Python标准库编码:
content = "温度告警".encode('utf-16be').hex().upper() # 得到 6E295EA6544A8B663. AT指令序列优化方案
3.1 完整PDU发送流程
以发送"设备重启"到13800138000为例:
初始化模块
AT+CMGF=0 # 切换PDU模式 AT+CSCS="UCS2" # 设置Unicode编码发送PDU数据
AT+CMGS=19 # PDU数据长度(字节) > 0011000B913168108001F0000800048BBEE7D6B53D\x1A其中PDU串组成:
00 # 使用预设短信中心 11 # 报文类型 00 # 消息参考 0B # 目标号码长度(11位) 91 # 国际号码 3168108001F0 # 编码后的手机号 00 # PID 08 # DCS(UCS2) 0004 # 有效期 04 # 用户数据长度(4字符×2) 8BBEE7D6B53D # "设备重启"的Unicode
3.2 错误处理机制
某次现场调试发现短信成功率只有60%,后来加入重试机制后提升到99%。关键策略:
- 超时检测:每条指令设置3秒超时
- 状态机控制:
graph TD A[发送AT+CMGS] -->|成功| B[等待>提示符] B --> C[发送PDU数据] C -->|5秒内无响应| D[记录错误日志] C -->|收到+CMGS| E[发送成功] - 典型错误码处理:
- CME ERROR 10:SIM卡未就绪→检查
AT+CPIN? - CME ERROR 302:存储满→发送
AT+CMGD=1,4清空
- CME ERROR 10:SIM卡未就绪→检查
4. 中文乱码问题排查清单
遇到乱码时,按照这个顺序检查:
编码一致性验证
- 确认所有环节统一使用UCS2
- 用
AT+CSCS?查询当前编码设置
PDU结构诊断
# 示例错误PDU 0891683108100005F011000B913168108001F0000800048BBEE7D6B53D # 分段验证: - 0891...F0 # 短信中心部分 - 11000B91...F0 # 目标地址部分 - 000804...B53D # 用户数据部分模块兼容性测试发现某些模块(如EC20)需要额外配置:
AT+QCMGS=1 # 启用长短信支持
实测案例:某项目用SIM7600发中文,必须添加AT+CSMP=17,167,0,8才能正常显示。
5. 性能优化技巧
- 预编码缓存:对于固定内容(如告警模板),提前计算PDU编码
- 批量发送策略:
// 伪代码示例 for(int i=0; i<3; i++){ if(send_pdu(pdu_data)) break; delay(1000 * (i+1)); // 指数退避 } - 内存管理:发送长短信时,建议分片:
# 第一片 AT+CMGS=82 > 0051000B...000A06C4E09B44... # 第二片 AT+CMGS=56 > 0051000B...000B4F60D898DE...
最后分享一个真实踩坑记录:某次发送"设备ID:A-102"始终失败,后来发现短横线"-"在GSM 7bit编码表中不存在。解决方案是强制使用UCS2编码,或者替换为全角"-"。