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

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

alienwalker 1 год назад
Родитель
Сommit
a9c16e08ba

+ 93 - 40
components/mempool/profiler/bind/luat_lib_profiler.c

@@ -1,3 +1,15 @@
+/*
+@module  profiler
+@summary 内存分析与性能分析
+@date    2020.03.30
+@author  wendal
+@demo    profiler
+@tag LUAT_USE_PROFILER
+@usage
+-- 当前已完成的功能:
+-- 1. 内存分配统计 profiler.mem_stat()
+*/
+
 #include "luat_base.h"
 #include "luat_mem.h"
 #include "luat_mem.h"
@@ -7,56 +19,97 @@
 #define LUAT_LOG_TAG "PRO"
 #include "luat_log.h"
 
-int luat_profiler_memdebug;
-luat_profiler_mem_t profiler_memregs[LUAT_PROFILER_MEMDEBUG_ADDR_COUNT];
-
-static int l_profiler_start(lua_State *L) {
-    (void)L;
-    luat_profiler_start();
-    return 0;
-}
-
-static int l_profiler_stop(lua_State *L) {
-    (void)L;
-    luat_profiler_stop();
-    return 0;
-}
-
-static int l_profiler_print(lua_State *L) {
-    (void)L;
-    luat_profiler_print();
-    return 0;
-}
-
-static int l_profiler_memdebug(lua_State *L) {
-    luat_profiler_memdebug = lua_toboolean(L, 1);
-    LLOGD("memdebug %d", luat_profiler_memdebug);
-    size_t total;
-    size_t used;
-    size_t max_used;
-    luat_meminfo_luavm(&total, &used, &max_used);
-    LLOGD("mem.lua %d %d %d", total, used, max_used);
-    luat_meminfo_sys(&total, &used, &max_used);
-    LLOGD("mem.sys %d %d %d", total, used, max_used);
-    if (luat_profiler_memdebug == 0) {
-        for (size_t i = 0; i < LUAT_PROFILER_MEMDEBUG_ADDR_COUNT; i++)
+#include "lgc.h"
+int l_profiler_mem_stat(lua_State *L) {
+    int counter = 0;
+    GCObject *gc = L->l_G->allgc;
+    uint32_t type_couter[32] = {0};
+    uint32_t type_heap[32] = {0};
+    TString *ts;
+    lu_byte rtt;
+    char tmpbuff[1024];
+    while (gc != NULL) {
+        counter ++;
+        // LLOGD("GCObject %p 类型 %d", gc, gc->tt);
+        rtt = gc->tt & 0xF;
+        type_couter[rtt] ++;
+        // 打印一下内容
+        switch (rtt)
         {
-            if (profiler_memregs[i].addr) {
-                LLOGD("leak %08X %d", profiler_memregs[i].addr, profiler_memregs[i].len);
+        case LUA_TNIL:
+            // LLOGD("空类型?? 不太可能有");
+            break;
+        case LUA_TLIGHTUSERDATA:
+            // LLOGD("lightuserdata %p", gc);
+            type_heap[rtt] += sizeof(void*) + sizeof(Value);
+            break;
+        case LUA_TNUMBER:
+            // LLOGD("数值 %p", gc);
+            type_heap[rtt] += sizeof(lua_Number) + sizeof(Value);
+            break;
+        case LUA_TSTRING:
+            ts = gco2ts(gc);
+            type_heap[rtt] += (gc->tt == LUA_TSHRSTR ? ts->shrlen : ts->u.lnglen) + sizeof(UTString) + 1;
+            if (gc->tt == LUA_TSHRSTR) {
+                LLOGD("短字符串 %08X %d %d %d %s", ts->hash, ts->marked, ts->extra, ts->shrlen, getstr(ts));
+                // toHex(getstr(ts), ts->shrlen, tmpbuff);
+                // LLOGD("{.str={.tt = LUA_TSHRSTR, .marked = 4, .extra = %d, .shrlen = %d, .hash = 0x%08X}, .data=\"%s\"},",
+                //     ts->extra, ts->shrlen, ts->hash, tmpbuff
+                // );
             }
+            break;
+        case LUA_TTABLE:
+            type_heap[rtt] += gco2t(gc)->sizearray * sizeof(TValue) + sizeof(Value);
+            break;
+        case LUA_TUSERDATA:
+            // LLOGD("userdata %p", gc);
+            type_heap[rtt] += gco2u(gc)->len + sizeof(Value);
+            break;
+        default:
+            break;
+        }
+        gc = gc->next;
+    }
+    LLOGD("allgc对象计数 %d seek %08X", counter, L->l_G->seed);
+
+    // counter = 0;
+    gc = L->l_G->fixedgc;
+    while (gc != NULL) {
+        counter ++;
+        rtt = gc->tt & 0xF;
+        switch (rtt)
+        {
+        case LUA_TSTRING:
+            ts = gco2ts(gc);
+            // LLOGD("不是一样的吗? %p %p", ts, gc);
+            type_heap[rtt] += (gc->tt == LUA_TSHRSTR ? ts->shrlen : ts->u.lnglen) + sizeof(UTString);
+            // if (gc->tt == LUA_TSHRSTR)
+            //     LLOGD("短字符串 %08X %d %d %d %s", ts->hash, ts->marked, ts->extra, ts->shrlen, getstr(ts));
+            break;
+        }
+        gc = gc->next;
+    }
+    // LLOGD("fixedgc对象计数 %d", counter);
+    for (size_t i = 0; i < 32; i++)
+    {
+        if (type_couter[i]) {
+            LLOGD("类型 %d 计数 %d", i, type_couter[i]);
+        }
+    }
+    for (size_t i = 0; i < 32; i++)
+    {
+        if (type_heap[i]) {
+            LLOGD("类型 %d 内存 %d", i, type_heap[i]);
         }
-        
     }
+    
     return 0;
 }
 
 #include "rotable2.h"
 static const rotable_Reg_t reg_profiler[] =
 {
-    { "start" ,        ROREG_FUNC(l_profiler_start)},
-    { "stop" ,         ROREG_FUNC(l_profiler_stop)},
-    { "print",         ROREG_FUNC(l_profiler_print)},
-    { "memdebug",      ROREG_FUNC(l_profiler_memdebug)},
+    { "mem_stat",          ROREG_FUNC(l_profiler_mem_stat)},
 	{ NULL,            ROREG_INT(0)}
 };
 

+ 1 - 1
components/mobile/luat_mobile.h

@@ -300,7 +300,7 @@ int luat_mobile_get_flymode(int index);
 int luat_mobile_get_local_ip(int sim_id, int cid, ip_addr_t *ip_v4, ip_addr_t *ip_v6);
 #endif
 /* -------------------------------------------------- cell info begin -------------------------------------------------- */
-#define LUAT_MOBILE_CELL_MAX_NUM 9
+#define LUAT_MOBILE_CELL_MAX_NUM 21
 
 typedef struct luat_mobile_gsm_service_cell_info
 {

+ 2 - 0
components/sfud/sfud.c

@@ -438,10 +438,12 @@ sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t
 #endif
             make_address_byte_array(flash, addr, &cmd_data[1]);
             cmd_size = flash->addr_in_4_byte ? 5 : 4;
+            #if SFUD_READ_DUMMY_BYTE_CNT > 0
             for (i = 0; i < SFUD_READ_DUMMY_BYTE_CNT; i++) {
                 cmd_data[cmd_size] = SFUD_DUMMY_DATA;
                 cmd_size++;
             }
+            #endif
             result = spi->wr(spi, cmd_data, cmd_size, data, size);
         }
     }

+ 98 - 0
demo/aliyun2/main.lua

@@ -0,0 +1,98 @@
+--[[
+本demo是演示aliyun2模块的使用,使用该模块可以连接阿里云物联网平台
+注意与aliyun库的差异
+
+TODO 本demo当前还不是很完整, 后续继续改造
+]]
+PROJECT = "aliyundemo"
+VERSION = "1.0.0"
+
+local sys = require "sys"
+local aliyun2 = require "aliyun2"
+
+if fskv then
+    fskv.init()
+end
+
+local rtos_bsp = rtos.bsp()
+function pinx() -- 根据不同开发板,给LED赋值不同的gpio引脚编号
+    if rtos_bsp == "AIR101" then -- Air101开发板LED引脚编号
+        return pin.PB08, pin.PB09, pin.PB10
+    elseif rtos_bsp == "AIR103" then -- Air103开发板LED引脚编号
+        return pin.PB26, pin.PB25, pin.PB24
+    elseif rtos_bsp == "AIR601" then -- Air103开发板LED引脚编号
+        return pin.PA7, 255, 255
+    elseif rtos_bsp == "AIR105" then -- Air105开发板LED引脚编号
+        return pin.PD14, pin.PD15, pin.PC3
+    elseif rtos_bsp == "ESP32C3" then -- ESP32C3开发板的引脚
+        return 12, 13, 255 -- 开发板上就2个灯
+    elseif rtos_bsp == "ESP32S3" then -- ESP32C3开发板的引脚
+        return 10, 11, 255 -- 开发板上就2个灯
+    elseif rtos_bsp == "EC618" then -- Air780E开发板引脚
+        return 27, 255, 255 -- AIR780E开发板上就一个灯
+    elseif rtos_bsp == "EC718P" then -- Air780E开发板引脚
+        return 27, 255, 255 -- AIR780EP开发板上就一个灯
+    elseif rtos_bsp == "UIS8850BM" then -- Air780UM开发板引脚
+        return 36, 255, 255 -- Air780UM开发板上就一个灯
+    else
+        log.info("main", "define led pin in main.lua")
+        return 0, 0, 0
+    end
+end
+
+
+--LED引脚判断赋值结束
+
+local P1,P2,P3=pinx()--赋值开发板LED引脚编号
+local LEDA= gpio.setup(P1, 0, gpio.PULLUP)
+-- local LEDB= gpio.setup(P2, 0, gpio.PULLUP)
+-- local LEDC= gpio.setup(P3, 0, gpio.PULLUP)
+
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    local ali = aliyun2.create({
+        productKey = "a1QPg8JEj02",
+        deviceName = "869300038718048",
+        -- deviceSecret = "8837e03c52650fde37614fd078cac6e9",
+        productSecret = "tHqlKgc2Evq5V2m1",
+        regnwl = true
+    })
+    if ali and aliyun2.start(ali) then
+        while 1 do
+            local result, event, data, payload = sys.waitUntil(ali.topic, 30000)
+            if result then
+                log.info("aliyun", "event", event, data)
+                if event == "ota" then
+                    log.info("aliyun", "ota", data.url)
+                elseif event == "recv" then
+                    if data == ali.sub_topics.down_raw then
+                        log.info("aliyun", "下行的透传信息", data)
+                    elseif data == ali.sub_topics.property_set then
+                        log.info("aliyun2", "属性设置")
+                        local jdata = json.decode(payload)
+                        if jdata and jdata.params then
+                            if jdata.params.LEDSwitch then
+                                if jdata.params.LEDSwitch == 0 then
+                                    log.info("aliyun2", "关闭LEDA")
+                                    if LEDA then
+                                        LEDA(0)
+                                    end
+                                else
+                                    log.info("aliyun2", "打开LEDA")
+                                    if LEDA then
+                                        LEDA(1)
+                                    end
+                                end
+                            end
+                        end
+                    end
+                end
+            end
+        end
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 385 - 0
script/libs/aliyun2.lua

@@ -0,0 +1,385 @@
+--[[
+@module aliyun2
+@summary 阿里云物联网平台(开发中)
+@version 1.0
+@date    2024.05.18
+@author  wendal
+@demo    aliyun2
+@usage
+-- 请查阅demo
+
+-- 本库基于阿里云物联网重新设计, 与aliyun.lua库不兼容
+-- 本库尚属开发测试阶段, API随时可能变化, 也可能不变^_^
+]]
+_G.sys = require("sys")
+
+local aliyun2 = {}
+local g_id = 1
+
+--[[
+初始化一个aliyun示例
+@api aliyun.create(opts)
+@table 参数表
+@return aliyun实例
+@usage
+-- 初始化一个aliyun示例
+local ali = aliyun.create(opts)
+if ali and aliyun2.start(ali) then
+    while 1 do
+        local result, tip, params = sys.waitUntil(ali.topic, 30000)
+        if result then
+            log.info("aliyun", "event", tip, params)
+        end
+    end
+else
+    log.error("aliyun", "初始化失败")
+end
+]]
+function aliyun2.create(opts)
+    if not opts then
+        log.error("aliyun2", "配置参数表不能是空")
+        return
+    end
+    -- 检查最基本的参数
+    if not opts.productKey or not opts.deviceName then
+        log.error("aliyun2", "缺失配置参数productKey")
+        return
+    end
+    if not opts.deviceName then
+        if mobile then
+            opts.deviceName = mobile.imei()
+        elseif wlan and wlan.getMac then
+            opts.deviceName = wlan.getMac()
+        else
+            opts.deviceName = mcu.unique_id():toHex()
+        end
+        log.info("aliyun2", "deviceName未指定,自动分配", opts.deviceName)
+    end
+    if opts.productSecret and #opts.productSecret == 0 then
+        opts.productSecret = nil
+    end
+    if opts.deviceSecret and #opts.deviceSecret == 0 then
+        opts.deviceSecret = nil
+    end
+    if not opts.store_key then
+        opts.store_key = opts.productKey .. "_" .. opts.deviceName
+    end
+    if opts.productSecret and not opts.deviceSecret then
+        -- 从本地存储读取设备密钥
+        local payload = nil
+        if fskv then
+            payload = fskv.get(opts.store_key)
+        end
+        if not payload or #payload < 16 then
+            payload = io.readFile(opts.store_key)
+        end
+        if payload then
+            local jdata = json.decode(payload)
+            if jdata then
+                opts.deviceSecret = jdata["deviceSecret"]
+                opts.deviceToken = jdata["deviceToken"]
+                opts.clientId = jdata["clientId"]
+            end
+        end
+    end
+
+    if not opts.productSecret and not opts.deviceSecret and not opts.deviceToken then
+        log.error("aliyun2", "请指定productSecret或deviceSecret")
+        return
+    end
+    local ctx = opts
+    -- 计算mqtt的host和port
+    if not ctx.mqtt_host then
+        -- ctx.mqtt_host = "iot-as-mqtt." .. ctx.productKey .. ".aliyuncs.com"
+        if ctx.instanceId then
+            ctx.mqtt_host = ctx.instanceId .. ".mqtt.iothub.aliyuncs.com"
+        else
+            ctx.mqtt_host = ctx.productKey .. ".iot-as-mqtt." .. (opts.regionId or "cn-shanghai") ..".aliyuncs.com"
+        end
+    end
+    if not ctx.mqtt_port then
+        ctx.mqtt_port = 1883
+    end
+    ctx.device_retry = 0
+    ctx.topic = "aliyun2_" .. g_id
+    g_id = g_id + 1
+
+    -- 生成mqtt的topic
+    if ctx.auto_topic == nil or ctx.auto_topic then
+        if not ctx.sub_topics then
+            ctx.sub_topics = {}
+        end
+        if not ctx.pub_topics then
+            ctx.pub_topics = {}
+        end
+        local topics = ctx.sub_topics
+        -- 订阅必要的topic
+        local dn = ctx.productKey .. "/" .. ctx.deviceName
+        -- 首先是OTA的topic
+        topics.ota = "/ota/device/upgrade/" .. dn
+        -- 配置更新信息
+        topics.config_push = "/sys/" .. dn .. "/thing/config/push"
+        -- 广播信息
+        topics.broadcast = "/broadcast/" .. ctx.productKey .. "/#"
+        -- NTP信息
+        topics.ntp = "/ext/ntp/" .. dn .. "/response"
+
+        -- 物模型, 透传信息
+        topics.raw_reply = "/sys/" .. dn .. "/thing/model/up_raw_reply"
+        topics.raw_down = "/sys/" .. dn .. "/thing/model/down_raw"
+        -- 非透传
+        topics.property_set = "/sys/".. dn .. "/thing/service/property/set"
+
+        -- 上行常用的topic
+        topics = ctx.pub_topics
+        topics.inform = "/ota/device/inform/".. dn
+        topics.ntp = "/ext/ntp/".. dn .. "/request"
+        topics.raw_up = "/sys/".. dn .. "/thing/model/up_raw"
+    end
+
+    if ctx.instanceId then
+        log.info("aliyun2", "instanceId", ctx.instanceId)
+    end
+    log.info("aliyun2", "deviceName", ctx.deviceName)
+    log.info("aliyun2", "mqtt_host", ctx.mqtt_host)
+    log.info("aliyun2", "mqtt_port", ctx.mqtt_port)
+    return ctx
+end
+
+local function aliyun_do_reg(ctx)
+    local mqttc = mqtt.create(ctx.adapter, ctx.mqtt_host, 443, true)
+    if not mqttc then
+        log.error("aliyun2", "创建mqtt实例失败")
+        return
+    end
+    -- 计算自动注册所需要的密钥
+    local tm = tostring(os.time())
+    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 ""))
+    log.info("aliyun2", "开始注册流程", client_id)
+    local user_name = ctx.deviceName .. "&"..ctx.productKey
+    local content = "deviceName"..ctx.deviceName.."productKey"..ctx.productKey.."random" .. tm
+    local password = crypto.hmac_sha1(content, ctx.productSecret)
+    log.info("aliyun2", "尝试注册", client_id, user_name, password)
+    mqttc:auth(client_id, user_name, password)
+    mqttc:keepalive(240) -- 实际上会忽略该属性
+    -- mqttc:debug(true)
+    mqttc:autoreconn(false, 3000) -- 不需要自动重连
+    local regok = false -- 记录注册成功与否
+    mqttc:on(function(mqtt_client, event, data, payload)
+        log.info("aliyun2", "event", event, data, payload)
+        if event == "recv" then
+            log.info("aliyun", "downlink", "topic", data, "payload", payload)
+            if payload then
+                local jdata,res = json.decode(payload) -- TODO 搞个alijson库进行封装
+                if jdata and (jdata["deviceSecret"] or jdata["deviceToken"]) then
+                    log.info("aliyun2", "获取到设备密钥")
+                    regok = true
+                    ctx.deviceSecret = jdata["deviceSecret"]
+                    ctx.deviceToken = jdata["deviceToken"]
+                    ctx.clientId = jdata["clientId"]
+                    sys.publish(tm, "reg", "ok")
+                    if fskv then
+                        log.info("aliyun2", "密钥信息存入fskv", ctx.store_key)
+                        fskv.set(ctx.store_key, payload)
+                    end
+                    log.info("aliyun2", "密钥信息存入文件系统", ctx.store_key)
+                    io.writeFile(ctx.store_key, payload)
+                else
+                    sys.publish(tm, "reg", "fail")
+                    return
+                end
+            end
+        elseif event == "close" then
+            sys.publish(tm, "close")
+        end
+    end)
+    mqttc:connect()
+    sys.waitUntil(tm, 5000)
+    sys.wait(100)
+    mqttc:close()
+    if regok then
+        log.info("aliyun2", "自动注册成功,密钥信息已获取")
+    else
+        log.info("alyun2", "自动注册失败,延迟30秒后重试")
+        sys.wait(30000)
+    end
+end
+
+local function aliyun_task_once(ctx)
+    -- 几个条件: 有设备密钥, 有产品密钥, 登陆失败的次数
+    -- 情况1: 只有设备密钥, 没有产品密钥, 那就固定是一机一密
+    -- 情况2: 只有产品密钥, 没有设备密钥, 那就是一机一密
+    -- 情况3: 有产品密钥, 有设备密钥, 登录失败次数少于设定值(默认3次),那继续用一机一密去尝试登陆
+    -- 情况4: 有产品密钥, 有设备密钥, 登录失败次数大于设定值(默认3次), 使用一型一密去尝试注册一次
+
+    if ctx.productSecret then
+        if ctx.deviceSecret or ctx.deviceToken then
+            if ctx.device_retry < 3 then
+                -- 失败次数还不多,先尝试用一机一密
+            else
+                log.info("aliyun2", "设备密钥已存在,但已经连续失败3次,尝试重新注册")
+                aliyun_do_reg(ctx)
+                ctx.device_retry = 0
+            end
+        else
+            aliyun_do_reg(ctx)
+        end
+    end
+
+    if not ctx.deviceSecret and not ctx.deviceToken then
+        log.info("aliyun2", "未能获取到设备密钥,等待重试")
+        return
+    end
+
+    local mqttc = mqtt.create(ctx.adapter, ctx.mqtt_host, ctx.mqtt_port, ctx.mqtt_isssl, ctx.ca_file)
+    if not mqttc then
+        log.error("aliyun2", "创建mqtt实例失败")
+        return
+    end
+    if ctx.deviceSecret then
+        local client_id,user_name, password = iotauth.aliyun(ctx.productKey, ctx.deviceName, ctx.deviceSecret)
+        log.info("aliyun2", "密钥模式", client_id,user_name, password)
+        mqttc:auth(client_id, user_name, password)
+    else
+        local client_id = ctx.clientId .. "|securemode=-2,authType=connwl|"
+        local user_name = ctx.deviceName.."&"..ctx.productKey
+        log.info("aliyun2", "token模式", client_id, user_name, ctx.deviceToken)
+        mqttc:auth(client_id, user_name, ctx.deviceToken)
+    end
+    mqttc:keepalive(ctx.mqtt_keepalive or 240)
+    -- mqttc:debug(true)
+    mqttc:autoreconn(false, 3000) -- 不需要自动重连
+    mqttc:on(function(mqtt_client, event, data, payload)
+        log.info("aliyun2", "event", event, data, payload)
+        if event == "conack" then
+            log.info("aliyun2", "连接成功,鉴权完成")
+            ctx.device_retry = 0
+
+            -- 需要订阅的topic
+            if ctx.sub_topics then
+                for k, v in pairs(ctx.sub_topics) do
+                    log.info("aliyun2", "订阅topic", v, "别名", k)
+                    mqttc:subscribe(v)
+                end
+            end
+
+            -- 上报一些基础信息
+            -- 版本信息
+            if ctx.pub_topics and ctx.pub_topics.inform then
+                local info = ctx.inform_data or {version=_G.VERSION, module=rtos.bsp():upper()}
+                info = json.encode({id="123", params=info})
+                log.info("aliyun2", "上报版本信息", ctx.pub_topics.inform, info)
+                mqttc:publish(ctx.pub_topics.inform, info, 1)
+            end
+            
+            sys.publish(ctx.topic, "conack")
+        elseif event == "recv" then
+            -- 收到消息
+            log.info("aliyun2", "收到下行信息", "topic", data, "payload前128字节", payload and payload:sub(1, 128))
+            sys.publish(ctx.topic, "recv", data, payload)
+            -- TODO 支持FOTA/OTA
+            if ctx.sub_topics and ctx.sub_topics.ota == data then
+                log.info("aliyun2", "收到ota信息", payload)
+                local jdata = json.decode(payload)
+                if jdata and jdata.data and jdata.data.url then
+                    log.info("aliyun2", "获取到OTA所需要的URL", jdata.data.url)
+                    sys.publish(ctx.topic, "ota", jdata.data)
+                end
+            end
+        end
+    end)
+    mqttc:connect()
+    sys.waitUntil(ctx.topic, 5000)
+    if mqttc:ready() then
+        ctx.mqttc = mqttc
+        ctx.device_retry = 0
+        while mqttc:ready() and ctx.running do
+            sys.waitUntil(ctx.topic, 5000)
+        end
+    else
+        ctx.device_retry = ctx.device_retry + 1
+    end
+    mqttc:close()
+    log.info("aliyun2", "单次任务结束")
+    ctx.mqttc = nil
+end
+
+local function aliyun_task (ctx)
+    -- 准备好参数
+
+    -- 开始主循环
+    while ctx.running do
+        aliyun_task_once(ctx)
+        sys.wait(2000)
+    end
+    log.info("aliyun2", "阿里云任务结束")
+end
+
+--[[
+启动aliyun实例
+@api aliyun2.start(ali)
+@return 成功返回true,失败返回false
+]]
+function aliyun2.start(ali)
+    ali.running = true
+    ali.c_task = sys.taskInit(aliyun_task, ali)
+    return ali.c_task ~= nil
+end
+
+--[[
+关闭aliyun实例
+@api aliyun2.stop(ali)
+@return boolean 成功返回true,失败返回false
+]]
+function aliyun2.stop(ali)
+    if ali.c_task then
+        ali.running = nil
+        sys.publish(ali.topic, "stop")
+    end
+end
+
+--[[
+是否已经连接上
+@api aliyun2.ready(ali)
+@return boolean 已经成功返回true,否则一概返回false
+]]
+function aliyun2.ready(ali)
+    if ali and ali.mqttc and ali.mqttc:ready() then
+        return true
+    end
+end
+
+--[[
+订阅自定义的topic
+@api aliyun2.subscribe(ali, topic)
+@return 成功返回true,失败返回false或者nil
+]]
+function aliyun2.subscribe(ali, topic, qos)
+    if not aliyun2.ready(ali) then
+        log.warn("aliyun2", "还没连上服务器,不能订阅", topic)
+        return
+    end
+    ali.mqttc:subscribe(topic, qos or 1)
+    return true
+end
+
+--[[
+上报消息,上行数据
+@api aliyun2.publish(ali, topic, payload, qos, retain)
+@return 成功返回true,失败返回false或者nil
+]]
+function aliyun2.publish(ali, topic, payload, qos, retain)
+    if not aliyun2.ready(ali) then
+        log.warn("aliyun2", "还没连上服务器,不能上报", topic)
+        return
+    end
+    ali.mqttc:public(topic, payload, qos or 1, retain or 0)
+    return true
+end
+
+-- TODO
+-- 1. alijson库
+-- 2. 对物模型做一些封装
+-- 3. 对透传进行一些封装
+
+return aliyun2