蓝牙Mesh

1,蓝牙mesh介绍
蓝牙Mesh_第1张图片
蓝牙Mesh网络模型:
蓝牙Mesh_第2张图片
蓝牙Mesh提高灵活度:

  • 代理节点(Proxy)
  • 低功耗节点(Low-Power)
  • 转发节点(Relay)
  • 朋友节点(Friend)
    蓝牙Mesh_第3张图片
    2,蓝牙Mesh基本概念
    a,蓝牙Mesh网络层次
    蓝牙Mesh_第4张图片
    分包组包:
    蓝牙Mesh_第5张图片
    蓝牙Mesh分层协议在light_ctl.c中的实现(具体分析代码opcode)
    b,蓝牙Mesh洪泛管理
    消息缓存队列
    消息寿命蓝牙Mesh_第6张图片
    c,蓝牙Mesh四大节点
    • 代理节点
    • 转发节点
    • 朋友节点
    • 低功耗节点
      在蓝牙Mesh中朋友节点和低功耗节点必须成对存在
      蓝牙Mesh_第7张图片
      d,蓝牙Mesh节点
      蓝牙Mesh_第8张图片
      蓝牙Mesh元素(插座上每个插孔都是一个元素)
      蓝牙Mesh_第9张图片
      蓝牙Mesh_第10张图片
      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的文件夹用来存放配置文件:
蓝牙Mesh_第11张图片
在vscode下创建文件(settings.json),将settings.json 的源码自行添加:
蓝牙Mesh_第12张图片

{
"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编辑

蓝牙Mesh_第13张图片

{
"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"
},
]
}

通过以上配置,进行任务查看:
蓝牙Mesh_第14张图片
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 修改点灯代码:
蓝牙Mesh_第15张图片
呼吸灯程序:
蓝牙Mesh_第16张图片
4,天猫精灵IOT
a,天猫精灵开放平台介绍
https://www.aligenie.com/
进入生活物联网平台
b,创建一个产品
c,调试中心
蓝牙Mesh_第17张图片
蓝牙Mesh_第18张图片
蓝牙Mesh_第19张图片
蓝牙Mesh_第20张图片
d,天猫精灵,蓝牙Mesh,云端三者之间的关系
蓝牙Mesh_第21张图片
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)来区分不同的数据种类进行不同的处理。

​ 协议图:
蓝牙Mesh_第22张图片
​ 上图可见,对于配网协议的分层只有三层,分别为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;
    }
}

蓝牙Mesh_第23张图片
蓝牙Mesh_第24张图片

3、配网步骤(ADV模式)

①信标阶段(Beacons)

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

​ 对应Beacon包格式如下:

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

​ 示意图:
蓝牙Mesh_第26张图片
​ 信标阶段发现了设备,同时也要确定该设备是否可被连接,则进行上图的Link Open,数据包中包含UUID。
蓝牙Mesh_第27张图片

Parameters字段 长度(字节) 备注
Link Open 1 Link open=0 GPCF = 3
UUID 16 设备UUID

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

②邀请阶段

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

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

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

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

③交换公钥阶段

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

​ 流程图:
蓝牙Mesh_第30张图片
​ 在此阶段,配网器会像未配网设备发送Provisioning Start和Provisioning Public Key,看下具体数据内容:

Provisioning Start:

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

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

④身份认证

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

​ 身份认证形式:
蓝牙Mesh_第31张图片
蓝牙Mesh_第32张图片
蓝牙Mesh_第33张图片
蓝牙Mesh_第34张图片

Provisioning Confirmation:

Parameters字段 长度(字节) 说明
Confirmation 16 OOB信息

Provisioning Random:

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

⑤分发配网数据

​ 经历了前面的坎坷,终于可以变身成为蓝牙Mesh的节点。
蓝牙Mesh_第35张图片

​ Provisioning Data:

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

Provisioning Complete:

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

四、总结:

总结得:蓝牙Mesh过程分为五个阶段:信标阶段,邀请阶段,交换公钥阶段,身份验证阶段,分发密钥数据阶段。
蓝牙Mesh_第36张图片
蓝牙Mesh_第37张图片

蓝牙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程序中加入厂商模型属性代码,如下:
蓝牙Mesh_第38张图片
编译成功后,将固件文件下载到开发板中,使用生活物联网平台下的调试界面,通过下发命令(将灯调为xx颜色),通过串口来看打印信息。进一步判定代码的正确性。

①通用服务模型字段说明

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

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

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

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

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

渐变字段高2位 渐变时间单位 渐变字段高2位 渐变时间单位
0b00 100ms 0b10 10s
0b01 1s 0b11 10min

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

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

②通用开关服务模型

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

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

获取开关状态

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

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

设置开关状态

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

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

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

设置开关状态(无确认)

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

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

开关状态

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

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

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

③ Lighting Server 模型

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

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

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

​ 元素、模型和状态三者之间个关系:
蓝牙Mesh_第39张图片
​ 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模型,这种关系叫做共生关系。
蓝牙Mesh_第40张图片

​ 上图所示: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具有相同结构:

字段 大小(字节) 说明
光亮度值 2 Light Lighness Actual状态的目标值
TID 1 传输标识,可以用此字段判定当前收到的消息是一条新消息还是重传消息
状态切换时间 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 状态

字段 大小(字节) 说明
当前亮度 2 Light lightness Actual 当前状态值
目标亮度 2 Light 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 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#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"
    },
]
}

你可能感兴趣的:(笔记,mesh)