拿到 Air780E 这款开发板那天我第一件事没去折腾 Lua 脚本也没急着连云平台而是先打开串口助手敲了一条AT。在 5 分钟里把短信发出去听起来简单实际是验证板子好坏、SIM 卡是否有效、串口链路是否通畅最快的方式。很多人在这个环节卡住不是模组有问题而是根本没把通信链路跑通。这篇文章就把我反复验证过的流程完整写下来基于 AT 指令而不是复杂固件开发附可直接复制运行的完整代码适合刚拿到 Air780E 开发板、想最快验证硬件和 SIM 卡的开发者参考。1. 为什么短信发送是第一道坎AT 指令又是最快的切入方式很多人拿到 Air780E 的第一反应是去查 Lua 开发文档或者想着用 C 写固件。但真实的项目经验是——第一步不是功能开发而是确认“模组能正常通信”。短信发送正好是这个确认过程的核心测试项它既依赖射频信号又依赖 SIM 卡鉴权还依赖串口数据通路任何一个环节有问题都会直接暴露。1.1 Air780E 是什么样的一块板子Air780E 是合宙推出的 LTE Cat.1 模组相比传统 2G 模组它存活在 4G 网络生态里支持移动、联通、电信的网络不需要 2G/3G 退网的问题。同时它主打低功耗适合智能表计、定位器、共享设备这类对功耗敏感的物联网场景。开发板一般把模组、天线座、SIM 卡座、电源电路、USB 转串口电路集成在一起用一根 USB 线就能连电脑非常省事。Air780E 支持三种开发方式AT 指令、Lua 脚本LuatOS和 C SDK。这里先说结论如果目标只是快速验证硬件和 SIM 卡或者给设备做一个简单的短信告警功能AT 指令是效率最高的路径。不需要搭建交叉编译环境不需要刷固件一根 USB 线加一个串口助手就能开干。1.2 三种开发方式应该怎么选开发方式上手成本适合场景主要短板AT 指令最低快速验证、简单告警、原理解析复杂业务逻辑难维护LuatOS Lua 脚本中等业务逻辑较复杂、需要远程升级需要刷固件、熟悉 Lua 语法C SDK较高深度定制、极致性能控制编译调试链路长、门槛高我个人的建议是第一次接触 Air780E先别急着选技术路线用 AT 指令把短信发通建立对这块板子的直觉。等确认硬件和网络没问题了再按项目需求选 Lua 还是 C。这样后续如果出了问题你知道问题到底在硬件、SIM 卡、网络还是代码里定位成本会低很多。1.3 短信功能在设备端的真实位置短信这个功能在物联网项目里经常被低估。它不走数据流量只要有蜂窝信号就能收发是成本最低的“带外通知通道”。我经手过的项目里短信发送大概集中在三类场景环境监测设备超过阈值后给值班人员发告警定位器把经纬度以短信形式发到家人手机设备首次上电后向服务器发送一条包含设备 ID 的注册短信。Air780E 上的短信功能本质上就是在可编程的串口流程上叠加 AT 指令链路一旦通了后面做任何功能都有底气。2. 物料清单与接线第一步就把串口搞对后面才能顺利短信发不出去有一半的概率根本不是短信的问题而是电脑压根没有和模组建立起来正常对话。串口链路不通后面全是白费功夫。2.1 需要准备的东西Air780E 开发板或核心板建议直接买带 USB 转串口电路的版本省去手动接线的麻烦。如果你手里只有裸模组就额外需要一个 USB 转 TTL 模块CH340 或 CP2102 都行。USB 数据线必须是数据线而不是纯充电线很多“USB 线不通数据”的问题就是这么来的。SIM 卡一张支持 4G 网络的手机卡或者兼容 Cat.1 的物联网卡。注意 Air780E 工作在 4G 网络需要确认使用区域有 4G 覆盖。4G 天线确保天线连接到模组天线座。有些人觉得只是发个短信不接天线也行实际上信号弱会导致短信发送失败或严重延迟别省这一步。串口调试软件Windows 下用 XCOM 或 SSCOMLinux 下用 minicom 或 picocom。后面示例代码用 Python 的 pyserial 库。2.2 接线与供电的核心原则用开发板的话接线没什么好担心的——USB 线插上即可电脑设备管理器会出现 COM 口。用裸模组就需要手工接线记住三个原则交叉接线、共地、供电充足。模组的 TX 引脚接 USB 转 TTL 的 RX模组的 RX 引脚接 USB 转 TTL 的 TXGND 与 GND 相连。很多新手在这里直接把 TX 接 TX、RX 接 RX结果 AT 指令完全没反应。供电方面Air780E 是 4G 模组发射瞬间电流能达到 1A 以上USB 转 TTL 模块上那点 3.3V 电压往往喂不饱它造成启动失败或随机重启。如果你是裸模组接线调试最好用官方 EVB 或一个能输出 3.8V~4.2V、电流余量充足的电源模块。接好之后在设备管理器里确认串口驱动已识别。如果出现两个 COM 口一般 AT 指令口对应主串口日志口是另一个选择标注为 AT 口的那一个。2.3 开机自检与串口链路验证打开串口调试工具波特率默认设 115200然后在发送区输入AT点击发送如果模组回了OK说明链路已经通了。新板子上电时通常会在串口里打出一串开机日志比如RDY、^MODE: 0等这些不是错误只是模组启动过程中的状态信息。如果发送AT之后完全没有反应先检查波特率是不是 115200有的固件默认是 9600换成 9600 再试一次。如果仍然无响应回到 2.2 核对 TX/RX 是否交叉、GND 是否连接、电源是否稳定。这里顺便说一个我踩过的坑串口调试工具里“发送新行”这个选项默认勾选发送AT时会自动追加\r\n大多数 AT 指令能兼容但后面章节会提到在输入短信内容时如果还开着“发送新行”可能会把\r\n混进短信正文里导致内容出现多余换行。建议把“发送新行”理解成“按具体场景单独控制”而不是全程打开。3. 逐条 AT 指令从信号检测到短信发送的过程拆解AT 指令这套体系看起来很古老但它是当前蜂窝模组最通用的控制方式。把每一条指令的意图和返回结果看懂比直接跑通一次更有价值。3.1 先确认信号和网络发短信也得有网拿到一块新板子建议按下面的顺序做基础检查AT握手返回OK说明串口通信正常。ATCSQ查询信号质量。返回格式CSQ: 23,99第一个数字代表接收信号强度范围 0~31数值越大越好小于 10 说明信号偏弱第二个数字是误码率99 表示未知或不可用。ATCREG?查询网络注册状态。返回CREG: 0,1表示已注册到本地网络0,5表示已注册但处于漫游状态其他数字一般代表未注册或正在搜索。ATCPIN?查询 SIM 卡状态。返回CPIN: READY说明 SIM 卡正常如果是其他结果说明卡没插好、卡损坏或者卡需要输入 PIN 码。这四条指令是蜂窝模组的“体检套餐”如果这里都没问题短信发送链路就已经具备了一半条件。3.2 检查短信中心号码容易被忽略的关键参数短信不是直接发给对方的而是先提交到运营商的短信中心再有短信中心转发出去。模组必须知道短信中心的号码否则短信无法提交。用ATCSCA?查询短信中心号码正常情况下会返回类似CSCA: 8613800755000,145的内容。如果返回空或者你想更换短信中心可以用ATCSCA8613800755000这样的格式设置。不同地区、不同运营商的短信中心号码不一样最简单的方式是把 SIM 卡插到你自己的手机里在短信设置里找到当前短信中心号码再填到模组里。Air780E 的 AT 手册里也会说明CSCA的用法。3.3 设置文本模式AT 指令下发短信有文本模式和 PDU 模式两种。PDU 模式是底层编码方式支持中文内容但指令格式复杂对新手不友好文本模式下内容可以直接作为字符串下发对英文和数字短信非常方便。发送ATCMGF1把模组设置成文本模式返回OK即可。后面章节会讲到中文短信在文本模式下还需要额外处理字符集这里先用英文或数字内容跑通链路。3.4 完整发送短信的指令流程核心指令是ATCMGS对方号码注意号码要用英文双引号包起来。下发这条指令后模组不会立刻返回OK而是返回一个提示符意思是“短信内容输入区就绪请开始输入”。此时不要按回车不要发送任何额外换行直接输入短信内容。内容输入完成后需要发送十六进制字节0x1A也就是键盘上的CTRLZ告诉模组“内容输入结束”。随后模组会返回类似CMGS: 123的结果后面跟一个OK表示这条短信已经进入发送流程。整个流程梳理成表格步骤发送内容预期返回说明1ATOK握手2ATCSQCSQ: 21,99信号正常3ATCREG?CREG: 0,1已注册4ATCPIN?CPIN: READYSIM 卡正常5ATCSCA?CSCA: 86138...短信中心存在6ATCMGF1OK文本模式7ATCMGS10086进入输入状态8Hello Air780E后发0x1ACMGS: 123加入发送队列这里有个细节CMGS: 123后面的数字是模组给这条短信分配的本地编号只代表短信已经成功提交到模组或短信中心不代表对方已经收到。短信送达是一个异步过程这个编号的作用是帮你日志里追踪是哪条短信。3.5 收发一体读取短信的入门方式短信功能不只是“发”也经常要做“收”比如服务器下发指令、验证码回传。如果你跑通了发送顺便看一下接收的指令格式并不难。ATCNMI2,1开启新短信提示有新短信进来时模组会主动上报提示。ATCMGLALL列出所有短信。ATCMGR序号读取指定序号的短信。接收链路和发送链路一样依赖信号和 SIM 卡状态后续如果要做短信点对点控制可以从这几条指令进一步扩展。4. 完整代码用 Python 把发短信做成可控过程用串口助手手工点按钮能验证流程但真实项目里不可能让人盯着屏幕敲 CTRLZ。把这套流程写成脚本才能真正变成“可用”的功能。4.1 为什么选用 Python用 Python 的原因很简单pyserial 库安装方便、跨平台、代码直观适合做验证和原型。如果你想最终把逻辑跑在 Air780E 上可以先用 Python 在电脑端验证串口协议确认逻辑没问题后再用 C 或 Lua 移植。Python 在这里的角色是“上位机调试脚本”不是最终固件。4.2 串口操作的核心逻辑代码的核心逻辑就是模拟我们在串口助手里的手工操作打开串口 - 发送基础检测指令 - 发送ATCMGS- 等待- 写内容 - 写0x1A- 读取最终结果。这里有一个容易写错的地方等待不能用 readline 阻塞因为模组返回时后面没有完整的换行readline 可能一直等不到数据。更稳妥的方式是用轮询在超时时间内循环读取串口缓冲区直到发现字符。另一个容易踩坑的点是0x1A的发送方式。在 Python 里用ser.write(bytes([0x1A]))bytes([0x1A])表示单个字节十进制值是 26对应 ASCII 码里的 SUB也就是 CTRLZ。如果你误写成了ser.write(1A.encode())发出去的会是两个可见字符1和A模组完全不会理解。4.3 可以直接运行的完整 Python 代码下面这段代码我实际跑过配合 Air780E 开发板默认 115200 波特率可以正常工作。你需要根据自己的实际 COM 口修改SERIAL_PORT变量。# -*- coding: utf-8 -*- import serial import time SERIAL_PORT COM8 # Windows 示例Linux 下通常是 /dev/ttyUSB0 BAUDRATE 115200 ser serial.Serial(SERIAL_PORT, BAUDRATE, timeout2) def send_at(cmd, expectOK, wait_time1.5): 发送一条 AT 指令并判断返回里是否包含期待的关键字 ser.reset_input_buffer() ser.write((cmd \r).encode()) time.sleep(wait_time) resp ser.read(ser.in_waiting or 1).decode(errorsignore) print(f命令: {cmd}) print(f返回: {resp.strip()}) return expect in resp def wait_prompt(timeout5): 等待模组返回 提示符 deadline time.time() timeout buf while time.time() deadline: data ser.read(ser.in_waiting or 1).decode(errorsignore) if data: buf data if in buf: print(已进入短信内容输入状态) return True print(等待 提示符超时) return False def send_sms(phone, content): 向指定号码发送一条文本短信 print(--- 发送短信开始 ---) # 基础链路检测任何一步失败都应停止 send_at(AT) send_at(ATCSQ) send_at(ATCREG?) send_at(ATCPIN?) send_at(ATCMGF1) # 下发 ATCMGS 指令注意用英文双引号包裹号码 ser.write(fATCMGS{phone}\r.encode()) if not wait_prompt(): return False # 输入短信内容后发送 0x1A 作为结束标志 ser.write(content.encode()) time.sleep(0.3) ser.write(bytes([0x1A])) # 读取模组最终返回 time.sleep(3) resp ser.read(ser.in_waiting or 1).decode(errorsignore) print(f最终返回: {resp.strip()}) return CMGS in resp if __name__ __main__: try: ok send_sms(10086, Air780E test sms) print(发送成功 if ok else 发送失败请检查上面日志) finally: ser.close()代码里做基础链路检测是有意为之不是拖沓——如果 SIM 卡没插好、信号弱、网络没注册后面的ATCMGS肯定失败提前排查比事后猜更快。4.4 运行结果长什么样正常运行时会输出类似下面的日志--- 发送短信开始 --- 命令: AT 返回: OK 命令: ATCSQ 返回: CSQ: 21,99 命令: ATCREG? 返回: CREG: 0,1 命令: ATCPIN? 返回: CPIN: READY 命令: ATCMGF1 返回: OK 已进入短信内容输入状态 最终返回: CMGS: 9 OK 发送成功CMGS: 9表示模组已经接受这条短信并分配了本地序号接下来的OK代表指令执行顺利。如果看到的是ERROR或CMS ERROR则按下一章的排查思路去查。4.5 如果想发中文短信怎么办文本模式下发英文和数字很简单但发中文内容就涉及字符集问题。模组默认字符集通常不是中文你需要手动切换。方法是在ATCMGF1之后再发送ATCSCSUCS2把模组字符集切到 UCS2然后把中文内容转成 UCS2 编码的十六进制字符串再发送。在 Python 里的转换方法是content_hex content.encode(utf-16-be).hex().upper()比如“你好”转出来是4F60597D然后把content_hex作为内容发送而不是直接发送中文原文。这里要特别提醒UCS2 转换在不同模组上行为可能有差异我在 Air780E 特定固件版本上验证过但你在实际项目里还是要以手里固件的 AT 手册为准。如果中文不是刚需建议先用英文或数字把链路跑通再处理编码问题。4.6 如果用 C 语言做核心逻辑怎么迁移如果你最终要在嵌入式 Linux 或单片机侧用 C 实现同样的功能核心逻辑不用改只需要把串口读写换成对应平台的接口。Linux 下可以用 termios 配置串口然后open()、write()、read()即可单片机侧则用你自己板子的串口驱动库。关键点还是那几个命令要带\r结尾内容发送结束后要发0x1A字节等待要用轮询而不是阻塞。5. 踩坑实录5 分钟没搞定短信按这个顺序排查这一章记录的是我实际调试过程中最常见的几类问题。如果你按上面流程走5 分钟内没发出短信不要慌按这个顺序一条条查。5.1 AT 指令完全没有响应这是最让人头大的情况但原因通常很基础串口号选错了打开设备管理器确认用的是 AT 口而不是日志口。TX/RX 接反了裸模组接线时容易出现TX 接 RX、RX 接 TX 才是正确姿势。波特率不对Air780E 常见默认波特率是 115200但不同固件默认值不同遇到无响应就换 9600 试试。供电不足4G 模组发射时电流很大如果用的是 USB 转 TTL 模块供电大概率会电压跌落换个独立 4V 电源解决。我的排查习惯是“先软件后硬件”先试波特率再换串口工具如果都不行再检查接线和电源。5.2 SIM 卡相关的报错发送ATCPIN?如果返回的不是READY说明 SIM 卡链路没打通。常见情况是卡没插到位Air780E 的卡座比较紧插进去后轻轻按压到卡到位。卡有 PIN 码保护需要在指令里用ATCPINxxxx解锁。卡本身是坏的或者欠费停机插手机验证一下。卡不支持 4G 网络。低版本物联网卡可能需要确认是否开通了 Cat.1 接入能力。5.3 发短信时返回 ERROR 或 CMS ERROR发送ATCMGS后如果返回ERROR多半是参数格式问题比如号码没加英文双引号、号码中间有空格。如果返回的是CMS ERROR: xxx那是模组层面拒绝提交短信常见的错误码有这些错误码大致含义排查方向310SIM 卡缺失检查 SIM 卡安装320短信中心地址未知执行ATCSCA?并配置短信中心512短信中心拒绝检查号码格式、短信中心、SIM 卡状态513无法经短信中心发送联系运营商确认短信权限515远端设备无响应检查目标号码是否有效、是否停机特别说下512和513它们往往不是模组的问题而是运营商侧拒绝。高频次群发同一内容、测试环境下的连续发送都可能触发风控。测试时不要用同一张卡在短时间内发几十条短信很容易被运营商限制短信功能一旦被限制恢复起来非常麻烦。5.4 短信显示发送成功但对方收不到CMGS: 编号出现并不代表对方已经收到短信。短信要经过短信中心转发可能延迟几分钟。如果长时间收不到目标手机信号如何是不是开启了骚扰拦截号码是不是空号或者停机短信网关是不是对当前短信内容有过滤号码格式是不是有问题尝试用86前缀再试一次。我个人在测试时习惯拿一个备用手机号当作测试目标不要用自己的主号避免收不到时难以判断是业务问题还是运营商问题。5.5 串口助手的十六进制模式和换行坑很多人在串口助手里手工发送短信时内容输完了不知道怎么发结束符。记住结束符0x1A是十六进制字节不是文本。在串口助手里你需要找到“HEX 发送”或“十六进制发送”的开关切换到该模式然后输入1A再发送。如果开着“发送新行”许多软件会在1A后面追加\r\n这可能导致模组误判短信发送失败。用 Python 代码就没有这个问题bytes([0x1A])是精确的单字节发送。6. 从一条短信到完整业务接下来往哪走短信跑通不是终点它应该成为你整个设备功能的一部分。最后聊聊后续扩展的几个方向。6.1 在短信内容里拼出有效信息真实项目里的短信内容不会是干巴巴的test而是包含业务信息。最简单的做法是在 Python 或用 Lua 脚本里做字符串拼接把时间戳、传感器数值、设备编号拼进去。比如[20250516 14:30:00] 设备ID: A78001 温度: 25.6℃ 湿度: 68% 告警: 温度超限这样的一条短信接收方一眼就能看懂。但要注意短信长度限制普通短信单条约 70 个汉字或 160 个英文字符超过长度会被运营商拆分成多条发送计费也会增加。生产环境里建议在内容设计阶段就控制长度。6.2 从手动发送到事件驱动短信告警最有价值的场景是“设备自己决定什么时候发”。在电脑端测试时脚本是手动触发的到了设备端应该用事件驱动的方式调用发送逻辑。逻辑上大概是循环读取传感器: 如果温度 阈值: 调用发送短信函数 进入冷却时间比如5分钟内不重复发送冷却时间非常重要否则传感器一旦持续超阈值短信会像洪水一样涌向手机既浪费短信费用又容易被运营商风控。6.3 功耗与稳定性的提醒短信发送的时候模组射频部分是瞬时高功耗状态供电设计一定要留出余量。我自己在项目里调试时遇到过几次模组在发短信时突然重启最后发现是电源方案输出电流不够换了一个有余量的电源模块后问题消失。另外如果设备是长期运行的建议尽快从“电脑端 AT 指令调试”切换到 LuatOS 或者 CSDK 方案利用模组自带的定时器、网络管理和降功耗机制比外部脚本裸控可靠得多。最后分享一个我自己的习惯每拿到一块新的蜂窝模组我都会先发一条ATCMGS到运营商服务号码把这一整套 AT 流程完整走一遍。这一步看似基础实际上把串口、SIM 卡、信号、短信中心、内容格式全部验证了一遍之后做任何业务逻辑心里都有底。Air780E 开发板的短信链路一旦跑通后面再往 Lua 或 C SDK 走你会发现很多概念都是相通的。