Skip to content

2.6 对接 HTTP 上报与拉取数据 ​

问题场景 ​

客户后端是传统 REST 服务,希望设备直接走 HTTP:定时 POST 上报数据,或者定时 GET 拉取配置和指令。比 MQTT 更简单直接,不用维护长连接。

做完之后能得到什么 ​

  • 上报:后端日志里能看到设备定时 POST 上来的 JSON
  • 拉取:设备定时向服务器 GET 请求,拿到配置后本地立即生效

HTTP 对接拓扑与 vs MQTT 对比

需要准备 ​

  • 设备已联网
  • 一个 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 升级包

想更深入 ​

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研