3、AT指令基础:AT指令的格式、三种模式、基本与扩展指令、超时机制

好,咱们进入第三章。这一章我打算把AT指令的“骨架”给你拆开看看。

很多新手拿到NB-IoT模组,上来就发AT,结果要么没反应,要么乱码。为什么?说白了,就是没搞懂AT指令的“规矩”。

这一节,咱们就把这些规矩讲透。包括指令怎么结尾、数据怎么表示、指令分几类、以及——万一模组卡住了怎么办。

3.1 AT指令的格式:结尾

AT指令的格式,其实特别简单。但越简单的东西,越容易出错。

每条AT指令,必须以回车换行符结尾。在ASCII码里,回车是0x0D,换行是0x0A。我们通常写成\r\n

核心要点:

  • 发送指令:AT\r\n → 模组回复:\r\nOK\r\n
  • 没有\r\n,模组会一直等,不处理你的指令
  • 我见过有人用串口助手,只发了“AT”没加回车,等了半天说模组坏了……

举个例子,正确的发送方式是这样的:

发送:AT<CR><LF>
接收:<CR><LF>OK<CR><LF>

注意看,模组回复时,前面也会带一个\r\n。这是标准协议规定的,别觉得奇怪。

我的习惯:在代码里,我通常用字符串"AT\r\n"来构造指令。如果你用C语言,记得用strcat拼接时别漏了\r\n。我曾经在某个项目里,因为用了\n而不是\r\n,结果模组偶尔不响应,排查了两天才发现。

3.2 三种模式:TEXT / PDU / HEX

AT指令处理的数据,有三种表示模式。你想想看,模组和MCU之间传的是字符串还是二进制?这决定了你用哪种模式。

模式 说明 典型场景
TEXT(文本模式) 数据以可打印ASCII字符表示 AT+CSQ查询信号强度,返回"+CSQ: 20,0"
PDU(协议数据单元) 用于短信等结构化数据,包含长度、地址、内容等 发送短信时,PDU串包含SMSC、TPDU等字段
HEX(十六进制模式) 数据以十六进制字符串表示,每个字节转成两个字符 AT+QCFG="nbsd",1 配置时,某些参数用HEX

TEXT模式最常用。你发AT,它回OK,这就是TEXT模式。简单直观,调试时一眼就能看懂。

PDU模式,嗯,这个有点复杂。我记得第一次接触PDU模式是在做短信应用时。PDU串长这样:

AT+CMGS=23<CR>
0011000D91686123456789F0000AA0E8329BFD06<Ctrl+Z>

你看,这一串数字字母,其实包含了短信中心号码、目标号码、编码方式、内容长度等等。说白了,就是把短信的“信封”和“信纸”打包在一起了。

HEX模式,其实就是把二进制数据转成可见的十六进制字符串。比如你要发送0x1A 0x2B 0x3C,在HEX模式下就写成"1A2B3C"

避坑指南:我曾经在配置NB-IoT模组的APN时,用了TEXT模式传了一个包含特殊字符的字符串,结果模组解析失败。后来换成HEX模式,把每个字符的ASCII码转成十六进制,问题就解决了。所以,遇到特殊字符或不可见字符时,优先考虑HEX模式。

3.3 基本指令与扩展指令的区别

AT指令分两类:基本指令和扩展指令。这个区分很重要,因为它们的语法不一样。

基本指令,格式是AT<指令>。比如:

  • AT — 测试连接
  • AT+CMEE=1 — 开启详细错误报告(注意,这个其实是扩展指令,但有些厂商把常用配置也归为基本)

实际上,NB-IoT模组里,基本指令很少。大部分功能都是扩展指令。

扩展指令,格式是AT+<指令>=<参数>。比如:

AT+CSQ          // 查询信号质量
AT+CGATT=1      // 附着网络
AT+NMGS=3,010203 // 发送3字节数据:0x01 0x02 0x03

扩展指令还有几种变体:

  • 测试指令AT+<指令>=? — 查询指令支持的参数范围
  • 查询指令AT+<指令>? — 查询当前参数值
  • 设置指令AT+<指令>=<参数> — 设置参数
  • 执行指令AT+<指令> — 执行某个操作(不带等号)

举个例子:

AT+CSQ=?   → 返回:+CSQ: (0-31),(0-7)   // 测试指令,告诉你信号强度范围0-31,误码率0-7
AT+CSQ?    → 返回:+CSQ: 20,0            // 查询指令,当前信号20,误码率0
AT+CSQ     → 返回:+CSQ: 20,0            // 执行指令,效果同上

我个人习惯,调试时先用AT+<指令>=?看看模组支持什么参数,再动手写代码。这样可以避免参数越界。

3.4 指令超时机制

这个点,很多人会忽略。但恰恰是它,决定了你的产品稳不稳定。

AT指令不是瞬间完成的。比如AT+CGATT=1(附着网络),模组需要去搜网、注册,这个过程可能耗时5秒、10秒,甚至更久。如果你发完指令就傻等,程序就卡死了。

超时机制的核心思想:给每条指令设定一个最大等待时间。超时了,就认为指令失败,做异常处理。

我一般这样设计:

发送指令 → 启动定时器(比如5秒) → 等待模组回复
  ├─ 收到"OK" → 成功,继续下一步
  ├─ 收到"ERROR" → 失败,重试或报错
  └─ 定时器超时 → 认为模组无响应,复位或重新发送

不同指令的超时时间不一样:

指令 建议超时时间 说明
AT 1秒 只是测试连接,模组应该立即回复
AT+CSQ 2秒 查询信号,很快
AT+CGATT=1 10-30秒 网络附着,取决于信号和网络状态
AT+NMGS 5秒 发送数据,等待ACK

我曾经踩过的坑:在一个水表项目中,我把所有指令的超时都设成了3秒。结果AT+CGATT=1经常超时,导致设备反复重启。后来我把网络附着的超时改成了20秒,问题就解决了。你想想看,信号不好的时候,模组搜网慢是正常的,不能一刀切。

另外,超时后怎么办?我建议:

  • 先重试1-2次,可能是网络抖动
  • 重试仍失败,复位模组(拉低PWRKEY或断电)
  • 记录错误日志,方便后续分析

我的小技巧:在代码里,我会用一个结构体来管理每条指令的超时参数。这样后期调整超时时间,改一个宏定义就行,不用到处翻代码。

typedef struct {
    char *cmd;
    int timeout_ms;
    int retry_count;
} at_cmd_t;

好了,这一章的内容就这些。AT指令的格式、三种模式、基本与扩展指令的区别、超时机制,都是基本功。但基本功越扎实,后面遇到问题就越从容。

下一章,咱们开始实战——用AT指令操作NB-IoT模组,从开机到联网,一步步来。