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