Skip to content

Commit

Permalink
添加保活功能,定时发送短信到指定号码
Browse files Browse the repository at this point in the history
  • Loading branch information
antonchen committed Nov 27, 2024
1 parent ddd07e4 commit 37e9975
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 96 deletions.
185 changes: 96 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,96 @@
# Air780E短信转发

利用ESP32驱动Air780E实现短信转发,兼容合宙ESP32S3和ESP32C3。

**⚠ 仅支持联通、移动网络,不支持电信网络 ⚠**

# 功能

- [x] 自动转发收到的短信,短信内容支持多种语言(其实就是ASCII和UCS-2字符集),目前已测试过英文、中文、日语、俄语字符
- [x] 支持多个推送平台,目前接入:
- [x] [LuatOS社区提供的推送服务器](https://push.luatos.org/)
- [x] Bark
- [x] Server酱
- [x] 钉钉机器人
- [x] 推送加 PushPlus
- [x] Telegram(感谢 [@wongJG](https://github.com/wongJG) 的 Pull Request)
- [x] 飞书机器人(感谢 [@mmdjiji](https://github.com/mmdjiji) 的 Pull Request)

# 使用方法

## 硬件组装

- 短接POW键上方的焊盘实现通电即开机

![](/image/shorting_soldering_pad_for_power_key.jpg)

- 按照下图方向为Air780e和ESP32焊上排针和排座。注意合宙不送排座,需要自己买。

| Air780E | ESP32S3 |
|--------------------------|--------------------------|
| ![](/image/air780e.jpeg) | ![](/image/esp32s3.jpeg) |

- 按图示方向插入SIM卡

![](/image/sim_card_direction.jpeg)

- 按图示方向将Air780E和ESP32组合

![](/image/put_together.jpeg)

## 为Air780e刷入AT固件

USB连接Air780e,选择 `Luatools/resource/618_lua_lod/版本号` 目录下的AT固件,将其烧录到Air780e。

## 修改脚本,刷入ESP32

- 修改[`config.lua`](config.lua)
- 修改`config.board_type`为正确的型号,可选值见注释
- 修改`config.wifi`,填入无线网络的SSID和密码
- 修改`config.notification_channel`,将要启用的通知通道的`enabled`配置置为`true`,并填写推送平台相关配置
- 烧录脚本
-[`firmware`](firmware)目录中对应的固件烧入开发板
- 将所有`lua`脚本下载至开发板
![](/image/burning_firmware_and_scripts.png)
- 将开发板上电开机,等待初始化完成后,即可转发短信到配置的通知通道

# LED灯状态含义

- ESP32
- C3的`D4`或S3的`LED A`为初始化状态灯,闪烁代表正在初始化,常亮代表初始化完成,准备转发短信
- C3的`D5`或S3的`LED B`为工作状态灯,平时长灭,收到新短信后高频闪烁,转发完成后熄灭

| ESP32C3 | ESP32S3 |
|-----------------------------|-----------------------------|
| ![](/image/esp32c3_led.jpg) | ![](/image/esp32s3_led.png) |

- Air780
- `POW`灯为电源指示灯,通电后常亮。注意,这个LED不代表开机状态,只要板子有电这个灯就会亮
- `NET`灯为网络状态指示灯,长亮短灭代表正在初始化蜂窝网络,短亮长灭代表网络注册成功,可以接收短信

# Firmware目录下的文件说明

- `LuatOS-SoC_V1004_ESP32C3_classic.soc`对应`ESP32C3 经典款`
- `LuatOS-SoC_V1004_ESP32C3_lite.soc`对应`ESP32C3 简约款`
- `LuatOS-SoC_V1004_ESP32S3.soc`对应`ESP32S3`

固件均通过[合宙云编译](https://wiki.luatos.com/develop/compile/Cloud_compilation.html)精简掉了不需要的功能,以保证内存空间充足。`LuaTools`自动下载的固件不能用,系统启动之后内存就不够用了,发不出去HTTP请求。

目前固件包含`gpio``uart``pwm``wdt``crypto``rtc``network``sntp``tls``wlan``pm``cjson``ntp``shell``dbg`

# 致谢

本项目参考[低成本短信转发器](https://github.com/chenxuuu/sms_forwarding)而来,尤其是PDU相关代码,没有`chenxuuu`的这份项目和[50元内自制短信转发器(Air780E+ESP32C3)](https://www.chenxublog.com/2022/10/28/19-9-sms-forwarding-air780e-esp32c3.html)这篇文章,我不会这么快就完成开发。

# 赞助

| 支付宝 | 微信 | Bitcoin |
| ------ | ---- | ------- |
| ![](https://sat02pap001files.storage.live.com/y4mQubRjj6HwFcaRN5WA43bM81G13d2xI-3OAoLSsXXDxJQZ_inF6qA_OFDB51Pg3yfjXu8CSyioCTUI3StB_Dltd7vmBWNHRT0Ok8zMd9Rf_WU42mgDY-pJW_yCrJ0KEUsd32yi5xqB1wjR4lv8jzMboKmpphgwoeOpPR5xgnfhNbfU8ozvDcfnnEiCpvZ6rLk?width=548&height=542&cropmode=none) | ![](https://sat02pap001files.storage.live.com/y4mRChq9zMZbQZK0gVO19Smbyt74YG1QWTI9RAgewZpJKn6BOEg0GK-_AgR9LwdjDSJriEgnz05YSc9fYUiH09i-PKnb40lZI0AqbvtcyXJvqVSdiWbGpeqPFmIktJb2t-bjIXqrupCzZxXWPXmrrFXXdFzgSWstjebkOujhr-ByhKWoLvgn3GHu2WpnGzbKgXs?width=602&height=599&cropmode=none) | ![3H8yBE359vkbpvC4nSP5xwafWThUh4JvGB](https://sat02pap001files.storage.live.com/y4m7ll7ouERuCbkCXI1x-PQJMYTzonfgpFoEL7Odz8HwPC-O2DngJrulJd23PzD6dJnucGf1zC6zGp4PFyVZjJecRWVT69c06Y4OPdjpEh5Z3E6qkRNg1ZMuP9bxQ3R_YKt2HtjzG_BD3_a9gUkRwHm-zmNH1gxJxnSbysa_qbS8xoiFenQioB4RcU-tMZn71z8?width=1044&height=1098&cropmode=none) |
# Air780E短信转发

利用ESP32驱动Air780E实现短信转发,兼容合宙ESP32S3和ESP32C3。

**⚠ 仅支持联通、移动网络,不支持电信网络 ⚠**

# 功能

- [x] 自动转发收到的短信,短信内容支持多种语言(其实就是ASCII和UCS-2字符集),目前已测试过英文、中文、日语、俄语字符
- [x] 支持多个推送平台,目前接入:
- [x] [LuatOS社区提供的推送服务器](https://push.luatos.org/)
- [x] Bark
- [x] Server酱
- [x] 钉钉机器人
- [x] 推送加 PushPlus
- [x] Telegram(感谢 [@wongJG](https://github.com/wongJG) 的 Pull Request)
- [x] 飞书机器人(感谢 [@mmdjiji](https://github.com/mmdjiji) 的 Pull Request)

# 使用方法

## 硬件组装

- 短接POW键上方的焊盘实现通电即开机

![](/image/shorting_soldering_pad_for_power_key.jpg)

- 按照下图方向为Air780e和ESP32焊上排针和排座。注意合宙不送排座,需要自己买。

| Air780E | ESP32S3 |
|--------------------------|--------------------------|
| ![](/image/air780e.jpeg) | ![](/image/esp32s3.jpeg) |

- 按图示方向插入SIM卡

![](/image/sim_card_direction.jpeg)

- 按图示方向将Air780E和ESP32组合

![](/image/put_together.jpeg)

## 为Air780e刷入AT固件

USB连接Air780e,选择 `Luatools/resource/618_lua_lod/版本号` 目录下的AT固件,将其烧录到Air780e。

## 修改脚本,刷入ESP32

- 修改[`config.lua`](config.lua)
- 修改`config.board_type`为正确的型号,可选值见注释
- 修改`config.wifi`,填入无线网络的SSID和密码
- 修改`config.notification_channel`,将要启用的通知通道的`enabled`配置置为`true`,并填写推送平台相关配置
- 烧录脚本
-[`firmware`](firmware)目录中对应的固件烧入开发板
- 将所有`lua`脚本下载至开发板
![](/image/burning_firmware_and_scripts.png)
- 将开发板上电开机,等待初始化完成后,即可转发短信到配置的通知通道

# LED灯状态含义

- ESP32
- C3的`D4`或S3的`LED A`为初始化状态灯,闪烁代表正在初始化,常亮代表初始化完成,准备转发短信
- C3的`D5`或S3的`LED B`为工作状态灯,平时长灭,收到新短信后高频闪烁,转发完成后熄灭

| ESP32C3 | ESP32S3 |
|-----------------------------|-----------------------------|
| ![](/image/esp32c3_led.jpg) | ![](/image/esp32s3_led.png) |

- Air780
- `POW`灯为电源指示灯,通电后常亮。注意,这个LED不代表开机状态,只要板子有电这个灯就会亮
- `NET`灯为网络状态指示灯,长亮短灭代表正在初始化蜂窝网络,短亮长灭代表网络注册成功,可以接收短信

# Firmware目录下的文件说明

- `LuatOS-SoC_V1004_ESP32C3_classic.soc`对应`ESP32C3 经典款`
- `LuatOS-SoC_V1004_ESP32C3_lite.soc`对应`ESP32C3 简约款`
- `LuatOS-SoC_V1004_ESP32S3.soc`对应`ESP32S3`

固件均通过[合宙云编译](https://wiki.luatos.com/develop/compile/Cloud_compilation.html)精简掉了不需要的功能,以保证内存空间充足。`LuaTools`自动下载的固件不能用,系统启动之后内存就不够用了,发不出去HTTP请求。

目前固件包含`gpio``uart``pwm``wdt``crypto``rtc``network``sntp``tls``wlan``pm``cjson``ntp``shell``dbg`

# 保活 API 说明

API 提供 GET 和 POST 请求支持。

- GET 请求返回 POST 请求存储的时间戳
- POST 请求接收 `{ "expiry": "1732622763" }`,并存储

# 致谢

本项目参考[低成本短信转发器](https://github.com/chenxuuu/sms_forwarding)而来,尤其是PDU相关代码,没有`chenxuuu`的这份项目和[50元内自制短信转发器(Air780E+ESP32C3)](https://www.chenxublog.com/2022/10/28/19-9-sms-forwarding-air780e-esp32c3.html)这篇文章,我不会这么快就完成开发。

# 赞助

| 支付宝 | 微信 | Bitcoin |
| ------ | ---- | ------- |
| ![](https://sat02pap001files.storage.live.com/y4mQubRjj6HwFcaRN5WA43bM81G13d2xI-3OAoLSsXXDxJQZ_inF6qA_OFDB51Pg3yfjXu8CSyioCTUI3StB_Dltd7vmBWNHRT0Ok8zMd9Rf_WU42mgDY-pJW_yCrJ0KEUsd32yi5xqB1wjR4lv8jzMboKmpphgwoeOpPR5xgnfhNbfU8ozvDcfnnEiCpvZ6rLk?width=548&height=542&cropmode=none) | ![](https://sat02pap001files.storage.live.com/y4mRChq9zMZbQZK0gVO19Smbyt74YG1QWTI9RAgewZpJKn6BOEg0GK-_AgR9LwdjDSJriEgnz05YSc9fYUiH09i-PKnb40lZI0AqbvtcyXJvqVSdiWbGpeqPFmIktJb2t-bjIXqrupCzZxXWPXmrrFXXdFzgSWstjebkOujhr-ByhKWoLvgn3GHu2WpnGzbKgXs?width=602&height=599&cropmode=none) | ![3H8yBE359vkbpvC4nSP5xwafWThUh4JvGB](https://sat02pap001files.storage.live.com/y4m7ll7ouERuCbkCXI1x-PQJMYTzonfgpFoEL7Odz8HwPC-O2DngJrulJd23PzD6dJnucGf1zC6zGp4PFyVZjJecRWVT69c06Y4OPdjpEh5Z3E6qkRNg1ZMuP9bxQ3R_YKt2HtjzG_BD3_a9gUkRwHm-zmNH1gxJxnSbysa_qbS8xoiFenQioB4RcU-tMZn71z8?width=1044&height=1098&cropmode=none) |
52 changes: 51 additions & 1 deletion air780_helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ end)
-- 发送AT指令
function air780_helper.send_at_command(command)
uart.write(uart_id, command)
-- 如果是PDU模式下的短信内容,直接返回
if command:sub(1, 6) == "001110" then
return
end
uart.write(uart_id, "\r\n")
log.debug("air780_helper", "发送AT指令\""..command.."\"")
end
Expand Down Expand Up @@ -80,6 +84,20 @@ sys.subscribe(constants.uart_ready_message, function()
return
end

-- 响应发送短信指令
if current_line:find(">", 1, true) then
-- log.debug("air780_helper", "捕获到短信发送提示符")
sys.publish(constants.air780_helper_sms_send_ready)
return
end

-- 响应发送短信成功
if current_line:find("+CMGS:", 1, true) then
-- log.debug("air780_helper", "捕获到短信发送成功")
sys.publish(constants.air780_send_sms_success)
return
end

local urc = current_line:match("^%+(%w+)")

if urc then -- URC上报
Expand Down Expand Up @@ -129,7 +147,7 @@ sys.subscribe(constants.uart_ready_message, function()
end
until #data == 0
end
end
end
end
end
end)
Expand All @@ -145,4 +163,36 @@ function air780_helper.send_at_command_and_wait(command, topic_listen_to, timeou
end
end

function air780_helper.topic_wait(topic, timeout)
local is_successful, r1, r2, r3 = sys.waitUntil(topic, timeout or 1000)
return is_successful
end

function air780_helper.sent_sms(to, text)
local logging_tag = "air780_helper.sent_sms"
local data, len = pdu_helper.encode_pdu(to, text)
if not data or not len then
log.error(logging_tag, "短信编码失败")
return false
end
air780_helper.send_at_command("AT+CMGS=" .. len)

-- 增加调试信息
log.debug(logging_tag, "等待短信发送提示符")
local result = air780_helper.topic_wait(constants.air780_helper_sms_send_ready, 3000)
if not result then
log.error(logging_tag, "短信发送失败,AT+CMGS=" .. len .. " 超时")
return false
end

air780_helper.send_at_command(data .. "\x1A")
local result = air780_helper.topic_wait(constants.air780_send_sms_success, 5000)
if result then
log.info(logging_tag, "短信发送成功")
return true
else
log.error(logging_tag, "短信发送失败")
return false
end
end
return air780_helper
17 changes: 12 additions & 5 deletions config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@ config.disable_rndis = true
-- 而1156版本AT固件第一次检测有概率检测不到SIM卡,需要重试
config.retry_sim_detection = true

-- 续期 API
config.renew_api = "https://api.example.com/sim/expiry"
-- 续期间隔
config.renew_day = 180
-- 续期检查间隔,单位小时。为 0 时禁用
config.renew_check_interval = 0
-- 续期接收号码
config.renew_number = "+8613881388138"
-- 续期短信内容
config.renew_content = "注意余额"

config.wifi = {
{
ssid = "Wi-Fi名",
password = "Wi-Fi密码",
},
-- {
-- ssid = "",
-- password = "",
-- }
}

-- 手动配置DNS服务器
Expand Down Expand Up @@ -73,7 +80,7 @@ config.notification_channel = {
-- telegram 机器人
telegram = {
enabled = false,
-- Webhook地址
-- Webhook地址, https://api.telegram.org/bot<token>/sendMessage
webhook_url = "",
-- chat_id, 通过 https://api.telegram.org/bot<token>/getUpdates 获取
chat_id = ""
Expand Down
2 changes: 2 additions & 0 deletions constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ constants.air780_message_topic_new_message_notification_configured = "NEW_MESSAG
constants.air780_message_topic_network_connected = "NETWORK_CONNECTED"
constants.air780_message_topic_new_sms_received = "NEW_SMS_RECEIVED"
constants.air780_message_topic_new_notification_request = "NEW_NOTIFICATION_REQUEST"
constants.air780_helper_sms_send_ready = "SMS_SEND_READY"
constants.air780_send_sms_success = "SMS_SENT_SUCCESSFULLY"

return constants
12 changes: 11 additions & 1 deletion main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ sys.taskInit(function ()
log.info(logging_tag, "GPRS已附着")
break
else
log.info(logging_tag, "GPRS未附着将在5秒后重新检查")
log.info(logging_tag, "GPRS未附着, 将在5秒后重新检查")
sys.wait(5000)
end
end
Expand All @@ -131,6 +131,16 @@ sys.taskInit(function ()
led_helper.light_status_led()
end)

sys.taskInit(function()
renew = require("renew")
sys.waitUntil(constants.air780_message_topic_new_message_notification_configured)
sys.waitUntil("NTP_UPDATE")
if config.renew_check_interval > 0 then
log.info("renew", "自动续期启动")
renew.renew()
end
end)

--[[
long_sms_buffer = {
[phone_number] = {
Expand Down
14 changes: 14 additions & 0 deletions pdu_helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,18 @@ function pdu_helper.decode_pdu(pdu, len)
return sender_number, sms_content_in_utf8, sms_receive_time, long_sms, total_message_count, current_idx, sms_id
end

-- 生成PDU短信编码
-- 仅支持单条短信,传入数据为utf8编码
-- 返回值为pdu编码与长度
function pdu_helper.encode_pdu(num,data)
data = utf8_to_ucs2(data):toHex()
local numlen, datalen, pducnt, pdu, pdulen, udhi = string.format("%02X", #num), #data / 2, 1, "", "", ""
if datalen > 140 then--短信内容太长啦
data = data:sub(1, 140 * 2)
end
datalen = string.format("%02X", datalen)
pdu = "001110" .. numlen .. number_to_bcd_number(num) .. "000800" .. datalen .. data
return pdu, #pdu // 2 - 1
end

return pdu_helper
Loading

0 comments on commit 37e9975

Please sign in to comment.