精华内容
下载资源
问答
  • 蓝牙mesh
    2021-10-05 22:45:49

     什么是蓝牙mesh模型? 根据蓝牙mesh术语表所述: “模型(Model)——定义的是一系列状态、状态转换、状态绑定、消息和其他相关性能的集合。节点中的元素必须支持一个或多个模型,并且模型定义了相关元素的功能。蓝牙技术联盟定义了许多模型,其中大部分模型被特意定位为能够在各类设备中使用的‘通用’模型。” 相关术语,请参阅蓝牙mesh术语表和蓝牙mesh技术概览。 从本质上讲,模型是标准软件组件的技术规格,决定了mesh设备的功能。模型是独立的组件,并且一个产品包含多个模型。总之,从网络角度来看,可以说模型决定了设备的功能。

     状态  模型包含各种状态,而状态是显示设备状况的数据项,例如开/关或高/低。简单状态可能只包含单个值,而复杂状态可包含多个字段,类似于C语言等编程语言中的结构体。 有时,不同状态项之间存在某种关系,这些关系被称为状态绑定。状态绑定指的是如果相关联的一方状态发生变化,另一方状态的数值则需要重新进行计算。有时,状态绑定需要满足一定的条件并且可能由其他状态启用或禁用。对于使用中的模型所定义的状态绑定,开发人员必须实现必要的逻辑并在需要时执行该逻辑。 相反,对于《蓝牙mesh模型规格》(Bluetooth Mesh Model Specification)中未明确定义的状态绑定,这种状态只能独立运行。例如,当通用开/关状态显示某设备当前处于关闭状态时,用户无法识别通用级状态的增加。通过将通用开/关状态设定到1,设备切换至开启状态,同时设备将开始在已设定的级别上运行。这一点很容易理解,设想它是一个旋转调光按钮,既可以通过转动旋钮调整房间内灯的亮度,也可以按下开关开灯或关灯。在关灯状态,转动旋钮不会产生任何变化,但如果在转动旋钮后按下开关,灯就会在之前所设定的亮度打开。  

    模型类别 模型分为不包含状态的客户端和包含状态的服务器两大类。状态是表示设备某方面状况的数据项术语,例如开启或关闭状态以及当前的级别。 一些服务器模型与另一个服务器模型相关联,其名称也与另一个模型名称类似,但包含“SetUp(设定)”字样。例如,与“传感器服务器模型”相关联的是“传感器设定服务器模型”。在技术层面,设定服务器模型与其他服务器模型没有区别,它们都包含状态并能够生成和使用特定类型的消息。其用途是允许将模型的配置设置与主模型状态项分离,从而可以采取不同的访问控制策略。通常,网络管理员可以通过设定服务器模型对该模型的关联参数进行配置,而标准用户一般不允许执行此项操作。  

    53763fefa5afc9a58a697d37c477dbfd.png 

    模型通信和性能 模型通过发送和接收消息进行相互通信。消息分为许多类型,这些类型被定义为每个模型规范的一部分,从而清楚展示模型可以产生、接收和理解的信息类型。 消息将状态值传递至其他设备,或是更改状态值,从而引发设备作出响应,并且这些响应通常是可见的。  软件开发者和蓝牙mesh模型  面向对象 对于软件开发人员,模型规格可以看作类似于面向对象(object-oriented)软件工程范例中的“类”,并将设备内部代码中的模型实现看作模型或对象实例。 蓝牙mesh技术规格并没有规定任何特定方式来实现代码中的模型,开发者可以根据正在使用的编程语言和应用程序接口(API)进行选择。但模型的确适用于面向对象的方式,并且蓝牙mesh技术规格中关于一个模型对另一个模型进行扩展的概念,也很容易让人联想到面向对象。  SDK变量 目前有许多用于开发mesh固件的软件开发人员工具包(SDK),其中有些来自专门为自身模块创建SDK的蓝牙模块供应商,而Zephyr RTOS SDK等其他SDK属于跨硬件平台的SDK,可以为许多不同的目标板创建固件。目前,Zephyr支持100种不同的目标板。 无论您使用的是哪种SDK,实现mesh固件所涉及的原则都是相同的。本文将从开发者的角度,并以使用Zephyr SDK创建的代码为例进行说明。

    models – an array of specific model definitionsstatic struct bt_mesh_model sig_models[] = {BT_MESH_MODEL_CFG_SRV&(cfg_srv),BT_MESH_MODEL_CFG_CLI(&cfg_cli),BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, generic_ onoff_op,&generic_onoff_pub, NULL),BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, generic_level_op,&generic_level_pub, NULL)};// elements – contains arrays of SIG models and vendor models (none in this case)static struct bt_mesh_elem elements[] = {BT_MESH_ELEM(0, sig_models, BT_MESH_MODEL_NONE),};// node composition – contains an array of elementsstatic const struct bt_mesh_comp comp = {.elem = elements,.elem_count = ARRAY_SIZE(elements),};


     节点组合 mesh固件开发者必须完成的首要关键任务之一是定义产品的mesh节点组合,即在代码中定义每个节点的元素个数以及每个元素所包含的模型。以上的节点图显示了节点、节点中的元素、元素中的模型以及每个模型所包含的状态项之间的关系。 各SDK在细节上会有所不同,但是在使用Zephyr SDK节点组合时需要创建一系列数组,每个数组都包含SDK提供的宏定义的结构。这似乎看起来与上面的示例很像:四个模型都属于同一个元素,而该元素是节点的唯一元素。  属性 蓝牙mesh模型中的数据项有两种形式。 状态值隶属于特定模型,其值的含义由模型规格定义。它们不具有自我描述性,并且与消息相关的状态可以从消息的操作码中推断来。 而属性,则是需要在特定上下文中解释的特征实例。 特征也与通用属性配置文件( GATT)一起使用。特征定义了其值所包含的字段,比如允许值及其含义。在与GATT一起使用时,特征包含通用唯一标识符(UUID)形式的显式类型标识符 。当在GATT中使用时,特征隶属于服务,而特征所属的服务可提供解释和使用该特征的上下文。例如,警报级别特征可以隶属于链路损耗(Link Loss)服务或即时警报(Immediate Alert)服务。特征的含义的变化取决于它所属服务的不同,这一点在GATT服务标准中也有所定义。
    蓝牙mesh不使用GATT服务,而是由属性提供解释相关特征的上下文。

    “温度8特征是一种表示温度测量的类型,它采用uint8格式,以0.5摄氏度为单位。蓝牙mesh模型已为该特征定义了若干属性,使其能够在各种上下文中被解释。当前室内环境温度属性表示温度8特征应被解释为在室内进行的测量,而当前室外环境温度属性则是在室外进行的测量。当前环境温度属性不取决于位置类型,而是将从其他位置属性派生。”

    属性通过属性ID明确标识。在使用属性的模型中,属性ID和属性值包含状态值。例如,传感器数据状态包含一对或多对属性ID和相应的传感器值。 属性使相同的模型能够与各种数据类型一起使用。这对于像传感器服务器模型这样的模型而言是非常有利的, 鉴于任何类型的传感器数据都可以在定义了合适属性的情况下,在任何上下文中处理和解释而如果没有这种数据描述和封装方法,就会需要许多不同类型的传感器模型,或者传感器服务器模型因可能需要具有大量状态,来支持的每一类传感器数据。 客户端和服务器去耦(Decoupling) 在实现模型时,客户端模型和服务器模型务必对彼此的实现细节一无所知。例如,服务器无须知道或选择利用客户端可能发送的特定值的知识。双方对于彼此都相当于一个“黑匣子”。 编码模型 除了规定节点组合中每个元素所包含的模型之外,开发者还需要做些什么才能加入为其产品选择的模型?有时,开发者什么都不需要做。而一些模型是强制性存在的,比如医疗服务器模型等,并且SDK可以为此类模型提供一个完整实现,使开发者能够轻松地将它加入到节点组合中。 在大多数其他情况中,还需要执行多个额外步骤:

    #define BT_MESH_MODEL_OP_GENERIC_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
    #define BT_MESH_MODEL_OP_GENERIC_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
    #define BT_MESH_MODEL_OP_GENERIC_ONOFF_SET_UNACK BT_MESH_MODEL_OP_ 2(0x82, 0x03)
    #define BT_MESH_MODEL_OP_GENERIC_ONOFF_STATUS BT_MESH_MODEL_OP_ 2(0x82, 0x04)
    // each array member contains opcode, min msg len, handler function
    static const struct bt_mesh_model_op generic_onoff_op[] = {
    {BT_MESH_MODEL_OP_GENERIC_ONOFF_GET, 0, generic_onoff_get},
    {BT_MESH_MODEL_OP_GENERIC_ONOFF_SET, 2, generic_onoff_set},
    {BT_MESH_MODEL_OP_GENERIC_ONOFF_SET_UNACK, 2,
    generic_onoff_set_unack},
    BT_MESH_MODEL_OP_END,
    };



    01 RX消息处理函数 所有与每个模型相关联且节点可能接收的(RX)消息操作码都需要进行寄存,并实现用于处理此类消息的一个或多个函数。上面的Zephyr代码便是如此。 模型接收的消息或是会更改状态值,(设定)或是通过请求在状态消息中报告特定状态的当前值(获取)。设定消息分为两种形式:无需响应(未确认)的消息和需要在状态消息中回复新状态值的消息。“设定”这一术语集有时被用于指代这两个变量中的任何一个。 在处理设定消息产生的状态更改时,开发者必须处理任何已定义的和活动的状态绑定,并根据需要重新计算其他随之变化的状态值。

    02 TX消息生成器函数 模型几乎都需要发送(TX)和接收消息。因此需要编写一些函数来编制mesh消息和使用相应的API发送消息,并且可以通过合适的事件或设备交互(例如用户按下按钮或转动旋钮)来触发函数的执行。开发者应主要关注消息的访问层部分,而非与堆栈较低层相关的字段,不过也可能有例外情况。为了避免设备被视为可疑重放攻击而遭到拒绝,需要显著增加SEQ 字段,否则软件框架也会自动执行这一操作。

    03 将应用程序密钥绑定到模型 为增加网络模式分析攻击的难度,所有mesh消息都使用AES-CCM进行加密和认证,同时标头字段也经过了混淆处理。堆栈上层的字段使用应用程序密钥加密,而堆栈下层的字段使用网络密钥加密,从而使网络与应用程序安全性分离,并允许节点执行网络功能,例如在无需或不具备消息应用负载解密能力的情况下收发消息。 一个好的mesh软件框架能够在设置设备时使用网络和应用程序密钥进行加密和混淆,自动保护消息的安全。但一个节点可能有多个应用程序密钥,并且每个应用程序密钥必须通过被称为“密钥绑定”的过程与特定模型相关联。这可以确保堆栈知道要使用哪种应用程序密钥以及哪种类型的消息。开发者必定需要在他们的代码中执行显式应用程序密钥绑定。在Zephyr上,应用程序密钥绑定如下:

    /* Bind to generic level server model */err = bt_mesh_cfg_mod_app_bind(net_idx,addr,addr,app_idx,BT_MESH_MODEL_ID_GEN_LEVEL_SRV,NULL);


    net_idx和app_idx属于索引值,它们引用一个或多个网络和应用程序密钥列表中的特定密钥,这些密钥是一个节点在最初设置和配置时就已配备的密钥。 应用程序密钥绑定是蓝牙mesh网络访问控制的基础。通过向网络管理员发放绑定到传感器设定服务器模型的应用程序密钥,使该用户能够更新相应模型状态并配置相关的传感器服务器模型。其他未获得此应用程序密钥的用户无法配置传感器设定服务器。  智能家居中的蓝牙mesh模型 自2019年1月起,蓝牙智能家居专项组就致力于创造能够集成到日常智能家居设备中的新蓝牙mesh模型。尽管这些模型是为智能家居所开发的,但它们也可以无缝应用于商业和工业智能建筑环境中。   您可通过以下视频了解蓝牙智能家居专项组如何实现智能化、一体化的智能家居体验。  蓝牙mesh模型完整介绍 您可在”硬禾学堂“后台回复 “蓝牙” 获取《蓝牙mesh模型——技术概览》全文来全面了解蓝牙mesh模型。这篇文章概述了泛型如何支持许多设备类型所具有的基本功能、如何通过照明模型满足商业照明需求以及如何使用传感器为其他设备提供可触发自动响应的环境数据。
     

    更多相关内容
  • 蓝牙Mesh知多少?

    2021-01-06 13:18:51
    什么是蓝牙Mesh? 概况的将,蓝牙Mesh是一种新的网络技术,能够与多个节点之间建立通信。注意:它并不是一种新的无线技术。 它与蓝牙的关系如何? 首先,低功耗蓝牙作为一种无线通讯技术,能够在超低功耗下实现数据...
  • 已经实现功能: 按过滤条件扫描设备、添加设备、修改mesh info, 加解密,开、关灯,调光,分组,状态显示等。
  • Android 蓝牙Mesh组网代码详解-附件资源
  • 本篇讲解蓝牙Mesh网络的通信原理以及在使用蓝牙Mesh开展设计时应掌握的各种重要概念。节点间通信蓝牙Mesh使用海量消息并发传输模式在节点间传输消息。海量并发模式是一种多路径消息传递实现方案,有足够冗余来确保...
  • 蓝牙Mesh

    千次阅读 2021-10-14 18:30:36
    1,蓝牙mesh介绍 蓝牙Mesh网络模型: 蓝牙Mesh提高灵活度: 代理节点(Proxy) 低功耗节点(Low-Power) 转发节点(Relay) 朋友节点(Friend) 2,蓝牙Mesh基本概念 a,蓝牙Mesh网络层次 分包组包: b,蓝牙Mesh...

    1,蓝牙mesh介绍
    在这里插入图片描述
    蓝牙Mesh网络模型:
    在这里插入图片描述
    蓝牙Mesh提高灵活度:

    • 代理节点(Proxy)
    • 低功耗节点(Low-Power)
    • 转发节点(Relay)
    • 朋友节点(Friend)
      在这里插入图片描述
      2,蓝牙Mesh基本概念
      a,蓝牙Mesh网络层次
      在这里插入图片描述
      分包组包:
      在这里插入图片描述
      蓝牙Mesh分层协议在light_ctl.c中的实现(具体分析代码opcode)
      b,蓝牙Mesh洪泛管理
      消息缓存队列
      消息寿命在这里插入图片描述
      c,蓝牙Mesh四大节点
      • 代理节点
      • 转发节点
      • 朋友节点
      • 低功耗节点
        在蓝牙Mesh中朋友节点和低功耗节点必须成对存在
        在这里插入图片描述
        d,蓝牙Mesh节点
        在这里插入图片描述
        蓝牙Mesh元素(插座上每个插孔都是一个元素)
        在这里插入图片描述
        在这里插入图片描述
        e,网络地址
    地址备注
    0x0000未分配地址
    0x0001-0x7FFF单播地址
    0x8000-0xBFFF虚拟地址
    0xC000-0xFFF组播地址

    3,蓝牙Mesh环境配置
    开发环境:vscode
    vscode下安装alios-studio插件
    SDK下载地址:https://github.com/alibaba/genie-bt-mesh-stack
    a,vscode 的配置
    打开vscode,安装插件(alios-studio)—>安装alios-cube 编译工具—>打开一个文件夹如下—>保存工作区,方便下次直接打开工程使用---->进行配置,在工程目录下创建一个.vscode的文件夹用来存放配置文件:
    在这里插入图片描述
    在vscode下创建文件(settings.json),将settings.json 的源码自行添加:
    在这里插入图片描述

    {
    "search.exclude": { //表示搜索区域
    "**/platform/mcu/[a-s]*" : true, //[a-s]* 表示a到s字符开头的全部屏蔽,数值也
    一样
    "**/board/bk3435devkit" : true,
    "**/board/pca10040" : true,
    "**/board/ch6121evb" : true,
    "**/board/tg7100b" : true,
    },
    "files.exclude":{ //表示显示区域
    "**/.git" : true, //true标识改目录不被显示
    "**/.g**" : true, //*号标识通配符,.g开头的均屏蔽,根据用途自行配置
    "**/platform/mcu/[a-s]*" : true, //[a-s]* 表示a到s字符开头的全部屏蔽,数值也
    一样
    "**/board/bk3435devkit" : true,
    "**/board/pca10040" : true,
    "**/board/ch6121evb" : true,
    "**/board/tg7100b" : true,
    }
    }
    
    	vscode配置编译任务(首先确定已经安装好了aos-cube),编辑任务创建,task,json编辑
    

    在这里插入图片描述

    {
    "version": "2.0.0",
    "tasks": [
    {
    "label": "tc825x", //标签,会在生成代码中显示出来此目标名
    "type": "shell", //类型为shell
    "command": "aos", //此处配置命令,本初使用的alios,所以指定了aos,如果你是
    直接make就指定make即可
    "args": [
    //此处为命令跟的参数,也就是aos make bluetooth.light_ctl@tc825x
    "make",
    "bluetooth.light_ctl@tc825x",
    ],
    "group": "build", //归属于build组下,编译目标类别内
    "presentation": {
    // 此处为出现错误时才会进行输出
    "reveal": "silent"
    },
    // 此处为使用标准的MS编译器输出错误和警告等信息
    "problemMatcher": "$msCompile"
    },
    //使用时如果没有第二个目标,可将下面的内容删除,注意格式即可
    {
    "label": "可以指定第二个标签",
    "type": "shell",
    "command": "你的命令",
    "args": [
    "你的参数",
    ],
    "group": "build",
    "presentation": {
    "reveal": "silent"
    },
    "problemMatcher": "$msCompile"
    },
    ]
    }
    

    通过以上配置,进行任务查看:
    在这里插入图片描述
    b,SDK目录分析
    下载器(烧写)工具----http://wiki.telink-semi.cn/wiki/IDE-and-Tools/Burning-and-Debugging-Tools-for-all-Series/
    开发板和烧录器的连接方式:烧录器的vpp,sws,GND分别连到开发板的VCC,PA7,GND上。
    烧录工具下,选择8258,点击SWS查看是否有烧录设备,检查到设备后,进行Erase擦除,。再使用一条USB线将USB连接到开发板,点击download,下载完成后复位一下开发板。打开串口,配置串口波特率为921600,查看打印情况。

    c,实现呼吸灯
    文档手册:https://help.aliyun.com/product/123206.html?spm=a2c4g.11186623.6.540.44cd25c0lMSwXs
    硬件IO:https://help.aliyun.com/document_detail/268746.html
    helloworld 修改点灯代码:
    在这里插入图片描述
    呼吸灯程序:
    在这里插入图片描述
    4,天猫精灵IOT
    a,天猫精灵开放平台介绍
    https://www.aligenie.com/
    进入生活物联网平台
    b,创建一个产品
    c,调试中心
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    d,天猫精灵,蓝牙Mesh,云端三者之间的关系
    在这里插入图片描述
    5,蓝牙Mesh对接(Mesh SDK分析)
    light_ctl.c
    智能灯点亮代码:

    #define RED_LED  TC825x_GET_PIN_NUM(GPIO_PB4)
    #define GREEN_LED  TC825x_GET_PIN_NUM(GPIO_PB5)
    #define  BLUE_LED   TC825x_GET_PIN_NUM(GPIO_PC1)
    gpio_dev_t my_gpio;
    static  void  _led_init(void)
    {
        my_gpio.config = OUTPUT_PUSH_PULL;
        my_gpio.port = BLUE_LED;
        hal_gpio_init(&my_gpio);
    
    }
    
    static void _led_set(u8_t onoff)
    {
        LIGHT_DBG("%d", onoff);
        if(onoff)
        {
            hal_gpio_output_high(&my_gpio);
            return ;
        }
        hal_gpio_output_low(&my_gpio);
    }
    
    static void _led_ctrl(elem_state_t *p_elem)
    {
        // static uint8_t last_onoff = 0;
        // static uint16_t last_acual = 0;
        // static uint16_t last_temperature = 0;
        
        uint8_t onoff = p_elem->state.onoff[T_CUR];
        // uint16_t actual = p_elem->state.actual[T_CUR];
        // uint16_t temperature = p_elem->state.temp[T_CUR];
    
        
            //LIGHT_DBG("%d,%d,%d", onoff, actual, temperature);
            _led_set(onoff);
        }
    }
    
    

    语音控制调整灯亮度的代码:

    #define RED_LED  TC825x_GET_PIN_NUM(GPIO_PB4)
    #define GREEN_LED  TC825x_GET_PIN_NUM(GPIO_PB5)
    #define  BLUE_LED   TC825x_GET_PIN_NUM(GPIO_PC1)
    gpio_dev_t my_gpio;
    pwm_dev_t  mYpwm;
    
    static void _led_init(void)
    {
    	mYpwm.config.duty_cycle = 0;
    	mYpwm.config.freq = 20000;
    	mYpwm.port = BLUE_LED;
    	hal_pwm_init(&mYpwm);
    	hal_pwm_start(&mYpwm);
    }
    
    static void _led_set(u8_t onoff,u16_t atual)
    {
    	LIGHT_DBG("%d,%d", onoff,actual);
    	pwm_config_t mypara;
    	if(onoff)
    	{
    		mypara.duty_cyle = actual;
    		mypara.freq = 20000;
    		hal_pwm_para_chg(&mYpwm,mypara);
    		return ;
    	}
    	mypara.duty_cycle = 0;
    	mypara.freq = 0;
    	hal_pwm_para_chg(&mYpwm,mypara);
    }
    static void _led_ctrl(elem_state_t *p_elem)
    {
    	uint8_t onoff = p_elem->state.onoff[T_CUR];
       	uint16_t actual = p_elem->state.actual[T_CUR];
       // uint16_t temperature = p_elem->state.temp[T_CUR];
    
       
           //LIGHT_DBG("%d,%d,%d", onoff, actual, temperature);
           _led_set(onoff,actual);
         }
    

    蓝牙Mesh配网流程

    https://help.aliyun.com/document_detail/173310.html

    ​ 配网相信所有同学以及用户都不会陌生,我们在使用Wi-Fi会配网,使用蓝牙会配网,以及其他的无线设备都会存在一个匹配的过程,本章内容着重阐述为什么要配网以及配网的过程中解决了哪些问题,通过蓝牙Mesh入网过程,同时可以理解几乎所有无线网络的配网流程以及它背后的含义。

    一、入网的目的

    ​ 通过官方定义(MshPRF第五章),我们可以看出,入网的过程就是把一个未配网的设备加入到Mesh网络中的过程,配网器会给未配网的设备提供一些数据让它变成Mesh中的一个节点,其中包括network key、当前的IV index还有每个元素的单播地址(对此三个参数请参考Mesh协议章节中)。

    二、入网过程

    ①邀请

    ​ 邀请可以对应我们在手机配对蓝牙耳机或者蓝牙鼠标的模式,我们通过扫描发现了可入网的设备,点击配对就是对蓝牙设备进行了入网的邀请,对于所有配网设备来说无疑必须要有此操作,不然我们的设备到底要加入哪一个网络就不得而知了,邀请它进入到我们的对话空间,对方同意之后在进行一步的操作。

    ②加密成安全通道

    ​ 一般的无线通信技术都会使用ECDH椭圆曲线非对称加密来创建一个加密通道进行通信。

    ③认证

    在保证无线通信安全性上面必须要想到这一点,让对方裸漏在你面前交换信息,这样才能确保真正的保证通信安全。

    ④分发密钥

    ​ 分发密钥阶段为入网的最后一个阶段,可以从上文看出,我们已经建立的最安全的通信方式,此时配网器需要给设备分发一些数据,对应蓝牙Mesh中就是分发了Network key、当前的IV index还有每个元素的单播地址。

    三、蓝牙Mesh配网过程

    ​ 蓝牙Mesh入网流程相比上面所说的四个步骤增加了一个交换公钥的阶段,出于安全考虑不同的无线通信设备都会对自己的具体实现做出一些修改,宏观上依然是上面讲述的四步。蓝牙Mesh入网五个阶段,分别是信标阶段、邀请阶段、交换公钥交换、身份验证阶段和分发密钥数据阶段,在每一步的讲解之前,需要先了解几个专属名词:

    1、配网角色

    ①未配网设备(Unprovisioned Device):还没有加入到任何Mesh网络中的设备。

    ②配网器(Provisioneder):用来给未配网设备配网配网的,比如:天猫精灵

    ③Mesh节点(Mesh Node):已经加入到蓝牙Mesh网络的每一个节点。

    ​ 实际上我们的配网过程,就是将未配网设备变成一个Mesh节点,所以每个设备都有可能是以上三种角色的任意角色,对于配网器来说,在已经入网的节点都有可能变成配网器的身份。

    2、蓝牙Mesh配网协议

    ​ 配网协议与此前讲述的蓝牙Mesh通信协议有共同之处,很显然对于空中数据包的接收必然是一样的,只不过在识别为是Beacons数据包的时候走了不同的分支处理,就和我们作分类是一个道理,在一条传送带上放着iPhone和iPad,通过什么方式将两个产品分开呢?最直观的方式通过大小的区分,在蓝牙空中数据包中,我们需要通过类型(Type)来区分不同的数据种类进行不同的处理。

    ​ 协议图:
    在这里插入图片描述
    ​ 上图可见,对于配网协议的分层只有三层,分别为Provisioning Bearer Layer层、Provisioning Transport Layer层和Provisioning Protocol。与此前Mesh通信协议对比,Bearer层上接收到的数据是相同的,只是通过判断了类型进行了分支语句的处理。

    //例:
    #define AD_TYPE_PB_ADV  0x29
    #define AD_TYPE_MESSAGE 0x2A
    #define AD_TYPE_BEACON  0x2B
    int main()
    {
    	int user_data = AD_TYPE_PB_ADV;
        switch(user_data)
        {
            case AD_TYPE_PB_ADV:
                //入网协议处理
                
                break;
            case AD_TYPE_MESSAGE:
                //消息通信协议处理
                
                break;
            case AD_TYPE_BEACON:
                //准备邀请处理
                
                break;
        }
    }
    

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

    3、配网步骤(ADV模式)

    ①信标阶段(Beacons)

    ​ 信标(Beacons)阶段主要是未配网设备在广播自己要加入Mesh网络中,在此之前在蓝牙Mesh协议规范中我们有接触到蓝牙Mesh广播数据类型,AD Type(Mesh协议规范)。
    在这里插入图片描述
    ​ 当未入网设备在等待配网器发送邀请的期间,会间隔的在空中发送AD Type为0x2B的信标包,AD Data里面则会带有其他信息,如天猫精灵的三元组信息,或者是其他公司的公司ID等等内容,对于配网器来说,扫描到空中有蓝牙数据包即进行识别,随后进行识别AD Type类型,如果是Mesh Beacon,且数据格式与此产品相符则发送邀请,如:天猫精灵找队友,发现为配网设备的Beacon包,并识别其中的三元组信息格式符合天猫精灵蓝牙Mesh规范则进行下一步操作。

    ​ 对应Beacon包格式如下:

    Parameters字段长度(字节)备注
    Length1Beacon包长度
    Type1Beacon包类型,值为0x2B
    Beacon类型1未配网Beacon类型,值为0
    UUID16设备唯一ID
    OOB信息2OOB信息类型,如:NFC、二维码,此处对应天猫精灵三元组
    URL Hash4URL的Hash值,可以用来帮助识别设备

    ​ 示意图:
    在这里插入图片描述
    ​ 信标阶段发现了设备,同时也要确定该设备是否可被连接,则进行上图的Link Open,数据包中包含UUID。
    在这里插入图片描述

    Parameters字段长度(字节)备注
    Link Open1Link open=0 GPCF = 3
    UUID16设备UUID

    ​ 当配网器发送Link Open给未配网设备时,未配网设备接收到Link Open消息,判断是否为自己的UUID,如果是则返回Link ACK表示应答,代表可以进行通信,准备进入下一阶段,到此信标阶段就结束了。
    在这里插入图片描述

    ②邀请阶段

    ​ 配网器和未配网设备已经建立成功的连接,二者即可以通过PB-ADV或者PB-GATT承载上建立连接进行邀请阶段的数据交互,邀请阶段要完成配网器对未配网设备的设备能力信息,比如是否有屏幕、是否有键盘,这将影响到后面的认证阶段。

    ​ 流程图:
    在这里插入图片描述
    在这里插入图片描述
    ​ 在此阶段,配网器会下发上图数据,给为配网设备,其含义是定时,开发者可以设定未配网设备要在多长时间之内进行回复,Attention Timer state的值为0则代表关闭此功能,非零代表剩余时间,单位为秒。

    ​ 接下来未配网设备拿到配网器的询问之后(询问配网能力)需要在指定时间内进行反馈,反馈内容如下(MshPRF-5.4.1.2):

    Parameters字段长度(字节)说明
    Number of Elements1未配网设备中的元素个数,涉及到下发地址
    Algorithm2支持的算法和其他功能,目前只有椭圆算法(Elliptic Curve)
    Public Key Type1设备公钥类型,是否支持OOB
    Static OOB Type1是否支持静态OOB,(out of band 带外数据)
    Output OOB Size1输出式OOB最大长度
    Output OOB Action2输出式OOB动作类型,如:LED闪烁、震动、输出数字、输出字母
    Input OOB Size1输入式OOB最大长度
    Input OOB Action2输入式OOB动作类型,如:按键、旋钮、输入数字、输入字母和数字

    ③交换公钥阶段

    ​ 配网器与未配网设备配网时需要下发地址、网络密钥,这些信息与整个蓝牙Mesh网络安全息息相关,所以蓝牙Mesh规范要求配网器与未配网设备在配网时需要约定加密的方式,确保加密有效需要双方交换公钥在对数据进行解密,如果拿到的值是一致的则认为双方加密有效。对于蓝牙Mesh来说,在同一网络中所有的Mesh节点都学习了本网络中的加密方式,对于外界获取到该数据包时则不会理解具体含义,具体数据加密过程在蓝牙Mesh协议分层的Network Layer层中详细阐述,配网过程的加密可以看作正在学习该加密方式并且对其数据解密验证。

    ​ 流程图:
    在这里插入图片描述
    ​ 在此阶段,配网器会像未配网设备发送Provisioning Start和Provisioning Public Key,看下具体数据内容:

    Provisioning Start:

    Parameters字段长度(字节)说明
    Algorithm1算法选择,目前仅支持ECDH
    Public Key10x00代表不使用OOB公钥,0x01使用OOB公钥
    Authentication Method1身份认证类型,如设备能力中阐述
    Authentication Action1验证动作,选择OOB输入或者输出等
    Authentication Size1验证长度

    ​ 如果我未配网设备存在OOB公钥则不需要反馈给配网器Public Key,如果未配网设备本身不存在OOB公钥,则需要生成Public Key发送给配网器,Public Key是由Privacy key生成的,每个设备都有自己的Privacy key。互相交换了密钥即相当于建立了安全通道。

    ④身份认证

    ​ 在了解完第三步的时候已经建立了安全通道,二者在数据交互时采用了加密的形式,为什么还要有身份认证呢?如果能回答这个问题对于此步骤的内容也就非常清晰了。

    ​ 身份认证形式:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    Provisioning Confirmation:

    Parameters字段长度(字节)说明
    Confirmation16OOB信息

    Provisioning Random:

    Parameters字段长度(字节)说明
    Random16此随机值为之前双方所交互的所有值和OOB随机数形成的加密散列值

    ⑤分发配网数据

    ​ 经历了前面的坎坷,终于可以变身成为蓝牙Mesh的节点。
    在这里插入图片描述

    ​ Provisioning Data:

    Parameters字段长度(字节)说明
    Encrypted Provisioning Data25包括Network Key、网络密钥索引、IV index、主元素地址等
    Provisioning Data MIC8PDU 完整性检查值

    Provisioning Complete:

    Parameters字段长度(字节)说明
    无参PDU,代表配网成功。

    四、总结:

    总结得:蓝牙Mesh过程分为五个阶段:信标阶段,邀请阶段,交换公钥阶段,身份验证阶段,分发密钥数据阶段。
    在这里插入图片描述
    在这里插入图片描述

    蓝牙Mesh模型

    蓝牙Mesh模型是Mesh模型架构中基础业务单元,每个模型其实就是对应一个功能,(也有可能对个模型组合对应一个特定功能),每个元素(Element)可以添加多个不同的模型,相同的模型也可以存在一个设备的不同元素中,例如:一盏RGB的氛围灯,其中红,黄,蓝每个颜色的LED为一个元素,一共三个元素,每个分别可以包含通用开关模型,通用上下等级模型等。
    蓝牙Mesh模型又分为三种不同的类型,分别是基础模型(Foundation Model),通用模型(Gemeric Model),以及厂商模型(Vendor Model)。

    1,基础模型

    基础模型定义了用于配置和管理蓝牙Mesh网络的消息,分为配置模型(configuration model)和健康模型(health mesh)两部分。其中配置模型存在于设备的主元素上,其他的元素不需要添加该模型;健康模型必须存在于设备的主元素上,其他元素可以根据需求决定是否添加该模型。

    2,通用模型

    通用模型定义了一系列用于非特定功能的标准设备模型。例如:许多设备都具有开关功能,如风扇,电灯,插座等。这些设备都可以通过支持通用模型的开关模型来实现开关功能,而不用为每种类型的设备分别提供一个开关模型等,这样也方便了不同厂商设备之间进行相互通信,控制。
    通用模型分为通用服务模型和通用终端模型,要在设备上配置的通常为通用服务模型,通用服务模型一共有14种,如下表:

    中文英文
    通用开关服务模型Generic OnOff Server Model
    通用等级服务模型Generic Level Server Model
    通用默认渐变时间服务模型Generic Default Transition Time Server Model
    通用上下电开关服务模型Generic Power OnOff Server Model
    通用上下电开关设置服务模型Generic Power OnOff Setup Server Model
    通用上下电等级服务模型Generic Power Level Server Model
    通用上下电等级设置服务模型Generic Power Level Setup Server Model
    通用电池服务模型Generic Battery Server Model
    通用位置信息服务模型Generic Location Server Model
    通用位置信息设置服务模型Generic Location Setup Server Model
    通用用户自定义属性服务模型Generic User Property Server Model
    通用管理员属性服务模型Generic Admin Proerty Server Model
    通用制造商属性服务模型Generic Manufacturer Proerty Server Model
    通用终端属性服务模型Generic Client Proerty Server Model

    厂商模型
    在物联网生活平台的物模型(灯)下添加厂商属性功能------灯的颜色
    在Mesh源码中的light_ctl.c程序中加入厂商模型属性代码,如下:
    在这里插入图片描述
    编译成功后,将固件文件下载到开发板中,使用生活物联网平台下的调试界面,通过下发命令(将灯调为xx颜色),通过串口来看打印信息。进一步判定代码的正确性。

    ①通用服务模型字段说明

    ​ TID:用于应用层判断消息ID是否重复,该字段数值由应用层定义。

    ​ 渐变时间:表示设备从一个状态变换到另一个状态所需要的时间,该字段在通用服务模型中长度为1字节,该字节的低6位表示渐变时间的具体数值(该6位的取值范围是0x00~0x3E);该字节的高2位表示渐变时间的单位。

    ​ 该字节数据格式如下图:

    ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a9Opgkzm-1634893429848)(蓝牙Mesh模型.assets/image-20200529104401728.png)]

    ​ 该字节对应的渐变时间单位为:

    渐变字段高2位渐变时间单位渐变字段高2位渐变时间单位
    0b00100ms0b1010s
    0b011s0b1110min

    ​ 例如:该字段为0x41,对应低6位为0x01,高2位为0x01,解析得到的时间为1×1s = 1s。

    ​ 延迟时间:表示设备从收到消息到消息开始执行变化的这段时间,该字段对应的实际延迟时间为该字段数值×单位步长5ms。例如:该字段为0x0A,解析得到的时间为10×5ms = 50ms。

    ②通用开关服务模型

    ​ 通用开关模型是一个根模型(Root Model),该模型支持广播和订阅。

    ​ 通用开关模型定义了一种通用的开关控制方式,不同的设备可以使用同一个消息格式控制开关,实现了多个设备/模型订阅同一地址时,可以通过向该地址发送开关控制执行来控制开关。

    获取开关状态

    ​ 获取开关状态(Generic OnOff Get)消息用于获取当前元素的开始状态。当设备收到获取开关状态消息时,需要返回的开关状态消息。获取开关状态消息格式如下:

    字段字段长度(字节)备注
    Opcode(操作码)20x82 0x01(Get)

    设置开关状态

    ​ 设置开关状态(Generic OnOff Set)消息用于设置当前元素的开关状态。当设备收到设置开关状态消息时,需要回复开关状态消息。设置开关消息格式如下:

    字段字段长度(字节)备注
    Opcode(操作码)20x82 0x02
    目标开关状态1需要设置的目标开关状态
    TID1TID
    渐变时间1渐变时间(可选字段)
    延迟时间1延迟时间(可选字段)

    ​ 渐变时间和延迟时间必须一起设置,如果渐变时间存在,则延迟时间必须存在;反之亦然。

    设置开关状态(无确认)

    ​ 设置开关状态(无确认)(Generic OnOff Set Unacknowledged)消息用于设置当前元素的开关状态。当设备接收设置开关状态(无确认)消息时,不需要回复任何消息,设置开关状态(无确认)消息格式:

    字段字段长度(字节)备注
    Opcode20x82 0x03
    目标开关状态1需要设置的开关状态
    TID1TID
    渐变时间1渐变时间(可选字段)
    延迟时间1延迟时间(可选字段)

    开关状态

    ​ 开关状态(Generic OnOff Status)消息用于上报设备的开关状态。消息格式如下:

    字段字段长度(字节)备注
    Opcode20x82 0x04
    当前开关状态1设备当前开关状态
    目标开关状态1设备目标开关状态
    剩余时间1设备切换到目标状态剩余时间

    ​ 目标开关状态和剩余时间必须一起返回,如果目标开关状态存在,则剩余时间必须存在,反之以然。

    ③ Lighting Server 模型

    ​ 接下来一起分析一个官方提供好的智能灯得一个服务模型。

    ​ 首先应该设想好4种消息的特点:

    类型特点
    GET用于获取该属性的状态、接收方需要发送该属性Status消息作为回应、此消息不包含其他参数
    SET用于设置该属性的状态、接收方需要发送该属性的Status作为消息回应
    Set Unacknowledged用于设置该属性的状态、接收方不需要回应
    Status用于报告该属性的状态(包含但不限于当前状态、目标状态、剩余转换时间等)、接收方不用回应

    ​ 元素、模型和状态三者之间个关系:
    在这里插入图片描述
    ​ Lighting Server模型定义了一组照明控制功能。此模型出包含了可调亮度及变色灯外,还包括一个允许由传感器触发特定行为的灯光控制模型,例如,根据人的位置开启灯光、根据环境光线调整灯光亮度,或在一段时间没任何东京的情况下调暗灯光并最终关闭。

    ​ 接下来将针对Light Lightness 及Light CTL这两个符合状态模型及其状态进行详细描述。

    ​ 在蓝牙Mesh网络中,Provision一般时提供属性给Provisioner进行操作的一方,属于Server角色,其中包含的模型即为Server模型。Lighting Server模型是由LightLightness Server、Light Lightness Setup Server、Light CTL Server、Light CTL Setup Server、Light CTL Temperature Server、Light HSL Server、Light HSL Hue Server、Light HSL Saturation Server、Light HSL Setup Server、Light xyL Server和Light xyL Setup Server这11种模型组成。其中前四种应用最为广泛,本次也主要与前4种模型相关的状态、消息及各种操作消息对应的行为。

    ​ 不同的模型之间存在继承和共生关系。以Light Lightness Server模型为例,它扩展了Generic OnOff Server 和Generic Level Server两种模型,这种扩展关系即集成关系;如果一个元素中包含Light Lightness Server模型,则必须包含Light Lightness Set Server模型,这种关系叫做共生关系。
    在这里插入图片描述

    ​ 上图所示:Light Lighteness Server Model、Light Lighteness Setup Server Model、Light CTL Server Model、Light CTL Setup Server Model模型与其他模型之间的关系。

    ​ 其中Light Lighteness Server Model模型和Light CTL Server Model模型同事支持注册和发布操作;而Light Lighteness Setup Server Model和Light CTL Setup Server Model模型仅支持注册操作,这些模型所包含的状态如下:

    Lightness 状态

    ​ Lightness状态时Lighting模型种应用最为广泛的一组符合状态。因为光源的类型及属性不同,所以在描述Lightness状态时会有所不同的表达方式。最简单的灯只有开和关两种状态,通过Generic OnOff描述和控制其他开关状态。高级一点的灯可以改变亮度,通过Light Lightness Actual描述和控制其亮度状态。

    ​ 除此之外,还有变色灯,可以通过Light CTL 和DUV(Delta UV)来描述和调节其色温。对于变色灯,可以通过调整亮度、色调、饱和度三个维度的值对其颜色进行调整,三个维度都可以被独立控制。

    Light Lightness 状态

    ​ Light Lightness状态是一个由Light Lightness Actual、Light Lightness Linear、LightLightness Last和Light Lightness Default组合成的符合状态。这4种状态都分别与其他一种或者多种状态存在绑定关系。可以理解为联动关系。

    Light Lightness Actual 状态

    取值描述
    0x0000元素处于未发光状态
    0x0001 ~ 0xFFFF元素发出的被实际感知的光强度
    0xFFFF元素所能发射的能被感知的最强光的强度

    ​ 与Light Lightness Actual状态操作对应的是Light Lightness消息。Light Lightness状态属于可设置状态,所以Light Lightness消息由Light Lightness Set、Light Lightness Get、Light Lightness Set Unacknowledged、和Light Lightness Status这4种消息组成。

    ​ Light Lightness Set、Light Lightness Set Unacknowledged具有相同结构:

    字段大小(字节)说明
    光亮度值2Light Lighness Actual状态的目标值
    TID1传输标识,可以用此字段判定当前收到的消息是一条新消息还是重传消息
    状态切换时间1可选
    状态切换延迟1开始状态切换时间必须添加此项

    ​ Light Lightness Server在收到这两种消息后,应该将Light Lighness Actual状态设置为Lightness目标值。如果6s内收到SRC、DST、TID栏位都相同,的多条消息,可以忽略掉第1条消息之后的其他消息。

    ​ 如果消息种包含“状态转换时间”字段,则元素状态切换应该符合该字段定义的行为,如果不包含“状态转换时间”字段,但元素支持“Generic Default Transition”状态的情况下,元素状态切换应该符合“Generic Default Transition”状态的行为定义,否则状态将立即执行。

    ​ 如果当前状态和目标状态是一致的话,应该认为状态切换过程已经完成。

    ​ 在收到Light Lightness Set消息之后,Light Lightness Server应该回复Light Lightness Status消息给发送方。

    Light Lightness Status 状态

    字段大小(字节)说明
    当前亮度2Light lightness Actual 当前状态值
    目标亮度2Light lightness Actual 目标值(可选)如果元素处于状态切换过程中,则需要包含此项,代表元素的目标亮度值
    剩余状态 切换时间1需要与“目标状态”字段同时出现 代表元素完成状态切换过程所剩余的时间

    蓝牙Mesh初尝试项目

    场景与项目创建(智能灯)

    在生活物联网平台创建灯的物模型,状态包括亮度,颜色,开关。

    项目驱动编写

    在Mesh源码的base目录下的tri_tuple_default.h中添加设备三元组信息。
    在Mesh源代码app/example 下的bluetooth目录下创建my_led的目录,同时在该目录下创建两个文件(my_led.c 和my_led.mk)
    my_led.c程序如下:

    #include "common/log.h"
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <aos/aos.h>
    #include <aos/kernel.h>
    
    #include <misc/printk.h>
    #include <misc/byteorder.h>
    #include <hal/soc/gpio.h>
    #include <hal/soc/pwm.h>
    
    #include <bluetooth.h>
    #include <soc.h>
    #include <api/mesh.h>
    #include "genie_app.h"
    
    #include "drivers/8258/gpio_8258.h"
    #include "vendor/common/alios_app_config.h"
    
    //硬件代码开始
    #define RED_GPIO         TC825X_GET_PIN_NUM(GPIO_PB4)
    #define GREEN_GPIO       TC825X_GET_PIN_NUM(GPIO_PB5)
    #define BLUE_GPIO        TC825X_GET_PIN_NUM(GPIO_PC1)
    
    //声明三个led引脚的全局结构体
    gpio_dev_t red_gpio;
    gpio_dev_t green_gpio;
    gpio_dev_t blue_gpio;
    //创建全局pwm配置结构体
    pwm_dev_t red_pwm;
    pwm_dev_t green_pwm;
    pwm_dev_t blue_pwm;
    
    //配置初始化led引脚
    void my_led_init(void)
    {
        //配置红色led引脚
        red_gpio.config = OUTPUT_PUSH_PULL;
        red_gpio.port = RED_GPIO;
        //配置绿色led引脚
        green_gpio.config = OUTPUT_PUSH_PULL;
        green_gpio.port = GREEN_GPIO;
        //配置蓝色led引脚
        blue_gpio.config = OUTPUT_PUSH_PULL;
        blue_gpio.port = BLUE_GPIO;
    
        hal_gpio_init(&red_gpio);
        hal_gpio_init(&green_gpio);
        hal_gpio_init(&blue_gpio);
    }
    void led_gpio_on(void)
    {
        hal_pwm_stop(&red_pwm);
        hal_pwm_stop(&green_pwm);
        hal_pwm_stop(&blue_pwm);
    
        hal_gpio_output_high(&red_gpio);
        hal_gpio_output_high(&green_gpio);
        hal_gpio_output_high(&blue_gpio);
    }
    
    void led_gpio_off(void)
    {
        hal_pwm_stop(&red_pwm);
        hal_pwm_stop(&green_pwm);
        hal_pwm_stop(&blue_pwm);
        hal_gpio_output_low(&red_gpio);
        hal_gpio_output_low(&green_gpio);
        hal_gpio_output_low(&blue_gpio);
    }
    
    void my_pwm_led_init(void)
    {
        led_gpio_off();
        //配置pwm
        red_pwm.port = RED_GPIO;
        red_pwm.config.duty_cycle = 0;
        red_pwm.config.freq = 20000;
        
        green_pwm.port = GREEN_GPIO;
        green_pwm.config.duty_cycle = 0;
        green_pwm.config.freq = 20000;
    
        blue_pwm.port = BLUE_GPIO;
        blue_pwm.config.duty_cycle = 0;
        blue_pwm.config.freq = 20000;
       
       //初始化pwm
        hal_pwm_init(&red_pwm);
        hal_pwm_init(&green_pwm);
        hal_pwm_init(&blue_pwm);
    }
    
    //调节灯光亮度
    void set_led_channel_pwm(pwm_dev_t *pwm_num,float cycle)
    {
        pwm_config_t new_config;
        new_config.duty_cycle = cycle;
        new_config.freq = 20000;
        hal_pwm_para_chg(pwm_num,new_config);
        hal_pwm_start(pwm_num);
    }
    
    //调节颜色
    enum color{
        RED = 0,
        GREEN,
        BLUE,
        WHITE,
        YELLOW,
        VIOLET,
    };
    u8_t now_color = WHITE;
    void set_color_led(u8_t color,float level)
    {
        my_pwm_led_init();
        switch(color)
        {
            case RED:
                set_led_channel_pwm(&red_pwm,level);
                set_led_channel_pwm(&green_pwm,0);
                set_led_channel_pwm(&blue_pwm,0);
                break;
            case GREEN:
                set_led_channel_pwm(&red_pwm,0);
                set_led_channel_pwm(&green_pwm,level);
                set_led_channel_pwm(&blue_pwm,0);
                break;
            case BLUE:
                set_led_channel_pwm(&red_pwm,0);
                set_led_channel_pwm(&green_pwm,0);
                set_led_channel_pwm(&blue_pwm,level);
                break;
            case WHITE:
                set_led_channel_pwm(&red_pwm,level);
                set_led_channel_pwm(&green_pwm,level);
                set_led_channel_pwm(&blue_pwm,level);
                break;
            case YELLOW:
                set_led_channel_pwm(&red_pwm,level);
                set_led_channel_pwm(&green_pwm,level);
                set_led_channel_pwm(&blue_pwm,0);
                break;
            case VIOLET:
                set_led_channel_pwm(&red_pwm,level);
                set_led_channel_pwm(&green_pwm,level/2);
                set_led_channel_pwm(&blue_pwm,level);
                break;
        }
    }
    //硬件代码结束
    
    /* unprovision device beacon adv time */
    #define MESH_PBADV_TIME 600 //unit:s
    
    #define DEFAULT_MESH_GROUP1 0xC000
    #define DEFAULT_MESH_GROUP2 0xCFFF
    
    uint32_t get_mesh_pbadv_time(void)
    {
        return MESH_PBADV_TIME*1000;    //ms
    }
    
    /* 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];
    
    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]),
    };
    
    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;
    }
    /* element configuration end */
    
    void mesh_sub_init(u16_t *p_sub)
    {
        memset(p_sub, 0, CONFIG_BT_MESH_MODEL_GROUP_COUNT<<1);
    
        p_sub[0] = DEFAULT_MESH_GROUP1;
        p_sub[1] = DEFAULT_MESH_GROUP2;
    }
    
    
    #ifdef CONFIG_GENIE_OTA
    bool ota_check_reboot(void)
    {
        // the device will reboot when it is off
        if(g_elem_state[0].state.onoff[T_CUR] == 0) {
            // save light para, always off
            g_powerup[0].last_onoff = 0;
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
            BT_DBG("reboot!");
            return true;
        }
        BT_DBG("no reboot!");
        return false;
    }
    #endif
    
    static void _init_light_para(void)
    {
        uint8_t i = 0;
        E_GENIE_FLASH_ERRCODE ret;
        // init element state
        memset(g_elem_state, 0, sizeof(g_elem_state));
        elem_state_init(MESH_ELEM_STATE_COUNT, g_elem_state);
    
        // load light para
        ret = genie_flash_read_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        
        if(ret == GENIE_FLASH_SUCCESS) {
            while(i < MESH_ELEM_STATE_COUNT) {
    #ifdef CONFIG_GENIE_OTA
                // if the device reboot by ota, it must be off.
                if(g_powerup[0].last_onoff == 0) {
                    g_elem_state[0].state.onoff[T_TAR] = 0;
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                        g_elem_state[0].powerup.last_actual = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                        g_elem_state[0].powerup.last_temp = g_powerup[0].last_temp;
                    }
                    clear_trans_para(&g_elem_state[0]);
                } else
    #endif
                {
                    memcpy(&g_elem_state[0].powerup, &g_powerup[0], sizeof(model_powerup_t));
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                    }
                    if(g_elem_state[0].state.onoff[T_TAR] == 1) {
                        g_elem_state[0].state.trans_start_time = k_uptime_get() + g_elem_state[0].state.delay * 5;
                        g_elem_state[0].state.trans_end_time = g_elem_state[0].state.trans_start_time + get_transition_time(g_elem_state[0].state.trans);
                    }
                }
                g_elem_state[0].state.temp[T_CUR] = g_elem_state[0].state.temp[T_TAR];
    
                i++;
            }
        }
    
    }
    
    static void _reset_light_para(void)
    {
        uint8_t i = 0;
        while(i < MESH_ELEM_STATE_COUNT) {
            g_elem_state[i].state.onoff[T_CUR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_CUR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_CUR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.onoff[T_TAR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_TAR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_TAR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.trans = 0;
            g_elem_state[i].state.delay = 0;
            g_elem_state[i].state.trans_start_time = 0;
            g_elem_state[i].state.trans_end_time = 0;
    
            g_elem_state[i].powerup.last_actual = LIGHTNESS_DEFAULT;
            g_elem_state[i].powerup.last_temp = CTL_TEMP_DEFAULT;
    
            g_powerup[i].last_onoff = GEN_ONOFF_DEFAULT;
            g_powerup[i].last_actual = LIGHTNESS_DEFAULT;
            g_powerup[i].last_temp = CTL_TEMP_DEFAULT;
            i++;
        }
        genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
    
    }
    
    static void _save_light_state(elem_state_t *p_elem)
    {
        uint8_t *p_read = aos_malloc(sizeof(g_powerup));
    
        if(p_elem->state.actual[T_CUR] != 0) {
            p_elem->powerup.last_actual = p_elem->state.actual[T_CUR];
            g_powerup[p_elem->elem_index].last_actual = p_elem->state.actual[T_CUR];
        }
    
        p_elem->powerup.last_temp = p_elem->state.temp[T_CUR];
        g_powerup[p_elem->elem_index].last_temp = p_elem->state.temp[T_CUR];
        // always on
        g_powerup[p_elem->elem_index].last_onoff = 1;
    
        genie_flash_read_userdata(GFI_MESH_POWERUP, p_read, sizeof(g_powerup));
    
        if(memcmp(g_powerup, p_read, sizeof(g_powerup))) {
           
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        }
        aos_free(p_read);
    }
    
    static void _user_init(void)
    {
        my_led_init();
        led_gpio_off();
    #ifdef CONFIG_GENIE_OTA
        // check ota flag
        if(ais_get_ota_indicat()) {
            g_indication_flag |= INDICATION_FLAG_VERSION;
        }
    #endif
    }
    
    
    
    void set_led_recv(elem_state_t *user_state)
    {
        if(user_state->state.onoff[TYPE_NUM])
        {
            led_gpio_on();
        }else{
            led_gpio_off();
            set_color_led(0,0);
            return ;
            }
        printf("level = %d\r\n",user_state->state.actual[T_CUR]);
        set_color_led(now_color,(((float)user_state->state.actual[T_CUR]) / 0xffff));
    
    }
    
    void analize_color(uint8_t light,uint8_t hue,uint8_t saturation)
    {
        if(light == 0 && hue == 128 && saturation ==0)
        {
            set_color_led(RED,1);
            now_color = RED;
        }
        if(light == 0 && hue == 128 && saturation ==170)
        {
            set_color_led(BLUE,1);
            now_color = BLUE;
        }
    
    }
    
    void vnd_model_recv(vnd_model_msg *my_vnd_msg)
    {
        u8_t hue,light,saturation;
        u16_t type = 0;
        type |= my_vnd_msg->data[1];
        type <<=8;
        type |= my_vnd_msg->data[0];
        printf("opcode = %x\r\n",my_vnd_msg->opid);
        printf("Type = %x\r\n",type);
        printf("light  %d\r\n",my_vnd_msg->data[2]);
        printf("hue  %d\r\n",my_vnd_msg->data[3]);
        printf("Saturation  %d\r\n",my_vnd_msg[4]);
        
        switch(type)
        {
            case 0x0123:
                hue = my_vnd_msg->data[3];
                light = my_vnd_msg->data[2];
                saturation = my_vnd_msg->data[4];
                analize_color(light,hue,saturation);
                break;
    
        }
        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);
    }
    
    void user_event(E_GENIE_EVENT event, void *p_arg)
    {
        E_GENIE_EVENT next_event = event;
        switch(event) {
            case GENIE_EVT_SW_RESET:
            case GENIE_EVT_HW_RESET_START:
    
                break;
            case GENIE_EVT_HW_RESET_DONE:
                _reset_light_para();
    
                break;
            case GENIE_EVT_SDK_MESH_INIT:
                _init_light_para();
                _user_init();
                if (!genie_reset_get_flag()) {
                    next_event = GENIE_EVT_SDK_ANALYZE_MSG;
                }
                break;
            case GENIE_EVT_SDK_MESH_PROV_SUCCESS:
                
                break;
            case GENIE_EVT_SDK_TRANS_CYCLE:
            case GENIE_EVT_SDK_ACTION_DONE:
                {
                    elem_state_t *user_state = (elem_state_t *)p_arg;
                    set_led_recv(user_state);
                }
                break;
            case GENIE_EVT_SDK_INDICATE:
                break;
            case GENIE_EVT_SDK_VENDOR_MSG:
                printf("GENIE_EVT_SDK_VENDOR_MSG");
                {
                    vnd_model_msg *my_vnd_msg = (vnd_model_msg *)p_arg;
                    vnd_model_recv(my_vnd_msg);
                }
    
                break;
            default:
                break;
        }
        if(next_event != event) {
            genie_event(next_event, p_arg);
        }
    }
    
    int application_start(int argc, char **argv)
    {
    
        genie_init();
        return 0;
    }
    
    
    

    my_led.mk程序如下:

    NAME := my_led
    
    GENIE_MAKE_PATH := app/example/bluetooth/$(NAME)
    
    $(NAME)_COMPONENTS  += genie_app bluetooth.bt_host bluetooth.bt_mesh yloop cli
    
    $(NAME)_INCLUDES += ../ \
    					../../../../genie_app \
    					../../../../genie_app/base \
    					../../../../genie_app/bluetooth/host \
    					../../../../genie_app/bluetooth/mesh
    
    $(NAME)_SOURCES  := my_led.c
    
    ble = 1
    bt_mesh = 1
    en_bt_smp = 1
    
    bt_host_tinycrypt = 1
    
    # Host configurations
    GLOBAL_DEFINES += CONFIG_BLUETOOTH
    GLOBAL_DEFINES += CONFIG_BT_CONN
    #GLOBAL_DEFINES += CONFIG_BT_CENTRAL
    GLOBAL_DEFINES += CONFIG_BT_PERIPHERAL
    #GLOBAL_DEFINES += CONFIG_BLE_50
    
    
    # Mesh function select
    GLOBAL_DEFINES += CONFIG_BT_MESH
    GLOBAL_DEFINES += CONFIG_BT_MESH_PROV
    GLOBAL_DEFINES += CONFIG_BT_MESH_PB_ADV
    GLOBAL_DEFINES += CONFIG_BT_MESH_PB_GATT
    GLOBAL_DEFINES += CONFIG_BT_MESH_PROXY
    GLOBAL_DEFINES += CONFIG_BT_MESH_GATT_PROXY
    GLOBAL_DEFINES += CONFIG_BT_MESH_RELAY
    #GLOBAL_DEFINES += CONFIG_BT_MESH_FRIEND
    #GLOBAL_DEFINES += CONFIG_BT_MESH_LOW_POWER
    #GLOBAL_DEFINES += CONFIG_BT_MESH_SHELL
    
    # Mesh foundation model select
    GLOBAL_DEFINES += CONFIG_BT_MESH_CFG_SRV
    GLOBAL_DEFINES += CONFIG_BT_MESH_HEALTH_SRV
    MESH_MODEL_GEN_ONOFF_SRV = 1
    #MESH_MODEL_DIABLE_TRANS = 1
    MESH_MODEL_LIGHTNESS_SRV = 1
    MESH_MODEL_CTL_SRV = 1
    MESH_MODEL_VENDOR_SRV = 1
    ALI_SIMPLE_MODLE = 1
    
    
    GLOBAL_DEFINES += CONFIG_BT_DEVICE_NAME=\"light\"
    
    GLOBAL_DEFINES += PROJECT_SW_VERSION=0x00010102
    GLOBAL_DEFINES += PROJECT_SECRET_KEY=\"00112233445566770011223344556677\"
    
    # Feature configurations
    GLOBAL_DEFINES += GENIE_OLD_AUTH
    GLOBAL_DEFINES += CONIFG_OLD_FLASH_PARA
    #GLOBAL_DEFINES += GENIE_ULTRA_PROV
    
    genie_ota = 1
    #genie_vendor_timer = 1
    
    
    # Host configurations
    GLOBAL_DEFINES += CONFIG_BT_SMP
    GLOBAL_DEFINES += CONFIG_BT_HCI_VS_EXT
    
    GLOBAL_DEFINES += CONFIG_BT_RX_BUF_LEN=151
    GLOBAL_DEFINES += CONFIG_BT_L2CAP_TX_MTU=143
    
    # project configurations
    #GLOBAL_DEFINES += CONIFG_LIGHT_HONGYAN
    
    genie_vendor_timer = 0
    
    tc825x_debug_mode = 1
    
    GLOBAL_DEFINES += CONFIG_BT_DEBUG_LOG
    ifeq ($(tc825x_debug_mode),1)
    GLOBAL_DEFINES += CONFIG_GENIE_DEBUG_CMD
    
    ############### debug ###############
    ## BLE debug log general control macro (Note: still to be affected by DEBUG)
    ## Enable below macros if BLE stack debug needed
    
    # Mesh debug message enable
    GLOBAL_DEFINES += GENIE_DEBUG_COLOR
    GLOBAL_DEFINES += MESH_DEBUG_RX
    GLOBAL_DEFINES += MESH_DEBUG_TX
    GLOBAL_DEFINES += MESH_DEBUG_PROV
    #GLOBAL_DEFINES += CONFIG_INIT_STACKS
    #GLOBAL_DEFINES += CONFIG_PRINTK
    
    ## BLE subsystem debug log control macro
    ## Enable below macros if component-specific debug needed
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_L2CAP
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_CONN
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_ATT
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_GATT
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_HCI_DRIVER
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_HCI_CORE
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_CORE
    
    ## MESH debug log control macro
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_ADV
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_BEACON
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_PROXY
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_PROV
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_NET
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_CRYPTO
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_TRANS
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FRIEND
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_LOW_POWER
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_ACCESS
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FLASH
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_MODEL
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_VENDOR_MODEL
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FACTORY
    GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_OTA
    else
    GLOBAL_DEFINES += CONFIG_INFO_DISABLE
    endif
    
    

    在tasks.json文件中添加如下代码:

    {
    "version": "2.0.0",
    "tasks": [
    {
    "label": "tc825x", //标签,会在生成代码中显示出来此目标名
    "type": "shell", //类型为shell
    "command": "aos", 
    "args": [
    //此处为命令跟的参数,也就是aos make bluetooth.light_ctl@tc825x
    "make",
    "bluetooth.helloworld@tc825x",
    ],
    "group": "build", //归属于build组下,编译目标类别内
    "presentation": {
    // 此处为出现错误时才会进行输出
    "reveal": "silent"
    },
    // 此处为使用标准的MS编译器输出错误和警告等信息
    "problemMatcher": "$msCompile"
    },
    //添加的代码:
    {
        "label": "MY_LED",
        "type": "shell",
        "command": "aos",
        "args": [
            "make",
            "bluetooth.my_led@tc825x",  
    
        ],
        "group": "build",
        "presentation": {
        "reveal": "silent"
        },
        "problemMatcher": "$msCompile"
        },
    ]
    }
    

    规定风扇四个引脚的连接:风扇和开发板连接规范
    INA--------PC4 引脚是否支持pwm输出
    INB--------PC3
    VCC-------5V
    GND------GND
    蓝牙Mesh项目模板:

    /* main.c - light demo */
    
    /*
     * Copyright (C) 2015-2018 Alibaba Group Holding Limited
     */
    
    #include "common/log.h"
    
    
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <aos/aos.h>
    #include <aos/kernel.h>
    
    #include <misc/printk.h>
    #include <misc/byteorder.h>
    #include <hal/soc/gpio.h>
    #include <hal/soc/pwm.h>
    
    #include <bluetooth.h>
    #include <soc.h>
    #include <api/mesh.h>
    #include "genie_app.h"
    
    #ifndef CONFIG_INFO_DISABLE
    #define LIGHT_DBG(fmt, ...)  printf("[%s]"fmt"\n", __func__, ##__VA_ARGS__)
    #else
    #define LIGHT_DBG(fmt, ...)
    #endif
    
    #define LIGHT_CTL_TEMP_MIN            (0x0320)    // 800
    #define LIGHT_CTL_TEMP_MAX            (0x4E20)    // 20000
    
    #define LED_FLASH_CYCLE MESH_TRNSATION_CYCLE
    
    typedef struct{
        struct k_timer timer;
        uint16_t temp_cur;
        uint16_t temp_tar;
        uint16_t actual_start;
        uint16_t actual_cur;
        uint16_t actual_tar;
        uint32_t time_end;
    } led_flash_t;
    
    led_flash_t g_flash_para;
    
    /* unprovision device beacon adv time */
    #define MESH_PBADV_TIME 600 //unit:s
    
    #define DEFAULT_MESH_GROUP1 0xC000
    #define DEFAULT_MESH_GROUP2 0xCFFF
    
    uint32_t get_mesh_pbadv_time(void)
    {
        return MESH_PBADV_TIME*1000;    //ms
    }
    
    /* 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];
    
    static struct bt_mesh_model element_models[] = {
        BT_MESH_MODEL_CFG_SRV(),
        BT_MESH_MODEL_HEALTH_SRV(),
    
    
    };
    
    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;
    }
    /* element configuration end */
    
    void mesh_sub_init(u16_t *p_sub)
    {
        memset(p_sub, 0, CONFIG_BT_MESH_MODEL_GROUP_COUNT<<1);
    
        p_sub[0] = DEFAULT_MESH_GROUP1;
        p_sub[1] = DEFAULT_MESH_GROUP2;
    }
    
    
    #ifdef CONFIG_GENIE_OTA
    bool ota_check_reboot(void)
    {
        // the device will reboot when it is off
        if(g_elem_state[0].state.onoff[T_CUR] == 0) {
            // save light para, always off
            g_powerup[0].last_onoff = 0;
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
            BT_DBG("reboot!");
            return true;
        }
        BT_DBG("no reboot!");
        return false;
    }
    #endif
    
    static void _init_light_para(void)
    {
        uint8_t i = 0;
        E_GENIE_FLASH_ERRCODE ret;
        // init element state
        memset(g_elem_state, 0, sizeof(g_elem_state));
        elem_state_init(MESH_ELEM_STATE_COUNT, g_elem_state);
    
        // load light para
        ret = genie_flash_read_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        
        if(ret == GENIE_FLASH_SUCCESS) {
            while(i < MESH_ELEM_STATE_COUNT) {
    #ifdef CONFIG_GENIE_OTA
                // if the device reboot by ota, it must be off.
                if(g_powerup[0].last_onoff == 0) {
                    g_elem_state[0].state.onoff[T_TAR] = 0;
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                        g_elem_state[0].powerup.last_actual = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                        g_elem_state[0].powerup.last_temp = g_powerup[0].last_temp;
                    }
                    clear_trans_para(&g_elem_state[0]);
                } else
    #endif
                {
                    memcpy(&g_elem_state[0].powerup, &g_powerup[0], sizeof(model_powerup_t));
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                    }
                    LIGHT_DBG("l:%d t:%d", g_powerup[0].last_actual, g_powerup[0].last_temp);
                    if(g_elem_state[0].state.onoff[T_TAR] == 1) {
                        g_elem_state[0].state.trans_start_time = k_uptime_get() + g_elem_state[0].state.delay * 5;
                        g_elem_state[0].state.trans_end_time = g_elem_state[0].state.trans_start_time + get_transition_time(g_elem_state[0].state.trans);
                    }
                }
                g_elem_state[0].state.temp[T_CUR] = g_elem_state[0].state.temp[T_TAR];
    
                i++;
            }
        }
    
    }
    
    static void _reset_light_para(void)
    {
        uint8_t i = 0;
        while(i < MESH_ELEM_STATE_COUNT) {
            g_elem_state[i].state.onoff[T_CUR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_CUR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_CUR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.onoff[T_TAR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_TAR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_TAR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.trans = 0;
            g_elem_state[i].state.delay = 0;
            g_elem_state[i].state.trans_start_time = 0;
            g_elem_state[i].state.trans_end_time = 0;
    
            g_elem_state[i].powerup.last_actual = LIGHTNESS_DEFAULT;
            g_elem_state[i].powerup.last_temp = CTL_TEMP_DEFAULT;
    
            g_powerup[i].last_onoff = GEN_ONOFF_DEFAULT;
            g_powerup[i].last_actual = LIGHTNESS_DEFAULT;
            g_powerup[i].last_temp = CTL_TEMP_DEFAULT;
            i++;
        }
        genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
    
        LIGHT_DBG("done");
    }
    
    static void _save_light_state(elem_state_t *p_elem)
    {
        uint8_t *p_read = aos_malloc(sizeof(g_powerup));
    
        if(p_elem->state.actual[T_CUR] != 0) {
            p_elem->powerup.last_actual = p_elem->state.actual[T_CUR];
            g_powerup[p_elem->elem_index].last_actual = p_elem->state.actual[T_CUR];
        }
    
        p_elem->powerup.last_temp = p_elem->state.temp[T_CUR];
        g_powerup[p_elem->elem_index].last_temp = p_elem->state.temp[T_CUR];
        // always on
        g_powerup[p_elem->elem_index].last_onoff = 1;
    
        genie_flash_read_userdata(GFI_MESH_POWERUP, p_read, sizeof(g_powerup));
    
        if(memcmp(g_powerup, p_read, sizeof(g_powerup))) {
            LIGHT_DBG("save %d %d", g_powerup[p_elem->elem_index].last_actual, g_powerup[p_elem->elem_index].last_temp);
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        }
        aos_free(p_read);
    }
    
    static void _user_init(void)
    {
    #ifdef CONFIG_GENIE_OTA
        // check ota flag
        if(ais_get_ota_indicat()) {
            g_indication_flag |= INDICATION_FLAG_VERSION;
        }
    #endif
    }
    
    
    void user_event(E_GENIE_EVENT event, void *p_arg)
    {
        E_GENIE_EVENT next_event = event;
        switch(event) {
            case GENIE_EVT_SW_RESET:
            case GENIE_EVT_HW_RESET_START:
    
                break;
            case GENIE_EVT_HW_RESET_DONE:
                _reset_light_para();
    
                break;
            case GENIE_EVT_SDK_MESH_INIT:
                _init_light_para();
                _user_init();
                if (!genie_reset_get_flag()) {
                    next_event = GENIE_EVT_SDK_ANALYZE_MSG;
                }
                break;
            case GENIE_EVT_SDK_MESH_PROV_SUCCESS:
                
                break;
            case GENIE_EVT_SDK_TRANS_CYCLE:
            case GENIE_EVT_SDK_ACTION_DONE:
    
                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);
        }
    }
    
    int application_start(int argc, char **argv)
    {
    
        genie_init();
        return 0;
    }
    
    

    项目开发流程:场景功能分析—>风扇模块使用---->驱动的编写---->蓝牙Mesh接入代码----->完成项目对接。
    驱动风扇代码如下:
    在生活物联网平台中创建一个风扇灯的物模型,属性包括风扇的速度,开关,方向,灯的亮度,颜色灯属性。
    在Mesh源码的base目录下的tri_tuple_default.h中添加设备三元组信息。
    在Mesh源代码app/example 下的bluetooth目录下创建my_fenshan的目录,同时在该目录下创建两个文件(my_fenshan.c 和my_fenshan.mk)
    my_fenshan.c程序如下:

    
    #include "common/log.h"
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <aos/aos.h>
    #include <aos/kernel.h>
    
    #include <misc/printk.h>
    #include <misc/byteorder.h>
    #include <hal/soc/gpio.h>
    #include <hal/soc/pwm.h>
    
    #include <bluetooth.h>
    #include <soc.h>
    #include <api/mesh.h>
    #include "genie_app.h"
    
    #include "drivers/8258/gpio_8258.h"
    #include "vendor/common/alios_app_config.h"
    
    #ifndef CONFIG_INFO_DISABLE
    #define LIGHT_DBG(fmt, ...)  printf("[%s]"fmt"\n", __func__, ##__VA_ARGS__)
    #else
    #define LIGHT_DBG(fmt, ...)
    #endif
    
    #define LIGHT_CTL_TEMP_MIN            (0x0320)    // 800
    #define LIGHT_CTL_TEMP_MAX            (0x4E20)    // 20000
    
    #define LED_FLASH_CYCLE MESH_TRNSATION_CYCLE
    /*
    typedef struct{
        struct k_timer timer;
        uint16_t temp_cur;
        uint16_t temp_tar;
        uint16_t actual_start;
        uint16_t actual_cur;
        uint16_t actual_tar;
        uint32_t time_end;
    } led_flash_t;
    
    led_flash_t g_flash_para;
    */
    /* unprovision device beacon adv time */
    #define MESH_PBADV_TIME 600 //unit:s
    
    #define DEFAULT_MESH_GROUP1 0xC000
    #define DEFAULT_MESH_GROUP2 0xCFFF
    
    uint32_t get_mesh_pbadv_time(void)
    {
        return MESH_PBADV_TIME*1000;    //ms
    }
    
    /* element configuration start */
    #define MESH_ELEM_COUNT 2  //风扇和灯
    #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];
    
    static struct bt_mesh_model Fan_element_models[] = {   //风扇的通用模型
        BT_MESH_MODEL_CFG_SRV(),
        BT_MESH_MODEL_HEALTH_SRV(),
    	MESH_MODEL_GEN_OFF_SRV(&g_elem_state[0]),  //通用的开关模型
    };
    
    static struct bt_mesh_model Led_element_models[] = {   //LED灯的通用模型
        BT_MESH_MODEL_CFG_SRV(),
        BT_MESH_MODEL_HEALTH_SRV(),
    	MESH_MODEL_GEN_OFF_SRV(&g_elem_state[1]),
    	MESH_MODEL_GEN_OFF_SRV(&g_elem_state[1]), //通用的开关和亮度模型
    };
    
    static struct bt_mesh_model g_element_vendor_models[] = {  //LED灯和风扇的厂商模型
        MESH_MODEL_VENDOR_SRV(&g_elem_state[0]),
        MESH_MODEL_VENDOR_SRV(&g_elem_state[1]),
    };
    
    struct bt_mesh_elem elements[] = {   //元素的注册(LED灯和风扇)
        BT_MESH_ELEM(0, Fan_element_models, g_element_vendor_models, 0),
        BT_MESH_ELEM(0, Led_element_models, g_element_vendor_models, 1),
    };
    
    uint8_t get_vendor_element_num(void)
    {
        return MESH_ELEM_COUNT;
    }
    /* element configuration end */
    
    void mesh_sub_init(u16_t *p_sub)
    {
        memset(p_sub, 0, CONFIG_BT_MESH_MODEL_GROUP_COUNT<<1);
    
        p_sub[0] = DEFAULT_MESH_GROUP1;
        p_sub[1] = DEFAULT_MESH_GROUP2;
    }
    
    
    #ifdef CONFIG_GENIE_OTA
    bool ota_check_reboot(void)
    {
        // the device will reboot when it is off
        if(g_elem_state[0].state.onoff[T_CUR] == 0) {
            // save light para, always off
            g_powerup[0].last_onoff = 0;
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
            BT_DBG("reboot!");
            return true;
        }
        BT_DBG("no reboot!");
        return false;
    }
    #endif
    
    static void _init_light_para(void)
    {
        uint8_t i = 0;
        E_GENIE_FLASH_ERRCODE ret;
        // init element state
        memset(g_elem_state, 0, sizeof(g_elem_state));
        elem_state_init(MESH_ELEM_STATE_COUNT, g_elem_state);
    
        // load light para
        ret = genie_flash_read_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        
        if(ret == GENIE_FLASH_SUCCESS) {
            while(i < MESH_ELEM_STATE_COUNT) {
    #ifdef CONFIG_GENIE_OTA
                // if the device reboot by ota, it must be off.
                if(g_powerup[0].last_onoff == 0) {
                    g_elem_state[0].state.onoff[T_TAR] = 0;
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                        g_elem_state[0].powerup.last_actual = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                        g_elem_state[0].powerup.last_temp = g_powerup[0].last_temp;
                    }
                    clear_trans_para(&g_elem_state[0]);
                } else
    #endif
                {
                    memcpy(&g_elem_state[0].powerup, &g_powerup[0], sizeof(model_powerup_t));
                    // load lightness
                    if(g_powerup[0].last_actual) {
                        g_elem_state[0].state.actual[T_TAR] = g_powerup[0].last_actual;
                    }
                    // load temperature
                    if(g_powerup[0].last_temp) {
                        g_elem_state[0].state.temp[T_TAR] = g_powerup[0].last_temp;
                    }
                    LIGHT_DBG("l:%d t:%d", g_powerup[0].last_actual, g_powerup[0].last_temp);
                    if(g_elem_state[0].state.onoff[T_TAR] == 1) {
                        g_elem_state[0].state.trans_start_time = k_uptime_get() + g_elem_state[0].state.delay * 5;
                        g_elem_state[0].state.trans_end_time = g_elem_state[0].state.trans_start_time + get_transition_time(g_elem_state[0].state.trans);
                    }
                }
                g_elem_state[0].state.temp[T_CUR] = g_elem_state[0].state.temp[T_TAR];
    
                i++;
            }
        }
    
    }
    
    static void _reset_light_para(void)
    {
        uint8_t i = 0;
        while(i < MESH_ELEM_STATE_COUNT) {
            g_elem_state[i].state.onoff[T_CUR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_CUR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_CUR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.onoff[T_TAR] = GEN_ONOFF_DEFAULT;
            g_elem_state[i].state.actual[T_TAR] = LIGHTNESS_DEFAULT;
            g_elem_state[i].state.temp[T_TAR] = CTL_TEMP_DEFAULT;
            g_elem_state[i].state.trans = 0;
            g_elem_state[i].state.delay = 0;
            g_elem_state[i].state.trans_start_time = 0;
            g_elem_state[i].state.trans_end_time = 0;
    
            g_elem_state[i].powerup.last_actual = LIGHTNESS_DEFAULT;
            g_elem_state[i].powerup.last_temp = CTL_TEMP_DEFAULT;
    
            g_powerup[i].last_onoff = GEN_ONOFF_DEFAULT;
            g_powerup[i].last_actual = LIGHTNESS_DEFAULT;
            g_powerup[i].last_temp = CTL_TEMP_DEFAULT;
            i++;
        }
        genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
    
        LIGHT_DBG("done");
    }
    
    static void _save_light_state(elem_state_t *p_elem)
    {
        uint8_t *p_read = aos_malloc(sizeof(g_powerup));
    
        if(p_elem->state.actual[T_CUR] != 0) {
            p_elem->powerup.last_actual = p_elem->state.actual[T_CUR];
            g_powerup[p_elem->elem_index].last_actual = p_elem->state.actual[T_CUR];
        }
    
        p_elem->powerup.last_temp = p_elem->state.temp[T_CUR];
        g_powerup[p_elem->elem_index].last_temp = p_elem->state.temp[T_CUR];
        // always on
        g_powerup[p_elem->elem_index].last_onoff = 1;
    
        genie_flash_read_userdata(GFI_MESH_POWERUP, p_read, sizeof(g_powerup));
    
        if(memcmp(g_powerup, p_read, sizeof(g_powerup))) {
            LIGHT_DBG("save %d %d", g_powerup[p_elem->elem_index].last_actual, g_powerup[p_elem->elem_index].last_temp);
            genie_flash_write_userdata(GFI_MESH_POWERUP, (uint8_t *)g_powerup, sizeof(g_powerup));
        }
        aos_free(p_read);
    }
    
    static void _user_init(void)
    {
    #ifdef CONFIG_GENIE_OTA
        // check ota flag
        if(ais_get_ota_indicat()) {
            g_indication_flag |= INDICATION_FLAG_VERSION;
        }
    #endif
    }
    
    #define FAN_INA    TC825X_GET_PIN_NUM(GPIO_PC4)
    #define FAN_INB    TC825X_GET_PIN_NUM(GPIO_PC3)
    #define LED_RED    TC825X_GET_PIN_NUM(GPIO_PB4)
    #define LED_GREEN   TC825X_GET_PIN_NUM(GPIO_PB5)
    #define LED_BLUE    TC825X_GET_PIN_NUM(GPIO_PC1)
    #define BUG_LOG 1
    pwm_dev_t red_gpio;
    pwm_dev_t green_gpio;
    pwm_dev_t blue_gpio;
    
    pwm_dev_t fan_ina;
    pwm_dev_t fan_inb;
    void led_init(void)
    {
    	red_gpio.config.duty_cycle = 0;
    	red_gpio.config.freq = 20000;
    	red_gpio.port = LED_RED;
    	
    	green_gpio.config.duty_cycle = 0;
    	green_gpio.config.freq = 20000;
    	green_gpio.port = LED_GREEN;
    	
    	blue_gpio.config.duty_cycle = 0;
    	blue_gpio.config.freq = 20000;
    	blue_gpio.port = LED_BLUE;
    	
    	hal_pwm_init(&red_gpio);
    	hal_pwm_init(&green_gpio);
    	hal_pwm_init(&blue_gpio);
    	hal_pwm_start(&red_gpio);
    	hal_pwm_start(&green_gpio);
    	hal_pwm_start(&blue_gpio);
    #if BUG_LOG
    	printf("led init is ok\n");
    #endif
    
    }
    
    void fan_init(void)
    {
    	fan_ina.config.duty_cycle = 0;
    	fan_ina.config.freq = 20000;
    	fan_ina.port = LED_INA;
    	
    	fan_inb.config.duty_cycle = 0;
    	fan_inb.config.freq = 20000;
    	fan_inb.port = LED_INB;
    
    	hal_pwm_init(&fan_ina);
    	hal_pwm_init(&fan_inb);
    	hal_pwm_start(&fan_ina);
    	hal_pwm_start(&fan_inb);
    #if BUG_LOG
    	printf("led init is ok\n");
    #endif
    
    }
    enum fat_dir{
    	FOREWARD,
    	REVERSAL,
    };
    enum color_enum{
    	RED = 0,
    	GREEN,
    	BLUE,
    	WHITE,
    };
    enum {    //风扇灯物模型下的厂商属性
    	DIR = 0x0521,
    	SPEED = 0x010A,
    	COLOR = 0x0123,
    
    };
    u8_t now_color = WHITE;
    float g_speed = 1;
    u8_t g_dir = 0;
    void set_light_color(float light,u8_t color)  //light 表示灯的亮度
    {
    	pwm_config_t temp_para = {light,20000};
    	hal_pwm_para_chg(&red_gpio,temp_para);
    	hal_pwm_para_chg(&green_gpio,temp_para);
    	hal_pwm_para_chg(&blue_gpio,temp_para);
    	switch(color)
    	{
    		case RED:
    			temp_para.duty_cycle = light;
    			hal_pwm_para_chg(&red_gpio,temp_para);
    			break;
    		case GREEN:
    			temp_para.duty_cycle = light;
    			hal_pwm_para_chg(&green_gpio,temp_para);
    			break;
    		case BLUE:
    			temp_para.duty_cycle = light;
    			hal_pwm_para_chg(&blue_gpio,temp_para);
    			break;
    		case WHITE:
    			temp_para.duty_cycle = light;
    			hal_pwm_para_chg(&red_gpio,temp_para);
    			hal_pwm_para_chg(&green_gpio,temp_para);
    			hal_pwm_para_chg(&blue_gpio,temp_para);
    			break;
    	}
    			
    }
    
    
    void set_fan_speed(float speed, u8_t dir)
    {
    	pwm_config_t temp_para = {0,20000};
    	hal_pwm_para_chg(&fan_ina,temp_para);
    	hal_pwm_para_chg(&fan_inb,temp_para);
    	switch(dir)
    	{
    		case FOREWARD:  //INA  正转
    			temp_para.duty_cycle = speed;
    			hal_pwm_para_chg(&fan_ina,temp_para);
    			break;
    		
    		case REVERSAL:  //INB   反转
    			temp_para.duty_cycle = speed;
    			hal_pwm_para_chg(&fan_inb,temp_para);
    			break;
    	}
    }
    
    void action_vnd_msg(vnd_model_msg *my_msg)
    {
    	uint16_t cmd = my_msg->data[1];
    	cmd <<= 8;
    	cmd |= my_msg->data[0];
    #if BUG_LOG
    	printf("cmd = %x\r\n",cmd);
    	int i;
    	for(i=0;i<my_msg->len;i++)
    	{
    		printf("%x  ",my_msg->data[i]);
    	}
    #endif
    	switch(cmd)
    	{
    		case  DIR:
    			set_fan_speed(g_speed,my_msg->data[2]);
    			g_dir = my_msg->data[2];
    			break;
    		case  SPEED:
    			set_fan_speed(my_msg->data[2]/100,g_dir);
    			g_speed = my_msg->data[2];
    			break;
    		case  COLOR:
    			
    			break;
    	}
    
    }
    
    void user_event(E_GENIE_EVENT event, void *p_arg)
    {
        E_GENIE_EVENT next_event = event;
        switch(event) {
            case GENIE_EVT_SW_RESET:
            case GENIE_EVT_HW_RESET_START:
    
                break;
            case GENIE_EVT_HW_RESET_DONE:
                _reset_light_para();
    
                break;
            case GENIE_EVT_SDK_MESH_INIT:
                _init_light_para();
                _user_init();
                led_init();
                fan_init();
                if (!genie_reset_get_flag()) {
                    next_event = GENIE_EVT_SDK_ANALYZE_MSG;
                }
                break;
            case GENIE_EVT_SDK_MESH_PROV_SUCCESS:
                
                break;
            case GENIE_EVT_SDK_TRANS_CYCLE:
            case GENIE_EVT_SDK_ACTION_DONE:  //只做 开关和亮度的通用模型
    				{
    					elem_state_t * elem_state = (elem_state_t * )p_arg;
    					set_light_color(elem_state->state.actual[T_CUR]/0xffff,now_color);
      					if(elem_state->state.onoff[T_CUR])
      					{
      						hal_pwm_start(&fan_ina);
      						hal_pwm_start(&fan_inb);
      						hal_pwm_start(&red_gpio);
      						hal_pwm_start(&green_gpio);
      						hal_pwm_start(&blue_gpio);
      						
      					}else{
      						hal_pwm_stop(&fan_ina);
      						hal_pwm_stop(&fan_ina);
      						hal_pwm_stop(&red_gpio);
      						hal_pwm_stop(&green_gpio);
      						hal_pwm_stop(&blue_gpio);
      					}
    				}
                break;
            case GENIE_EVT_SDK_INDICATE:
                break;
            case GENIE_EVT_SDK_VENDOR_MSG:  //厂商模型
      				{
      					vnd_model_msg * my_msg = (vnd_model_msg *)p_arg;
      					action_vnd_msg(my_msg);
      				}
                break;
            default:
                break;
        }
        if(next_event != event) {
            genie_event(next_event, p_arg);
        }
    }
    
    int application_start(int argc, char **argv)
    {
    /*
    	led_init();
    	fan_init();
    	while(1) //实现效果:红,绿,蓝,白灯依次亮,然后灭,风扇正转,反转,停。循环。
    	{
    		set_light_color(0.5,RED);
    		aos_msleep(500);
    		set_light_color(0.5,GREEN);
    		aos_msleep(500);
    		set_light_color(0.5,BLUE);
    		aos_msleep(500);
    		set_light_color(0.5,WHITE);
    		aos_msleep(500);
    
    		set_fan_speed(0,WHITE);
    		aos_msleep(500);
    		set_fan_speed(1,FOREWARD);
    		aos_msleep(1000);
    		set_fan_speed(1,REVERSAL);
    		aos_msleep(1000);
    		set_fan_speed(0,REVERSAL);
    		aos_msleep(1000);
    		
    	}
    */
    
        genie_init();
        return 0;
    }
    
    

    my_fenshan.mk程序如下:

    NAME := my_fenshan
    
    GENIE_MAKE_PATH := app/example/bluetooth/$(NAME)
    
    $(NAME)_COMPONENTS  += genie_app bluetooth.bt_host bluetooth.bt_mesh yloop cli
    
    $(NAME)_INCLUDES += ../ \
    					../../../../genie_app \
    					../../../../genie_app/base \
    					../../../../genie_app/bluetooth/host \
    					../../../../genie_app/bluetooth/mesh
    
    $(NAME)_SOURCES  := my_fenshan.c
    
    ble = 1
    bt_mesh = 1
    en_bt_smp = 1
    
    bt_host_tinycrypt = 1
    
    # Host configurations
    GLOBAL_DEFINES += CONFIG_BLUETOOTH
    GLOBAL_DEFINES += CONFIG_BT_CONN
    #GLOBAL_DEFINES += CONFIG_BT_CENTRAL
    GLOBAL_DEFINES += CONFIG_BT_PERIPHERAL
    #GLOBAL_DEFINES += CONFIG_BLE_50
    
    
    # Mesh function select
    GLOBAL_DEFINES += CONFIG_BT_MESH
    GLOBAL_DEFINES += CONFIG_BT_MESH_PROV
    GLOBAL_DEFINES += CONFIG_BT_MESH_PB_ADV
    GLOBAL_DEFINES += CONFIG_BT_MESH_PB_GATT
    GLOBAL_DEFINES += CONFIG_BT_MESH_PROXY
    GLOBAL_DEFINES += CONFIG_BT_MESH_GATT_PROXY
    GLOBAL_DEFINES += CONFIG_BT_MESH_RELAY
    #GLOBAL_DEFINES += CONFIG_BT_MESH_FRIEND
    #GLOBAL_DEFINES += CONFIG_BT_MESH_LOW_POWER
    #GLOBAL_DEFINES += CONFIG_BT_MESH_SHELL
    
    # Mesh foundation model select
    GLOBAL_DEFINES += CONFIG_BT_MESH_CFG_SRV
    GLOBAL_DEFINES += CONFIG_BT_MESH_HEALTH_SRV
    MESH_MODEL_GEN_ONOFF_SRV = 1
    #MESH_MODEL_DIABLE_TRANS = 1
    MESH_MODEL_LIGHTNESS_SRV = 1
    MESH_MODEL_CTL_SRV = 1
    MESH_MODEL_VENDOR_SRV = 1
    ALI_SIMPLE_MODLE = 1
    
    
    GLOBAL_DEFINES += CONFIG_BT_DEVICE_NAME=\"light\"
    
    GLOBAL_DEFINES += PROJECT_SW_VERSION=0x00010102
    GLOBAL_DEFINES += PROJECT_SECRET_KEY=\"00112233445566770011223344556677\"
    
    # Feature configurations
    GLOBAL_DEFINES += GENIE_OLD_AUTH
    GLOBAL_DEFINES += CONIFG_OLD_FLASH_PARA
    #GLOBAL_DEFINES += GENIE_ULTRA_PROV
    
    genie_ota = 1
    #genie_vendor_timer = 1
    
    
    # Host configurations
    GLOBAL_DEFINES += CONFIG_BT_SMP
    GLOBAL_DEFINES += CONFIG_BT_HCI_VS_EXT
    
    GLOBAL_DEFINES += CONFIG_BT_RX_BUF_LEN=151
    GLOBAL_DEFINES += CONFIG_BT_L2CAP_TX_MTU=143
    
    # project configurations
    #GLOBAL_DEFINES += CONIFG_LIGHT_HONGYAN
    
    genie_vendor_timer = 0
    
    tc825x_debug_mode = 1
    
    GLOBAL_DEFINES += CONFIG_BT_DEBUG_LOG
    ifeq ($(tc825x_debug_mode),1)
    GLOBAL_DEFINES += CONFIG_GENIE_DEBUG_CMD
    
    ############### debug ###############
    ## BLE debug log general control macro (Note: still to be affected by DEBUG)
    ## Enable below macros if BLE stack debug needed
    
    # Mesh debug message enable
    GLOBAL_DEFINES += GENIE_DEBUG_COLOR
    GLOBAL_DEFINES += MESH_DEBUG_RX
    GLOBAL_DEFINES += MESH_DEBUG_TX
    GLOBAL_DEFINES += MESH_DEBUG_PROV
    #GLOBAL_DEFINES += CONFIG_INIT_STACKS
    #GLOBAL_DEFINES += CONFIG_PRINTK
    
    ## BLE subsystem debug log control macro
    ## Enable below macros if component-specific debug needed
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_L2CAP
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_CONN
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_ATT
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_GATT
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_HCI_DRIVER
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_HCI_CORE
    #GLOBAL_DEFINES += CONFIG_BT_DEBUG_CORE
    
    ## MESH debug log control macro
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_ADV
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_BEACON
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_PROXY
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_PROV
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_NET
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_CRYPTO
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_TRANS
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FRIEND
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_LOW_POWER
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_ACCESS
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FLASH
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_MODEL
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_VENDOR_MODEL
    #GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_FACTORY
    GLOBAL_DEFINES += CONFIG_BT_MESH_DEBUG_OTA
    else
    GLOBAL_DEFINES += CONFIG_INFO_DISABLE
    endif
    
    

    在tasks.json文件中添加如下代码:

    {
    "version": "2.0.0",
    "tasks": [
    {
    "label": "tc825x", //标签,会在生成代码中显示出来此目标名
    "type": "shell", //类型为shell
    "command": "aos", 
    "args": [
    //此处为命令跟的参数,也就是aos make bluetooth.light_ctl@tc825x
    "make",
    "bluetooth.helloworld@tc825x",
    ],
    "group": "build", //归属于build组下,编译目标类别内
    "presentation": {
    // 此处为出现错误时才会进行输出
    "reveal": "silent"
    },
    // 此处为使用标准的MS编译器输出错误和警告等信息
    "problemMatcher": "$msCompile"
    },
    //添加的代码:
    {
        "label": "MY_FEN",
        "type": "shell",
        "command": "aos",
        "args": [
            "make",
            "bluetooth.my_fenshan@tc825x",  
    
        ],
        "group": "build",
        "presentation": {
        "reveal": "silent"
        },
        "problemMatcher": "$msCompile"
        },
    ]
    }
    
    展开全文
  • 统,完成了手机蓝牙与底层照明设备上BLE(低功耗蓝牙)模块的配对,通过APP 实现对大规模照明设备的集中管理与个性化控制;系统开发完成后,将该照明控制 系统和底层照明设备放在无线办公场景中进行测试,验证该智能...
  • 众所周知,众多通信协议上都离不开地址的概念,大名鼎鼎的IP地址、基石的MAC地址,蓝牙Mesh也不例外,本文将着重讲解Mesh地址的概念,希望能够给读者以清晰的理解。 地址的分类 在蓝牙Mesh规范中,将地址分为四类: ...
  • This specification contains definitions of device properties that are required by the Bluetooth Mesh Profile and Bluetooth Mesh Model specifications.
  • 本文档介绍了蓝牙网状网络 的基本概念,并概述了配置文件的操作和功能,并解释了网状网络设备的生命周期。更多蓝牙mesh具体信息获取vivi_kuang@163.com。
  • silicon lib 蓝牙mesh APP

    2019-03-21 11:15:21
    silicon lib 的 蓝牙mesh APP。 主要有 provisioner, 灯控等功能。 可以和 CSR 和 telink 的 蓝牙芯片兼容。
  • 蓝牙Mesh规范v1.0

    2017-08-10 23:52:50
    蓝牙Mesh规范v1.0,官网搬运,蓝牙技术联盟于7月19日正式发布
  • 解析蓝牙Mesh网络

    2018-12-05 17:41:28
    解析蓝牙Mesh网络是讲蓝牙Mesh的网络拓扑,蓝牙Mesh的基本概念,有这一个文档蓝牙Mesh基本能入门。文档简短内容清晰,页面高清。
  • Silicon Labs公司在2018年3月份底发布了蓝牙Mesh标准协议的第一个SDK正式版本,加入调光功能,这标志着蓝牙Mesh网络的商用之路将逐渐被照亮。贝能国际推出基于蓝牙Mesh网络标准协议的参考方案,为更多物联网产品设计...
  • 一种应用于物联网的低功耗蓝牙Mesh组网方案设计
  • 该文档为蓝牙mesh组网关于设备属性的说明
  • 蓝牙mesh协议,讲述蓝牙mesh的功能,以及协议详解
  • 基于蓝牙Mesh网络标准协议的参考方案
  • 蓝牙mesh基础概念讲解,节点,Model, Provision,元素,单播地址,组播地址等等概念性知识讲解。
  • 蓝牙组织官方文档转换得来。 Mesh Profile Bluetooth® Specification ▪ Revision: v1.0.1 ▪ Revision Date: 2019-01-21 ▪ Group Prepared By: Mesh Working Group ▪ Feedback Email: mesh-main@...
  • 蓝牙Mesh学习总结一(蓝牙Mesh介绍)

    千次阅读 2021-12-01 13:59:00
    Blutooth Low Energy Mesh 是基于低功耗蓝牙技术(BLE)的网状网络解决方案。目前使用的是泛洪网状网络(flooding-based mesh network)。 BLE的通信信道有adversing信道(37、38、39)和data信道(0-37).详细见下图。 ...

    1.Mesh简介

           Blutooth Low Energy Mesh 是基于低功耗蓝牙技术(BLE)的网状网络解决方案。目前使用的是泛洪网状网络(flooding-based mesh network)。

            BLE的通信信道有adversing信道(37、38、39)和data信道(0-37).详细见下图。

            Mesh主要工作在advertising信道上通过scan和advertising进行接受和发送。而data信道主要为了兼容不支持advertising设备,可以通过LE link方式进行通信。通俗一点说就是mesh的网络通信通过广播和扫描来进行收发。广播发送,扫描接受。因为Mesh是网状网络结构,所以中间节点在收到信息后进行判断,如果不是自己需要的则进行转发,从而可以使信息快速、大面积的向外传播。这就是泛洪。而通过广播来进行数据收发,所以Mesh节点使用的通信信道就是广播信道。而有一些不支持广播通信的设备,就需要使用数据信道,通过Proxy(代理)节点,接入到mesh网络中。

    1.1Device UUID

            每个设备出厂时被分配一个唯一的16字节UUID,称作Device UUID,用于唯一标识一个Mesh设备,不用依赖蓝牙地址来标识设备。在建立pb-adv link时,需要Device UUID字段来标识device。然后,当mesh device获取mesh地址后,即可用mesh address来唯一标识device。

    1.2Mesh地址

            除了建立le link,mesh通信并不依赖蓝牙地址,即节点的蓝牙地址可以一样,或者随机变化。mesh定义了一套长度为2字节的mesh地址,分为unassigned address、unicast address、virtual address和group address,地址范围如下图。

             mesh地址并不是出厂时设置的,而是由用户自己统一管理和分配的。用户在配置设备入网时,通过provisioning流程给设备分配地址provisioner需要确保给每个设备分配的地址是不重复的mesh设备可能不止一个mesh地址,设备内每个element会被分配一个地址,且地址是连续的。多地址被设计用于区分mesh设备上重复的功能模块model

    1.3应用模型

             BLE是master连接slave的一对一通信,而mesh网络是多对多通信。因而,mesh网络存在一个天然特性,就是节点之间并不知道其他节点的存在。此时,需要一个第三方,通常是Provisioner,来扮演月老的角色,将节点之间联系起来。例如一个通用的开关,出厂后不知道自己要控制哪盏或者哪些灯,需要provisioner通过configuration配置开关发布publish消息(设置目的地址为单播、组播、广播地址)。如果是组播,则需同事配置相应的灯泡订阅subscribe消息(设置分组,即增加组播地址到订阅表)。然后,开关就可以控制这一盏灯、一组灯或者所有灯。

            Mesh将典型应用场景的操作进行了标准化,每个Mesh设备上的应用是以model为单位进行组织的。model定义了一个model id、一套opcode和一组状态,规定发送和接收哪些消息,分别操作哪些状态。Model和BLE的GATT service是类似的,都用于定义一个特定的应用场景。

            为了支持多个相同的model,定义了element的概念,每个element会单独分配一个element address,且地址是连续的。第一个element(primary element)的element address是在provisioning过程中分配的node address,其他element的地址顺序往后排。例如一个Mesh设备上有两盏完全一样的且可以独立控制的灯,开关设备去控制这个灯设备,需要区分控制哪盏灯。让这两盏灯对应的两个medel分开放在两个element中,这样每盏灯分别有一个mesh address,就可以通过mesh address将两盏灯区分开来了,进行单独控制。当然,两盏灯的model也可以订阅同一个组地址,实现同时控制。这样即能独立控制,也能同时控制。

            设备上element、model组成情况通过composition data page 0表达,provisioner可以通过获取设备的composition data page 0来辨识设备支持的应用。

    1.4安全性

            mesh中有很多保护网路安全和隐私的设计,能够抵挡被动监听、中间人攻击、重放攻击、垃圾桶攻击和暴力破解等常见的攻击、

            mesh网络中所有mesh消息都会被加密和校验,防止被窃听和篡改。mesh网络中密钥分两层:Netkey和Appkey,每层最多可以有4096个密钥,通过12Bit的Index标识。AppKey必须绑定有且只能一个NetKey。应用层发送消息会依次经过AppKey和NetKey两层加密和校验,接受信息会依次经过NetKey和AppKey两层解密和校验。采用两层密钥,是为了防止relay节点窃听货篡改消息。例如节点A通过节点B转发给节点C发数据,A/B/C有相同的NetKey,A/C有相同的AppKey,而B没有该AppKey。那么A和C间的应用层通信对B来说是保密的,B只是使用NetKey在网络层帮忙转发,因为没有AppKey而不能进行窃听或者篡改应用层消息

            NetKey支持多个密钥,多密钥可以用来划分网络范围,实现设备间的隔离。Key index为0的是主网络密钥,其余的都是普通的其他子网络密钥。只有主网络中的节点才能参与IV Updata Procedure,并将IV更新信息传递到其他子网中。也就是说,只有主网络节点才能更新IV index网络参数,其他子网的节点只能被动的接收IV index更新。这样不平等的网络密钥设计的目的是约束子网络节点的数据发送频次,防止子网络节点滥用IV index更新而耗尽IV index,从而导致网络安全问题。通常大部分节点在主网络中,部分节点同时处于主网络和某个子网络,少量节点只处于某个子网络,此时这些少量节点只能在子网络内进行局部通信,从而限定这些少量节点的通信范围,例如酒店顾客只能控制自己房间内的灯。

            Device上的NetKey和AppKey是provisioner通过provision和configuration分发和管理的。provisioner是网络管理员,他管理着所有的key,即管理网络中各个device各自可以使用哪些key,而device间只有共享相同的密钥才能相互通信,例如灯和灯的开关使用相同的密钥。provision过程会分发mesh address和有且只有一个NetKey,后续通过configuration来管理,例如通过configuration增加NetKey和AppKey。

            Provision过程还会随机生成一个特殊的AppKey,称作DevKey。DevKey只有provisioner和device两者知道,不和任何其他device共享,保证了provisioner可以单独和某一个device进行秘密的一对一通信。configuration配置被限制只能使用DevKey,只有provisioner才知道device的DevKey,所有只有provisioner才可以配置device。例如,开关只能控制灯泡亮灭,而不能去配置灯泡的分组。

    1.5 Provisioning

            device出厂默认是没有地址和密钥的,需要通过provisioning过程从provisioner获取device被provisioning后,就称作node。provisioning过程类似于Bt pairing中的secure connection,采用ecdh算法进行密钥协商和发布,通过authenticaion data进行身份鉴权,能够防止窃听、暴力破解和中间人攻击。

            privisioning流程可以工作在advertising信道和data信道两种信道上,分别对应pb-adv和pb-gatt两种传输层device是被强制要求支持pb-adv的,如果同时支持pb-gatt,被provisioning时可以任选一个。

    1.6 Configuration

            网络参数的管理是在model层实现的,称作configuration models。可以配置的网络参数有很多,例如NeyKey和AppKey增加、删除、修改等,model的密钥绑定、消息发布、消息订阅等,节点应用结构Composition data page 0的获取,节点的默认ttl、支持的deature、网络重传次数等

            网络参数的配置被限定为只能使用DevKey,也就是说只有provisioner才能配置节点的网络参数。

    1.7 Proxy(代理)

            mesh主要工作在advertising 信道上,为了兼容一些不能灵活自有的advertising的设备,mesh定义了proxy特性。基于BLE GATT profile,定义了proxy service,让这些设备利用BLE link的方式连接到支持proxy的节点上,从而接入Mesh网络

    展开全文
  • 蓝牙mesh基本概念讲解

    千次阅读 2021-08-03 23:02:12
    1.蓝牙MESH基本概念 网状网(mesh) Mesh网络,就是一个多对多网络(Many to Many)。 每个设备节点都可以和别的节点自由通讯。在这种拓扑中,因为很多节点可以中继(relay)收到的消息(Message), 所以端对端的通信...

    https://blog.csdn.net/JaLLs/article/details/88864829

    1.蓝牙MESH基本概念
    网状网(mesh)

        Mesh网络,就是一个多对多网络(Many to Many)。 每个设备节点都可以和别的节点自由通讯。在这种拓扑中,因为很多节点可以中继(relay)收到的消息(Message), 所以端对端的通信可以比原来单点之间的通讯距离要远很多。就好像小组讨论自由发言,如果做的太远听不见的话,别人也可以代为传话。

                      

    设备和节点(Devices and Nodes)

    如果一个设备成为了蓝牙mesh网络的一部分,我们就把它叫做节点(node)。 反之,我们就把它叫做“未配网的设备”(unprovisioned devices)。把未配网的设备变成节点的过程我们把它叫做 “配网” (provisioning)。

    元素(element)

    如下图,三个子灯组成一大灯,每个子灯都可以通过寻址访问,子灯就称之为元素(element),大灯称之为节点(node)。

               

    消息(Messages)

    消息也是蓝牙mesh里的一个重要概念。当一个节点需要查询别的节点的状态(Status)或者控制别的节点的时候,就需要发一个对应类型的消息。当一个节点向别人报告状态改变的时候,他也需要发出一个消息。在蓝牙mesh里面定义了很多种类型的消息,每种消息都有对应的操作码(opcode)。消息可以按照有应答(acknowledged)和没应答(unacknowledged)来分类。 和大多数的通讯系统一样,有应答的消息需要接收节点给个响应。“发给你了” “收到两条了”。这样做有两个目的:一是告诉你我收到了。二是可以返回一些需要的值。如果发现没有收到应答,发送端可以再次发送。当然,如果是不用应答的消息,接收端收到就算了,不会告诉发送端的。

    地址(Addresses)

    在蓝牙mesh里面定义了三种类型的地址:单播地址(unicast address),组播地址(group address)和虚拟地址(virtual address)。

    唯一性的单播地址可以识别出一个元素。这个地址是在配网的时候分配给设备的。一个mesh网络可以有32767个单播地址。

    组播地址是一个表示一个及以上元素的广播地址。组播地址可以由蓝牙SIG来定义(SIG Fixed Group Addresses), 也可以动态分配。目前SIG定义了4个组播地址,分别是“All-proxies”, “All-friends”,“All-relays” and “All-nodes”。 动态分配的组播地址是设计成在用户配置的时候,可以分配代表一些实际的场所,比如可以定义几个组播地址代表几个不同的房间。一共可以有16384个组播地址,其中SIG保留了256个,其他16128个都是动态分配的。

    虚拟地址基本可以认为是组播地址的一个扩展,厂家自定义的地址类型。

    https://blog.csdn.net/hesuping/article/details/108688419 

    蓝牙Mesh中地址分为三种,单播地址、虚拟地址、群组地址。

    单播地址: 网络内每个元素都有一个单播地址,所以一个有多个元素的节点也就有多个单播地址,单播地址范围为1- 0x7fff。
    群组地址: 群组地址是多个元素共有的地址,地址范围为 0xc000 - 0xffff(中间包括特定群组地址和部分保留区域),群组地址一般由Provisioner设置,在数据库中进行统一的分配和管理,防止网络中发生群组地址冲突。
    虚拟地址: 虚拟地址是由节点的label UUID通过hash处理生成的,设置虚拟地址时,Provisioner会将label UUID发送给对应的设备,对应设备在收到目的地址是虚拟地址的数据后,会尝试用之前设置的label UUID进行验证。故Provisioner不用在数据库中维护虚拟地址的分配管理。虚拟设备也可以不经过Provisioner,由两个设备通过协商来生成。
     

    https://blog.csdn.net/JaLLs/article/details/88864924

    发布和订阅(Publish/Subscribe)

    在蓝牙mesh里面发消息的动作我们叫做发布(Publish)。光从字面意思理解大家基本上就能看懂了。我想告诉别人什么事情发生或者做什么事情就叫做发布。谁对某些消息感兴趣就可以订阅这些内容。节点发布消息到单播地址,组播地址或者虚拟地址。节点有兴趣接收这些数据的可以订阅这些地址。

    在上图中,开关1 发布信息给组播地址”厨房“, 节点灯1, 灯2, 灯3 每个都注册到了”厨房“这个地址上, 因此他们能收到处理发给厨房的消息。换句话说, 灯1,灯2 和灯3 都能被开关1控制开关。 开关2 发布消息到”餐厅“,只有灯3订阅了"餐厅"这个地址,所以只有灯3能被开关2控制。在这个例子里同样说明了每个节点可以订阅多个确切的地址。同样的,你一定也注意到了,开关5和开关6同样都可以发布消息到”花园“。

    状态和属性(States and Properties)

    在蓝牙mesh中,元素的不同情况叫做状态(States),状态是一个特定类型的值,在每个元素内部存在。除了本身的值以外,状态还有一些相关的行为。 拿灯来说,蓝牙mesh定义了一个状态叫”Generic OnOff.“  灯收到了一个ON的状态,理解以后就会执行相应的动作比方说 点亮灯泡的行为。

    在蓝牙mesh里面的属性它提供一个来解释Characteristic的上下文。举个例子。如果有一个Characteristic,叫做Temperature 8,一个8位的温度状态类型,它有着一些相关联的属性, 包含现在室内环境温度和现在室外环境温度。这两个属性允许一个传感器来发布传感器消息,收到的客户端(关于客户端和服务器端的内容我们后面会讲)会根据属性得知到底是哪个温度信息。

    消息,状态和属性的关系 (Messages, States and Properties)

    蓝牙Mesh里面,要进行某种操作,就是调用消息这一基本机制。一个给定的消息类型代表了一个对状态的操作或者对多个状态的采集。所有的消息都可以分成三种简单类型:get 、set 、 status。

    GET 顾名思义,就是获取一个节点或者多个节点的给定的状态。当收到GET消息以后,STATUS消息就发出来了。当然,它里面带着的是相对应的状态内容。

    SET消息分为有应答和无应答两种。如果是有应答的,就会有STATUS消息跟着出来,如果是无应答的话,那就没有应答包。

    STATUS 消息,除上面的两种情况会出现之外,也可以在其他的消息中出现,当然也可以独立出现。比方说某个元素用定时器每隔一段时间发送一次。

    在蓝牙mesh里面定义了很多种消息,通过Opcode来区分,还包含了相关联的参数和行为。Opcode可以是单字节,双字节(常见)或者三字节(厂商指定)。

    绝大部分的mesh消息都是对状态进行操作的,只有特别的和属性相关的消息,才会对属性进行操作,而且需要制定16位的属性ID。

    状态转换(State transitions)

    上面说到了状态的设置和获取,那么在进行状态改变的时候,这种改变可以是立刻发生的,也可以是过一段时间发生的。下图把不同的时间给表示出来了。

    初始化状态(initial State)是指刚收到SET新的状态值的时间。从收到SET消息到状态改变的时间叫做转换时间。从STATUS消息发出(可以在中间的任何时间点)到目标状态完成这个叫保持时间(Remaining time), 所以当你收到STATUS消息的时候,状态可能还没有变化,在STATUS消息里也可以包含离目标状态的变化还有多少时间。

    状态绑定(Bound States)

    不同的状态之间可能会有一些关系。比如说一个状态的变化会造成另外状态的触发,这种关系叫做状态绑定。状态的绑定是可以跨Model的,(Model这个重要概念我们马上会提到),也可以在多个元素中。再举个例子,灯光亮度状态和开光状态。当你把亮度状态改到0了,也就触发了开关状态的“关”状态,反之亦然。


    https://blog.csdn.net/JaLLs/article/details/88864967

    模型(Models)

    模型(Model)定义了一个节点的基本功能。一个节点当然可以包含多个Model。一个Model定义了节点所需要的所有的状态。消息会给基于这些状态进行操作,当然也会有相应的行为随之产生。

    Mesh的应用定义的是使用“发布-订阅(publish-subscribe)”的典型的“服务器-客户端(client-server)”的架构。在Mesh里面,并没有沿用传统的端到端的“Profile”的概念,而是定义了三种不同的模式, Client, Server 和Control。

    Server model: 定义了状态states, 状态转换 state transitions, 状态绑定state bindings 和包含了哪些消息,当然也同样定义了与这些消息,状态,状态转换相关的行为Behaviors。

    Control model:具备client model的功能与其他的server model进行交互,同时也可以有server model功能与其他client model进行交互。内置了逻辑控制层(一套规则和行为在各个与之连接的模型中进行协调交互)。

    Client model: 没有定义任何的状态States,但是它定义了要收发哪些消息。定义这些消息是为了GET,SET或者获取在Server models里面定义的状态。

    下图展示的是Device C(server model)带有状态,支持R S T X Y Z消息,Device A(client model)支持 X Y Z消息,Device B(client model)支持 R S T Z消息。

    下图中 Device C(control model)可以作为client model与server model(device A与device B)进行通讯(分别支持X Y Z 和R S T消息),也可以作为server model与client model(device D)进行通讯(支持 A B C消息)。

     通用(Generics)

    为了满足不同的需求,Mesh中定义了多种不同的models,实际上SIG确实有一个Model specification。现在是1.0的版本,只定义了几个Model group:Generics,Sensors,Time and Scenes和Lighting 。你仔细看一下这几个名字就会发现,其实Generic这个model就是把很多不同应用的相似的部分例如ON/OFF,Level一些状态和行为放在了一个叫做 通用 的model里面。如果你的产品没有办法找到对应的model,就可以先用 generic 这个model先用。

    配网(Provisioning)

    配网的全过程包括大概5个步骤,分别是

    Step 1. Beaconing;

    Step 2. Invitation;

    Step 3. Exchanging Public Keys;

    Step 4. Authentication;

    Step 5. Distribution of the Provisioning Data

    其实也很简单,第一步,告诉你我要配网,这里使用的是新定义的AD广播包类型, Mesh AD。第二部, 配网者Provisioner听到了这个Beacon以后,就发一个邀请,这个邀请就是配网邀请PDU(Protocol Data Unit)。要入网的设备收到邀请以后,会把自己的一些配网的能力(Provisioning capabilities)发回来。接下来,既然郎有情妾有意, 就公开交换信物-公钥呗。接下来就会有一个互动随机数的认证流程,这点和原来蓝牙输入0000  的密码很像, 但是会简单一点点。 最后一步,认证完成,从公钥和两个设备的私钥派生出Session Key。后面的配网的信息交互的过程会用这个Session key来加密。配网成功以后,就会根据最后一步里面包含交换的NetKey来加密后面的数据交换。跟加密相关的一些参数例如IV index, 和单播地址,会存在配网者那里。

    节点特性(node features)

    mesh里面还给每个节点有一些额外的四种可选的特性(Features)。分别是中继Relay,代理Proxy,朋友Friend和 低功耗Low Power features。节点可以在某个时间点选择不支持或者支持多个Feature。

    中继(Relay)支持中继的节点,可以帮忙转发收到的消息。因为有了Relay,Mesh网络就可以实现多跳(Hops)。

    低功耗和朋友(Low Power Nodes and Friend Nodes), 这是搭配来用的。我们先说Low power节点,类似于对功耗有要求的设备,例如温度传感器。这种类型的设备为了节约功耗,很大的时间都是在休眠的。也就是意味着他们收不到网络中发过来的消息。Friend节点能帮LP节点暂存消息。当LP节点需要的时候,可以发消息给Friend节点, 问问有没有“waiting message”。如果有,就会一条条的发给LP节点。简而言之,Friend节点就像是门卫的张大爷,你(Low power node)想起来的时候去门卫拿你要的信就好了。这种方式和zigbee里面的enddevice向父节点拿数据的方式类似。

    下面简单的拓扑结构基本上说明了这两种Feature和其他一般Node的关系和消息路径。显而易见,如果有消息要发从T发给L,S会帮忙转发,O会帮忙L存起来。L要消息的时候,O再把T的消息发给L。

    图 :  mesh 网络中的拓扑结构

    代理(Proxy)的feature又是什么呢?这就是Mesh想到的兼容现有的非Mesh设备的方法。在Proxy节点, 其实是可以通过BLE的GATT来交流的。(这是现在非Mesh BLE设备最常用的数据交流方式)。

    图: 有mesh功能与无mesh功能之间的通讯方式

    每个节点当然还有一系列配套的配置集,在Configuration Server Model和Configuration Client Model里的States都有实现。比如,上面说到的不同的Feature,都是在Configuration Server states说明的。在蓝牙Mesh也一样定义了配置消息(configuration messages)集。


    https://blog.csdn.net/JaLLs/article/details/88865194

    蓝牙mesh协议架构
    mesh协议层架构图

    图:mesh系统架构

    承载层(Bearer Layer)

    Bearer Layer 定义了Mesh节点怎么传递网络消息的。定义了两种Bearer,广播advertising bearer 和GATT bearer 。

    Advertising Bearer 利用的是BLE GAP广播包的advertising 和scanning
    的功能来传递接收mesh的报文。

    The GATT Bearer 允许不支持Advertising Bearer的设备间接的与mesh节点进行通讯。怎么通讯呢?使用前面讲的代理(Proxy Protocol)。Proxy Protocol是封装在GATT里面,当然会用特别定义的GATT characteristics。上一讲我们讲到了Proxy Feature,支持Proxy Feature的Proxy Node也就是代理节点,因为可以同时支持两种Bearer Layer,所以可以作为mesh节点和非mesh节点的中间桥梁。

    网络层(Network Layer)

    网络层定义了几件事情, 一个是定义了多种网络地址类型,我之前有说过关于Mesh地址的内容。二是定义了网络层的格式,打通传输层(Transport layer)和承载层(Bearer layer);三是定义了一些输入输出Filter,决定哪些消息需要转发,处理还是拒绝。四是定义了网络消息的加密和认证。

    底层传输层(Lower Transport Layer)

    这层做的事情很简单,就是拆拆拼拼。把太长的传输层的包拆成若干个分给网络层,把短的网络层的包再组成一个长的传输层的PDU(Protocol Data Unit)。

    上层传输层(Upper Transport Layer)

    上层传输层主要是负责加密,揭秘和应用数据授权。一句话,消息的安全性和机密性就是有这一层负责的。还有就是会定义一些节点间在这一层的一些会话,比如Friend功能,心跳包(Heartbeats)。

    访问层(Access Layer)

    访问层主要负责:1.定义更高层的应用如何跟upper transport layer通讯。2.定义应用数据的格式。3.定义和控制upper transport layer应用数据的加解密。4.在把应用数据扔到上层之前,会检查校验接收过来的应用数据是否合法。

    基础Model层(Foundation Models Layer)

    基础model层定义访问层(access layer)的状态,消息,模型配置和mesh网络管理。

    Model层(Model Layer)

    Model层定义了典型的用户场景标准化操作的相关models(相关的models定义在Bluetooth Mesh Model specification文档中)。更高层次模型规范的例子包括照明和传感器的模型。

     
    ————————————————
    版权声明:本文为CSDN博主「JaLLs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/JaLLs/article/details/88865194

    展开全文
  • 蓝牙mesh文档

    2018-04-10 11:09:30
    蓝牙mesh文档,蓝牙mesh规范 蓝牙mesh文档,蓝牙mesh规范
  • 教你1天上手蓝牙Mesh应用解决方案,包括业内性能领先的Mesh协议栈、解决多场景配网难题、支持天猫精灵生态等内容
  • 目录 蓝牙Mesh的安全性: 配网安全性: 防中间人攻击(Man-in-the-MiddleAttack): 通信安全性: 安全分级: 区域隔离: 防跟踪: 防中继攻击(Replay attack): 防垃圾桶攻击(Trashcan attack): 蓝牙Mesh的缺陷...
  • 蓝牙Mesh简介

    千次阅读 2020-09-20 01:13:21
    蓝牙Mesh的优势使的其智能设备产品可以覆盖家用、商用方面的照明、电工、传感等多个领域。而蓝牙Mesh技术拓展了蓝牙的通讯关系,打破了以往蓝牙ble设备只能够一对一、一对多通讯的限制,使网络内的蓝牙设备可以实现...
  • 蓝牙mesh的基本架构: 承载层(bearer layer):承载层定义了如何使用底层低功耗堆栈传输PDU。目前定义了两个承载层:广播承载层(Advertising Bearer)和GATT承载层。 网络层(network layer):网络层定义了...
  • 因为篇幅有限该系列文章重点是在介绍蓝牙mesh相关的内容,但是由于蓝牙mesh并不是一个单独的技术,而是建立在 低功耗蓝牙(BLE) 基础之上的,因此会涉及到一些 BLE 相关的知识需要读者自行查阅资料。读者最好对 ...
  • 蓝牙mesh协议规范文档

    2019-03-26 08:54:40
    蓝牙MESH协议规范官方文档,英文版

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,002
精华内容 1,200
关键字:

蓝牙mesh