天天看点

望而知之谓之神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文件的命名编译流程调试信息事件查询厂商模型元素填充

继续阅读