望而知之谓之神

经言,望而知之谓之神,闻而知之谓之圣,问而知之谓之工,切脉而知之谓之巧。

以下是我个人总结出来的工程项目框架,重点有三部分:

  1. 事件响应user_event()
  2. 元素模型g_elem_state[]
  3. 组播地址genie_sub_list_init()
    在这里插入图片描述
    举个例子:假如我要开发一个天猫精灵语音控制的小风扇,这时候就要涉及到组播地址 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文件
在这里插入图片描述

创建前
在这里插入图片描述

创建后
在这里插入图片描述

全局查找所打印的信息sys_init ,从而找到入口位置。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

往下查看会发现这个
在这里插入图片描述

这个是拿了BLE的协议栈过来用,而以下的是对接天猫精灵的
在这里插入图片描述

程序入口

在这里插入图片描述

上图的 init genie()改成genie_init(),然后追入genie_init()函数,该函数使能之后会创建一个线程,管理mesh收发的数据,追协议栈也是在此。genie_init()创建了关于mesh数据的回调线程。
在这里插入图片描述

宏定义情况可以在对应的.mk 文件中使能控制,比如说上面的CONFIG_BT_MESH,所以在编译的时候该宏会被激活
在这里插入图片描述

再进一步追入genie_flash_init(),发现这个是处理三元组的
在这里插入图片描述

Alt+左键和Alt+逗号:表示追入和返回

追回到上一次然后追入genie_mesh_init()
在这里插入图片描述

1243行那个函数是蓝牙使能函数bt_enable(),bt指bluetooth
在这里插入图片描述
在这里插入图片描述

由此可见,在genie_mesh_init()里的bt_enable()创建线程收发蓝牙mesh的消息

进一步追入函数bt_enable()所带参数_genie_mesh_ready
在这里插入图片描述

先判断是否数据传输成功,然后是一个事件函数入口genie_event(),这个函数很重要很重要非常重要。追入函数里的事件参数

在这里插入图片描述

枚举了很多类型事件

继续追入到genie_event()里面,发现有事件函数user_event(),这个一样是非常重要

在这里插入图片描述

和genie_event()类似,事件参数也是枚举方式,和genie_event()一起在同个枚举结构里

在这里插入图片描述

再说一遍,事件函数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);
    }
}

在这里插入图片描述

元素模型g_elem_state[ ]

每个设备节点对应一个元素,例如led1、led2;每个元素对应有多个模型,所谓模型就是对应的功能,比如开关、调亮度等等;每个模型里包含多个状态,比如开关模型就有开状态和关状态两个状态。

在这里插入图片描述

/* 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文件的命名

在这里插入图片描述

.mk文件修改

在这里插入图片描述

编译流程

在这里插入图片描述
三元组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qN27HCIY-1630674196693)(C:\Users\BMn94\AppData\Roaming\Typora\typora-user-images\image-20210729192430077.png)]

调试信息

在这里插入图片描述

事件查询

在这里插入图片描述

在这里插入图片描述

追入_genie_event_handle_indicate

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以发现这个是通用开关内容

厂商模型元素填充

数据上报

在这里插入图片描述

        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;
    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);

以上是数据上报的相关部分代码

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44035986/article/details/120090156
今日推荐