04 来电和短信转发
作者:马亚丹 | 最后修改:2026-04-14
一、什么是短信和来电转发
1.1 背景与核心价值
许多人持有多张需保号但低频使用的 SIM 卡(如绑定银行、平台的备用号),既不愿废弃号码,又因多卡携带不便导致信息遗漏。短信和来电转发技术通过专用转发器(可插入目标 SIM 卡),将该号码的短信、来电信息按规则同步至指定接收端,同时确保号码持续存续且关键信息不遗漏。
其核心优势在于多场景适配性:转发对象覆盖主流协同工具(飞书、钉钉、企业微信)、手机号等;附加功能可满足深度需求 —— 通话过程中支持录音,实现语音信息留存;当不想使用 SIM 卡流量时,还能通过 WiFi 或以太网接入家庭宽带完成转发,突破蜂窝网络限制。
1.2 转发器的物联网场景应用
短信和来电转发并非仅服务于个人场景,更在物联网领域承担 “低成本应急通信桥梁” 的角色。依托合宙 Cat.1 等模组对语音、短信功能的原生支持,其能适配工业、安防、车载等多领域的实时响应需求。
1.2.1 工业自动化与设备运维
工业场景对设备故障的响应速度直接影响生产效益,转发器可构建 “无延迟预警链路”:
- 故障预警与远程控制:生产线传感器实时监测设备振动、温度等参数,当数值超出阈值时,通过合宙模组自动发送含故障代码的短信至工程师手机,触发提醒。
1.2.2 智能家居与安防系统
聚焦 “网络中断时的可靠响应”,解决 WiFi / 宽带故障导致的报警失效问题:
- 安全事件即时响应:烟雾报警器、门窗磁传感器触发报警后,发送报警短信通知用户。
- 设备状态上报:智能门锁、扫地机器人等终端完成关键操作后(如 “门锁异常开启”“电量低于 10%”),通过短信推送结果,避免用户因 APP 未登录而错过重要信息,显著提升使用体验。
1.2.3 车载与交通管理
适配移动场景的实时性与应急性需求,尤其适合高速移动或信号波动环境:
- 车辆防盗与追踪:车载 GPS 终端检测到非法启动、车门异常开启时,立即拨打车主电话并发送报警短信;物流车队中,货车的超速、偏离路线等状态通过短信实时上报,管理员可即时调度调整行程。
1.2.4 农业与环境监测
破解 “偏远地区通信覆盖不足” 的痛点,成为野外设备的 “保底通信方案”:
- 精准农业管理:土壤湿度传感器、气象站通过短信告知农户灌溉需求或极端天气预警。
- 偏远地区数据传输:在无光纤、WiFi 覆盖的山区,水文监测设备通过合宙模组将水位、水质数据转为短信发送至水利局平台。类似青藏高原的生态监测场景,短信转发成为无人机中继之外的 “低成本备用方案”,确保数据不丢失。
1.2.5 商业服务场景
兼顾企业运营效率与用户体验,实现信息精准触达:
- 备用卡集中管理:企业将绑定业务系统的保号 SIM 卡插入转发器,把银行对账通知、平台验证码等短信统一转发至指定号码货终端,避免多卡分散管理导致的信息遗漏。
- 商业信息精准推送:零售企业通过转发器向客户发送订单发货、促销活动等短信,对高价值订单同步触发来电语音确认,提升用户信任度与转化率。
二、功能演示概述
本文演示合宙 4G 模组使用 LuatOS 开发时, 把 短信内容和来电号码转发到指定终端的应用功能.
使用 Air780EHV/EGH/EHM 核心板下载 Air780EHV/EGH/EHM 的 LuatOS 示例代码中 sms_call_forward 的例程进行验证,例程中演示了短信内容转发和来电号码转发,实现的功能核心业务逻辑分别为:
短信内容转发 sms_forward.lua:
1、配置飞书,钉钉,企业微信机器人的 webhook 和 secret(加签)。
2、send_sms(),发送短信的功能函数,等待 CC_IND 消息后,手机卡可以进行收发短信。
3、receive_sms(),接收短信处理的功能函数,收到短信后获取来信号码和短信内容,通过回调函数 sms_handler(num, txt)转发到指定的机器人。
来电号码转发 cc_forward.lua:
1、配置飞书,钉钉,企业微信机器人的 webhook 和 secret(加签)。
2、cc_setup(),初始化电话功能,做好接收来电的准备。
3、cc_state(state),电话状态判断并获取来电号码,来电或者挂断等不同情况做不同处理。
4、cc_forward(),来电号码信息转发到指定机器人
三、准备硬件环境
参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。
1、Air780EHV/EGH/EHM 核心板一块 + 支持短信和电话功能的手机 sim 卡一张 + 网线一根:
- sim 卡插入核心板的 sim 卡槽
- 天线装到核心板上
- 网线一端插入核心板网口,另外一端连接可以上外网的路由器网口
2、TYPE-C USB 数据线一根 ,Air780EHV/EGH/EHM 核心板和数据线的硬件接线方式为:
- Air780EHV/EGH/EHM 核心板通过 TYPE-C USB 口供电;(外部供电/USB 供电 拨动开关 拨到 USB 供电一端)
- TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
3、可选 AirETH_1000 配件板一块,Air780EHV/EGH/EHM 核心板和 AirETH_1000 配件板的硬件接线方式为:
| Air780EHV/EGH/EHM核心板 | AirETH_1000配件板 |
| 3V3 | 3.3v |
| gnd | gnd |
| 86/SPI0CLK | SCK |
| 83/SPI0CS | CSS |
| 84/SPI0MISO | SDO |
| 85/SPI0MOSI | SDI |
| 107/GPIO21 | INT |
4、Air780EHV 核心板购买链接:https://item.taobao.com/item.htm?id=943253206359&spm=a1z10.3-c-s.w4002-24045920836.11.52ca6ee580L9nu
5、Air780EGH 核心板购买链接:https://item.taobao.com/item.htm?id=944408168973&skuId=5846323761716&spm=a1z10.3-c-s.w4002-24045920836.9.48b36ee5ZezeNE
6、Air780EHM 核心板购买链接:https://item.taobao.com/item.htm?id=944405208876&skuId=6012483723790&spm=a1z10.3-c-s.w4002-24045920836.9.5fe56ee5KM4Lbo
四、准备软件环境
在开始实践本示例之前,先准备一下软件环境:
1.Luatools 工具,如果是第一次使用 Luatools 工具,请仔细阅读此链接教程。
2.内核固件文件(底层 core 固件文件):
本demo开发测试时使用的固件为LuatOS-SoC_V2016_Air780EHV_1.soc,本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;
3.luatos 需要的脚本和资源文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM_Air780EHV_Air780EGH/project/sms_call_forward
main.lua:主程序入口文件,加载以下 4 个文件运行。
netdrv_device.lua:网卡驱动设备,可以配置使用 netdrv 文件夹内的四种网卡(单 4g 网卡,单 wifi 网卡,单 spi 以太网卡,多网卡)中的任何一种网卡
sms_forward.lua: 短信转发功能模块文件
cc_forward.lua:来电转发功能模块文件
sntp_app.lua:时间同步应用功能模块
4.lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;准备好软件环境之后,接下来查看如何烧录项目文件到 Air780EHV 核心板,将本篇文章中演示使用的项目文件,即本章第三小节描述的 5 个文件烧录到 Air780EHV 核心板中。
5.钉钉机器人:参考 780EHV-sms,5.1 章节
6.飞书机器人:参考 780EHV-sms,5.2 章节
7.企业微信机器人:参考 780EHV-sms,5.3 章节
五、API 接口说明
六、示例代码和功能展示
6.1 流程介绍
1、搭建好硬件环境
2、demo 脚本代码 netdrv_device.lua 中,按照自己的网卡需求启用对应的 Lua 文件
- 如果需要单 4G 网卡,打开 require "netdrv_4g",其余注释掉
- 如果需要单 WIFI STA 网卡,打开 require "netdrv_wifi",其余注释掉;同时 netdrv_wifi.lua 中的 wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1),前两个参数,修改为自己测试时 wifi 热点的名称和密码;注意:仅支持 2.4G 的 wifi,不支持 5G 的 wifi
- 如果需要以太网卡,打开 require "netdrv_eth_spi",其余注释掉
- 如果需要 pc 模拟器网卡,打开 require "netdrv_pc",其余注释掉
- 如果需要多网卡,打开 require "netdrv_multiple",其余注释掉;同时 netdrv_multiple.lua 中的 ssid = "茶室-降功耗,找合宙!", password = "Air123456", 修改为自己测试时 wifi 热点的名称和密码;注意:仅支持 2.4G 的 wifi,不支持 5G 的 wif
3、main.lua 中加载 netdrv_device.lua、sms_forward.lua、cc_forward.lua、sntp_app.lua
4、Luatools 烧录内核固件和修改后的 demo 脚本代码
5、netdrv_device.lua 中默认使用 require "netdrv_4g",即单 4G 网卡。
6.2 代码和 log
6.2.1 main.lua 代码示例
(点击查看 sms_call_forward 的完整 demo)
PROJECT = "sms_call_forward"
VERSION = "001.000.000"
require "sys"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
if wdt then
--配置喂狗超时时间为9秒钟
wdt.init(9000)
--启动一个循环定时器,每隔3秒钟喂一次狗
sys.timerLoopStart(wdt.feed, 3000)
end
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)
--加载cc_forward功能模块
require "cc_forward"
--加载sms_forward功能模块
require "sms_forward"
-- 加载网络驱动设备功能模块,在该文件中修改自己使用的联网方式
require"netdrv_device"
-- 加载sntp时间同步应用功能模块(转发短信是携带时间参数,socket需要时间同步功能)
require "sntp_app"
-- 启动系统调度(必须放在最后)
sys.run()
log 日志
烧录成功后,代码会自动运行,log 日志打印 IP_READY、时间同步等,log 日志打印如下:
[2025-11-11 11:38:47.437][000000000.365] self_info 127:model Air780EHV_A10 imei 867920075014846
[2025-11-11 11:38:47.443][000000000.365] self_info 129:firmware[1] TTS+VOLTE
[2025-11-11 11:38:47.452][000000000.365] self_info 131:zone(kbytes) fs 768 script 512
[2025-11-11 11:38:47.456][000000000.366] I/main LuatOS@Air780EHV base 25.03 bsp V2016 32bit
[2025-11-11 11:38:47.461][000000000.366] I/main ROM Build: Oct 10 2025 10:52:53
[2025-11-11 11:38:47.465][000000000.369] W/pins /luadb/pins_air780ehv.json not exist!!
[2025-11-11 11:38:47.475][000000000.372] D/main loadlibs luavm 4194296 16096 16096
[2025-11-11 11:38:47.485][000000000.372] D/main loadlibs sys 3211688 104756 105984
[2025-11-11 11:38:47.491][000000000.372] D/main loadlibs psram 3211688 104848 106076
[2025-11-11 11:38:47.496][000000000.390] I/user.main cc_sms_forward 001.000.000
[2025-11-11 11:38:47.500][000000000.397] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:47.505][000000000.423] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:47.785][000000001.398] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:47.816][000000001.424] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:48.785][000000002.399] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:48.815][000000002.425] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:49.789][000000003.400] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:49.818][000000003.425] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:50.781][000000004.401] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:50.810][000000004.425] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:51.790][000000005.402] W/user.cc wait IP_READY 1 1
[2025-11-11 11:38:51.821][000000005.426] W/user.sntp_task_func wait IP_READY 1 1
[2025-11-11 11:38:52.725][000000006.328] D/mobile cid1, state0
[2025-11-11 11:38:52.732][000000006.329] D/mobile bearer act 0, result 0
[2025-11-11 11:38:52.742][000000006.329] D/mobile TIME_SYNC 0
[2025-11-11 11:38:52.750][000000006.329] D/mobile NETIF_LINK_ON -> IP_READY
[2025-11-11 11:38:52.760][000000006.330] I/user.netdrv_4g.ip_ready_func IP_READY 10.183.48.180 255.255.255.255 0.0.0.0 nil
[2025-11-11 11:38:52.766][000000006.331] W/user.sntp_task_func recv IP_READY
[2025-11-11 11:38:52.773][000000006.332] D/sntp query ntp.aliyun.com
[2025-11-11 11:38:52.779][000000006.332] dns_run 676:ntp.aliyun.com state 0 id 1 ipv6 0 use dns server0, try 0
[2025-11-11 11:38:52.787][000000006.334] I/user.cc recv IP_READY 1 1
[2025-11-11 11:38:52.795][000000006.388] dns_run 693:dns all done ,now stop
[2025-11-11 11:38:53.031][000000006.649] D/sntp Unix timestamp: 1762832332
[2025-11-11 11:38:53.040][000000006.650] soc_cms_proc 2219:cenc report 1,51,1,15
[2025-11-11 11:38:53.049][000000006.654] I/user.sntp_task_func 时间同步成功 本地时间 Tue Nov 11 11:38:52 2025
[2025-11-11 11:38:53.057][000000006.654] I/user.sntp_task_func 时间同步成功 UTC时间 Tue Nov 11 03:38:52 2025
[2025-11-11 11:38:53.073][000000006.655] I/user.sntp_task_func 时间同步成功 RTC时钟(UTC时间) {"year":2025,"min":38,"hour":3,"mon":11,"sec":52,"day":11}
[2025-11-11 11:38:53.092][000000006.656] I/user.sntp_task_func 时间同步成功 本地时间戳 1762832332
[2025-11-11 11:38:53.102][000000006.657] I/user.sntp_task_func 时间同步成功 本地时间os.date() json格式 {"wday":3,"min":38,"yday":315,"hour":11,"isdst":false,"year":2025,"month":11,"sec":52,"day":11}
[2025-11-11 11:38:53.112][000000006.657] I/user.sntp_task_func 时间同步成功 本地时间os.date(os.time()) 1762861132
[2025-11-11 11:38:53.118][000000006.737] D/mobile NETIF_LINK_ON -> IP_READY
[2025-11-11 11:38:53.132][000000006.738] I/user.netdrv_4g.ip_ready_func IP_READY 10.183.48.180 255.255.255.255 0.0.0.0 nil
[2025-11-11 11:38:54.659][000000008.275] D/mobile ims reg state 0
[2025-11-11 11:38:54.670][000000008.276] D/mobile LUAT_MOBILE_EVENT_CC status 0
[2025-11-11 11:38:54.681][000000008.276] D/mobile LUAT_MOBILE_CC_READY
[2025-11-11 11:38:54.694][000000008.277] I/user.发送短信前wait CC_IND
[2025-11-11 11:38:54.699][000000008.278] I/user.现在可以收发短信
[2025-11-11 11:38:54.704][000000008.278] I/user.mobile.number(id) = nil
[2025-11-11 11:38:54.714][000000008.278] I/user.mobile.iccid(id) = 89860325743780541565
[2025-11-11 11:38:54.719][000000008.279] I/user.mobile.simid(id) = 0
[2025-11-11 11:38:54.726][000000008.279] I/user.mobile.imsi(index) = 460115726670673
[2025-11-11 11:38:54.733][000000008.279] D/sms pdu len 18
[2025-11-11 11:38:54.744][000000008.281] I/user.通话准备完成,可以拨打电话或者呼入电话了
luatools 页面打印


6.2.2 sms_forward.lua 核心代码部分
(点击查看 sms_call_forward 的完整 demo)
-- webhook_feishu和secret_feishu要换成你自己机器人的值
-- webhook_feishu是钉钉分配给机器人的URL
-- secret_feishu是选取 "加签", 自动生成的密钥
-- 下面的给一个测试群发消息, 随时可能关掉, 请换成你自己的值
local webhook_feishu = "https://open.feishu.cn/open-apis/bot/v2/hook/bb089165-4b73-4f80-9ed0-da0c908b44e5"
local secret_feishu = "dp9w8i5IZrrZQpLW0bTcI"
local webhook_dingding =
"https://oapi.dingtalk.com/robot/send?access_token=03f4753ec6aa6f0524fb85907c94b17f3fa0fed3107d4e8f4eee1d4a97855f4d"
local secret_dingding = "SECac5b455d6b567f64073a456e91feec6ad26c0f8f7dcca85dd2ce6c23ea466c52"
local webhook_weixin = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=71017f82-e027-4c5d-a618-eb4ee01750e9"
-- 飞书关于机器人的文档 https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN
--1.功能函数:短信转发到飞书
local function feishu_post_sms(num, rctxt)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
-- LuatOS的时间戳只到秒,飞书也只需要秒
local timestamp = tostring(os.time())
local sign = crypto.hmac_sha256("", timestamp .. "\n" .. secret_feishu):fromHex():toBase64()
log.info("timestamp", timestamp)
log.info("sign", sign)
-- 注意, 这里的参数跟钉钉不同, 钉钉有个access_token参数, 飞书没有
local url = webhook_feishu
log.info("url", url)
-- json格式也需要按飞书的来
local data = { msg_type = "text" }
data["timestamp"] = timestamp
data["sign"] = sign
-- text就是要发送的文本内容, 其他格式按飞书的要求拼接table就好了
local text = "我的id是" ..
tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "发来短信,内容是:" .. rctxt
data["content"] = { text = text }
local rbody = (json.encode(data))
log.info("feishu", rbody)
local code, headers, body = http.request("POST", url, rheaders, rbody).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("feishu", code, body)
end
--2.功能函数:短信转发到钉钉
local function dingding_post(num, rctxt)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
-- LuatOS的时间戳只到秒,但钉钉需要毫秒,补3个零
local timestamp = tostring(os.time()) .. "000"
local sign = crypto.hmac_sha256(timestamp .. "\n" .. secret_dingding, secret_dingding):fromHex():toBase64()
:urlEncode()
log.info("timestamp", timestamp)
log.info("sign", sign)
local url = webhook_dingding .. "×tamp=" .. timestamp .. "&sign=" .. sign
log.info("url", url)
local data = { msgtype = "text" }
-- content就是要发送的文本内容, 其他格式按钉钉的要求拼接table就好了
local content = "我的id是" ..
tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "发来短信,内容是:" .. rctxt
data["text"] = { content = content }
local rbody = (json.encode(data))
log.info("dingding", rbody)
local code, headers, body = http.request("POST", url, rheaders, (json.encode(data))).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("dingding", code, body)
end
--3.功能函数:短信转发到企业微信
local function weixin_post(num, rctxt)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
local timestamp = tostring(os.time()) .. "000"
log.info("timestamp", timestamp)
local url = webhook_weixin .. "×tamp=" .. timestamp
log.info("url", url)
local data = { msgtype = "text" }
-- content就是要发送的文本内容, 其他格式按钉钉的要求拼接table就好了
local content = "我的id是" ..
tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "发来短信,内容是:" .. rctxt
data["text"] = { content = content }
local rbody = (json.encode(data))
log.info("weixin", rbody)
local code, headers, body = http.request("POST", url, rheaders, (json.encode(data))).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("weixin", code, body)
end
--4.功能函数:接收短信的回调函数
local function sms_handler(num, txt)
-- num 给我发短信的手机号码
-- txt 收到的短信文本内容
log.info("转发到飞书")
feishu_post_sms(num, txt)
log.info("转发到钉钉")
dingding_post(num, txt)
log.info("转发到微信")
weixin_post(num, txt)
end
--------------------------------------------------------------------
--5. 功能函数:接收短信, 支持多种方式, 选一种就可以了
-----5.1. 设置回调函数
--sms.setNewSmsCb(sms_handler)
-----5.2. 订阅系统消息
--sys.subscribe("SMS_INC", sms_handler)
-- 5.3. 在task里等着
local function receive_sms()
while true do
local ret, num, txt = sys.waitUntil("SMS_INC", 300000)
if num then
log.info("num是", num)
log.info("收到来自" .. num .. "的短信:" .. txt)
sms_handler(num, txt)
end
end
end
-------------------------------------------------------------------
-- 6.功能函数:发送短信, 直接调用sms.send就行, 是不是task无所谓
local function send_sms()
--系统消息CC_IND到了才能收发短信
--按照规范的做法,这里应该等待"SMS_READY"消息,
--目前内核固件正在开发支持"SMS_READY"消息功能,
--等开发好了之后,再使用"SMS_READY"消息,
--当前阶段,先使用"CC_IND"替代
sys.waitUntil("CC_IND")
log.info("发送短信前wait CC_IND")
local cont = 1
while true do
log.info("现在可以收发短信")
--获取本机号码,如果卡商没写入会返回nil
log.info("mobile.number(id) = ", mobile.number())
--获取本机iccid,失败返回nil
log.info("mobile.iccid(id) = ", mobile.iccid())
--获取本机simid,失败返回-1
log.info("mobile.simid(id) = ", mobile.simid())
--获取本机imsi,失败返回nil
log.info("mobile.imsi(index) = ", mobile.imsi())
--给自己发短信
--local result = sms.send("1593868****", "test")
--电信卡给10001发送“102”查话费
local result = sms.send("10001", "102")
if result then
--sms.send 的同步结果仅表示任务启动成功,
--最终发送状态需通过异步事件 "SMS_SEND_RESULT" 获取;
--目前内核固件正在开发支持"SMS_SEND_RESULT"消息功能
--当前阶段,sys.waitUntil等不到这个消息,最终5秒超时退出。
sys.waitUntil("SMS_SEND_RESULT",5*1000)
log.info("给10001发送查询短信", "这是第" .. cont .. "次发送", " 发送结果:", result)
else
log.warn("sms", "短信发送失败")
end
cont = cont + 1
sys.wait(10 * 60 * 1000)
end
end
--发送短信
sys.taskInit(send_sms)
--接收短信
sys.taskInit(receive_sms)
log 日志
此处短信演示使用了电信卡,发送"102"给"10001"查询余额,会收到电信回复的短信,并转发到飞书、钉钉和微信,log 打印如下:
[2025-11-11 11:38:54.699][000000008.278] I/user.现在可以收发短信
[2025-11-11 11:38:54.704][000000008.278] I/user.mobile.number(id) = nil
[2025-11-11 11:38:54.714][000000008.278] I/user.mobile.iccid(id) = 89860325743780541565
[2025-11-11 11:38:54.719][000000008.279] I/user.mobile.simid(id) = 0
[2025-11-11 11:38:54.726][000000008.279] I/user.mobile.imsi(index) = 460115726670673
[2025-11-11 11:38:54.733][000000008.279] D/sms pdu len 18
[2025-11-11 11:38:54.744][000000008.281] I/user.通话准备完成,可以拨打电话或者呼入电话了
[2025-11-11 11:38:55.279][000000008.900] luat_sms_proc 1239:[DIO 1239]: CMI_SMS_SEND_MSG_CNF is in
[2025-11-11 11:38:55.289][000000008.900] I/sms long sms callback seqNum = 1
[2025-11-11 11:38:56.251][000000009.867] luat_sms_proc 1236:[DIO 1236]: CMI_SMS_NEW_MSG_IND is in
[2025-11-11 11:38:56.264][000000009.868] D/sms dcs 2 | 0 | 0 | 0
[2025-11-11 11:38:56.272][000000009.869] I/sms long-sms, wait more frags 2/2
[2025-11-11 11:38:59.153][000000012.766] luat_sms_proc 1236:[DIO 1236]: CMI_SMS_NEW_MSG_IND is in
[2025-11-11 11:38:59.163][000000012.767] D/sms dcs 2 | 0 | 0 | 0
[2025-11-11 11:38:59.175][000000012.767] I/sms long-sms is ok
[2025-11-11 11:38:59.182][000000012.769] I/user.num是 10001
[2025-11-11 11:38:59.195][000000012.770] I/user.收到来自10001的短信:截止到2025年11月11日11时,本机可用余额:45.4元,帐户余额:45.4,欠费:0.0元 查询结果仅供参考,实际费用以出账单为准。更多服务请微信关注【河南电信】公众号,或下载欢go客户端( http://a.189.cn/JJTh4u )。
[2025-11-11 11:38:59.226][000000012.770] I/user.转发到飞书
[2025-11-11 11:38:59.235][000000012.772] I/user.timestamp 1762832338
[2025-11-11 11:38:59.245][000000012.772] I/user.sign hS1xPjJCUwNaWP5j/CZuZ+eVJOJ52rs7MZF1iOWSfcg=
[2025-11-11 11:38:59.257][000000012.773] I/user.url https://open.feishu.cn/open-apis/bot/v2/hook/bb089165-4b73-4f80-9ed0-da0c908b44e5
[2025-11-11 11:38:59.267][000000012.775] I/user.feishu {"content":{"text":"我的id是nil,Tue Nov 11 11:38:58 2025,Air780EHV, 10001发来短信,内容是:截止到2025年11月11日11时,本机可用余额:45.4元,帐户余额:45.4,欠费:0.0元 查询结果仅供参考,实际费用以出账单为准。更多服务请微信关注【河南电信】公众号,或下载欢go客户端( http:\/\/a.189.cn\/JJTh4u )。"},"sign":"hS1xPjJCUwNaWP5j\/CZuZ+eVJOJ52rs7MZF1iOWSfcg=","msg_type":"text","timestamp":"1762832338"}
[2025-11-11 11:38:59.274][000000012.781] dns_run 676:open.feishu.cn state 0 id 2 ipv6 0 use dns server0, try 0
[2025-11-11 11:38:59.285][000000012.825] dns_run 693:dns all done ,now stop
[2025-11-11 11:38:59.700][000000013.317] I/user.给10001发送查询短信 这是第1次发送 发送结果: true
[2025-11-11 11:39:00.405][000000014.017] I/user.feishu 200 {"StatusCode":0,"StatusMessage":"success","code":0,"data":{},"msg":"success"}
[2025-11-11 11:39:00.415][000000014.017] I/user.转发到钉钉
[2025-11-11 11:39:00.428][000000014.019] I/user.timestamp 1762832339000
[2025-11-11 11:39:00.439][000000014.019] I/user.sign kgaeJUMqVQfYzxpqu%2B80dTRY24V0PKZnhrV%2FnjcL69c%3D
[2025-11-11 11:39:00.445][000000014.019] I/user.url https://oapi.dingtalk.com/robot/send?access_token=03f4753ec6aa6f0524fb85907c94b17f3fa0fed3107d4e8f4eee1d4a97855f4d×tamp=1762832339000&sign=kgaeJUMqVQfYzxpqu%2B80dTRY24V0PKZnhrV%2FnjcL69c%3D
[2025-11-11 11:39:00.462][000000014.021] I/user.dingding {"text":{"content":"我的id是nil,Tue Nov 11 11:38:59 2025,Air780EHV, 10001发来短信,内容是:截止到2025年11月11日11时,本机可用余额:45.4元,帐户余额:45.4,欠费:0.0元 查询结果仅供参考,实际费用以出账单为准。更多服务请微信关注【河南电信】公众号,或下载欢go客户端( http:\/\/a.189.cn\/JJTh4u )。"},"msgtype":"text"}
[2025-11-11 11:39:00.472][000000014.022] dns_run 676:oapi.dingtalk.com state 0 id 3 ipv6 0 use dns server0, try 0
[2025-11-11 11:39:00.486][000000014.066] dns_run 693:dns all done ,now stop
[2025-11-11 11:39:01.117][000000014.738] I/user.dingding 200 {"errcode":0,"errmsg":"ok"}
[2025-11-11 11:39:01.123][000000014.739] I/user.转发到微信
[2025-11-11 11:39:01.128][000000014.739] I/user.timestamp 1762832340000
[2025-11-11 11:39:01.145][000000014.739] I/user.url https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=71017f82-e027-4c5d-a618-eb4ee01750e9×tamp=1762832340000
[2025-11-11 11:39:01.156][000000014.741] I/user.weixin {"text":{"content":"我的id是nil,Tue Nov 11 11:39:00 2025,Air780EHV, 10001发来短信,内容是:截止到2025年11月11日11时,本机可用余额:45.4元,帐户余额:45.4,欠费:0.0元 查询结果仅供参考,实际费用以出账单为准。更多服务请微信关注【河南电信】公众号,或下载欢go客户端( http:\/\/a.189.cn\/JJTh4u )。"},"msgtype":"text"}
[2025-11-11 11:39:01.174][000000014.742] dns_run 676:qyapi.weixin.qq.com state 0 id 4 ipv6 0 use dns server0, try 0
[2025-11-11 11:39:01.184][000000014.790] dns_run 693:dns all done ,now stop
[2025-11-11 11:39:02.307][000000015.930] I/user.weixin 200 {"errcode":0,"errmsg":"ok"}
luatools 页面打印


6.2.3 cc_forward.lua 核心代码部分
(点击查看 sms_call_forward 的完整 demo)
-- webhook_feishu和secret_feishu要换成你自己机器人的值
-- webhook_feishu是钉钉分配给机器人的URL
-- secret_feishu是选取 "加签", 自动生成的密钥
-- 下面的给一个测试群发消息, 随时可能关掉, 请换成你自己的值
local webhook_feishu = "https://open.feishu.cn/open-apis/bot/v2/hook/bb089165-4b73-4f80-9ed0-da0c908b44e5"
local secret_feishu = "dp9w8i5IZrrZQpLW0bTcI"
local webhook_dingding =
"https://oapi.dingtalk.com/robot/send?access_token=03f4753ec6aa6f0524fb85907c94b17f3fa0fed3107d4e8f4eee1d4a97855f4d"
local secret_dingding = "SECac5b455d6b567f64073a456e91feec6ad26c0f8f7dcca85dd2ce6c23ea466c52"
local webhook_weixin = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=71017f82-e027-4c5d-a618-eb4ee01750e9"
-- 飞书关于机器人的文档 https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN
-- status,通话状态,string类型,取值如下:
-- "READY":通话准备完成,可以拨打电话或者呼入电话了
-- "INCOMINGCALL":有电话呼入
-- "CONNECTED":电话已经接通
-- "DISCONNECTED":电话被对方挂断
-- "SPEECH_START":通话开始
-- "MAKE_CALL_OK":拨打电话请求成功
-- "MAKE_CALL_FAILED":拨打电话请求失败
-- "ANSWER_CALL_DONE":接听电话请求完成
-- "HANGUP_CALL_DONE":挂断电话请求完成
-- "PLAY":开始有音频输出
local cnt = 0
local phone_num = nil
local multimedia_id = 0 -- 音频通道 0
--1.功能函数:来电转发到飞书
local function feishu_post_cc(num)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
-- LuatOS的时间戳只到秒,飞书也只需要秒
local timestamp = tostring(os.time())
local sign = crypto.hmac_sha256("", timestamp .. "\n" .. secret_feishu):fromHex():toBase64()
log.info("timestamp", timestamp)
log.info("sign", sign)
-- 注意, 这里的参数跟钉钉不同, 钉钉有个access_token参数, 飞书没有
local url = webhook_feishu
log.info("url", url)
-- json格式也需要按飞书的来
local data = { msg_type = "text" }
data["timestamp"] = timestamp
data["sign"] = sign
-- text就是要发送的文本内容, 其他格式按飞书的要求拼接table就好了
local text = "我的id是" .. tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "来电"
data["content"] = { text = text }
local rbody = (json.encode(data))
log.info("feishu", rbody)
local code, headers, body = http.request("POST", url, rheaders, rbody).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("feishu", code, body)
end
--2.功能函数:来电转发到钉钉
local function dingding_post_cc(num)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
-- LuatOS的时间戳只到秒,但钉钉需要毫秒,补3个零
local timestamp = tostring(os.time()) .. "000"
local sign = crypto.hmac_sha256(timestamp .. "\n" .. secret_dingding, secret_dingding):fromHex():toBase64()
:urlEncode()
log.info("timestamp", timestamp)
log.info("sign", sign)
local url = webhook_dingding .. "×tamp=" .. timestamp .. "&sign=" .. sign
log.info("url", url)
local data = { msgtype = "text" }
-- content就是要发送的文本内容, 其他格式按钉钉的要求拼接table就好了
local content = "我的id是" ..
tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "来电"
data["text"] = { content = content }
local rbody = (json.encode(data))
log.info("dingding", rbody)
local code, headers, body = http.request("POST", url, rheaders, (json.encode(data))).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("dingding", code, body)
end
--3.功能函数:来电转发到企业微信
local function weixin_post_cc(num)
local rheaders = {}
rheaders["Content-Type"] = "application/json"
local timestamp = tostring(os.time()) .. "000"
log.info("timestamp", timestamp)
local url = webhook_weixin .. "×tamp=" .. timestamp
log.info("url", url)
local data = { msgtype = "text" }
-- content就是要发送的文本内容, 其他格式按钉钉的要求拼接table就好了
local content = "我的id是" ..
tostring(device_id) .. "," .. (os.date()) .. "," .. rtos.bsp() .. ", " .. num .. "来电"
data["text"] = { content = content }
local rbody = (json.encode(data))
log.info("weixin", rbody)
local code, headers, body = http.request("POST", url, rheaders, (json.encode(data))).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("weixin", code, body)
end
--4.初始化cc
local function cc_setup()
--查看网卡适配器的联网状态是否IP_READY,true表示已经准备好可以联网了,false暂时不可以联网
while not socket.adapter(socket.dft()) do
log.warn("cc", "wait IP_READY", socket.dft())
-- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
-- 或者等待1秒超时退出阻塞等待状态;
-- 注意:此处的1000毫秒超时不要修改的更长;
-- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
-- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
-- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
sys.waitUntil("IP_READY", 1000)
end
-- 检测到了IP_READY消息,设置默认网络适配器编号
log.info("cc", "recv IP_READY", socket.dft())
--初始化电话功能
local cc_int = cc.init(multimedia_id)
if cc_int then
return true
else
lngo.info("初始化电话功能失败")
end
end
--5.转发号码
local function cc_forward()
log.info(" 来电号码转发到飞书")
feishu_post_cc(phone_num)
log.info("来电号码转发到钉钉")
dingding_post_cc(phone_num)
log.info("来电号码转发到微信")
weixin_post_cc(phone_num)
end
--6.来电判断
local function cc_state(state)
if state == "READY" then
log.info("通话准备完成,可以拨打电话或者呼入电话了")
--有电话呼入
elseif state == "INCOMINGCALL" then
if cnt == 0 then
log.info("获取最后一次通话的号码")
phone_num = cc.lastNum()
log.info("来电号码是:", phone_num)
if phone_num then
--转发通知来电
sys.taskInit(cc_forward)
end
end
cnt = cnt + 1
if cnt > 3 then
--自动接听
--cc.accept(0)
--响4声以后自动自动挂断
cc.hangUp()
cnt = 0
end
--电话被对方挂断
elseif state == "DISCONNECTED" then
cnt = 0
end
end
--7.功能测试主函数
local function test_fun()
if not cc_setup() then
return
end
sys.subscribe("CC_IND", cc_state)
end
sys.taskInit(test_fun)
log 日志
此处来电转发演示使用了电信卡,用另外手机拨打模组上的电话号码,响铃 4 声后自动挂断,获取到来电号码转发到飞书,钉钉和企业微信,log 打印如下:
[2025-11-11 11:39:31.138][000000044.751] D/mobile LUAT_MOBILE_EVENT_CC status 12
[2025-11-11 11:39:31.147][000000044.752] D/mobile LUAT_MOBILE_EVENT_CC status 1
[2025-11-11 11:39:31.151][000000044.752] I/user.获取最后一次通话的号码
[2025-11-11 11:39:31.160][000000044.754] I/user.来电号码是: 18317857567
[2025-11-11 11:39:31.163][000000044.754] I/user. 来电号码转发到飞书
[2025-11-11 11:39:31.166][000000044.755] I/user.timestamp 1762832370
[2025-11-11 11:39:31.175][000000044.756] I/user.sign Be6GXYvLRzO6iIBvvbkd1MzzecU0Vg+hxwF1uyRmFBc=
[2025-11-11 11:39:31.178][000000044.756] I/user.url https://open.feishu.cn/open-apis/bot/v2/hook/bb089165-4b73-4f80-9ed0-da0c908b44e5
[2025-11-11 11:39:31.182][000000044.757] I/user.feishu {"content":{"text":"我的id是nil,Tue Nov 11 11:39:30 2025,Air780EHV, 18317857567来电"},"sign":"Be6GXYvLRzO6iIBvvbkd1MzzecU0Vg+hxwF1uyRmFBc=","msg_type":"text","timestamp":"1762832370"}
[2025-11-11 11:39:31.190][000000044.759] dns_run 676:open.feishu.cn state 0 id 5 ipv6 0 use dns server0, try 0
[2025-11-11 11:39:31.194][000000044.761] D/mobile LUAT_MOBILE_EVENT_CC status 2
[2025-11-11 11:39:31.231][000000044.847] dns_run 693:dns all done ,now stop
[2025-11-11 11:39:32.101][000000045.724] I/user.feishu 200 {"StatusCode":0,"StatusMessage":"success","code":0,"data":{},"msg":"success"}
[2025-11-11 11:39:32.109][000000045.724] I/user.来电号码转发到钉钉
[2025-11-11 11:39:32.115][000000045.726] I/user.timestamp 1762832371000
[2025-11-11 11:39:32.129][000000045.726] I/user.sign 4%2B1ksJhCL5xqQ8zk%2BKrV7qTXq1EDtYqMISzyT5T2Ykg%3D
[2025-11-11 11:39:32.133][000000045.726] I/user.url https://oapi.dingtalk.com/robot/send?access_token=03f4753ec6aa6f0524fb85907c94b17f3fa0fed3107d4e8f4eee1d4a97855f4d×tamp=1762832371000&sign=4%2B1ksJhCL5xqQ8zk%2BKrV7qTXq1EDtYqMISzyT5T2Ykg%3D
[2025-11-11 11:39:32.136][000000045.728] I/user.dingding {"text":{"content":"我的id是nil,Tue Nov 11 11:39:31 2025,Air780EHV, 18317857567来电"},"msgtype":"text"}
[2025-11-11 11:39:32.140][000000045.729] dns_run 676:oapi.dingtalk.com state 0 id 6 ipv6 0 use dns server0, try 0
[2025-11-11 11:39:32.178][000000045.776] dns_run 693:dns all done ,now stop
[2025-11-11 11:39:32.782][000000046.390] I/user.dingding 200 {"errcode":0,"errmsg":"ok"}
[2025-11-11 11:39:32.790][000000046.391] I/user.来电号码转发到微信
[2025-11-11 11:39:32.793][000000046.391] I/user.timestamp 1762832371000
[2025-11-11 11:39:32.795][000000046.392] I/user.url https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=71017f82-e027-4c5d-a618-eb4ee01750e9×tamp=1762832371000
[2025-11-11 11:39:32.802][000000046.393] I/user.weixin {"text":{"content":"我的id是nil,Tue Nov 11 11:39:31 2025,Air780EHV, 18317857567来电"},"msgtype":"text"}
[2025-11-11 11:39:32.806][000000046.394] dns_run 676:qyapi.weixin.qq.com state 0 id 7 ipv6 0 use dns server0, try 0
[2025-11-11 11:39:32.811][000000046.428] dns_run 693:dns all done ,now stop
[2025-11-11 11:39:33.618][000000047.230] I/user.weixin 200 {"errcode":0,"errmsg":"ok"}
[2025-11-11 11:39:37.172][000000050.750] D/mobile LUAT_MOBILE_EVENT_CC status 1
[2025-11-11 11:39:43.140][000000056.750] D/mobile LUAT_MOBILE_EVENT_CC status 1
[2025-11-11 11:39:49.137][000000062.750] D/mobile LUAT_MOBILE_EVENT_CC status 1
[2025-11-11 11:39:49.179][000000062.757] luat_i2s_load_old_config 287:i2s0 old param not saved!
[2025-11-11 11:39:49.204][000000062.757] D/cc VOLTE_EVENT_PLAY_STOP
[2025-11-11 11:39:49.223][000000062.758] D/mobile LUAT_MOBILE_EVENT_CC status 12
[2025-11-11 11:39:49.293][000000062.911] soc_cms_proc 2219:cenc report 1,38,1,7
[2025-11-11 11:39:49.322][000000062.912] D/mobile cid7, state2
[2025-11-11 11:39:51.135][000000064.754] D/mobile LUAT_MOBILE_EVENT_CC status 10
luatools 页面打印

6.2.4 钉钉机器人接收到转发的短信和来电号码

6.2.5 飞书机器人接收到转发的短信和来电号码

6.2.6 企业微信机器人接收到转发的短信和来电号码

七、总结
本文档主要介绍 4G 通信中 短信和来电转发的实际应用。
结合 demo 例程讲解了 转发器的基本原理,介绍了使用的主要 API,旨在最简单的快速上手 Air780EHV 的 LuatOS 的 短信和来电转发开发.
八、常见问题
1、物联网卡支持短信功能吗?
一般来说,物联网卡是不支持短信功能的,具体需要咨询物联网卡供应商。
2、支持移动、电信,联通卡的短信功能吗?
1,对于电信卡来说,SMS 短信功能跟核心库 CC(VoLTE 通话)是伴生关系,只有支持 CC 核心库的固件才支持电信卡的 SMS 短信功能;
2,对于移动/联通卡来说,SMS 短信功能合宙所有全网通 4G 模组都默认支持;
3,对于某些只支持单一运营商的型号,比如只支持移动运营商的 Air700ECH/Air700ECT,则相应的也只支持移动卡的 SMS 短信功能;
4,有些流量卡可能没有开通 SMS 短信功能,请咨询卡商或运营商确认,无法确认时建议用自己的手机卡测试确认;
5,再次提醒!由于合宙二次开发型号模组支持多种固件版本,使用电信卡 SMS 短信功能时请务必选择同时支持 CC(VoLTE 通话)的固件版本;

3、是否支持彩信功能
不支持