01 短消息(sms)
作者:王城钧 | 最后修改:2026-07-23
一、SMS 简介
SMS(短消息服务,Short Message Service)功能主要用于在蜂窝网络中传输短消息。
移动,联通卡不需开通 VOLTE 可以支持短信功能。使用电信卡 SMS 短信功能时请务必选择同时支持 CC(VoLTE 通话)的固件版本并且需要电信卡开通 VoLTE 功能
在 4G 网络中,短信可以在数据传输的同时进行,不会因数据业务占用网络资源而被延迟或阻塞。
二、演示功能概述
本 demo 演示了向运营商发送话费查询短信后,收到回复的短信,将回复的短信转发给飞书,钉钉,企业微信机器人。
三、准备硬件环境


1、Air8201G 开发板一块;
2、TYPE-C USB 数据线一根;
3、Air8201配套的BTB扩展板一个;
Air8201G板子通过FPC线与BTB扩展板连接;
TYPE-C USB 数据线直接插到BTB扩展板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
四、软件环境
在开始实践本示例之前,先筹备一下软件环境:
1.烧录工具: Luatools 工具;
2.内核固件:Air8201G固件(基于Air780EGH)
3、脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8201/demo/sms
五、转发机器人设置介绍
5.1 钉钉机器人
打开钉钉主界面,鼠标左键点击消息框,然后再用鼠标左键点击一个内部群

打开群设置

点击机器人设置

选择自定义机器人

设置对应参数

记住 webhook 参数,然后点击完成

5.2 飞书机器人
在飞书界面鼠标左键点击消息框,然后鼠标左键点击选择一个内部群

鼠标左键点击右上角三个点

鼠标左键点击设置

鼠标左键打开群机器人

添加自定义机器人

自行设置机器人

复制 webhook 地址以及 secret(可选)

5.3 企业微信机器人
企业微信,找到需要接收消息的内部群,右键单击管理聊天信息
注意:此操作只有这个群的管理员才可以进行。

鼠标左键点击消息推送

设置机器人信息,并复制 webhook 地址

六、代码示例介绍
只需配置平台信息后,后台自动循环发送话费查询短信,同时监听新短信,收到后自动转发到指定办公平台。
本 demo 的效果是给运营商发短信消息,查询话费信息,并将接收的短信自动转发到飞书、钉钉、企业微信。
6.1 发送短信
设备就绪后,等待系统发布"CC_IND"准备就绪消息,然后每隔 10 分钟自动向电信运营商发送话费查询短信,并持续输出设备相关信息。
**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
log.info("开始发短信")
**while** 1 **do**
log.info("现在可以收发短信")
--获取本机号码,如果卡商没写入会返回nil
log.info("mobile.number(id) = ", mobile.number())
--获取本机iccid,如果卡商没写入会返回nil
log.info("mobile.iccid(id) = ", mobile.iccid())
--获取本机simid,如果卡商没写入会返回nil
log.info("mobile.simid(id) = ", mobile.simid())
--获取本机imsi,如果卡商没写入会返回nil
log.info("mobile.imsi(index) = ", mobile.imsi())
-- 注意:以下查话费的三行代码只需根据自己卡的运营商打开其一即可,其余两行关闭,不要全部打开
-- 电信卡查话费
**local** result = sms.send("10001", "102")
-- 中移动卡查短信
-- local result = sms.send("10086", "301")
-- 联通卡查话费
-- local result = sms.send("10010", "101")
-- 注意:V2018及更高版本固件才有"SMS_SENT"系统消息
**if** result **then**
**local** wait_msg, success = sys.waitUntil("SMS_SENT", 10000)
log.info("发送查询短信", "这是第" .. cont .. "次发送", " 发送结果:", wait_msg and (success and "成功" or "失败") or "超时")
**else**
log.info("发送查询短信", "这是第" .. cont .. "次发送", " 发送结果:同步发送失败")
**end**
log.info("等待10分钟")
cont = cont + 1
sys.wait(10 * 60 * 1000)
**end**
**end**
6.2 接收短信
持续等待并接收新短信,获取短信相关信息后,进回调函数进行后续操作。
local function receive_sms()
while true do
local ret, num, txt = sys.waitUntil("SMS_INC", 30000)
log.info("收到来自短信:", num)
if num then
log.info("num是", num)
log.info("收到来自" .. num .. "的短信:" .. txt)
--local isReady1, index1 = socket.adapter()
log.info("当前网络", socket.adapter())
sms_handler(num, txt)
end
end
end
6.3 接收短信回调函数
收到的短信(包含发送号码和内容)依次转发到飞书、钉钉、微信三个平台
local function sms_handler(num, txt)
-- num 给我发短信的手机号码
-- txt 收到的短信文本内容
log.info("转发到飞书")
feishu_post_sms(num, txt)
--等待1秒, 非必须
sys.wait(1000)
log.info("转发到钉钉")
dingding_post(num, txt)
--等待1秒, 非必须
sys.wait(1000)
log.info("转发到微信")
weixin_post(num, txt)
end
6.4 平台信息转发函数
根据各平台的接口规范,将短信信息格式化并通过 HTTP 请求转发。
--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, { timeout = 5000 }).wait()
-- 正常会返回 200, {"errcode":0,"errmsg":"ok"}
-- 其他错误, 一般是密钥错了, 仔细检查吧
log.info("飞书机器人",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
**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("钉钉机器人",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
**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("企业微信机器人",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
**end**
七、功能验证
烧录代码,插入自己的手机卡,本演示使用的为电信手机卡,日志如下:



钉钉界面群消息转发如下:

飞书界面群消息转发如下:

企业微信界面群消息转发如下:

八、总结
至此,我们演示了 sms 短信转发的全过程。
常见问题
1、物联网卡支持短信功能吗?
一般来说,物联网卡是不支持短信功能的,具体需要咨询物联网卡供应商。
2、支持移动、电信,联通卡的短信功能吗?
1,对于移动/联通卡来说,SMS 短信功能合宙所有全网通 4G 模组都默认支持;
2,对于某些只支持单一运营商的型号,比如只支持移动运营商的 Air700ECH/Air700ECT,则相应的也只支持移动卡的 SMS 短信功能;
3,有些流量卡可能没有开通 SMS 短信功能,请咨询卡商或运营商确认,无法确认时建议用自己的手机卡测试确认;
4,再次提醒!由于合宙二次开发型号模组支持多种固件版本,使用电信卡 SMS 短信功能时请务必选择支持电信卡 SMS 短信功能的固件版本;

3、是否支持彩信功能
不支持
4、短信功能发送英文短信、 中文短信,分别怎么发? 发长短信怎么发?
发送英文短信、中文短信、中英文混合短信可参考下方代码块中的 sms.send 接口使用示例:
local function sms_task()
sys.wait(10000)
-- 中移动卡查短信
-- sms.send("+8610086", "301")
-- 联通卡查话费
sms.send("10010", "101")
local sms1 = sms.send("num", "TEST")
log.info("英文短信",sms1)
sys.wait(5000)
local sms2 = sms.send("num", "你好")
log.info("中文短信",sms2)
sys.wait(5000)
local sms3 = sms.send("num", "你好test")
log.info("中英文混合",sms3)
end
sys.taskInit(sms_task)
发送长短信可在文本部分填入,英文算 1 个字节,中文算 2 个字节。
发送长短信可参考下方代码块中的 sms.sendLong 接口使用示例:
local function send_long_sms_task()
-- 等待短信就绪(根据实际情况,可以等待CC_IND或SMS_READY)
log.info("sms", "等待短信就绪...")
sys.waitUntil("CC_IND")
log.info("sms", "短信功能已就绪,可以开始发送长短信")
-- 构造一个150字节的短信内容
-- 中文字符通常占用更多字节,这里使用英文字符确保准确控制长度
local msg_content = string.rep("1234567890", 15) -- 每个"1234567890"是10个字节,重复15次正好是150字节
-- 接收短信的手机号码(请替换为实际需要发送的号码)
local phone_number = "+861380013780" -- 示例号码
-- 使用sms.sendLong发送长短信,并等待发送结果
log.info("sms", "开始发送150字节长短信...")
local result = sms.sendLong(phone_number, msg_content).wait()
log.info("短信内容",result)
end
sys.taskInit(send_long_sms_task)