最稳方案是使用 serialport 或 libserial 库发 at 指令;必须严格遵循 \r\n 结尾、等待 > 提示符、发送 \x1a 结束、校验波特率等参数,并结合 at+creg?/at+csq 等指令诊断失败原因。

用 serialport 库发 AT 指令最稳
Windows/Linux 下 C++ 直接操作串口发短信,核心就是发 AT 指令。别自己封装 CreateFile 或 open() + ioctl,容易丢字节、乱时序。推荐用成熟的 serialport(C++ binding)或 libserial,它们自动处理缓冲、超时、行尾换行符(\r\n)等细节。
常见错误:用 std::cout 或 printf 往串口写,结果没刷新、没加 \r\n,模块完全没响应;或者写完立刻读,但模块还没来得及回复,读到空数据。
- 必须用
write()后跟read(),且两次调用之间加短延时(比如 100ms),或等模块返回OK/ERROR - AT 指令结尾固定是
\r\n,不是\n,Windows 下用"AT+CMGF=1\r\n",别漏\r -
AT+CMGS="139xxxxxxx"发送前要等模块返回>提示符,再发短信内容 +\x1A(Ctrl+Z)结束
AT+CMGF=1 和 AT+CMGF=0 别混用
文本模式(AT+CMGF=1)和 PDU 模式(AT+CMGF=0)语法、编码、长度限制完全不同。C++ 程序里一旦设成文本模式,就别在中途切回 PDU——有些 GSM 模块不支持动态切换,会卡死或返回 ERROR。
文本模式适合中文短消息(需模块支持 UCS2 编码),但实际发中文时仍要手动转 UCS2 字节流;PDU 模式更底层、兼容性好,但解析复杂。普通用途直接用文本模式 + ASCII 号码 + 英文内容最省事。
立即学习“C++免费学习笔记(深入)”;
- 设文本模式:
AT+CMGF=1\r\n→ 必须收到OK才能继续 - 发中文前确认模块是否支持:
AT+CSCS="UCS2"\r\n,否则中文变乱码 - 发英文短信时,
AT+CMGS="+86139xxxxxxx"\r\n后等待>,再发内容(如"Hello")加\x1A
串口参数错一个,整个通信就静音
GSM 模块默认波特率多为 9600 或 115200,但不同厂商/固件版本可能不同。如果发指令后无任何返回(连 ERROR 都没有),第一反应就是查波特率、校验位、停止位是否和模块一致。
典型配置是:9600 波特率、8 数据位、无校验(N)、1 停止位(1)。有些模块要求硬件流控关闭(RTS/CTS = OFF),开的话会握手失败。
- 先用串口调试工具(如 XShell、Minicom)连上,手动发
AT\r\n确认能通,再写 C++ 代码 - Linux 下注意权限:
/dev/ttyUSB0通常需要加到dialout组,否则open()返回 -1 - Windows 下端口号可能是
COM3、COM4,不能硬编码,最好让用户可配或扫描可用端口
短信发送失败时,ERROR 不等于“没发出去”
模块返回 ERROR 只说明指令执行失败,原因可能是 SIM 卡未就绪、信号弱、存储满、号码格式错,甚至刚插卡还没注册上网络。不能一见 ERROR 就报“发送失败”,得结合 AT+CREG?(网络注册状态)、AT+CSQ(信号强度)、AT+CPIN?(SIM 卡状态)一起查。
尤其注意:部分模块在信号差时返回 +CMS ERROR: 500(“No network service”),而不是通用 ERROR;这种错误码要单独解析,不能全当异常抛出。
- 发
AT+CMGS前务必检查:AT+CREG?返回+CREG: 0,1或+CREG: 0,5(已注册) - 避免连续快速重试,模块有指令队列限制,高频发
AT可能被丢弃 - 每次发送后清空串口输入缓冲区,防止旧响应干扰下一次读取
OK 和 +CMTI 这类关键响应。










