Просмотр исходного кода

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

alienwalker 2 лет назад
Родитель
Сommit
63b44279e0
4 измененных файлов с 326 добавлено и 23 удалено
  1. 23 0
      demo/txiot/ca.crt
  2. 210 0
      demo/txiot/main.lua
  3. 3 9
      luat/modules/luat_lib_fota.c
  4. 90 14
      script/turnkey/qcloud100_switch/main.lua

+ 23 - 0
demo/txiot/ca.crt

@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIJALM1winYO2xzMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNV
+BAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxETAPBgNVBAcMCFNoZW5aaGVuMRAw
+DgYDVQQKDAdUZW5jZW50MRcwFQYDVQQLDA5UZW5jZW50IElvdGh1YjEYMBYGA1UE
+AwwPd3d3LnRlbmNlbnQuY29tMB4XDTE3MTEyNzA0MjA1OVoXDTMyMTEyMzA0MjA1
+OVoweTELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwI
+U2hlblpoZW4xEDAOBgNVBAoMB1RlbmNlbnQxFzAVBgNVBAsMDlRlbmNlbnQgSW90
+aHViMRgwFgYDVQQDDA93d3cudGVuY2VudC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDVxwDZRVkU5WexneBEkdaKs4ehgQbzpbufrWo5Lb5gJ3i0
+eukbOB81yAaavb23oiNta4gmMTq2F6/hAFsRv4J2bdTs5SxwEYbiYU1teGHuUQHO
+iQsZCdNTJgcikga9JYKWcBjFEnAxKycNsmqsq4AJ0CEyZbo//IYX3czEQtYWHjp7
+FJOlPPd1idKtFMVNG6LGXEwS/TPElE+grYOxwB7Anx3iC5ZpE5lo5tTioFTHzqbT
+qTN7rbFZRytAPk/JXMTLgO55fldm4JZTP3GQsPzwIh4wNNKhi4yWG1o2u3hAnZDv
+UVFV7al2zFdOfuu0KMzuLzrWrK16SPadRDd9eT17AgMBAAGjUDBOMB0GA1UdDgQW
+BBQrr48jv4FxdKs3r0BkmJO7zH4ALzAfBgNVHSMEGDAWgBQrr48jv4FxdKs3r0Bk
+mJO7zH4ALzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDRSjXnBc3T
+d9VmtTCuALXrQELY8KtM+cXYYNgtodHsxmrRMpJofsPGiqPfb82klvswpXxPK8Xx
+SuUUo74Fo+AEyJxMrRKlbJvlEtnpSilKmG6rO9+bFq3nbeOAfat4lPl0DIscWUx3
+ajXtvMCcSwTlF8rPgXbOaSXZidRYNqSyUjC2Q4m93Cv+KlyB+FgOke8x4aKAkf5p
+XR8i1BN1OiMTIRYhGSfeZbVRq5kTdvtahiWFZu9DGO+hxDZObYGIxGHWPftrhBKz
+RT16Amn780rQLWojr70q7o7QP5tO0wDPfCdFSc6CQFq/ngOzYag0kJ2F+O5U6+kS
+QVrcRBDxzx/G
+-----END CERTIFICATE-----

+ 210 - 0
demo/txiot/main.lua

@@ -0,0 +1,210 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "txiot_demo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+
+-- 产品ID和产品动态注册秘钥
+local ProductId = "SU83PBK5YF"
+local ProductSecret = "DliTrlLmab4zo2FiZFNOyLsQ"
+local mqttc
+local mqtt_isssl = false
+
+--[[
+函数名:getDeviceName
+功能  :获取设备名称
+参数  :无
+返回值:设备名称
+]]
+local function getDeviceName()
+    --默认使用设备的IMEI作为设备名称,用户可以根据项目需求自行修改
+    return mobile.imei()
+end
+
+
+
+function device_enrol()
+    local deviceName = getDeviceName()
+    local nonce = math.random(1, 100)
+    local timestamp = os.time()
+    local data = "deviceName=" .. deviceName .. "&nonce=" .. nonce .. "&productId=" ..
+        ProductId .. "&timestamp=" .. timestamp
+    local hmac_sha1_data = crypto.hmac_sha1(data, ProductSecret):lower()
+    local signature = crypto.base64_encode(hmac_sha1_data)
+    local tx_body = {
+        productId = ProductId,
+        deviceName = deviceName,
+        nonce = nonce,
+        timestamp = timestamp,
+        signature = signature,
+    }
+    local tx_body_json = json.encode(tx_body)
+    local code, headers, body = http.request("POST", "https://ap-guangzhou.gateway.tencentdevices.com/register/dev",
+        {
+            ["Content-Type"] = "application/json; charset=UTF-8",
+            ["X-TC-Version"] = "2019-04-23",
+            ["X-TC-Region"] = "ap-guangzhou"
+        }, tx_body_json, { timeout = 30000 }).wait()
+    log.info("http.post", code, headers, body)
+    if code == 200 then
+        local m, result, err = json.decode(body)
+        log.info(" m,result,err", m, result, err)
+        if result == 0 then
+            log.info("json解析失败", err)
+            device_enrol()
+        end
+        if m.message == "success" then
+            log.info("腾讯云注册设备成功:", body)
+            log.info("http.body.message", m.message)
+            local result = io.writeFile("/txiot.dat", body)
+            log.info("密钥写入结果", result)
+        else
+            log.info("腾讯云注册设备失败:失败原因", m.message)
+        end
+    else
+        log.info("http请求失败:", body)
+    end
+end
+
+sys.subscribe("MQTT_SIGN_AUTH", function(clientid, username, password)
+    sys.taskInit(function()
+        log.info("clientid,username,password", result, clientid, username, password, payload)
+
+        local mqtt_host = ProductId .. ".iotcloud.tencentdevices.com"
+        mqttc = mqtt.create(nil, mqtt_host, 1883, mqtt_isssl)
+        mqttc:auth(clientid, username, password, false) -- client_id必填,其余选填
+        mqttc:keepalive(300)                            -- 默认300s
+        mqttc:autoreconn(true, 3000)                    -- 自动重连机制
+        mqttc:on(function(mqtt_client, event, data, payload)
+            -- 用户自定义代码
+            log.info("mqtt", "event", event, mqtt_client, data, payload)
+            if event == "conack" then
+                log.info("mqtt", "sent", "pkgid", data)
+                --连上了
+                sys.publish("mqtt_conack")
+                local txiot_subscribetopic = {
+                    ["$thing/down/property/" .. ProductId .. "/" .. getDeviceName()] = 0
+                }
+                mqtt_client:subscribe(txiot_subscribetopic)
+            elseif event == "recv" then
+                log.info("mqtt", "downlink", "topic", data, "payload", payload)
+                --TODO:根据需求自行处理data.payload
+                --sys.publish("mqtt_payload", data, payload)
+                mqttc:publish("$thing/up/property/" .. ProductId .. "/" .. getDeviceName(),
+                    "publish from luat mqtt client",
+                    0)
+            elseif event == "sent" then
+                log.info("mqtt", "sent", "pkgid", data)
+            elseif event == "disconnect" then
+                log.info("连接失败")
+                --     --非自动重连时,按需重启mqttc
+                --     mqtt_client:connect()
+            end
+        end)
+        mqttc:connect()
+        sys.waitUntil("mqtt_conack")
+        while true do
+            -- 如果没有其他task上报, 可以写个空等待
+            sys.wait(60000000)
+        end
+    end)
+end)
+
+
+sys.subscribe("MQTT_CERT_AUTH", function() ---证书认证连接
+    sys.taskInit(function()
+        local clientid = ProductId .. getDeviceName()
+        local connid = math.random(10000, 99999)
+        log.info("connid类型", type(connid))
+        local expiry = "32472115200"
+        local username = string.format("%s;12010126;%s;%s", clientid, connid, expiry) --生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
+        local password = 123                                                          --证书认证不会验证password
+        log.info("clientid1,username1,password1", clientid, username, password)
+        local mqtt_host = ProductId .. ".iotcloud.tencentdevices.com"
+        mqttc = mqtt.create(nil, mqtt_host, 8883,
+        { server_cert = io.readFile("/luadb/ca.crt"),
+        client_cert = io.readFile("/client.crt"),
+        client_key = io.readFile("/client.key") })
+        mqttc:auth(clientid, username, password) -- client_id必填,其余选填
+        mqttc:keepalive(300)                     -- 默认值300s
+        mqttc:autoreconn(true, 20000)            -- 自动重连机制
+
+
+        mqttc:on(function(mqtt_client, event, data, payload)
+            -- 用户自定义代码
+            log.info("mqtt", "event", event, mqtt_client, data, payload)
+            if event == "conack" then
+                log.info("mqtt", "sent", "pkgid", data)
+                --连上了
+                sys.publish("mqtt_conack")
+                local txiot_subscribetopic = {
+                    ["$thing/down/property/" .. ProductId .. "/" .. getDeviceName()] = 0
+                }
+                mqtt_client:subscribe(txiot_subscribetopic)
+            elseif event == "recv" then
+                log.info("mqtt", "downlink", "topic", data, "payload", payload)
+                --TODO:根据需求自行处理data.payload
+                --sys.publish("mqtt_payload", data, payload)
+                mqttc:publish("$thing/up/property/" .. ProductId .. "/" .. getDeviceName(),
+                    "publish from luat mqtt client",
+                    0)
+            elseif event == "sent" then
+                log.info("mqtt", "sent", "pkgid", data)
+            elseif event == "disconnect" then
+                log.info("连接失败")
+                --     --非自动重连时,按需重启mqttc
+                --     mqtt_client:connect()
+            end
+        end)
+        local result = mqttc:connect()
+        log.info("connect.result", result)
+        sys.waitUntil("mqtt_conack")
+
+        while true do
+            -- 如果没有其他task上报, 可以写个空等待
+            sys.wait(60000000)
+        end
+    end)
+end)
+
+sys.taskInit(function()
+    if mobile.status() ~= 1 and not sys.waitUntil("IP_READY", 600000) then
+        log.info("网络初始化失败!")
+    end
+    log.info("io.exists", io.exists("/txiot.dat"))
+    if not io.exists("/txiot.dat") then
+        device_enrol()
+    end
+    local dat, result, err = json.decode(io.readFile("/txiot.dat"))
+    log.info("dat,result,err", dat, result, err)
+    if result == 0 then
+        log.info("json解码失败", err)
+        device_enrol() --解析失败重新下载文件
+        local dat, result, err = json.decode(io.readFile("/txiot.dat"))
+    end
+    local payload = json.decode(crypto.cipher_decrypt("AES-128-CBC", "ZERO", crypto.base64_decode(dat.payload),
+        string.sub(ProductSecret, 1, 16), "0000000000000000"))
+    log.info("payload[encryptionType]", payload.encryptionType)
+    log.info("payload[psk]", payload.psk)
+    if payload.encryptionType == 2 then
+        local clientid, username, password = iotauth.qcloud(ProductId, getDeviceName(), payload.psk)
+        sys.publish("MQTT_SIGN_AUTH", clientid, username, password) --签名认证
+    elseif payload.encryptionType == 1 then
+        log.info("payload date ", payload.encryptionType, payload.psk, payload.clientCert, payload.clientKey)
+        io.writeFile("/client.crt", payload.clientCert)
+        io.writeFile("/client.key", payload.clientKey)
+        sys.publish("MQTT_CERT_AUTH") --证书认证
+    end
+end)
+
+
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 3 - 9
luat/modules/luat_lib_fota.c

@@ -135,7 +135,7 @@ static int l_fota_file(lua_State* L)
         return 3;
     }
     #define BUFF_SIZE (4096)
-    char buff = luat_heap_malloc(BUFF_SIZE);
+    char *buff = luat_heap_malloc(BUFF_SIZE);
     if (buff == NULL) {
         luat_fs_fclose(fd);
         LLOGE("out of memory when reading file %s", path);
@@ -144,9 +144,9 @@ static int l_fota_file(lua_State* L)
         lua_pushinteger(L, 0);
         return 3;
     }
-    size_t len  = 0;
+    int len  = 0;
     while (1) {
-        len = luat_fs_fread(buff + len, BUFF_SIZE - len, 1, fd);
+        len = luat_fs_fread(buff , BUFF_SIZE, 1, fd);
         if (len < 1) {
             // EOF 结束了
             break;
@@ -155,12 +155,6 @@ static int l_fota_file(lua_State* L)
         if (result < 0) {
             break;
         }
-        result = len;
-        if (len >= BUFF_SIZE) {
-            LLOGD("too many data to write! bug??");
-            result = -7;
-            break;
-        }
     }
     luat_heap_free(buff);
     luat_fs_fclose(fd);

+ 90 - 14
script/turnkey/qcloud100_switch/main.lua

@@ -12,46 +12,120 @@ VERSION = "1.0.0"
 local sys = require "sys"
 require("sysplus")
 
+--[[
+使用指南
+1. 登录腾讯云 https://cloud.tencent.com/ 扫码登录
+2. 访问物联网平台 https://console.cloud.tencent.com/iotexplorer
+3. 进入公共实例, 如果没有就开通
+4. 如果没有项目, 点"新建项目", 名字随意, 例如 luatos
+5. 这时,新项目就好出现在列表里, 点击进去
+6. 接下来, 点"新建产品", 产品名称随意(例如abc), 品类选自定义品类, 通信方式选4G或者Wifi,认证方式选择密钥认证,数据协议选物模型
+7. 左侧菜单, 选"设备管理", "添加设备", 产品选刚刚建好的, 输入一个英文的设备名称, 例如 abcdf, 确定后新设备出现在列表里
+8. 点击该设备的"查看", 就能看到详情的信息, 逐个填好
+9. 如果是wifi设备, 在当前文件搜索ssid,填好wifi名称和密码
+10. 接下来刷机, 不懂刷机就到 wiki.luatos.com 看视频
+
+补充说明, 物模型的设置:
+1. 左侧菜单, "产品开发", 然后点之前建好的项目
+2. 页面往下拉到底, 选择"新建自定义功能"
+3. 为了跑通这个本例子, 需要新建3个属性, 对着界面逐个添加
+   |功能类型|功能名称|标识符       |数据类型|取值范围    |
+   |属性    |LED灯   |power_switch|布尔型  |不用选     |
+   |属性    |电压    |vbat        |整型    | -1, 100000|
+   |属性    |温度    |temp        |整型    | -1, 100000|
+
+不要输入"|",那是分割符
+]]
+product_key = "K7NURKF5J3" -- 产品ID, 一串英文字母,填前面的双引号以内
+device_id = "abcdf" -- 设备名称, 一定要改成自己的数据
+device_secret = "ChhVXLtSPiTKFzGatB80Jw==" -- 设备密钥
+
+
+sub_topic = "$thing/down/property/" .. product_key .. "/".. device_id
+pub_topic = "$thing/up/property/" .. product_key .. "/".. device_id
+
+adc.open(adc.CH_VBAT)
+adc.open(adc.CH_CPU)
+
+function on_btn()
+    log.info("btn", "按键触发")
+    -- 按一次, 上报一次电压值
+    -- 格式参考 https://cloud.tencent.com/document/product/1081/34916
+    if mqttc and mqttc:ready() then
+        
+        local data = {
+            method = "report",
+            clientToken = "123",
+            params = {
+                vbat = adc.get(adc.CH_VBAT), -- 读取电压值
+                temp = adc.get(adc.CH_CPU)
+                -- 如果新建更多物模型的属性, 这里可以继续填,注意格式,尤其是逗号
+            }
+        }
+        local jdata = (json.encode(data))
+        log.info("准备上传数据", jdata)
+        -- 可以直接调用mqttc对象进行上报
+        -- mqttc:publish(pub_topic, (json.encode(data)), 1)
+        -- 也可以通过消息机制,传递到sys.waitUntil("mqtt_pub", 30000)语句进行间接上报
+        sys.publish("mqtt_pub", pub_topic, jdata, 1)
+    end
+end
+
 sys.taskInit(function()
     -- 统一联网函数
-    if rtos.bsp():startsWith("ESP32") then -- ESP32系列, C3/S3都可以
-        LED = gpio.setup(12, 0, gpio.PULLUP) -- 控制的灯对应的GPIO号
+    if wlan and wlan.connect then -- ESP32系列, C3/S3都可以
+        if rtos.bsp():startsWith("ESP32") then
+            LED = gpio.setup(12, 0, gpio.PULLUP) -- 控制的灯对应的GPIO号
+            gpio.debounce(9, 100, 1)
+            BTN = gpio.setup(9, on_btn, gpio.PULLUP, gpio.FALLING) -- BOOT按键当按钮用
+        else
+            -- air101/air103,内测用
+            LED = gpio.setup(pin.PB09, 0, gpio.PULLUP) -- 控制的灯对应的GPIO号
+            gpio.debounce(pin.PA0, 100, 1)
+            BTN = gpio.setup(pin.PA0, on_btn, gpio.PULLUP, gpio.FALLING) -- BOOT按键当按钮用
+        end
         wlan.init()
         --
         -- ESP32系列, 这里要填wifi的名称和密码. 只支持2.4G频段
         --
-        local ssid, password = "wifi的名字", "wifi的密码"
+        local ssid, password = "uiot", "12345678"
         wlan.setMode(wlan.STATION)
         wlan.connect(ssid, password, 1)
-        local result, data = sys.waitUntil("IP_READY")
-        log.info("wlan", "IP_READY", result, data)
     elseif rtos.bsp() == "AIR105" then -- Air105走网卡,W5500
         w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
         w5500.config() --默认是DHCP模式
         w5500.bind(socket.ETH0)
         LED = gpio.setup(62, 0, gpio.PULLUP)
+        gpio.debounce(pin.PA10, 100, 1)
+        BTN = gpio.setup(pin.PA10, on_btn, gpio.PULLUP, gpio.FALLING) -- BOOT按键当按钮用
         sys.wait(1000)
     elseif rtos.bsp() == "EC618" then -- Air780E,走4G一堆网络
         -- mobile.simid(2) -- 自动选卡, 如果不清楚在哪个卡槽,就取消注释
         LED = gpio.setup(27, 0, gpio.PULLUP)
-        sys.waitUntil("IP_READY") -- 等联网就行,要插卡的
-        log.info("mobile", "IP_READY", mobile.imei(), mobile.iccid())
+        gpio.debounce(0, 100, 1)
+        BTN = gpio.setup(0, on_btn, gpio.PULLUP, gpio.FALLING) -- BOOT按键当按钮用
     else
         while 1 do 
             sys.wait(1000)
             log.info("bsp", "未支持的模块", rtos.bsp())
         end
     end
+    log.info("等待联网")
+    local result, data = sys.waitUntil("IP_READY")
+    log.info("wlan", "IP_READY", result, data)
+    log.info("联网成功")
+    sys.publish("net_ready")
+end)
 
-    -- 往下就是连接到腾讯云了, 下面3个参数要改成自己的值, 到腾讯云上建项目建设备才有的
-    local product_key = "产品id" -- 一串英文字母,填前面的双引号以内
-    local device_id = "设备名称" -- 一定要改成自己的数据
-    local device_secret = "设备密钥" -- 设备密钥
-    local client_id, user_name, password = iotauth.qcloud(product_key, device_id, device_secret, "sha1", 1700561166)
+sys.taskInit(function()
+    sys.waitUntil("net_ready")
+    -- 往下就是连接到腾讯云了
+
+    local client_id, user_name, password = iotauth.qcloud(product_key, device_id, device_secret, "sha1")
     log.info("mqtt参数", client_id, user_name, password)
 
     -- MQTT参数准备好了,开始连接,并监听数据下发
-    local mqttc = mqtt.create(nil, product_key .. ".iotcloud.tencentdevices.com", 1883)
+    mqttc = mqtt.create(nil, product_key .. ".iotcloud.tencentdevices.com", 1883)
     mqttc:auth(client_id, user_name, password)
     mqttc:keepalive(240) -- 默认值240s
     mqttc:autoreconn(true, 3000) -- 自动重连机制
@@ -61,7 +135,7 @@ sys.taskInit(function()
                 -- 连上了,鉴权也ok
                 sys.publish("mqtt_conack")
                 log.info("mqtt", "mqtt已连接")
-                mqtt_client:subscribe("$thing/down/property/" .. product_key .. "/".. device_id)
+                mqtt_client:subscribe(sub_topic)
             elseif event == "recv" then
                 log.info("mqtt", "收到消息", data, payload)
                 local json = json.decode(payload)
@@ -93,6 +167,8 @@ sys.taskInit(function()
     mqttc = nil
 end)
 
+
+
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()