LV04-天猫蓝牙Mesh项目-02-初级开发-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设备开发指南 阿里云生活服务平台开发手册——蓝牙设备开发一节中的内容

这一LV的笔记起始跟前面有所重叠,互相补充吧算是。

一、创建风扇控制工程

(1)复制key_fan工程,更名为fan_node。

(2)修改fan_node.mk文件,工程和文件名,并开启天猫精灵认证宏和厂商模型服务。

1
2
3
GLOBAL_DEFINES += GENIE_OLD_AUTH    # 认证的时候需要

MESH_MODEL_VENDOR_SRV = 1

(3)配置天猫精灵三元组信息。

在 genie-bt-mesh-stack-master/genie_app/base/tri_tuple_default.h 文件中。

1
2
3
#define DEFAULT_PID 18662569
#define DEFAULT_SECRET "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define DEFAULT_MAC "xxxxxxxxxxxx"

二、初始化流程代码分析

1. 函数调用关系

genie_mesh_init初始化流程

2. 配网信息广播包

2.1 UUID格式

下面这个表在AliGenie开发者平台 (tmall.com)——天猫精灵蓝牙mesh软件基础规范的3.2.1 mesh广播包一节,新的文档中也有,只是有些许不同,但大概都是一个意思,新的文档在这里:蓝牙Mesh模组软件规范——UUID格式-生活物联网平台(飞燕平台)-阿里云帮助中心 (aliyun.com)

天猫精灵广播包

2.2 代码中组合UUID

下面两个函数都位于genie_app/base/tri_tuple.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
51
52
53
54
55
56
57
int8_t genie_tri_tuple_load(void)
{
//三元组在 tri_tuple_default.h 文件定义
char l_mac[] = DEFAULT_MAC;
char l_key[] = DEFAULT_SECRET;
E_GENIE_FLASH_ERRCODE ret;
//从flash中读取三元组(三元组没在flash里放)
ret = genie_flash_read_trituple(&g_pid, g_mac, g_key);

if(ret != GENIE_FLASH_SUCCESS) {
BT_ERR("read error, use default");
//拷贝宏定义提供的三元组
g_pid = DEFAULT_PID;
stringtohex(l_key, g_key, 16);
stringtohex(l_mac, g_mac, 6);
return -1;
}

return 0;
}

uint8_t *genie_tri_tuple_get_uuid(void)
{
int i;

// all fields in uuid should be in little-endian
// CID: Taobao
g_uuid[0] = 0xa8;
g_uuid[1] = 0x01;

// PID
// Bit0~Bit3: 0001 (broadcast version)
// Bit4: 1 (one secret pre device)
// Bit5: 1 (OTA support)
// Bit6~Bit7: 01 (ble 4.0)
g_uuid[2] = 0x71;

// Product ID
for (i = 0; i < 4; i++) {
g_uuid[3 + i] = (g_pid >> (i<<3)) & 0xFF;
}

// mac addr (device name)
for (i = 0; i < 6; i++) {
g_uuid[7 + i] = g_mac[5 - i];
}

g_uuid[13] = UNPROV_ADV_FEATURE_AUTO_BIND_MODEL_SUB;

g_uuid[14] = UNPROV_ADV_FLAG_GENIE_MESH_STACK;
#ifdef GENIE_ULTRA_PROV
g_uuid[14] |= UNPROV_ADV_FEATURE_ULTRA_PROV;
#endif
BT_INFO("uuid: %s", bt_hex(g_uuid, 16));

return g_uuid;
}

3. 成分数据

3.1 成分数据包格式

成分数据包格式

3.2 代码实现

  • 填充一些成分数据
image-20231231105113226
  • 成分数据注册

成分数据注册是在network/bluetooth/bt_mesh/src/main.c的bt_mesh_init()函数中:

image-20231231105645441

4. 元素封装

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
/* 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];

// 当有多个元素的时候,要为每一个元素都定义一个元素模型变量,因为每个元素的模型可能是不一样的
static struct bt_mesh_model element_models[] = {
BT_MESH_MODEL_CFG_SRV(), // 配置服务端模型
BT_MESH_MODEL_HEALTH_SRV(), // 配置健康服务模型

#ifdef CONFIG_MESH_MODEL_GEN_ONOFF_SRV
MESH_MODEL_GEN_ONOFF_SRV(&g_elem_state[0]), // 通用开关模型
#endif
};

// 当有多个元素的时候,厂商模型就定义这一个就够了,但是要添加所有的元素进来
static struct bt_mesh_model g_element_vendor_models[] = {

#ifdef CONFIG_MESH_MODEL_VENDOR_SRV
MESH_MODEL_VENDOR_SRV(&g_elem_state[0]), //厂商服务模型
#endif
};

//SIG模型和厂商模型 封装到一起
// 当有多个元素的时候,要把所有的元素模型和厂商模型度进行封装
struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, element_models, g_element_vendor_models, 0),
};

三、代码实现

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

static float last_speed = 0.0;
//厂商消息控制风扇
static uint8_t vendor_fan_ctrl(vnd_model_msg *p_msg)
{
float speed = 0.0;
if(p_msg == NULL)
{
printf("p_msg 错误\r\n");
goto error;
}

if(p_msg->len == 3) //风扇方向或者风扇风速进入
{
if(p_msg->data[0] == 0x21 && p_msg->data[1] == 0x05) //风扇方向
{
if(p_msg->data[2] == 0) //风扇方向 - 正转
current_status = FOREWARD;
else if(p_msg->data[2] == 1) //风扇方向 - 反转
current_status = REVERSAL;

fan_ctrl(current_status,current_speed);

}
else if(p_msg->data[0] == 0x0A && p_msg->data[1] == 0x01) //风速控制
{
speed = ((float)p_msg->data[2]) / 10;
fan_ctrl(current_status,speed);
}
else
{
printf("属性类型不支持\r\n");
goto error;
}

}
else if(p_msg->len == 5) //风扇风速调大一点或者小一点进入
{
if(p_msg->data[0] == 0x0B && p_msg->data[1] == 0xF0) //0xF00B 风扇风速调大小
{
if(p_msg->data[2] == 0x0A && p_msg->data[3] == 0x01) //0x010A 厂商风扇风速属性
{
speed = current_speed;
if(p_msg->data[4] == 0x01) //调大一点
{
speed += 0.1;
if(speed > 1)
speed = 1;
}
else if(p_msg->data[4] == 0xFF) //调小一点
{
speed -= 0.1;
if(speed < 0)
speed = 0;
}
else
goto error;

fan_ctrl(current_status,speed);

}
}
else /*不是 0xF00B*/
goto error;
}
return 0;

error:
return 1;

}
//发送风扇风速状态
static uint8_t vendor_fan_status_send(void)
{
vnd_model_msg reply_msg;
uint8_t payload[3] = {0};
static uint8_t tid = 0;
uint16_t attr_type = SPEED_TYPE;

payload[0] = attr_type & 0xff;
payload[1] = (attr_type >> 8) & 0xff;
payload[2] = (uint8_t)(current_speed*10);


reply_msg.opid = VENDOR_OP_ATTR_STATUS;
reply_msg.tid = tid++;
reply_msg.data = payload;
reply_msg.len = sizeof(payload);
reply_msg.p_elem = &elements[0];
reply_msg.retry_period = 120 + 300;
reply_msg.retry = 1;

genie_vendor_model_msg_send(&reply_msg);

printf("发送风扇风速 = %u tid = %u\r\n",payload[2],reply_msg.tid);

}
//厂商消息接收
u16_t vendor_model_msg_handle(vnd_model_msg *p_msg)
{
printk("vendor model message received\n");
if (!p_msg)
return -1;

printf("opcode:0x%x, tid:%d, len:%d\r\n", p_msg->opid, p_msg->tid, p_msg->len);
if (p_msg->data && p_msg->len)
printk("payload: %s", bt_hex(p_msg->data, p_msg->len));

switch (p_msg->opid) {
case VENDOR_OP_ATTR_GET_STATUS:
/* report VENDOR_OP_ATTR_STATUS */

printf("获得属性状态\r\n");
break;
case VENDOR_OP_ATTR_SET_ACK:
/* TODO: set status
* report VENDOR_OP_ATTR_STATUS
* */
printf("设置属性状态需应答\r\n");
if(vendor_fan_ctrl(p_msg) == 0)
{
printf("设置成功\r\n");
//回复响应
genie_vendor_model_msg_send(p_msg);
}

break;
case VENDOR_OP_ATTR_SET_UNACK:
/* TODO: set status */
printf("设置属性状态不需应答\r\n");
break;
case VENDOR_OP_ATTR_CONFIME:
/* clear indicate status */

break;
case VENDOR_OP_ATTR_TRANS_MSG:
break;
default:
break;
}

return 0;
}

//通用开关事件调用风扇开关
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:
BT_DBG("FLASH x5");
led_flash(5);
reset_light_para();
break;
case GENIE_EVT_SDK_MESH_INIT:
user_init();
if (!genie_reset_get_flag()) {
next_event = GENIE_EVT_SDK_ANALYZE_MSG;
}
break;
case GENIE_EVT_SDK_MESH_PROV_SUCCESS:
BT_DBG("FLASH x3");
led_flash(3);
break;
case GENIE_EVT_SDK_ACTION_DONE:
{
elem_state_t *p_elem = (elem_state_t *)p_arg;
#if defined(CONFIG_MESH_MODEL_GEN_ONOFF_SRV)

if(p_elem->state.onoff[0] == 0)
{
fan_ctrl(current_status,0.0);
last_speed = current_speed;
printf("关闭风扇\r\n");
}
else if(p_elem->state.onoff[0] == 1)
{
fan_ctrl(current_status,last_speed);
printf("开启风扇\r\n");
}

#endif

break;
}
#ifdef CONFIG_MESH_MODEL_VENDOR_SRV
case GENIE_EVT_SDK_INDICATE:
break;
case GENIE_EVT_SDK_VENDOR_MSG:
{
printf("进入到厂商消息\r\n");
vendor_model_msg_handle((vnd_model_msg *)p_arg);
break;
}
#endif
case GENIE_EVT_HW_RESET_DONE:
printk("GENIE_EVT_HW_RESET_DONE\n");
break;
default:
break;
}

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