LV03-04-天猫蓝牙Mesh开发-05-天猫精灵控制智能灯

本文主要是天猫蓝牙Mesh开发——天猫精灵控制智能灯的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows版本 windows11
Ubuntu版本 Ubuntu22.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
点击查看本文参考资料
分类 网址 说明
官方网站 阿里云 阿里云官网主页
阿里生活物联平台 生活物联网平台(飞燕平台)主页
AliGenie 天猫精灵开放平台AliGenie主页
阿里物联网平台 阿里物联网平台主页
Bluetooth 技术网站 蓝牙协议规范什么的可以来这里找
Telink Telink | Chips for a Smarter IoT (telink-semi.com)
Telink中文官网
开发手册 AliOS Things开发指南 AliOS Things开发指南,这里是最新版本,可以直接从官网找到
AliOS Things开发指南 AliOS Things应用开发指南,这里应该是3.3版本的完整开发文档
AliOS Things开发指南(3.0) AliOS Things应用开发指南,这里应该是3.0版本的完整开发文档
生活物联网平台开发文档 生活物联网平台(飞燕平台)开发文档
《设备端开发指南》
Wi-Fi IoT品类定义与功能开发 天猫精灵IoT开放平台——Wi-Fi IoT品类定义与功能开发
硬件平台 mk3080 WiFi开发板 WiFi开发板使用指南-阿里云开发者社区
esp8266开发板 一个教程:ESP8266-NodeMCU开发板详解-太极创客 (taichi-maker.com)
TLSR8258 Datasheet Datasheet for Telink BLE + IEEE802.15.4 MultiStandard Wireless SoC TLSR8258
参考资料 AliOS Things 3.0 应用开发指南 这个只是一篇参考文章,里面是一些环境搭建相关的,可以参考
IP知识百科 - 华为 (huawei.com) IP的一些相关知识点
点击查看相关文件下载
分类 网址 说明
蓝牙规范相关文档 Core Specification 5.2 核心规格 5.2,该规范定义了创建可互操作的Bluetooth 设备所需的技术。
《Core_v5.2.pdf》
Mesh Model(v1.1) 本Bluetooth 规范定义了模型(以及它们所需的状态和消息),这些模型用于在mesh 网络中的节点上执行基本功能,超出了Bluetooth Mesh 配置文件 规范中定义的基础模型。
本规范包括定义跨设备类型标准功能的通用模型,以及支持关键mesh 场景的模型,如照明控制、传感器、时间和场景。
《MshMDL_v1.1.pdf》
Mesh Profile(v1.0.1) 该Bluetooth 规范定义了基本要求,以实现可互操作的mesh 网络解决方案,用于Bluetooth 低能量无线技术。
《MshPRFv1.0.1.pdf》
Mesh Device Properties 本规范包含Bluetooth Mesh 配置文件 和Bluetooth Mesh 模型规范所要求的设备属性的定义。
但是跟之前的有些区别,我主要看的之前的版本:《MMeshDeviceProperties_v1.2.pdf》
GATT Specification Supplement GATT Specification Supplement | Bluetooth® Technology Website。
好像可以在线看:《GATT Specification Supplement》
Assigned Numbers GATT的一些类型定义可以在这里找。
AliOS Things alios-things/AliOS-Things Gitee上的AliOSThings SDK源码仓库
alibaba/AliOS-Things GitHub上的AliOSThings SDK源码仓库
天猫精灵蓝牙Mesh协议栈 alibaba-archive/genie-bt-mesh-stack GitHub上的天猫精灵蓝牙Mesh协议栈源码仓库。
之前是在alibaba/genie-bt-mesh-stack这个仓库。
写笔记的时候最新提交为faf523618a6a2560090fc423222b9db80984bb7a
蓝牙Mesh设备开发指南 阿里云生活服务平台开发手册——蓝牙设备开发一节中的内容

一、控制灯的开关

1. 精灵事件

1.1 genie_event()

我们找到这个函数,它定义在SDK源码顶层目录的genie_app/base/genie_event.c中,声明在对应的h文件中:

1
2
3
4
5
6
7
8

/**
* @brief The handler for the underlying events. If necessary
* this handler dispatch the user events to applications.
* @param[in] event refers to the event details.
* @param[in] args refers to the additional information for the event.
*/
void genie_event(E_GENIE_EVENT event, void *args);

可以看到这个函数是用于处理事件的,我们看一下都是怎么使用的在 genie_app/bluetooth/mesh/genie_mesh.c中

1
2
3
4
5
6
static void _genie_mesh_ready(int err)
{
//......
//send event
genie_event(GENIE_EVT_SDK_MESH_INIT, NULL);
}

可以看到这个函数传入了一个宏GENIE_EVT_SDK_MESH_INIT,这其实就是一个事件的宏,然后在函数中进行处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

void genie_event(E_GENIE_EVENT event, void *p_arg)
{
E_GENIE_EVENT next_event = event;
uint8_t ignore_user_event = 0;

#ifdef CONFIG_MESH_MODEL_TRANS
if(event != GENIE_EVT_SDK_TRANS_CYCLE) {
GENIE_MESH_EVENT_PRINT(event);
}
#endif
switch(event) {
case GENIE_EVT_SW_RESET:
//call user_event first
user_event(GENIE_EVT_SW_RESET, p_arg);
ignore_user_event = 1;
next_event = _genie_event_handle_sw_reset();
break;
//中间还有大量的宏定义,这里省略......
case GENIE_EVT_SDK_MESH_INIT:
//update p_arg to user_event
p_arg = (void *)&g_elem_state[0];
next_event = _genie_event_handle_mesh_init();
break;
//中间还有大量的宏定义,这里省略......
default:
break;
}

if(!ignore_user_event)
user_event(event, p_arg);

if(next_event != event) {
genie_event(next_event, p_arg);
}
}

可以看到这里面有大量的case,每一个case里面会执行对应的分支。而且,在最下面,每一个分支执行完毕之后,若是ignore_user_event为0,都还会执行一个user_event()。

1.2 user_event()

我们找一下它的定义,会发现,它定义在我们的demo中(如app/example/bluetooth/light_ctl_demo/light_ctl_demo.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void user_event(E_GENIE_EVENT event, void *p_arg)
{
E_GENIE_EVENT next_event = event;

//BT_DBG("%s, %s %p\n", __func__, genie_event_str[event], p_arg);
switch(event) {
case GENIE_EVT_SW_RESET:
case GENIE_EVT_HW_RESET_START:
_led_flash(5, 1);
break;
case GENIE_EVT_HW_RESET_DONE:
_reset_light_para();
BT_DBG("GENIE_EVT_HW_RESET_DONE\n");
break;
case GENIE_EVT_SDK_MESH_INIT:
#if defined(BOARD_TG7100B) || defined(BOARD_CH6121EVB)
_user_init();
#else
_led_init();
_init_light_para();
_user_init();
if (!genie_reset_get_flag()) {
next_event = GENIE_EVT_SDK_ANALYZE_MSG;
}
#endif
break;
case GENIE_EVT_SDK_MESH_PROV_SUCCESS:
_led_flash(3, 0);
break;
case GENIE_EVT_SDK_TRANS_CYCLE:
case GENIE_EVT_SDK_ACTION_DONE:
{
elem_state_t *p_elem = (elem_state_t *)p_arg;
_led_ctrl(p_elem);
if(event == GENIE_EVT_SDK_ACTION_DONE)
_save_light_state(p_elem);
break;
}
case GENIE_EVT_SDK_INDICATE:
break;
case GENIE_EVT_SDK_VENDOR_MSG:
break;
default:
break;
}

if(next_event != event) {
genie_event(next_event, p_arg);
}
}

这里也是一堆的case分支,每个分支会去处理不同的事,我们看到有这么一个分支GENIE_EVT_SDK_MESH_INIT,是不是很熟悉,上面的genie_event也有,也就是说,上面调用了genie_event()之后,就会进入user_event()进行处理。

1.3 总结

从上面的分析可知,当出现有genie_event()时候会处理一些内容,然后就会调用user_event()去处理用户自定义事件。

2. LED控制

2.1 light_ctl_default.c文件

这里有坑,这只是第一次写的时候踩的,就放在这里吧。后面直接看2.6节即可。

2.1.1 _led_init()

这个函数在app/example/bluetooth/light_ctl_demo/light_ctl_default.c中定义,我们在这里完成这个初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define LED0_PIN TC825X_GET_PIN_NUM(GPIO_PB4) // RED——PC1
#define LED1_PIN TC825X_GET_PIN_NUM(GPIO_PB5) // GREEN——PC1
#define LED2_PIN TC825X_GET_PIN_NUM(GPIO_PC1) // BLUE——PC1

static gpio_dev_t led_dev[3] = {0};

static void _led_init(void)
{
int32_t ret = 0;
led_dev[0].port = LED0_PIN; /* gpio port config */
led_dev[0].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[0]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}

led_dev[1].port = LED1_PIN; /* gpio port config */
led_dev[1].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[1]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
led_dev[2].port = LED2_PIN; /* gpio port config */
led_dev[2].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[2]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
printf("++++++++++ led init! ++++++++++\n");
return ret;
}

2.1.2 _led_set()

这个函数在app/example/bluetooth/light_ctl_demo/light_ctl_default.c中定义,我们在这里完成这个led状态设置:

1
2
3
4
5
6
7
8
9
10
11
static void _led_set(uint8_t onoff, uint16_t actual, uint16_t temperature)
{
if(onoff)
{
hal_gpio_output_high(&led_dev[0]);
}
else
{
hal_gpio_output_low(&led_dev[0]);
}
}

2.1.3 完整代码

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <aos/aos.h>
#include <hal/soc/gpio.h>
#include "drivers/8258/gpio_8258.h"

#define LED0_PIN TC825X_GET_PIN_NUM(GPIO_PB4) // RED——PC1
#define LED1_PIN TC825X_GET_PIN_NUM(GPIO_PB5) // GREEN——PC1
#define LED2_PIN TC825X_GET_PIN_NUM(GPIO_PC1) // BLUE——PC1

static gpio_dev_t led_dev[3] = {0};

static void _led_init(void)
{
int32_t ret = 0;
led_dev[0].port = LED0_PIN; /* gpio port config */
led_dev[0].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[0]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}

led_dev[1].port = LED1_PIN; /* gpio port config */
led_dev[1].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[1]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
led_dev[2].port = LED2_PIN; /* gpio port config */
led_dev[2].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[2]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
printf("++++++++++ led init! ++++++++++\n");
return ret;
}

static void _led_set(uint8_t onoff, uint16_t actual, uint16_t temperature)
{
if(onoff)
{
hal_gpio_output_high(&led_dev[0]);
}
else
{
hal_gpio_output_low(&led_dev[0]);
}
}

2.2 _init_light_para()

2.2.1 函数定义

这个函数暂时不需要修改,这里需要了解一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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].powerup.last_onoff = g_powerup[0].last_onoff;
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);

// cal transition
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++;
}
}
//LIGHT_DBG("done");
}

这里比较重要的是这个g_elem_state数组

2.2.2 g_elem_state[]数组

这个数组的定义为:

1
2
#define MESH_ELEM_COUNT 1
elem_state_t g_elem_state[MESH_ELEM_STATE_COUNT];

它表示的事蓝牙Mesh中“元素”的状态,这个数组中每个元素(与前面的元素含义不同,这里指数组的元素)的数据类型为:

1
2
3
4
5
6
typedef struct{
u8_t elem_index;
model_state_t state;
model_powerup_t powerup;
void *user_data;
} elem_state_t;

powerup是用来干嘛的?我们来看一下它的定义:

1
2
3
4
5
6
7
8
9
typedef struct{
#ifdef CONFIG_MESH_MODEL_GEN_ONOFF_SRV
u8_t last_onoff;
#endif
#ifdef CONFIG_MESH_MODEL_LIGHTNESS_SRV
u16_t last_actual;
#endif
//......
} model_powerup_t;

可以看到里面是一些变量,开了宏之后才会使用,那么具体有什么用?假设我们现在使用天猫精灵开了灯,不巧的是,停电了,那么再有电的时候,我们的灯是不是应该保持停电前的状态呢?为了保存掉电之前的状态,就会把相关的内容保存在这里,保存在这个结构体中的数据会被写入到flash中,重新上电的时候会从flash读取对应的数据,恢复原来的状态。所以这里会有一个全局变量数组,它的成员数量与g_elem_state保持一致,每个“元素”都要有这样的一个保存状态的东西:

1
model_powerup_t g_powerup[MESH_ELEM_STATE_COUNT];

2.3 element_models[]

app/example/bluetooth/light_ctl_demo/light_ctl_demo.c中还有这样一个模型的数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static struct bt_mesh_model element_models[] = {
// 这两个必须加上
BT_MESH_MODEL_CFG_SRV(),
BT_MESH_MODEL_HEALTH_SRV(),

MESH_MODEL_GEN_ONOFF_SRV(&g_elem_state[0]), // 通用的开关模型
MESH_MODEL_LIGHTNESS_SRV(&g_elem_state[0]),
MESH_MODEL_CTL_SRV(&g_elem_state[0]),
#ifndef CONFIG_ALI_SIMPLE_MODLE
MESH_MODEL_GEN_LEVEL_SRV(&g_elem_state[0]),
MESH_MODEL_CTL_SETUP_SRV(&g_elem_state[0]),
#endif

};

前面我们知道,这里的模型起始就是对应功能,其实网页好像也叫模型产品管理 - 生活物联网平台 (aliyun.com)——功能定义

image-20231225234403787

在阿里云上的物模型的定义为:物模型是对设备在云端的功能描述,包括设备的属性、服务和事件。物联网平台通过定义一种物的描述语言来描述物模型,称之为 TSL(即 Thing Specification Language),采用 JSON 格式,您可以根据 TSL 组装上报设备的数据。您可以导出完整物模型,用于云端应用开发;您也可以只导出精简物模型,配合设备端 SDK 实现设备开发。

所以说,这里面的开关属性,就是代码中的MESH_MODEL_GEN_ONOFF_SRV()模型。需要注意的是,一个元素可以有多个模型,也就是多个功能,我们只要放在这里就可以啦,后面会有地方调用到的。

2.4 g_element_vendor_models[]

1
2
3
static struct bt_mesh_model g_element_vendor_models[] = {
MESH_MODEL_VENDOR_SRV(&g_elem_state[0]),
};

这个是通用厂商模型,比如我们要定义一些特殊的功能,这些功能在阿里云生活服务平台没有,就需要用到这个来进行自定义啦。

2.5 elements[]

1
2
3
struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, element_models, g_element_vendor_models, 0),
};

这里就是元素啦,这里可以算是元素的各种信息的填充,

2.6 light_ctl_tc825x.c

我们按照2.1小节去修改对应的文件,定义对应的函数,会发现,这里巨坑,这个源文件似乎并没有参与编译(light_ctl_demo.mk中根本没有这个文件),然后一看还有一个light_ctl_tc825x.c文件,里面也有定义了led相关函数,可是最终调用的是哪里的函数?我们看一下light_ctl_demo.c文件的最后面:

image-20231227213041935

会发现这里通过include来包含了c文件,我们搜一下是否有定义BOARD_TC825X这个宏:

image-20231227213226494

会发现是有定义的,所以我们实际使用的是这个文件中的函数,我们修改这个文件如下,这里相对于原文件,我只修改了一部分的内容。

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

#include "drivers/8258/gpio_8258.h"
#include "vendor/common/alios_app_config.h"

#ifdef CONIFG_LIGHT_HONGYAN
#define WARM_PIN TC825X_GET_PIN_NUM(GPIO_PB0)
#define COLD_PIN TC825X_GET_PIN_NUM(GPIO_PB1)
#else
#define WARM_PIN TC825X_GET_PIN_NUM(PWM_R)
#define COLD_PIN TC825X_GET_PIN_NUM(PWM_G)
#endif
#define LIGHT_FREQ 32000

static pwm_dev_t light_led_c;
static pwm_dev_t light_led_w;

uint16_t duty_list[] = {
#include "duty_list.h"
};
#define LED0_PIN TC825X_GET_PIN_NUM(GPIO_PB4) // RED——PC1
#define LED1_PIN TC825X_GET_PIN_NUM(GPIO_PB5) // GREEN——PC1
#define LED2_PIN TC825X_GET_PIN_NUM(GPIO_PC1) // BLUE——PC1

static gpio_dev_t led_dev[3] = {0};

static void _led_init(void)
{
int32_t ret = 0;
led_dev[0].port = LED0_PIN; /* gpio port config */
led_dev[0].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[0]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}

led_dev[1].port = LED1_PIN; /* gpio port config */
led_dev[1].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[1]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
led_dev[2].port = LED2_PIN; /* gpio port config */
led_dev[2].config = OUTPUT_PUSH_PULL;/* set as output mode */
ret = hal_gpio_init(&led_dev[2]); /* configure GPIO with the given settings */
if (ret != 0) {
printf("gpio init error !\n");
}
printf("++++++++++ led init! ++++++++++\n");
return ret;
}

//temperature 800~20000
//ligntness 655~65535
//return duty 0-100
static void _get_led_duty(uint8_t *p_duty, uint16_t actual, uint16_t temperature)
{
uint8_t cold = 0;
uint8_t warm = 0;

if(temperature > LIGHT_CTL_TEMP_MAX) {
temperature = LIGHT_CTL_TEMP_MAX;
}
if(temperature < LIGHT_CTL_TEMP_MIN) {
temperature = LIGHT_CTL_TEMP_MIN;
}

//0-100
cold = (temperature - LIGHT_CTL_TEMP_MIN) * 100 / (LIGHT_CTL_TEMP_MAX - LIGHT_CTL_TEMP_MIN);
warm = 100 - cold;

//0-100
p_duty[LED_COLD_CHANNEL] = (actual * cold) / 65500;
p_duty[LED_WARM_CHANNEL] = (actual * warm) / 65500;
if(p_duty[LED_COLD_CHANNEL] == 0 && p_duty[LED_WARM_CHANNEL] == 0) {
if(temperature > (LIGHT_CTL_TEMP_MAX - LIGHT_CTL_TEMP_MIN)>>1) {
p_duty[LED_COLD_CHANNEL] = 1;
} else {
p_duty[LED_WARM_CHANNEL] = 1;
}
}

//LIGHT_DBG("%d %d [%d %d] [%d %d]", actual, temperature, warm, cold, p_duty[LED_COLD_CHANNEL], p_duty[LED_WARM_CHANNEL]);

}

static int _set_pwm_duty(uint8_t channel, uint8_t duty)
{
int err = -1;
pwm_config_t pwm_cfg;

if(duty > 100) {
LIGHT_DBG("invaild");
return -1;
}

pwm_cfg.freq = LIGHT_FREQ;
pwm_cfg.duty_cycle = (float)duty_list[duty]/duty_list[100];

if (channel == LED_COLD_CHANNEL) {
err = hal_pwm_para_chg(&light_led_c, pwm_cfg);
if (err) {
LIGHT_DBG("cold err %d", err);
return -1;
}

} else if (channel == LED_WARM_CHANNEL) {
err = hal_pwm_para_chg(&light_led_w, pwm_cfg);
if (err) {
LIGHT_DBG("warm err %d", err);
return -1;
}
}
return 0;
}

static void _led_set(uint8_t onoff, uint16_t actual, uint16_t temperature)
{
if(onoff)
{
hal_gpio_output_high(&led_dev[0]);
}
else
{
hal_gpio_output_low(&led_dev[0]);
}
}

3. 工程代码

详细的修改可以看这里:feat:支持天猫精灵和APP控制LED灯 · sumumm/my-genie-bt-mesh-stack@95e872e (github.com)

4. 编译工程

1
aos make bluetooth.light_ctl_demo@tc825x

5. 烧录程序

image-20231226074138417

6. 实验现象

我们烧录完成后,需要重新用天猫精灵连接开发板,并且会有这些打印信息:

image-20231226074305237

这就说明我们的LED初始化过了。

二、呼吸灯实现

1. 亮度模型?

我们可以到genie_app/bluetooth/mesh/mesh_model/inc目录找一些已经定义好的模型,像亮度模型是直接有的。

image-20231226074807652

2. 代码修改

这里不写了,就是加上pwm就可以了。直接看github仓库本次提交的修改吧:feat:天猫精灵调节灯亮度实验 · sumumm/my-genie-bt-mesh-stack@b4d5978 (github.com)

3. 云端属性添加

我们需要在功能定义中添加一个亮度属性:

image-20231226075918166

需要注意的是,这里实际传到我们使用的地方的时候,值范围是0~65536,而非0~100,另外这里是可以配置的:

image-20231226080106730

4. 编译工程

1
aos make bluetooth.light_ctl_demo@tc825x

5. 烧录程序

跟前面配置一样。

6. 实验现象

首先初始化会有如下打印信息:

image-20231226120612484

我们的led和pwm都已初始化。然后使用天猫精灵连接设备,然后用语音“灯的亮度调到100”来调整灯的亮度。