4、ELM327 AT指令集:AT指令格式、常用AT指令、初始化流程、响应解析
好,咱们进入正题。ELM327 这颗芯片,说白了就是 OBD 协议翻译官。你发一句「AT 咒语」给它,它帮你跟车上的 ECU 沟通。这一章,我就带你把这套咒语彻底搞明白。
4.1 AT指令格式:别小看这串字符
AT 指令的格式其实很简单:AT<命令><参数>,后面跟一个回车符(\r)。注意,是回车,不是换行。我见过不少新手用 \n 结尾,结果 ELM327 根本不搭理你。
标准格式:
AT<命令>[参数]\r
比如:ATZ\r 就是复位,ATE0\r 就是关闭回显。
这里有个坑:所有 AT 指令都是大写。虽然有些 ELM327 固件兼容小写,但我建议你统一用大写。我在项目里吃过亏——某批国产模块只认大写,小写直接返回 ?。
4.2 常用AT指令:我的工具箱
ELM327 的 AT 指令有几十条,但日常用的就那七八条。我按使用频率给你排个序:
| 指令 | 含义 | 我的备注 |
|---|---|---|
ATZ |
复位模块 | 初始化第一步,必用 |
ATE0 |
关闭回显 | 不关的话响应里会混着你的指令 |
ATL0 |
关闭换行符 | 方便程序解析,我习惯关掉 |
ATS0 |
关闭空格 | 响应更紧凑,解析省事 |
ATH1 |
开启响应头 | 能看到 ECU 的 ID,调试时有用 |
ATSP0 |
自动选择协议 | 省心,但速度慢,我一般手动指定 |
我的习惯:初始化时固定用 ATE0 + ATL0 + ATS0 三连。这样返回的数据干净得像白纸,解析起来特别爽。
4.3 初始化流程:从冷启动到就绪
每次上电,ELM327 都要走一套固定的初始化流程。我把它总结成 5 步:
- 发送
ATZ\r—— 复位模块,等待ELM327 v1.5之类的版本信息返回。 - 发送
ATE0\r—— 关闭回显。不关的话,后面每条指令的响应前面都会多出你刚发的指令。 - 发送
ATL0\r—— 关闭换行符。这样响应就是连续的一串,方便用split()处理。 - 发送
ATS0\r—— 关闭空格。比如41 0C 1A会变成410C1A,解析效率高不少。 - 发送
ATSP0\r—— 自动协议检测。这一步最耗时,可能要等 2-3 秒。
注意:ATSP0 之后,ELM327 会尝试所有协议。如果车比较老,可能会卡在某个协议上。我建议你根据车型手动指定协议,比如 ATSP6 是 CAN 总线,大部分现代车都支持。
嗯,这里要特别说一下:每次发送指令后,一定要等模块返回 > 提示符。这个提示符表示模块空闲了,可以发下一条指令。我曾经写过一个脚本,没等提示符就连发三条指令,结果模块直接死机了。
4.4 响应解析:从原始数据到可用信息
ELM327 的响应格式其实很固定。咱们拿一个实际例子来说:
发送:010C\r
响应:41 0C 1A 0A>
这串东西怎么拆?我教你一个口诀:「去头、去尾、取中间」。
- 去头:去掉模式字节
41(表示响应的是模式 01 的请求)。 - 去尾:去掉最后的
>提示符。 - 取中间:剩下的
0C 1A 0A就是有效数据。
其中 0C 是 PID 号(发动机转速),1A 0A 是转速值。计算公式是:(0x1A * 256 + 0x0A) / 4,算出来就是 1670 RPM。
解析代码片段(Python):
def parse_rpm(raw_response):
# 去掉提示符和空格
clean = raw_response.replace('>', '').replace(' ', '')
# 取后4个字符(转速占2字节)
hex_str = clean[-4:]
# 转成整数再除以4
rpm = int(hex_str, 16) / 4
return rpm
你想想看,如果没关空格和换行,这解析代码得多写好几行。所以我一直强调:初始化时把格式调干净,后面解析就省心。
4.5 避坑指南:我踩过的雷
做 OBD 开发这些年,我在 AT 指令上栽过不少跟头。挑几个典型的说说:
- 波特率不匹配:ELM327 默认 38400,但有些模块被改过。我建议初始化前先发几个
ATZ试探一下,不行就换波特率。 - 指令间不加延时:ELM327 处理指令需要时间,尤其是协议检测时。我一般每条指令后等 100ms 以上。
- 忽略
NO DATA响应:有些 PID 车不支持,会返回NO DATA。别傻等,直接跳过。
我的调试技巧:用串口助手手动发指令,先确认每条指令的响应格式。等摸透了,再写代码自动化。这样能省一半的调试时间。
好了,AT 指令这块就这些。说白了就是「发什么、怎么发、怎么收」三个问题。你把这套流程跑通了,后面读取车速、水温、故障码什么的,都是换汤不换药。