天天看點

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

阿裡雲&天貓精靈實戰

  • SDK說明
  • 程式入口
  • 事件響應user_event()
  • 元素模型g_elem_state[ ]
  • 編譯生成.bin檔案的命名
    • .mk檔案修改
  • 編譯流程
  • 調試資訊
  • 事件查詢
  • 廠商模型元素填充
    • 資料上報

經言,望而知之謂之神,聞而知之謂之聖,問而知之謂之工,切脈而知之謂之巧。

以下是我個人總結出來的工程項目架構,重點有三部分:

  1. 事件響應user_event()
  2. 元素模型g_elem_state[]
  3. 多點傳播位址genie_sub_list_init()
    望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
    舉個例子:假如我要開發一個天貓精靈語音控制的小風扇,這時候就要涉及到多點傳播位址 0xC007以及對應裝置的三元組。事件響應地方寫相關驅動程式。元素模型主要是功能定義(比如開關風扇、調轉速等)。這裡先暫時介紹這麼多,有個大概架構印象。

SDK說明

官方源碼

  1. genie_init()建立了關于mesh資料的回調線程。
  2. 追入genie_mesh_init()進行初始化
  3. 函數bt_enable()使能藍牙,建立線程收發藍牙mesh的消息
  4. 追入函數bt_enable()所帶參數_genie_mesh_ready
  5. genie_event()天貓精靈事件函數入口
  6. 事件函數genie_event()之後調用事件函數user_event(),進行使用者事件處理

工程支援多種闆子(在board目錄),很多重名檔案,為了篩選,建立 .vscode 檔案夾,裡邊放.json檔案

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

建立前

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

建立後

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

全局查找所列印的資訊sys_init ,進而找到入口位置。

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

往下檢視會發現這個

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

這個是拿了BLE的協定棧過來用,而以下的是對接天貓精靈的

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

程式入口

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

上圖的 init genie()改成genie_init(),然後追入genie_init()函數,該函數使能之後會建立一個線程,管理mesh收發的資料,追協定棧也是在此。genie_init()建立了關于mesh資料的回調線程。

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

宏定義情況可以在對應的.mk 檔案中使能控制,比如說上面的CONFIG_BT_MESH,是以在編譯的時候該宏會被激活

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

再進一步追入genie_flash_init(),發現這個是處理三元組的

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

Alt+左鍵和Alt+逗号:表示追入和傳回

追回到上一次然後追入genie_mesh_init()

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

1243行那個函數是藍牙使能函數bt_enable(),bt指bluetooth

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

由此可見,在genie_mesh_init()裡的bt_enable()建立線程收發藍牙mesh的消息

進一步追入函數bt_enable()所帶參數_genie_mesh_ready

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

先判斷是否資料傳輸成功,然後是一個事件函數入口genie_event(),這個函數很重要很重要非常重要。追入函數裡的事件參數

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

枚舉了很多類型事件

繼續追入到genie_event()裡面,發現有事件函數user_event(),這個一樣是非常重要

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

和genie_event()類似,事件參數也是枚舉方式,和genie_event()一起在同個枚舉結構裡

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

再說一遍,事件函數user_event()非常重要,因為這個是自定義事件的地方。一般情況下genie_event()都是不怎麼需要修改,而user_event()是實作功能事件的入口。

事件響應user_event()

該函數的位置和application_start()在同個c檔案中,而且通常在application_start()前面。

下面是light_ctl.c裡邊的

void user_event(E_GENIE_EVENT event, void *p_arg)
{
    E_GENIE_EVENT next_event = event;

    //BT_DBG("%s, %s %p\n", __func__, genie_event_str[event], p_arg);
    switch(event) {
        case GENIE_EVT_SW_RESET:				//複位事件标志
        case GENIE_EVT_HW_RESET_START://複位開始
            _led_flash(5, 1);					//閃燈架構,_led_set()裡自定義設定燈,下圖所示
            break;
        case GENIE_EVT_HW_RESET_DONE://複位結束
            _reset_light_para();				//複位燈的狀态
            BT_DBG("GENIE_EVT_HW_RESET_DONE\n");
            break;
        case GENIE_EVT_SDK_MESH_INIT://初始化

            _led_init();			//要自定義實作,下圖所示
            _init_light_para();		//注冊相關元素
            _user_init();			//必須使能該函數裡邊的CONFIG_GENIE_OTA,否則整個編譯會過不去
            if (!genie_reset_get_flag()) {
                next_event = GENIE_EVT_SDK_ANALYZE_MSG;
            }
            break;
        case GENIE_EVT_SDK_MESH_PROV_SUCCESS://代理服務成功
            _led_flash(3, 0);		//閃爍
            break;
        case GENIE_EVT_SDK_TRANS_CYCLE:
        case GENIE_EVT_SDK_ACTION_DONE://動作完成
        {
            elem_state_t *p_elem = (elem_state_t *)p_arg;//函數傳遞的第二個參數,天貓精靈下發的指令元素資料
            _led_ctrl(p_elem);		//給元素賦能,然後又聯系到_led_set()
            if(event == GENIE_EVT_SDK_ACTION_DONE)
                _save_light_state(p_elem);
            break;
        }
        case GENIE_EVT_SDK_INDICATE:
            break;
        case GENIE_EVT_SDK_VENDOR_MSG:
            break;
        default:
            break;
    }
    
    if(next_event != event) {
        genie_event(next_event, p_arg);
    }
}
           
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

元素模型g_elem_state[ ]

每個裝置節點對應一個元素,例如led1、led2;每個元素對應有多個模型,所謂模型就是對應的功能,比如開關、調亮度等等;每個模型裡包含多個狀态,比如開關模型就有開狀态和關狀态兩個狀态。

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
/* element configuration start */
#define MESH_ELEM_COUNT 1
#define MESH_ELEM_STATE_COUNT MESH_ELEM_COUNT

elem_state_t g_elem_state[MESH_ELEM_STATE_COUNT];//聲明元素個數
model_powerup_t g_powerup[MESH_ELEM_STATE_COUNT];//最新資料last_data的備份
static struct bt_mesh_model element_models[] = {
    BT_MESH_MODEL_CFG_SRV(),		//必須要加的模型
    BT_MESH_MODEL_HEALTH_SRV(),		//必須要加的模型

    MESH_MODEL_GEN_ONOFF_SRV(&g_elem_state[0]),		//通用開關服務

    /*************以下的是其它服務,本例程不需要**************/
//    MESH_MODEL_LIGHTNESS_SRV(&g_elem_state[0]),	//亮度服務
//    MESH_MODEL_CTL_SRV(&g_elem_state[0]),
//#ifndef CONFIG_ALI_SIMPLE_MODLE
//    MESH_MODEL_GEN_LEVEL_SRV(&g_elem_state[0]),
//    MESH_MODEL_CTL_SETUP_SRV(&g_elem_state[0]),
//#endif

};
           

元素結構體

typedef struct{
    u8_t elem_index;			//元素個數
    model_state_t state;		//狀态
    model_powerup_t powerup;	//儲存狀态,相當于緩存,避免斷電影響
    void *user_data;		   //填充的資料
} elem_state_t;
           

通用廠商模型

static struct bt_mesh_model g_element_vendor_models[] = {//通用廠商模型(用于拓展)
    MESH_MODEL_VENDOR_SRV(&g_elem_state[0]),
};
struct bt_mesh_elem elements[] = {//元素填充
    BT_MESH_ELEM(0, element_models, g_element_vendor_models, 0),
};

uint8_t get_vendor_element_num(void)
{
    return MESH_ELEM_COUNT;//傳回元素個數,友善配置設定位址
}
           

編譯生成.bin檔案的命名

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

.mk檔案修改

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

編譯流程

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

三元組

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

調試資訊

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

事件查詢

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

追入_genie_event_handle_indicate

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

可以發現這個是通用開關内容

廠商模型元素填充

資料上報

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
vnd_model_msg reply_msg;
        seg_count = get_seg_count(i + 4);

        reply_msg.opid = VENDOR_OP_ATTR_INDICATE;
        if(g_version_tid != 0xFF) {
            reply_msg.tid = g_version_tid;
        } else {
            reply_msg.tid = 0;
        }
        reply_msg.data = buff;
        reply_msg.len = i;
        reply_msg.p_elem = &elements[p_elem->elem_index];
        //reply_msg.retry_period = GENIE_DEFAULT_DURATION * seg_count + SEG_RETRANSMIT_TIMEOUT;
        reply_msg.retry_period = 125 * seg_count + 400;
        if(seg_count > 1) {
            reply_msg.retry_period *= 2;    //(1 + SEG_RETRANSMIT_ATTEMPTS);
        }
        reply_msg.retry = VENDOR_MODEL_MSG_DFT_RETRY_TIMES;

        genie_vendor_model_msg_send(&reply_msg);
           

上面這段代碼引用出來放在廠商模型的函數裡

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充
望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

是以有個地方代碼調整一下

vnd_model_msg reply_msg;
    seg_count = get_seg_count(i + 4);

    
    reply_msg.opid = VENDOR_OP_ATTR_INDICATE;
    if (g_version_tid != 0xFF)
    {
        reply_msg.tid = g_version_tid;
    }
    else
    {
        reply_msg.tid = 0;
    }
    reply_msg.data = buff;
    reply_msg.len = i;
    reply_msg.p_elem = &elements[p_elem->elem_index];

    //reply_msg.retry_period = GENIE_DEFAULT_DURATION * seg_count + SEG_RETRANSMIT_TIMEOUT;
        reply_msg.retry_period = 125 * seg_count + 400;
        if(seg_count > 1) {
            reply_msg.retry_period *= 2;    //(1 + SEG_RETRANSMIT_ATTEMPTS);
        }
        reply_msg.retry = VENDOR_MODEL_MSG_DFT_RETRY_TIMES;

        genie_vendor_model_msg_send(&reply_msg);
           

改成

vnd_model_msg reply_msg;
    u8_t seg_count;
    seg_count = get_seg_count(my_vnd_msg->len + 4);

    reply_msg.opid = VENDOR_OP_ATTR_INDICATE;
    reply_msg.tid = vendor_model_msg_gen_tid();
    reply_msg.data = my_vnd_msg->data;//指定類型
    reply_msg.len = my_vnd_msg->len;
    reply_msg.p_elem = &elements[0];//綁定給元素

    reply_msg.retry_period = 125 * seg_count + 400;
    
    reply_msg.retry = VENDOR_MODEL_MSG_DFT_RETRY_TIMES;

    genie_vendor_model_msg_send(&reply_msg);
           

以上是資料上報的相關部分代碼

望而知之謂之神SDK說明程式入口事件響應user_event()元素模型g_elem_state[ ]編譯生成.bin檔案的命名編譯流程調試資訊事件查詢廠商模型元素填充

繼續閱讀