AliOS Things 蓝牙协议栈解析

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示例可以参考:

HaaS100低功耗蓝牙体验

 

2、蓝牙协议栈介绍

按照蓝牙SIG组织的定义,整个蓝牙协议大致可以分为经典蓝牙,低功耗蓝牙(BLE)和 最新的蓝牙mesh技术,大家可以看下图来了解整个蓝牙协议的框架。

AliOS Things 蓝牙协议栈解析_第1张图片

由于目前市面上绝大部分的蓝牙芯片通常分为单模(单低功耗)芯片和双模(经典+低功耗)芯片,所以为了适配这两种芯片,我们在AliOS Things里的蓝牙协议栈也分为两种:

  • BLE协议栈,路径components/wireless/bluetooth/ble_host
  • 经典蓝牙+低功耗蓝牙+mesh协议栈,路径components/wireless/bluetooth/btstack/。

 

3、BLE协议栈代码架构

低功耗蓝牙协议栈采用的是开源的Zephyr的协议栈,其代码目录结构以及主要内容如下,协议栈实现的代码在ble_host/bt_host/host/目录下,如下图所示。

image.png

可以看到,对照第二节的蓝牙协议栈层次图,蓝牙协议栈实现的代码文件名都是按照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目录,其代码目录结构及主要内容如下图所示:

image.png

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

image.png

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/

你可能感兴趣的:(AliOS,Things物联网操作系统,HaaS,实时操作系统,嵌入式,bluetooth,iot)