
1、简介
AliOS Things 的3.1.0版本里包含有全功能的蓝牙协议栈,那么到底我们应该如何使用AliOS Thing系统里的蓝牙功能呢?
本文以AliOS Things的HaaS分支:https://github.com/alibaba/AliOS-Things/tree/dev_3.1.0_haas为例,带大家来了解一下AliOS Things里的蓝牙协议栈架构。
了解完蓝牙协议栈架构,HaaS100上BLE示例可以参考:
2、蓝牙协议栈介绍
按照蓝牙SIG组织的定义,整个蓝牙协议大致可以分为经典蓝牙,低功耗蓝牙(BLE)和 最新的蓝牙mesh技术,大家可以看下图来了解整个蓝牙协议的框架。

由于目前市面上绝大部分的蓝牙芯片通常分为单模(单低功耗)芯片和双模(经典+低功耗)芯片,所以为了适配这两种芯片,我们在AliOS Things里的蓝牙协议栈也分为两种:
- BLE协议栈,路径components/wireless/bluetooth/ble_host
- 经典蓝牙+低功耗蓝牙+mesh协议栈,路径components/wireless/bluetooth/btstack/。
3、BLE协议栈代码架构
低功耗蓝牙协议栈采用的是开源的Zephyr的协议栈,其代码目录结构以及主要内容如下,协议栈实现的代码在ble_host/bt_host/host/目录下,如下图所示。

可以看到,对照第二节的蓝牙协议栈层次图,蓝牙协议栈实现的代码文件名都是按照ble协议的不同层和功能模块命名的,例如hci_core.c,hci_ecc.c,l2cap.c,att.c,gatt.c,smp.c等等。
- hci_core.c: 蓝牙HCI协议层的核心代码。
- hci_ecc.c: 软件实现HCI椭圆曲线算法部分,方便适配性能比较弱的蓝牙芯片。
- l2cap.c: 蓝牙L2CAP层实现。
- att.c: 蓝牙ATT协议层实现。
- gatt.c: 蓝牙GATT规范实现,这一层定义BLE基础交互规范。
- smp.c 蓝牙SMP协议层实现,这一层是BLE的安全层,用于协商加密密钥。
大家可以阅读相关代码来学习该层协议是如何实现的。
开始时候用蓝牙协议时需要先对hci接口以及蓝牙协议栈进行初始化,增加以下代码:
/* we need first init hci driver */
hci_h4_driver_init();
/* bt stack init */
ret = ble_stack_init(&init);
if (ret) {
EXAMPLE_TRACE_ERROR("ble_stack_init!, ret = %x\r\n", ret);
return -1;
}
3.1 BLE广播
BLE常用功能包括BLE广播,设置BLE广播并开始/停止广播的接口如下
/** @brief Start advertising
*
* Set advertisement data, scan response data, advertisement parameters
* and start advertising.
*
* @param param Advertising parameters.
* @param ad Data to be used in advertisement packets.
* @param ad_len Number of elements in ad
* @param sd Data to be used in scan response packets.
* @param sd_len Number of elements in sd
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_adv_start(const struct bt_le_adv_param *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len);
/** @brief Stop advertising
*
* Stops ongoing advertising.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_adv_stop(void);
3.2 BLE连接
建立或断开BLE连接的接口在conn.c中
/** @brief Disconnect from a remote device or cancel pending connection.
*
* Disconnect an active connection with the specified reason code or cancel
* pending outgoing connection.
*
* @param conn Connection to disconnect.
* @param reason Reason code for the disconnection.
*
* @return Zero on success or (negative) error code on failure.
*/
int bt_conn_disconnect(struct bt_conn *conn, u8_t reason);
/** @brief Initiate an LE connection to a remote device.
*
* Allows initiate new LE link to remote peer using its address.
* Returns a new reference that the the caller is responsible for managing.
*
* @param peer Remote address.
* @param param Initial connection parameters.
*
* @return Valid connection object on success or NULL otherwise.
*/
struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer,
const struct bt_le_conn_param *param);
3.3 BLE服务
如果使用已经实现了的profile,可以直接在协议栈初始化结束后的回调函数里增加各个profile的初始化,例如
hrs_init(0x01);
bas_init();
dis_init("AOS_BLE_MODEL", "Manufacturer");
而如果想使用自定义的服务,则可以使用这两个接口对gatt service进行注册/注销
/* Server API */ /** @brief Register GATT service. * * Register GATT service. Applications can make use of * macros such as BT_GATT_PRIMARY_SERVICE, BT_GATT_CHARACTERISTIC, * BT_GATT_DESCRIPTOR, etc. * * @param svc Service containing the available attributes * * @return 0 in case of success or negative value in case of error. */ int bt_gatt_service_register(struct bt_gatt_service *svc); /** @brief Unregister GATT service. * * * @param svc Service to be unregistered. * * @return 0 in case of success or negative value in case of error. */ int bt_gatt_service_unregister(struct bt_gatt_service *svc);
对本机服务属性的读写的处理函数接口则在结构体bt_gatt_attr中定义
struct bt_gatt_attr {
/** Attribute UUID */
const struct bt_uuid *uuid;
/** Attribute read callback
*
* @param conn The connection that is requesting to read
* @param attr The attribute that's being read
* @param buf Buffer to place the read result in
* @param len Length of data to read
* @param offset Offset to start reading from
*
* @return Number fo bytes read, or in case of an error
* BT_GATT_ERR() with a specific ATT error code.
*/
ssize_t (*read)(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, u16_t len,
u16_t offset);
/** Attribute write callback
*
* @param conn The connection that is requesting to write
* @param attr The attribute that's being written
* @param buf Buffer with the data to write
* @param len Number of bytes in the buffer
* @param offset Offset to start writing from
* @param flags Flags (BT_GATT_WRITE_*)
*
* @return Number of bytes written, or in case of an error
* BT_GATT_ERR() with a specific ATT error code.
*/
ssize_t (*write)(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, u16_t len,
u16_t offset, u8_t flags);
/** Attribute user data */
void *user_data;
/** Attribute handle */
u16_t handle;
/** Attribute permissions */
u8_t perm;
};
而要读写对方设备的某个属性则使用这几个接口
/** @brief Read Attribute Value by handle
*
* This procedure read the attribute value and return it to the callback.
*
* Note: This procedure is asynchronous therefore the parameters need to
* remains valid while it is active.
*
* @param conn Connection object.
* @param params Read parameters.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params);
/** @brief Write Attribute Value by handle
*
* This procedure write the attribute value and return the result in the
* callback.
*
* Note: This procedure is asynchronous therefore the parameters need to
* remains valid while it is active.
*
* @param conn Connection object.
* @param params Write parameters.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params);
/** @brief Write Attribute Value by handle without response
*
* This procedure write the attribute value without requiring an
* acknowledgment that the write was successfully performed
*
* @param conn Connection object.
* @param handle Attribute handle.
* @param data Data to be written.
* @param length Data length.
* @param sign Whether to sign data
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_gatt_write_without_response(struct bt_conn *conn, u16_t handle,
const void *data, u16_t length,
bool sign);
4、经典蓝牙+BLE+mesh协议栈代码架构
为了适配经典蓝牙+BLE双模芯片,AliOS Things同样提供了另一套经典蓝牙+BLE+mesh的协议栈,这套协议栈移植于开源的Bluedroid协议栈。
mesh部分使用的是Zehpyr的开源mesh协议栈,位于bt_stack/mesh目录,其代码目录结构及主要内容如下图所示:

经典蓝牙+BLE协议栈使用Bluedroid,位于bt_stack/host/bluedroid目录,其代码目录结构及主要内容如下图所示:

Bluedroid的协议栈解析在网上已经有很多了,大家可以查阅网上其他对bluedroid协议栈的架构和解析文章,在这里就不深入细节讨论。提供给上层应用使用的接口函数都放在bt_stack/host/bluedroid/api/目录下。 可以看到,文件也都是按照支持的profile来命名。
对蓝牙协议栈进行初始化以及开启关闭蓝牙的接口在yoc_bt_main.c中
/**
* @brief Get bluetooth stack status
*
* @return Bluetooth stack status
*
*/
yoc_bluedroid_status_t yoc_bluedroid_get_status(void);
/**
* @brief Enable bluetooth, must after yoc_bluedroid_init()
*
* @return
* - BT_OK : Succeed
* - Other : Failed
*/
bt_err_t yoc_bluedroid_enable(void);
/**
* @brief Disable bluetooth, must prior to yoc_bluedroid_deinit()
*
* @return
* - BT_OK : Succeed
* - Other : Failed
*/
bt_err_t yoc_bluedroid_disable(void);
/**
* @brief Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff
*
* @return
* - BT_OK : Succeed
* - Other : Failed
*/
bt_err_t yoc_bluedroid_init(void);
/**
* @brief Deinit and free the resource for bluetooth, must be after every bluetooth stuff
*
* @return
* - BT_OK : Succeed
* - Other : Failed
*/
bt_err_t yoc_bluedroid_deinit(void);
其余接口大家可以根据自己的实际需要去对应文件中查找。
5、蓝牙应用
如上即是Alios Things的蓝牙协议栈介绍,可以看出Alios Things的两套蓝牙协议栈分别可用于资源要求较高的BLE场景以及功能要求多的经典蓝牙+BLE场景。
而HaaS100使用的蓝牙芯片是一款同时支持BLE+经典蓝牙的双模蓝牙芯片,可以无缝使用Alios Things这2套蓝牙协议栈。后续我们会有比较多的蓝牙应用文章来使用HaaS100这块开发板完成各种场景下的蓝牙应用,敬请期待。
6、开发者技术支持
如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号

更多技术与解决方案介绍,请访问阿里云AIoT首页https://iot.aliyun.com/
转载:https://blog.csdn.net/HaaSTech/article/details/112716348