11 -- 本库基于阿里云物联网重新设计, 与aliyun.lua库不兼容
12 -- 本库尚属开发测试阶段, API随时可能变化, 也可能不变^_^
14 _G
.sys
= require("sys")
21 @api aliyun.create(opts)
26 local ali = aliyun.create(opts)
27 if ali and aliyun2.start(ali) then
29 local result, tip, params = sys.waitUntil(ali.topic, 30000)
31 log.info("aliyun", "event", tip, params)
35 log.error("aliyun", "初始化失败")
38 function aliyun2
.create(opts
)
40 log.error("aliyun2", "配置参数表不能是空")
44 if not opts
.productKey
or not opts
.deviceName
then
45 log.error("aliyun2", "缺失配置参数productKey")
48 if not opts
.deviceName
then
50 opts
.deviceName
= mobile
.imei()
51 elseif wlan
and wlan
.getMac
then
52 opts
.deviceName
= wlan
.getMac()
54 opts
.deviceName
= mcu
.unique_id():toHex()
56 log.info("aliyun2", "deviceName未指定,自动分配", opts
.deviceName
)
58 if opts
.productSecret
and #opts
.productSecret
== 0 then
59 opts
.productSecret
= nil
61 if opts
.deviceSecret
and #opts
.deviceSecret
== 0 then
62 opts
.deviceSecret
= nil
64 if not opts
.store_key
then
65 opts
.store_key
= opts
.productKey
.. "_" .. opts
.deviceName
67 if opts
.productSecret
and not opts
.deviceSecret
then
71 payload
= fskv
.get(opts
.store_key
)
73 if not payload
or #payload
< 16 then
74 payload
= io
.readFile(opts
.store_key
)
77 local jdata
= json
.decode(payload
)
79 opts
.deviceSecret
= jdata
["deviceSecret"]
80 opts
.deviceToken
= jdata
["deviceToken"]
81 opts
.clientId
= jdata
["clientId"]
86 if not opts
.productSecret
and not opts
.deviceSecret
and not opts
.deviceToken
then
87 log.error("aliyun2", "请指定productSecret或deviceSecret")
92 if not ctx
.mqtt_host
then
93 -- ctx.mqtt_host = "iot-as-mqtt." .. ctx.productKey .. ".aliyuncs.com"
94 if ctx
.instanceId
then
95 ctx
.mqtt_host
= ctx
.instanceId
.. ".mqtt.iothub.aliyuncs.com"
97 ctx
.mqtt_host
= ctx
.productKey
.. ".iot-as-mqtt." .. (opts
.regionId
or "cn-shanghai") ..".aliyuncs.com"
100 if not ctx
.mqtt_port
then
104 ctx
.topic
= "aliyun2_" .. g_id
108 if ctx
.auto_topic
== nil or ctx
.auto_topic
then
109 if not ctx
.sub_topics
then
112 if not ctx
.pub_topics
then
115 local topics
= ctx
.sub_topics
117 local dn
= ctx
.productKey
.. "/" .. ctx
.deviceName
119 topics
.ota
= "/ota/device/upgrade/" .. dn
121 topics
.config_push
= "/sys/" .. dn
.. "/thing/config/push"
123 topics
.broadcast
= "/broadcast/" .. ctx
.productKey
.. "/#"
125 topics
.ntp
= "/ext/ntp/" .. dn
.. "/response"
128 topics
.raw_reply
= "/sys/" .. dn
.. "/thing/model/up_raw_reply"
129 topics
.raw_down
= "/sys/" .. dn
.. "/thing/model/down_raw"
131 topics
.property_set
= "/sys/".. dn
.. "/thing/service/property/set"
134 topics
= ctx
.pub_topics
135 topics
.inform
= "/ota/device/inform/".. dn
136 topics
.ntp
= "/ext/ntp/".. dn
.. "/request"
137 topics
.raw_up
= "/sys/".. dn
.. "/thing/model/up_raw"
140 if ctx
.instanceId
then
141 log.info("aliyun2", "instanceId", ctx
.instanceId
)
143 log.info("aliyun2", "deviceName", ctx
.deviceName
)
144 log.info("aliyun2", "mqtt_host", ctx
.mqtt_host
)
145 log.info("aliyun2", "mqtt_port", ctx
.mqtt_port
)
149 local function aliyun_do_reg(ctx
)
150 local mqttc
= mqtt
.create(ctx
.adapter
, ctx
.mqtt_host
, 443, true)
152 log.error("aliyun2", "创建mqtt实例失败")
156 local tm
= tostring(os
.time())
157 local client_id
= string.format("%s|securemode=%s,authType=%s,random=%s,signmethod=hmacsha1%s|", ctx
.deviceName
, ctx
.regnwl
and -2 or 2, ctx
.regnwl
and "regnwl" or "register", tm
, (ctx
.instanceId
and ",instanceId=" .. ctx
.instanceId
or ""))
158 log.info("aliyun2", "开始注册流程", client_id
)
159 local user_name
= ctx
.deviceName
.. "&"..ctx
.productKey
160 local content
= "deviceName"..ctx
.deviceName
.."productKey"..ctx
.productKey
.."random" .. tm
161 local password
= crypto
.hmac_sha1(content
, ctx
.productSecret
)
162 log.info("aliyun2", "尝试注册", client_id
, user_name
, password
)
163 mqttc
:auth(client_id
, user_name
, password
)
164 mqttc
:keepalive(240) -- 实际上会忽略该属性
166 mqttc
:autoreconn(false, 3000) -- 不需要自动重连
167 local regok
= false -- 记录注册成功与否
168 mqttc
:on(function(mqtt_client
, event
, data
, payload
)
169 log.info("aliyun2", "event", event
, data
, payload
)
170 if event
== "recv" then
171 log.info("aliyun", "downlink", "topic", data
, "payload", payload
)
173 local jdata
,res
= json
.decode(payload
) -- TODO 搞个alijson库进行封装
174 if jdata
and (jdata
["deviceSecret"] or jdata
["deviceToken"]) then
175 log.info("aliyun2", "获取到设备密钥")
177 ctx
.deviceSecret
= jdata
["deviceSecret"]
178 ctx
.deviceToken
= jdata
["deviceToken"]
179 ctx
.clientId
= jdata
["clientId"]
180 sys
.publish(tm
, "reg", "ok")
182 log.info("aliyun2", "密钥信息存入fskv", ctx
.store_key
)
183 fskv
.set(ctx
.store_key
, payload
)
185 log.info("aliyun2", "密钥信息存入文件系统", ctx
.store_key
)
186 io
.writeFile(ctx
.store_key
, payload
)
188 sys
.publish(tm
, "reg", "fail")
192 elseif event
== "close" then
193 sys
.publish(tm
, "close")
197 sys
.waitUntil(tm
, 5000)
201 log.info("aliyun2", "自动注册成功,密钥信息已获取")
203 log.info("alyun2", "自动注册失败,延迟30秒后重试")
208 local function aliyun_task_once(ctx
)
209 -- 几个条件: 有设备密钥, 有产品密钥, 登陆失败的次数
210 -- 情况1: 只有设备密钥, 没有产品密钥, 那就固定是一机一密
211 -- 情况2: 只有产品密钥, 没有设备密钥, 那就是一机一密
212 -- 情况3: 有产品密钥, 有设备密钥, 登录失败次数少于设定值(默认3次),那继续用一机一密去尝试登陆
213 -- 情况4: 有产品密钥, 有设备密钥, 登录失败次数大于设定值(默认3次), 使用一型一密去尝试注册一次
215 if ctx
.productSecret
then
216 if ctx
.deviceSecret
or ctx
.deviceToken
then
217 if ctx
.device_retry
< 3 then
220 log.info("aliyun2", "设备密钥已存在,但已经连续失败3次,尝试重新注册")
229 if not ctx
.deviceSecret
and not ctx
.deviceToken
then
230 log.info("aliyun2", "未能获取到设备密钥,等待重试")
234 local mqttc
= mqtt
.create(ctx
.adapter
, ctx
.mqtt_host
, ctx
.mqtt_port
, ctx
.mqtt_isssl
, ctx
.ca_file
)
236 log.error("aliyun2", "创建mqtt实例失败")
239 if ctx
.deviceSecret
then
240 local client_id
,user_name
, password
= iotauth
.aliyun(ctx
.productKey
, ctx
.deviceName
, ctx
.deviceSecret
)
241 log.info("aliyun2", "密钥模式", client_id
,user_name
, password
)
242 mqttc
:auth(client_id
, user_name
, password
)
244 local client_id
= ctx
.clientId
.. "|securemode=-2,authType=connwl|"
245 local user_name
= ctx
.deviceName
.."&"..ctx
.productKey
246 log.info("aliyun2", "token模式", client_id
, user_name
, ctx
.deviceToken
)
247 mqttc
:auth(client_id
, user_name
, ctx
.deviceToken
)
249 mqttc
:keepalive(ctx
.mqtt_keepalive
or 240)
251 mqttc
:autoreconn(false, 3000) -- 不需要自动重连
252 mqttc
:on(function(mqtt_client
, event
, data
, payload
)
253 log.info("aliyun2", "event", event
, data
, payload
)
254 if event
== "conack" then
255 log.info("aliyun2", "连接成功,鉴权完成")
259 if ctx
.sub_topics
then
260 for k
, v
in pairs(ctx
.sub_topics
) do
261 log.info("aliyun2", "订阅topic", v
, "别名", k
)
268 if ctx
.pub_topics
and ctx
.pub_topics
.inform
then
269 local info
= ctx
.inform_data
or {version
=_G
.VERSION
, module
=rtos
.bsp():upper()}
270 info
= json
.encode({id
="123", params
=info
})
271 log.info("aliyun2", "上报版本信息", ctx
.pub_topics
.inform
, info
)
272 mqttc
:publish(ctx
.pub_topics
.inform
, info
, 1)
275 sys
.publish(ctx
.topic
, "conack")
276 elseif event
== "recv" then
278 log.info("aliyun2", "收到下行信息", "topic", data
, "payload前128字节", payload
and payload
:sub(1, 128))
279 sys
.publish(ctx
.topic
, "recv", data
, payload
)
281 if ctx
.sub_topics
and ctx
.sub_topics
.ota
== data
then
282 log.info("aliyun2", "收到ota信息", payload
)
283 local jdata
= json
.decode(payload
)
284 if jdata
and jdata
.data
and jdata
.data
.url
then
285 log.info("aliyun2", "获取到OTA所需要的URL", jdata
.data
.url
)
286 sys
.publish(ctx
.topic
, "ota", jdata
.data
)
292 sys
.waitUntil(ctx
.topic
, 5000)
293 if mqttc
:ready() then
296 while mqttc
:ready() and ctx
.running
do
297 sys
.waitUntil(ctx
.topic
, 5000)
300 ctx
.device_retry
= ctx
.device_retry
+ 1
303 log.info("aliyun2", "单次任务结束")
307 local function aliyun_task (ctx
)
312 aliyun_task_once(ctx
)
315 log.info("aliyun2", "阿里云任务结束")
320 @api aliyun2.start(ali)
321 @return 成功返回true,失败返回false
323 function aliyun2
.start(ali
)
325 ali
.c_task
= sys
.taskInit(aliyun_task
, ali
)
326 return ali
.c_task
~= nil
331 @api aliyun2.stop(ali)
332 @return boolean 成功返回true,失败返回false
334 function aliyun2
.stop(ali
)
337 sys
.publish(ali
.topic
, "stop")
343 @api aliyun2.ready(ali)
344 @return boolean 已经成功返回true,否则一概返回false
346 function aliyun2
.ready(ali
)
347 if ali
and ali
.mqttc
and ali
.mqttc
:ready() then
354 @api aliyun2.subscribe(ali, topic)
355 @return 成功返回true,失败返回false或者nil
357 function aliyun2
.subscribe(ali
, topic
, qos
)
358 if not aliyun2
.ready(ali
) then
359 log.warn("aliyun2", "还没连上服务器,不能订阅", topic
)
362 ali
.mqttc
:subscribe(topic
, qos
or 1)
368 @api aliyun2.publish(ali, topic, payload, qos, retain)
369 @return 成功返回true,失败返回false或者nil
371 function aliyun2
.publish(ali
, topic
, payload
, qos
, retain
)
372 if not aliyun2
.ready(ali
) then
373 log.warn("aliyun2", "还没连上服务器,不能上报", topic
)
376 ali
.mqttc
:public(topic
, payload
, qos
or 1, retain
or 0)