基于ESP32 + Air780EPV的短信转发
# 前言
本来是打算基于 Air780E 开发板实现的,后来发现有个新品 Air780EPV 还支持电信短信,关键这个芯片 9.9 盲订的时候我还知道这件事,但当时的说法是 9.9 盲订 + 尾款,结果是 9.9 到手(错过 1 个亿~)。秉持着买新不买旧的原则(大坑),最后选择了原价且不包邮的 Air780EPV。
# 材料
名称 | 数量 | 价格 | 说明 |
---|---|---|---|
合宙 Air780EPV 开发板 | 1 | 39 | |
合宙 ESP32C3 简约版 | 1 | 9.9 | |
运费 | - | 7 | |
1×16p 2.54MM 直插单排母 | 2 | 0.5 | 某宝 2.5/10 件。 |
飞线 | 2 | - |
笔记
- 原本开发板是在闲鱼 29 收的,结果遇到一个不发货的 ** 卖家,最后还是去了官方淘宝店买,又刚好遇到合宙现在不包邮了,估计我的买入价是最高位,随着后面闲鱼有货,应该能 50 以内搞定。
- 实际上排母买
1×2p
的就可以了
注意
如果只是转发移动、联通网络的短信,使用 合宙Air780E开发板
可以减少很多不必要的麻烦
# 最佳实践
# 硬件
# 焊接
注意
实践过程中发现,esp32c3 与 air780epv 接口并不完全兼容,如没有把握能解决兼容问题,千万不要在直接把整排 16p 排母焊到 esp32c3 上,同时又把 20pin 的排针焊在 air780epv 开发板上,否则焊时有多爽,拆时就有多惨!
在 esp32c3 上方的下述引脚焊接排母
01
13-14
17-18
31-32
- 上述实际上只用到
31-32
供电引脚,其他引脚上的排母主要作用是连接时支撑开发板。
在 air780epv 的下方的下述引脚焊接排针
03-04
25-26
17-18
38
- 当然排针可以全焊上去,只要保证无关的引脚不会与 esp32c3 的引脚连通即可。
# 连接
不知道是故意的还是不小心的,air780epv 的 UART1_RXD、UART1_TXD 与 air780e 的引脚是相反的,因此不能像 air780e 一样直接用排母连接 eps32c3,需要用跳线对调。
- 将焊接的排母和排针连接
- 使用飞线,将 esp32c3 的
02
与 air780epv 的36
,03
与37
连接。
# 软件
# 准备与连接
- LuaTools 下载 (右键另存为) (opens new window)(创建一个文件夹
luatools
存放,因为下面会在启动目录下载资源) - 将 esp32c3 通过 usb 连接到电脑
- 查看设备管理器,确认串口号
- 在 Luatools 中
- 勾选
通用串口打印
- 选择 esp32c3 的串口号
- 串口日志波特率:
921600
- 打开串口
- 能打印日志即为成功连接
- 勾选
# 烧录固件
下载 NipGeihou/sms_forwarder_air780ep_esp32 (opens new window) 项目到本地
luatools 右上角,
项目管理测试
创建项目
,名字随意选择文件,
\sms_forwarder_air780_esp32-master\firmware\LuatOS-SoC_V1006_ESP32C3_lite.soc
增加脚本或资源文件
,将\sms_forwarder_air780_esp32-master\
下的所有 lua 文件倒入进来使用 vscode (编辑器) 编辑
config.lua
config.board_type = "esp32c3"
config.wifi
:配置 WiFi 账号、密码config.notification_channel
:开启并配置推送方式
luatools 的项目管理,点击
下载底层和脚本
将固件和脚本写入 esp32c3 中长按 air780epv 的 power 按键,出现绿灯即为开机
[2024-03-02 14:42:54.502] E (598) task_wdt: esp_task_wdt_init(593): TWDT already initializI/main.lua:60 main - 初始化Air780 Air780E已连接
[2024-03-02 14:42:54.502] I/main.lua:62 main - 初始化Air780 正在检查有无SIM卡
[2024-03-02 14:42:54.608] I/main.lua:41 main - 初始化网络 无线网络连接成功,IP地址:10.10.xx.xx
[2024-03-02 14:42:54.608] I/main.lua:44 main - 初始化网络 配置第1个DNS服务器为119.29.29.29
[2024-03-02 14:42:54.608] I/main.lua:44 main - 初始化网络 配置第2个DNS服务器为223.5.5.5
[2024-03-02 14:42:54.630] I/main.lua:44 main - 初始化网络 配置第3个DNS服务器为8.8.8.8
[2024-03-02 14:42:54.630] I/main.lua:44 main - 初始化网络 配置第4个DNS服务器为1.1.1.1
[2024-03-02 14:42:54.630] I/main.lua:48 main - 初始化网络 等待时间同步
[2024-03-02 14:42:54.630] I/main.lua:75 main - 初始化Air780 正在配置短信功能
[2024-03-02 14:42:54.939] I/main.lua:82 main - 初始化Air780 短信功能配置完成
[2024-03-02 14:42:54.939] I/main.lua:85 main - 初始化Air780 正在禁用RNDIS
[2024-03-02 14:42:54.939] I/main.lua:89 main - 初始化Air780 检查GPRS附着状态
[2024-03-02 14:42:55.062] I/main.lua:96 main - 初始化Air780 GPRS未附着,将在5秒后重新检查
[2024-03-02 14:42:55.549] I/main.lua:50 main - 初始化网络 时间同步完成
[2024-03-02 14:43:00.153] I/main.lua:93 main - 初始化Air780 GPRS已附着
[2024-03-02 14:43:00.153] I/main.lua:102 main - 初始化Air780 正在关闭NET灯闪烁
[2024-03-02 14:43:00.153] I/main.lua:106 main - 初始化Air780 初始化完成,等待新短信...
[2024-03-02 16:36:26.339] I/main.lua:60 main - 初始化Air780 Air780E已连接
[2024-03-02 16:36:26.339] I/main.lua:62 main - 初始化Air780 正在检查有无SIM卡
[2024-03-02 16:36:26.446] I/main.lua:75 main - 初始化Air780 正在配置短信功能
[2024-03-02 16:36:26.767] I/main.lua:82 main - 初始化Air780 短信功能配置完成
[2024-03-02 16:36:26.767] I/main.lua:85 main - 初始化Air780 正在禁用RNDIS
[2024-03-02 16:36:26.767] I/main.lua:89 main - 初始化Air780 检查GPRS附着状态
[2024-03-02 16:36:26.888] I/main.lua:93 main - 初始化Air780 GPRS已附着
[2024-03-02 16:36:26.888] I/main.lua:102 main - 初始化Air780 正在关闭NET灯闪烁
[2024-03-02 16:36:26.888] I/main.lua:106 main - 初始化Air780 初始化完成,等待新短信...
如果顺利将会看到类似上面的日志
# 优化
# 通电开机
默认情况下 air780epv 通电后需要长按 power 按键开机。
想让开发板上电同时自动开机,这种方式也能实现,但是需要调整开发板上的电阻了。 把下图标准的电阻短接,会将 Air780EP 的 POWKEY 信号拉低,也就实现开发板上电开机。
笔记
尝试使用一坨锡短接,发现连不起来,最后是先在两个触点上锡,再找一段排针连接。
# 对英文发件人的支持
海外的一些短信是没有手机号的,只有一个英文名,如 3HK
、 Apple
,而原项目中并为对英文发件人支持,因此转发的短信这些短信的发件人会是一段 16 进制值。
在 50 元内自制短信转发器(Air780E+ESP32C3) (opens new window)一文的留言中,有人提及这个问题,并且项目作者已增加对此的支持,而我使用的是另一个项目 (opens new window)的源码,而这个项目中目前并没有,因此我打算尝试一下提交个 PR。
解码工具:Online SMS PDU Decoder/Converter | Diafaan SMS Server (opens new window)
PDU 协议
时间关系,这里只分析收短信的 PDU 格式,由 wiki 可在 SMS Center (SC) -> Mobile Station (MS)
的 message type (消息类型) 为 SMS-DELIVER
,对应的 TP-MTI 为 00
07915862337418F62410D0C3B0FC5D9F97D96C0000320113324282238CC3B0FC5D9F97D96CD0F04D2EBB40CEB2BD2C07CDD1617919947FD7E5A0F19B5C06DDD3743428ECCEBFDD65500B340CCBDFF57999CD0695DB70F63B5F2ECF41F7349B0D7297ED6539283C5F83CC6F39284D7781B2EFBA1C347E93CBA0F41C24A3C1702E50920E4ACF41F6303B4D0699DF72900CD44EBBEBF4F2DC05
# SMSC
07
91
58 62 33 74 18 F6
# 第一个字节
24
# Originating Address 始发地址
10 #(10 hex = 16 dec,16个半字节 = 8字节)
D0
C3 B0 FC 5D 9F 97 D9 6C # 8字节
#TP-PID
00
# TP-DCS
00
# TP-SCTS
32 01 13 32 42 82 23
# TP-UDL
8C
# TP-UD
C3B0FC5D9F97D96CD0F04D2EBB40CEB2BD2C07CDD1617919947FD7E5A0F19B5C06DDD3743428ECCEBFDD65500B340CCBDFF57999CD0695DB70F63B5F2ECF41F7349B0D7297ED6539283C5F83CC6F39284D7781B2EFBA1C347E93CBA0F41C24A3C1702E50920E4ACF41F6303B4D0699DF72900CD44EBBEBF4F2DC05
SMSC
SMSC 我想应该类似于网关一样的东西
这一段在 wiki 中没有找到,但交叉佐证可以肯定是有这么一段数据。
这段数据的格式跟 Originating Address 的格式是类似的,表明由谁承运?
First Octet 第一个字节
bit(s) | field | Meaning | 参考值 |
---|---|---|---|
0-1 | TP-MTI | Message Type Indicator 消息类型指示器 | 00 |
2 | TP-MMS | More Messages to Send 更多要发送的消息 | 0 |
3 | TP-LP | Loop Prevention 环路预防 | 0 |
4 | - | 0 | |
5 | TP-SRI | Status Report Indication 状态报告指示 是否将状态报告返回给短消息实体 1 是 0 否 | 0 |
6 | TP-UDHI | User Data Header Indicator 用户数据标头指示器 | 0 |
7 | TP-RP | Reply Path 回复路径 | 0 |
TP-OA,Originating Address 始发地址
参考:GSM_03.40#Addresses - Wikipedia (opens new window)
字节索引 | 含义 | 参考值 |
---|---|---|
0 | 地址数字共有多少个半字节 | 0B |
1 | {EXT,1}{TON,3}{NPI,4} | 国际号码 91( 1 0001 0001 )字母数字 D0( 1 001 0000 ) |
2-11 | 地址数字 | 5862337418F6( 85263347816 ) |
EXT:固定为
1
TON:
0 0 0
:未知0 0 1
:国际号码,即1001 = 9(hex)
1 0 1
:字母数字,即1101 = D(hex)
NPI:Numbering plan identification
0 0 0 0
:未知0 0 0 1
:ISDN / 电话编号计划(E.164/E.163),即1(hex)
当用户发送的收件人以
+
开头是,发送时就会将+
删除,并地址开头设为91
(TON=1、NPI=1);反之如果不以+
开头,则地址开头设为01
(TON=0、NPI=1),由运营商匹配。(所以国内使用 00?)地址数字:
NPI = 1
:每个数字占半个字节,同时需要将同一个字节的两个数对调,其中最后一个字节的 F 在对调之后丢弃,得到手机号NPI = 0
:则可能是字母数字,需要使用 GSM 7-bit 解码
GSM7-bit编解码代码
import (
"encoding/hex"
"github.com/warthog618/sms/encoding/gsm7"
)
func main() {
text := "Design@Home"
println("编码:")
// 转换为gsm7编码
gsm7encode, _ := gsm7.Encode([]byte(text))
println(hex.EncodeToString(gsm7encode)) // 44657369676e00486f6d65
// 按7位打包成一个字节
pack := gsm7.Pack7Bit(gsm7encode, 0)
println(hex.EncodeToString(pack)) // c4f23c7d760390ef7619
println("解码:")
// 解码
gsm7encode = gsm7.Unpack7Bit(pack, 0)
println(hex.EncodeToString(gsm7encode)) // 44657369676e00486f6d65
decode, _ := gsm7.Decode(gsm7encode)
println(string(decode)) // Design@Home
}
编码思路
# Design@Home
# 根据GSM 7-bit编码表得到16进制的:
44 65 73 69 67 6E 00 48 6F 6D 65
# 将其转换为2进制:
01000100 01100101 01110011 01101001 01100111 01101110 00000000 01001000 01101111 01101101 01100101
# 由于7-bit编码最高位均为0,需要去掉:
1000100 1100101 1110011 1101001 1100111 1101110 0000000 1001000 1101111 1101101 1100101
# 组合成8位,第一个字节现在只有7位,需将下一个字节的最低位添加到第一个字节的最高位:
待处理1:110010 1110011 1101001 1100111 1101110 0000000 1001000 1101111 1101101 1100101
已处理1:11000100
# 此时待处理1第一个字节只有6位,需要将下一个字节的最低位依次添加到第一个字节的最高位:
待处理2:11100 1101001 1100111 1101110 0000000 1001000 1101111 1101101 1100101
已处理2:11000100 11110010
# 以此类推
待处理3:1101 1100111 1101110 0000000 1001000 1101111 1101101 1100101
已处理3:11000100 11110010 00111100
待处理4:110 1101110 0000000 1001000 1101111 1101101 1100101
已处理4:11000100 11110010 00111100 01111101
待处理5:11 0000000 1001000 1101111 1101101 1100101
已处理5:11000100 11110010 00111100 01111101 01110110
...
# 最后一次处理,后面没有字节了,则在前面补0,直到8位
最终得到:11000100 11110010 00111100 01111101 01110110 00000011 10010000 11101111 01110110 00011001(16进制:C4 F2 3C 7D 76 03 90 EF 76 19)
TP-DCS,Data Coding Scheme 数据编码方案
GSM_03.40#Data_Coding_Scheme (opens new window)
GSM 03.38 - Wikipedia (opens new window)
Data_Coding_Scheme#SMS_data_coding_scheme (opens new window)
GSM 7-bit
:0
,默认,包含大多数西欧语言中最常用的符号(以及一些希腊大写字母),7 位 bitUCS-2
:08
,即 UTF-16,中文、韩语或日语消息必须使用 UTF-16 字符编码进行编码,16 位 bit8-bit data
:04
,8 位 bit
TP-SCTS,Service Centre Time Stamp 服务中心时间戳
octet | Content | 参考值 |
---|---|---|
0 | Last two digits of the year 年份的最后两位数字 | 31 |
1 | Month 月 | 30 |
2 | Day 天 | 52 |
3 | Hour 小时 | 32 |
4 | Minute 分钟 | 10 |
5 | Second 秒 | 65 |
6 | Time zone 时区 | 8A |
2013 年 3 月 25 日 23:01:56 PST (GMT-7),同样为转位(数字 35 存储为十六进制 53)
TP-UDL,User Data Length 用户数据长度
TP-UDL * TP-DCS对应单位长度
为 TP-UD
的长度
TP-UD,User Data 用户数据
todo
- 由于只是对发件人(手机号)部分调整,不涉及到其他内容,只需要对解析手机号部分代码修改即可。
- 经过翻阅 wiki 可知当 TP-OA 的第二个字节为
D0
时则为 Alphanumeric(字母数字),需要使用 GSM 7-bit 解码。因此只需要在代码中添加对此类短信的判断,并复用对 TP-UD 的 GSM 7-bit 解码逻辑即可来对 TP-OA 部分进行相同的操作即可。 - 实际操作查看:Add support for alphanumeric SMS center type · NipGeihou/sms_forwarder_air780ep_esp32@55f23a6 (opens new window)
参考:
# 参考
- 50 元内自制短信转发器(Air780E+ESP32C3) – 晨旭的博客~ (opens new window)
- Air780EPV 文档中心 (opens new window)
- boris1993/sms_forwarder_air780_esp32: 使用合宙 ESP32 和 Air780E 构建的短信转发器 (opens new window)
- 0wQ/air780e-forwarder: 合宙 Air780 系列 4G 模组,短信转发,来电通知,Air724 在这里 -> https://github.com/0wQ/air724ug-forwarder (opens new window)
- Luat 4G LTE 模块 QAT 命令手册 V1.0.0 (opens new window)