Bluetooth Pairing Guide

Last Updated on : 2022-11-24 09:20:26download

This topic describes the Bluetooth pairing process. After reading this topic, you will get to know how to perform Bluetooth pairing and complete adaptation of Bluetooth pairing in the integrated gateway SDK.

Background information

Bluetooth pairing is to transmit pairing information over Bluetooth, such as the activation token, and the SSID and password of a router, and thus complete the pairing of devices.

The process of Bluetooth pairing works like this: The gateway sends device information through Bluetooth broadcast, and the mobile app receives and parses the Bluetooth broadcast packet. If the broadcast packet contains Tuya’s services, the mobile app initiates a scan request to further get the device details. After getting the complete information, the mobile app will display devices waiting for pairing. When a user chooses to activate the device, the mobile app establishes a Bluetooth connection to the gateway and sends the pairing information to the gateway over Bluetooth. The gateway gets the pairing information to complete the activation process.

Tuya Bluetooth pairing uses private services and private data. All data is parsed and encapsulated by the SDK, so you do not need to care about the data content.

Implementation: Tuya Bluetooth module

If you use Tuya Bluetooth module designed for the gateway, the adaptation of the Bluetooth pairing has been implemented in the SDK, and the application does not need any code for adaptation. For more information, see Tuya Bluetooth Service Guide.

Implementation: custom module

If a custom module is used, you need to complete the interface adaptation. The SDK has implemented the underlying logic of Bluetooth pairing and defined a set of adaptation interfaces. This section describes how to complete the adaptation.

Pairing process

The following figure shows the interaction process of Bluetooth pairing:

Bluetooth Pairing Guide

A Bluetooth device is paired in the following steps:

  1. Initialize the registration of the Bluetooth adaptation.
  2. After the SDK is initialized, notify the application to initialize the Bluetooth protocol stack or service, and configure the Bluetooth as a Bluetooth low energy (LE) central device.
  3. The SDK encapsulates the broadcast and scan response data and transmits it to the application. The application saves the data and enables broadcast transmission.
  4. The application receives the scan request from the mobile app and returns the scan response data in Step 3 as a response.
  5. The mobile app parses the Bluetooth broadcast packet. If it conforms to Tuya’s Bluetooth data format, the mobile app will display the devices waiting for pairing.
  6. Users can start the pairing process to pair a desired device.
  7. The mobile app establishes a Bluetooth connection to the gateway. As a central device, the gateway monitors the connection event of the Bluetooth peripheral devices, and the application transmits the connection event to the SDK.
  8. The mobile app exchanges data with the gateway over Bluetooth. The gateway application sends the received Bluetooth data to the SDK for parsing and processing. The SDK encapsulates and transmits the response data to the gateway application. Then, the application replies to the mobile app over Bluetooth.
  9. After the SDK parses the pairing information, it transmits the SSID and password of the router to the application. This allows the application to connect to the router. Then, the SDK sends an activation request to the cloud to complete the activation process.

Data format

The following figure shows the data format of Bluetooth broadcast and scan response.

Bluetooth Pairing Guide

The Bluetooth broadcast and scan responses are defined in the Bluetooth specification. The SDK encapsulates the broadcast data and scan response data into the AD Structure format. The application does not need additional parsing and can use the data directly in the broadcast and scan response.

The following figure shows the data format of Bluetooth transmission.

Bluetooth Pairing Guide

After a Bluetooth connection is established between the mobile app and the gateway, the data is transmitted based on the Attribute Protocol (ATT). The data encapsulated and parsed by the SDK belongs to the ATT payload.

Service UUID: 0x1910.

Characteristic UUID Attribute Description
Write characteristic 0x2B11 Write without response The mobile app sends data to the Bluetooth LE device.
Notify characteristic 0x2B10 Notify The Bluetooth LE device sends data to the mobile app.

Interface adaptation

Next, perform the following steps to complete adaptation of Bluetooth pairing:

  1. Before initializing the SDK, initialize the registration of the Bluetooth adaptation.

    // ...
    #if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
    #include "tuya_os_adapt_bt.h"
    #endif
    
    int main(int argc, char **argv)
    {
        /**
         * Must be called earlier than other interfaces of the SDK
         */
    #if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
        tuya_os_adapt_reg_bt_intf();
    #endif
    
        tuya_os_intf_init();
    
        // ...
    }
    
  2. Adapt to the following interface to initialize the Bluetooth protocol stack, configure the Bluetooth to the Bluetooth LE central device mode, and save the function pointer of data processing. Through the function pointer, the received Bluetooth data will be sent to the SDK for processing.

    TY_BT_MSG_CB bt_msg_cb;
    
    OPERATE_RET tuya_adapter_bt_port_init(ty_bt_param_t *p)
    {
        /**
         * The following features need to be implemented in the function:
         *   a) Initialize the Bluetooth protocol stack to ensure normal communications over Bluetooth.
         *   b) Save the function pointer variable, and pass the received Bluetooth data to the SDK through the function pointer.
         */
        bt_msg_cb = p->cb;
    
        return OPRT_OK;
    }
    
  3. Adapt to the interface of resetting the Bluetooth broadcast data, save the broadcast data and scan response data, and enable the broadcast.

    tuya_ble_data_buf_t adv_data = {0};
    tuya_ble_data_buf_t rsp_data = {0};
    
    OPERATE_RET tuya_adapter_bt_adv_reset(tuya_ble_data_buf_t *adv, tuya_ble_data_buf_t *scan_resp)
    {
        if ((adv == NULL) || (adv->len == 0) || (scan_resp == NULL) || (scan_resp->len == 0)) {
            return OPRT_INVALID_PARM;
        }
    
        if (adv_data.data != NULL) {
            Free(adv_data.data);
            adv_data.data = NULL;
        }
        adv_data.len = adv->len;
        adv_data.data = Malloc(adv_data.len);
        if (adv_data.data != NULL) {
            memcpy(adv_data.data, adv->data, adv->len);
        }
        
        if (rsp_data.data != NULL) {
            Free(rsp_data.data);
            rsp_data.data = NULL;
        }
        rsp_data.len = scan_resp->len;
        rsp_data.data = Malloc(rsp_data.len);
        if (rsp_data.data != NULL) {
            memcpy(rsp_data.data, scan_resp->data, scan_resp->len);
        }
    
        /**
         * The logic to be implemented:
         * a) Enable the broadcast (data: adv_data.data, data length: adv_data.len).
         * b) Save the scan response data, and return the data when the scan response request is received (data: rsp_data.data, data length: rsp_data.len).
         */
    
        return OPRT_OK;
    }
    
  4. Adapt to the following interface to send the data to the mobile app.

    OPERATE_RET tuya_adapter_bt_send(BYTE_T *data, UINT8_T len)
    {
        /**
         * Logic to be implemented: Send the data to the mobile app through Send Characteristic Notification of GATT Server.
         */
    
        return OPRT_OK;
    }
    
  5. Send the received Bluetooth data to the SDK for parsing.

    When the Bluetooth connection is established, send an event to the SDK:

    VOID example_ble_connected(VOID)
    {
        tuya_ble_data_buf_t data = {0};
    
        if (bt_msg_cb == NULL) {
            return;
        }
    
        data.len = 0;
        data.data = NULL;
        bt_msg_cb(0, TY_BT_EVENT_CONNECTED, &data);
    }
    

    When the Bluetooth connection is disconnected, send an event to the SDK:

    VOID example_ble_disconnected(VOID)
    {
        tuya_ble_data_buf_t data = {0};
    
        if (bt_msg_cb == NULL) {
            return;
        }
    
        data.len = 0;
        data.data = NULL;
        bt_msg_cb(0, TY_BT_EVENT_DISCONNECTED, &data);
        bt_msg_cb(0, TY_BT_EVENT_ADV_READY, &data);
    }
    

    When the data is received over Bluetooth, send an event to the SDK:

    /**
     * Logic to be implemented: The application receives the Bluetooth data and passes the attribute value of the GATT server in the data to the SDK for processing.  
     */
    VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
    {
        tuya_ble_data_buf_t data = {0};
    
        if (bt_msg_cb == NULL) {
            return;
        }
    
        data.len = rx_len;
        data.data = rx_data;
        bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
    }
    

Appendix

main.c

#include <unistd.h>

#include "uni_log.h"
#include "base_os_adapter.h"
#include "tuya_iot_base_api.h"
#include "tuya_iot_com_api.h"

#include "tuya_iot_sdk_api.h"
#include "tuya_iot_sdk_defs.h"

#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
#include "tuya_os_adapt_bt.h"
#endif

#define PID "fljmamufiym5fktz" // Replaced with your own product ID
#define UUID "tuya461dbc63aeeb991f" // Replaced with your own UUID
#define AUTHKEY "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug" // Replace with your own AuthKey

STATIC VOID __gw_reset_cb(GW_RESET_TYPE_E type)
{
    PR_DEBUG("gw reset callback, type: %d", type);
    if (GW_RESET_DATA_FACTORY != type) {
        exit(0);
    }

    return;
}

STATIC VOID __gw_upgrade_cb(CONST FW_UG_S *fw)
{
    PR_DEBUG("gw upgrade callback");

    if (fw == NULL) {
        PR_ERR("invalid param");
        return;
    }

    PR_DEBUG("        tp: %d", fw->tp);
    PR_DEBUG("    fw_url: %s", fw->fw_url);
    PR_DEBUG("    sw_ver: %s", fw->sw_ver);
    PR_DEBUG("   fw_hmac: %s", fw->fw_hmac);
    PR_DEBUG(" file_size: %u", fw->file_size);

    return;
}

STATIC VOID __gw_active_stat_cb(GW_STATUS_E status)
{
    PR_DEBUG("gw active stat callback, status: %d", status);

    return;
}

STATIC VOID __gw_reboot_cb(VOID)
{
    PR_DEBUG("gw reboot callback");

    exit(0);

    return;
}

STATIC VOID __nw_stat_cb(IN CONST SDK_NW_STAT_T stat)
{
    PR_DEBUG("network stat: %d", stat);

    return;
}

STATIC VOID __wired_stat_cb(IN CONST SDK_WIRED_NW_STAT_T stat)
{
    PR_DEBUG("wired stat: %d", stat);

    return;
}

int main(int argc, char **argv)
{
    OPERATE_RET rt = OPRT_OK;
    GW_PROD_INFO_S prod_info = {0};

    /* gw base callback */
    TY_GW_INFRA_CBS_S gw_cbs = {
        .gw_reset_cb       = __gw_reset_cb,
        .gw_upgrade_cb     = __gw_upgrade_cb,
        .gw_active_stat_cb = __gw_active_stat_cb,
        .gw_reboot_cb      = __gw_reboot_cb,
    };

#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
    tuya_os_adapt_reg_bt_intf();
#endif

    /* initiate os-layer service*/
    tuya_os_intf_init();

    /* initiate iot-layer service */
    TUYA_CALL_ERR_RETURN(tuya_iot_init("./"));

    /* set the logging level to debug */
    SET_PR_DEBUG_LEVEL(TY_LOG_LEVEL_DEBUG);

    PR_DEBUG("SDK INFO: %s", tuya_iot_get_sdk_info());

    /* set uuid and authkey */
    prod_info.uuid     = UUID;
    prod_info.auth_key = AUTHKEY;
    TUYA_CALL_ERR_RETURN(tuya_iot_set_gw_prod_info(&prod_info));

    /* pre-initiate sdk service */
    TUYA_CALL_ERR_RETURN(tuya_iot_sdk_pre_init(TRUE));  

    /* initiate application service, more service in here */
    TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));

    /* initiate sdk service */
#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1)
    TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#elif defined(WIFI_GW) && (WIFI_GW==1)
    TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#else
    TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
#endif

    /* register net stat notification callback */
    TUYA_CALL_ERR_RETURN(tuya_iot_sdk_reg_netstat_cb(__nw_stat_cb, __wired_stat_cb, NULL));

    /* start application service, more service in here */
    TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));

    while (1) {
        sleep(10);
    }

    return 0;
}

hal_bt.c

#include "tuya_os_adapt_bt.h"
#include "mem_pool.h"

TY_BT_MSG_CB bt_msg_cb = NULL;

tuya_ble_data_buf_t adv_data = {0};
tuya_ble_data_buf_t rsp_data = {0};

OPERATE_RET tuya_adapter_bt_port_init(ty_bt_param_t *p)
{
    /**
     * The following features need to be implemented in the function:
     *   a) Initialize the Bluetooth protocol stack to ensure normal communications over Bluetooth.
     *   b) Save the function pointer variable, and pass the received Bluetooth data to the SDK through the function pointer.
     */
    bt_msg_cb = p->cb;

    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_port_deinit(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_gap_disconnect(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_send(BYTE_T *data, UINT8_T len)
{
    /**
     * Logic to be implemented: Send the data to the mobile app through Send Characteristic Notification of GATT Server.
     */

    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_adv_reset(tuya_ble_data_buf_t *adv, tuya_ble_data_buf_t *scan_resp)
{
    if ((adv == NULL) || (adv->len == 0) || (scan_resp == NULL) || (scan_resp->len == 0)) {
        return OPRT_INVALID_PARM;
    }

    if (adv_data.data != NULL) {
        Free(adv_data.data);
        adv_data.data = NULL;
    }
    adv_data.len = adv->len;
    adv_data.data = Malloc(adv_data.len);
    if (adv_data.data != NULL) {
        memcpy(adv_data.data, adv->data, adv->len);
    }
    
    if (rsp_data.data != NULL) {
        Free(rsp_data.data);
        rsp_data.data = NULL;
    }
    rsp_data.len = scan_resp->len;
    rsp_data.data = Malloc(rsp_data.len);
    if (rsp_data.data != NULL) {
        memcpy(rsp_data.data, scan_resp->data, scan_resp->len);
    }

    /**
     * The logic to be implemented:
     * a) Enable the broadcast (data: adv_data.data, data length: adv_data.len).
     * b) Save the scan response data, and return the data when the scan response request is received (data: rsp_data.data, data length: rsp_data.len).
     */

    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_adv_start(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_adv_stop(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_get_rssi(SCHAR_T *rssi)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_scan_assign(INOUT ty_bt_scan_info_t *info)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_scan_init(IN TY_BT_SCAN_ADV_CB scan_adv_cb)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_scan_start(VOID_T)
{
    return OPRT_OK;
}

OPERATE_RET tuya_adapter_bt_scan_stop(VOID_T)
{
    return OPRT_OK;
}

VOID example_ble_connected(VOID)
{
    tuya_ble_data_buf_t data = {0};

    if (bt_msg_cb == NULL) {
        return;
    }

    data.len = 0;
    data.data = NULL;
    bt_msg_cb(0, TY_BT_EVENT_CONNECTED, &data);
}

VOID example_ble_disconnected(VOID)
{
    tuya_ble_data_buf_t data = {0};

    if (bt_msg_cb == NULL) {
        return;
    }

    data.len = 0;
    data.data = NULL;
    bt_msg_cb(0, TY_BT_EVENT_DISCONNECTED, &data);
    bt_msg_cb(0, TY_BT_EVENT_ADV_READY, &data);
}

/**
 * Logic to be implemented: The application receives the Bluetooth data and passes the attribute value of the GATT server in the data to the SDK for processing.  
  */
VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
{
    tuya_ble_data_buf_t data = {0};

    if (bt_msg_cb == NULL) {
        return;
    }

    data.len = rx_len;
    data.data = rx_data;
    bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
}

/**
 * Logic to be implemented: The application receives the Bluetooth data and passes the attribute value of the GATT server in the data to the SDK for processing.  
  */
VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
{
    tuya_ble_data_buf_t data = {0};

    if (bt_msg_cb == NULL) {
        return;
    }

    data.len = rx_len;
    data.data = rx_data;
    bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
}