0
点赞
收藏
分享

微信扫一扫

泰凌微8258入门教程 进阶篇⑤——Vendor Model 的使用和案例(自定义呼吸灯模型)


文章目录

  • 一、前言
  • 二、演示
  • 三、日志
  • 四、源码
  • 五、自定义Model(呼吸灯)
  • 5.1 定义操作码opcode
  • 5.2 定义操作数组 mesh_cmd_sig_func_t
  • 5.3 定义回调函数cb
  • 5.4 测试
  • 5.5 增加应答rsp
  • 六、呼吸灯的具体实现


一、前言

Bluetooth SIG Mesh Model已经制定得很详细了,详情看:MshMDLv1.0.1.pdf 这份文档。

足以满足我们大部分的应用场景,但是总有一些私人定制类的需求。这时候就需要我们自定义Model。

二、演示

  • 首页——>CMD按钮
  • 选择Vendor On、Vendor Off、Vendor Get等功能
  • Vendor Model的操作码opcode都以0211开头,因为这两个字节是Vendor ID。详情见:蓝牙Sig Mesh 概念入门⑤——Mesh通信消息格式详解
  • 泰凌微8258入门教程 进阶篇⑤——Vendor Model 的使用和案例(自定义呼吸灯模型)_Telink


  • 泰凌微8258入门教程 进阶篇⑤——Vendor Model 的使用和案例(自定义呼吸灯模型)_泰凌微_02


  • 泰凌微8258入门教程 进阶篇⑤——Vendor Model 的使用和案例(自定义呼吸灯模型)_Vendor Model_03


三、日志

  • Vendor On

[2021-04-17 22:53:09.236]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00c2,par_len:2,par:01 09 
[USER]:(USER)cb_vd_group_g_set par=01 09 
[USER]:(USER)cb_vd_group_g_get par=01 09 
[LIB]:(sdk)mesh tx NoAck,op:0xc4,src:0x0136,dst:0x0001,par_len:1 par:01 
[INFO]:(mesh)mesh_tx_access_key_get:print index part0,0,0

  • Vendor Off

RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00c2,par_len:2,par:00 0a 
[USER]:(USER)cb_vd_group_g_set par=00 0a 
[USER]:(USER)cb_vd_group_g_get par=00 0a 
[LIB]:(sdk)mesh tx NoAck,op:0xc4,src:0x0136,dst:0x0001,par_len:1 par:00 
[INFO]:(mesh)mesh_tx_access_key_get:print index part0,0,0

  • Vendor On/Off Get

[2021-04-17 22:56:09.756]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00c1,par_len:0,par:
[USER]:(USER)cb_vd_group_g_get par=
[LIB]:(sdk)mesh tx NoAck,op:0xc4,src:0x0136,dst:0x0001,par_len:1 par:00 
[INFO]:(mesh)mesh_tx_access_key_get:print index part0,0,0

  • Vendor On NO-ACK

[2021-04-17 22:56:55.442]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00c3,par_len:2,par:01 0d 
[USER]:(USER)cb_vd_group_g_set par=01 0d

四、源码

  • 源码主要集中在vendor\common\vendor_model.cvendor_model.h
  • 这里面的流程就不再过多的赘述,详情见:泰凌微8258入门教程 基础篇⑥——接收数据流程

去掉多余的#if和不必要的代码

  • 定义opcode操作码

#define VD_GROUP_G_GET				    0xC1
#define VD_GROUP_G_SET				    0xC2
#define VD_GROUP_G_SET_NOACK		    0xC3
#define VD_GROUP_G_STATUS		        0xC4

  • 定义 mesh_cmd_sig_func_t数组
  • 数组的主要作用是将操作码opcoderesp opcodecb回调函数关联起来。

typedef struct{
    u16 op;
    u16 status_cmd; // receive status message even though there is not server model        // u16 for align
    u32 model_id_tx;
    u32 model_id_rx;
    cb_cmd_sig2_t cb;
    u32 op_rsp;     // -1 for no rsp and ensure 4BYTE align
}mesh_cmd_sig_func_t;

mesh_cmd_sig_func_t mesh_cmd_vd_func[] = {	

    {VD_GROUP_G_SET, 0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_group_g_set, VD_GROUP_G_STATUS},
	{VD_GROUP_G_GET, 0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_group_g_get, VD_GROUP_G_STATUS},
	{VD_GROUP_G_SET_NOACK, 0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_group_g_set, STATUS_NONE},
    {VD_GROUP_G_STATUS, 1, VENDOR_MD_LIGHT_S, VENDOR_MD_LIGHT_C, cb_vd_group_g_status, STATUS_NONE},

};

  • 回调函数是我们主要实现业务代码的地方

// --------- vendor sub op code end  --------
int cb_vd_group_g_get(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{
    u8 sub_op = 0;
	LOG_USER_MSG_INFO(par, par_len, "cb_vd_group_g_get par=", 0);
    if(par_len){    // compatible with legacy version that is no par.
        sub_op = par[0];
    }
    
    model_g_light_s_t *p_model = (model_g_light_s_t *)cb_par->model;
    cb_vd_group_g_sub_tx_st p_cb = (cb_vd_group_g_sub_tx_st)search_vd_group_g_func(sub_op, SEARCH_VD_GROUP_G_FUNC_TYPE_TX_ST);
    if(p_cb){
        return p_cb(cb_par->model_idx, sub_op, p_model->com.ele_adr, cb_par->adr_src, 0, 0);
    }else{
        return -1;
    }
}

int cb_vd_group_g_set(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{
    int err = -1;
	int pub_flag = 0;
	LOG_USER_MSG_INFO(par, par_len, "cb_vd_group_g_set par=", 0);
    //model_g_light_s_t *p_model = (model_g_light_s_t *)cb_par->model;
    vd_group_g_set_t *p_set = (vd_group_g_set_t *)par;
    u8 sub_op = p_set->sub_op;

    if(!cb_par->retransaction){
        cb_vd_group_g_sub_set p_cb_set = (cb_vd_group_g_sub_set)search_vd_group_g_func(sub_op, SEARCH_VD_GROUP_G_FUNC_TYPE_SET);
        if(p_cb_set){
            pub_flag = p_cb_set(par, par_len, cb_par);
        }else{
            return -1;
        }
    }

    if(VD_GROUP_G_SET_NOACK != cb_par->op){
        err = cb_vd_group_g_get(par, par_len, cb_par);
    }else{
        err = 0;
    }

    if(!err && pub_flag){
        if(is_vd_onoff_op(sub_op)){ // only onoff need publish now
            model_pub_check_set(ST_G_LEVEL_SET_PUB_NOW, cb_par->model, 1);
        }
    }
    
    return err;
}

五、自定义Model(呼吸灯)

  • 了解了Vendor Model的流程,现在开始我们自定义的模型开发之旅
  • 我们选择做一个呼吸灯的模型
  • 可以查询当前呼吸灯的状态
  • 设置呼吸灯开始呼吸和停止呼吸

5.1 定义操作码opcode

  • vendor\common\vendor_model.h
  • 定义4个opcode,分别是
  • 查询呼吸灯
  • 设置呼吸灯
  • 不需要ACK的设置呼吸灯
  • 呼吸灯当前的状态(应答)

#define VD_BREATH_G_GET			        0xF0
#define VD_BREATH_G_SET			        0xF1
#define VD_BREATH_G_SET_NOACK	        0xF2
#define VD_BREATH_G_STATUS		        0xF3

5.2 定义操作数组 mesh_cmd_sig_func_t

  • vendor\common\vendor_model.c

mesh_cmd_sig_func_t mesh_cmd_vd_func[] = {

    {VD_BREATH_G_SET, 		0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_breath_g_set, 	VD_BREATH_G_STATUS},
	{VD_BREATH_G_GET, 		0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_breath_g_get, 	VD_BREATH_G_STATUS},
	{VD_BREATH_G_SET_NOACK, 0, VENDOR_MD_LIGHT_C, VENDOR_MD_LIGHT_S, cb_vd_breath_g_set, 	STATUS_NONE},
    {VD_BREATH_G_STATUS, 	1, VENDOR_MD_LIGHT_S, VENDOR_MD_LIGHT_C, cb_vd_scence_g_status, STATUS_NONE},

}

5.3 定义回调函数cb

  • vendor\common\vendor_model.c

// --------- vendor sub op code end  --------
int cb_vd_breath_g_get(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{ 
	LOG_USER_MSG_INFO(par, par_len,"cb_vd_breath_g_get par=", 0);

	return 0;
}

int cb_vd_breath_g_set(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{ 
	LOG_USER_MSG_INFO(par, par_len, "cb_vd_breath_g_set par=", 0);
	if(par[0]==0x01){
		LOG_USER_MSG_INFO(0, 0, "kangweijian start breath", 0);
	}else{
		LOG_USER_MSG_INFO(0, 0, "kangweijian stop breath", 0);
	}	
	return 0;
}

5.4 测试

泰凌微8258入门教程 进阶篇⑤——Vendor Model 的使用和案例(自定义呼吸灯模型)_Model_04

2021-04-17 23:30:07.385]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:01 
[USER]:(USER)cb_vd_breath_g_set par=01 
[USER]:(USER)kangweijian start breath

[2021-04-17 23:30:08.651]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:01 
[USER]:(USER)cb_vd_breath_g_set par=01 
[USER]:(USER)kangweijian start breath

[2021-04-17 23:30:09.970]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:01 
[USER]:(USER)cb_vd_breath_g_set par=01 
[USER]:(USER)kangweijian start breath

[2021-04-17 23:30:28.429]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:00 
[USER]:(USER)cb_vd_breath_g_set par=00 
[USER]:(USER)kangweijian stop breath

[2021-04-17 23:30:29.690]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:00 
[USER]:(USER)cb_vd_breath_g_set par=00 
[USER]:(USER)kangweijian stop breath

[2021-04-17 23:30:31.016]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:00 
[USER]:(USER)cb_vd_breath_g_set par=00 
[USER]:(USER)kangweijian stop breath

现在出现了一个问题,为什么会连续发三次吗,间隔1s左右。是分包吗,看着不像。最后深究发现,是因为App下发后,设备没有应答!

5.5 增加应答rsp

  • 主要是增加了mesh_tx_cmd_rsp应答函数
  • 最后测试,有了应答,命令就只下发一次
  • vendor\common\vendor_model.c

int cb_vd_breath_g_get(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{ 
	LOG_USER_MSG_INFO(par, par_len,"cb_vd_breath_g_get par=", 0);

    model_g_light_s_t *p_model = (model_g_light_s_t *)cb_par->model;
	u8 rsp = 00;
    return mesh_tx_cmd_rsp(VD_BREATH_G_STATUS, (u8 *)&rsp, sizeof(rsp), p_model->com.ele_adr, cb_par->adr_src, 0, 0);
}

int cb_vd_breath_g_set(u8 *par, int par_len, mesh_cb_fun_par_t *cb_par)
{ 
	LOG_USER_MSG_INFO(par, par_len, "cb_vd_breath_g_set par=", 0);
	if(par[0]==0x01){
		LOG_USER_MSG_INFO(0, 0, "kangweijian start breath", 0);
	}else{
		LOG_USER_MSG_INFO(0, 0, "kangweijian stop breath", 0);
	}	
	if(VD_GROUP_G_SET_NOACK != cb_par->op){
        return cb_vd_breath_g_get(par, par_len, cb_par);
    }else{
        return 0;
    }
}

  • 日志Log

[2021-04-17 23:50:06.776]
RX:[LIB]:(sdk)rcv access layer,retransaction:0,ttl:10,src:0x0001,dst:0xffff op:0x00f1,par_len:1,par:01 
[USER]:(USER)cb_vd_breath_g_set par=01 
[USER]:(USER)kangweijian start breath
[USER]:(USER)cb_vd_breath_g_get par=01

六、呼吸灯的具体实现

呼吸灯的具体实现,详情见下一篇博文:泰凌微8258入门教程 进阶篇⑥——proc_led指示灯处理函数和呼吸灯实现


举报

相关推荐

0 条评论