主题
2.6 对接 HTTP 上报与拉取数据
问题场景
客户后端是传统 REST 服务,希望设备直接走 HTTP:定时 POST 上报数据,或者定时 GET 拉取配置和指令。比 MQTT 更简单直接,不用维护长连接。
做完之后能得到什么
- 上报:后端日志里能看到设备定时 POST 上来的 JSON
- 拉取:设备定时向服务器 GET 请求,拿到配置后本地立即生效
需要准备
- 设备已联网
- 一个 HTTP 后端接口(或用 webhook.site 临时测试)
- Postman 或 curl
预计耗时:20 分钟
动手做
第 1 步 · 上报 · POST JSON 到后端
http.get(url) / http.post(url, body) 这种简单模式带不了自定义请求头,需要认证的接口必须用 http.creat + add_head 的对象模式。另外同步 get/post 会阻塞整个 Lua 事件循环,在 timer 回调里应使用 post_asy / get_asy 异步接口。
lua
TOKEN = "换成你的令牌"
REPORT_URL = "http://example.com/api/report"
-- 客户端对象用全局变量长期持有,否则请求还没回来对象就可能被 GC 回收
reporter = http.creat(REPORT_URL)
reporter:add_head("Content-Type", "application/json")
reporter:add_head("Authorization", "Bearer " .. TOKEN)
timer.start(30000, function()
local body = json.encode({
device_id = "LP3202-001",
ts = os.time(),
temp = plc.d(100) / 10 -- D100 存放放大 10 倍的温度整数
})
-- 回调签名固定为 (errcode, data, datalen),errcode == 0 才算成功
reporter:post_asy(REPORT_URL, body, function(errcode, data, datalen)
if errcode == 0 then
-- 上报成功,data 是服务端返回的响应体(无返回体时为空串)
else
print("上报失败,错误码", errcode)
end
end)
end)第 2 步 · 拉取 · GET 配置并本地生效
lua
CONFIG_URL = "http://example.com/api/config?device_id=LP3202-001"
fetcher = http.creat(CONFIG_URL)
fetcher:add_head("Authorization", "Bearer " .. TOKEN)
timer.start(60000, function()
fetcher:get_asy(CONFIG_URL, function(errcode, data, datalen)
if errcode ~= 0 then return end -- 断网或超时,本轮跳过
local ok, cfg = pcall(json.decode, data)
if ok and type(cfg) == "table" and cfg.setpoint then
plc.d(200, cfg.setpoint * 10) -- 应用新设定值到 D200
end
end)
end)第 3 步 · 用 curl / Postman 联调
先不接设备,用 curl 模拟设备行为验证服务端接口通不通:
bash
curl -X POST http://example.com/api/report \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"device_id":"LP3202-001","ts":1700000000,"temp":25.6}'接口验证通过后再让设备发送,可以避免大量定位责任方的沟通成本。
第 4 步 · 错误处理
异步回调只给 errcode(传输层结果),拿不到 HTTP 状态码。所以业务层的成功/失败(比如 token 过期)要让后端在响应体里返回,脚本自己解析。把第 1 步里的回调换成下面这个写法:
lua
function on_report(errcode, data, datalen)
if errcode ~= 0 then
-- 断网、DNS 失败、超时都会走到这里,退避后下轮重试,避免连续重试
print("传输失败,错误码", errcode)
return
end
-- 传输成功,再看后端在 body 里给的业务码
local ok, resp = pcall(json.decode, data)
if not ok or type(resp) ~= "table" then return end
if resp.code == 401 then
refresh_token() -- token 过期,刷新后下次重试
end
end
-- 第 1 步的 timer 里改成:
-- reporter:post_asy(REPORT_URL, body, on_report)验证成功
- 后端日志里有 POST 记录
- 修改 GET 接口的返回值后,设备本地 D200 寄存器同步变化
注意事项
WARNING
- timer 里必须用异步:同步
get/post会阻塞整个 Lua 事件循环,网络变慢时定时器和其它回调都会停止 - 异步回调拿不到 HTTP 状态码:回调签名是
(errcode, data, datalen),errcode == 0只表示传输成功;业务成败需靠后端在 body 里返回状态码 - 简单模式带不了请求头:
http.get(url)无法添加 Authorization 等头部,需要认证时应使用http.creat+add_head对象模式 - 客户端对象要长期持有:应使用全局变量,
local变量在请求返回前可能被 GC 回收 - HTTPS 自签证书:需在设备上安装根证书,或关闭校验(生产环境不建议)
- 同步请求超时固定 15 秒:4G 网络下可能不足,建议优先使用异步接口避免阻塞
- 上报频率:不宜每秒发送,会显著增加后端负载和流量消耗。30 秒~5 分钟是常见区间
- Token 会过期:后端返回 401 类业务码时需能自动刷新,否则设备会持续静默失败
- 单包不宜过大:超过几十 KB 时考虑分批上报
举一反三
- HTTP 还是 MQTT? HTTP 简单直接,适合低频上报 + 明确的请求响应;MQTT 适合高频推送和双向实时控制。两者可以并存
- 上传报警文件 / 运行日志(multipart)
- 走 HTTP 触发企业微信 / 钉钉 Webhook 报警
- 从服务器拉取 OTA 升级包