配网方式-蓝牙

更新时间:2023-09-06 10:41:15下载pdf

概述

基本概念

本文详细介绍 SDK 上蓝牙配网功能,实现网关设备接入到涂鸦生态。

配网流程的业务逻辑已经在 SDK 内部实现,SDK 定义一套 TuyaOS Kernel Layer(简称 TKL)接口来屏蔽硬件和系统差异。TKL 接口由开发者实现,在 TKL 接口完成底层硬件的操作。本文档将提供 TKL 接口开发指导。

功能描述

蓝牙配网是将网关通过蓝牙接收手机发来的配网信息(路由热点的 SSID 和 PW),收到配网信息后网关走激活流程直至配网成功。

适用场景

蓝牙配网主要适用场景如下:

  • 网关处于未配网状态。
  • 手机蓝牙处于开启状态。

工作原理/实现方案

网关设备把其设备信息加密后定期发送蓝牙广播数据包,手机 App 打开蓝牙并接收蓝牙广播数据包。如果广播数据包中包含涂鸦服务,则发起扫描响应请求进一步获取设备的详细信息。成功获取到设备信息后,会在 自动发现 页面显示待配网设备。

用户在手机 App 上单击 添加 激活待配网设备时,手机 App 与设备建立蓝牙连接,把配网信息发送给设备。设备收到配网信息后走激活流程,直到配网完成。

涂鸦蓝牙配网用的是私有服务和私有数据,所有数据均由 SDK 解析和封装,开发者无需关心数据内容,直接透传处理。

蓝牙配网的流程图如下图所示:

配网方式-蓝牙

开发指导

使用方法

  • tkl_ble_stack_init

    OPERATE_RET tkl_ble_stack_init(UCHAR_T role);
    

    SDK 初始化时,会调用该接口,应用可以在该接口中实现初始化蓝牙协议栈的功能。如果不需要初始化蓝牙相关业务,该接口为空实现即可。

  • tkl_ble_gap_callback_register

    OPERATE_RET tkl_ble_gap_callback_register(CONST TKL_BLE_GAP_EVT_FUNC_CB gap_evt);
    

    注册蓝牙低功耗 Generic Access Profile(GAP)层的事件回调接口,应用需要把 gap_evt 函数指针保存到全局变量。当应用收到蓝牙低功耗 GAP 数据时,通过函数指针把 GAP 数据传递给 SDK 处理。

    蓝牙配网过程中,主要关注的 GAP 是 TKL_BLE_GAP_EVT_CONNECT 事件。当应用检测到手机与网关设备建立蓝牙连接时,应用通过函数指针把 GAP 数据传递给 SDK。

  • tkl_ble_gatt_callback_register

    OPERATE_RET tkl_ble_gatt_callback_register(CONST TKL_BLE_GATT_EVT_FUNC_CB gatt_evt);
    

    注册蓝牙低功耗 Generic Attribute Protocol(GATT)层的事件回调接口,应用需要把 gatt_evt 函数指针保存到全局变量。当应用收到蓝牙低功耗 GATT 数据时,通过函数指针把 GATT 数据传递给 SDK 处理。

    蓝牙配网过程中,主要关注的 GATT 是 TKL_BLE_GATT_EVT_WRITE_REQ 事件。当应用收到手机发送到 Service UUID = 0x1910 的 Write Request 时,应用通过函数指针把 GATT 数据传递给 SDK。

  • tkl_ble_gap_adv_rsp_data_set

    OPERATE_RET tkl_ble_gap_addr_set(TKL_BLE_GAP_ADDR_T CONST *p_peer_addr);
    

    SDK 设置广播以及扫描响应数据的接口,应用需要在接口中实现把广播数据以及扫描响应数据缓存起来。广播时广播使用广播数据,回复扫描响应请求时使用扫描响应数据。

  • tkl_ble_gap_adv_start

    OPERATE_RET tkl_ble_gap_adv_start(TKL_BLE_GAP_ADV_PARAMS_T CONST *p_adv_params);
    

    SDK 通知应用开启蓝牙低功耗广播的接口,应用需要在接口中实现开启蓝牙低功耗广播的功能,广播和扫描响应数据由 tkl_ble_gap_adv_rsp_data_set 接口设置。

  • tkl_ble_gap_adv_stop

    OPERATE_RET tkl_ble_gap_adv_stop(VOID);
    

    SDK 通知应用停止蓝牙低功耗广播的接口,应用需要在接口中实现关闭广播功能。

  • tkl_ble_gatts_value_notify

    OPERATE_RET tkl_ble_gatts_value_notify(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length);
    

    SDK 需要发送蓝牙低功耗 GATT 数据给手机时,会调用该接口。应用需要在接口中实现把 GATT 数据通过蓝牙发送给手机。

数据格式

蓝牙广播和扫描响应的数据格式:

配网方式-蓝牙

蓝牙广播和扫描响应遵循蓝牙规范,SDK 把广播数据和扫描响应数据封装成 AD Structure 格式,应用无需解析处理,直接完成透传。

蓝牙传输的数据格式:

配网方式-蓝牙

App 和网关建立蓝牙连接后,通过蓝牙 The Attribute Protocol(ATT)协议来传输数据,SDK 封装和解析的数据都属于 ATT Payload。

Service 和 Characteristic 定义如下:

Service UUID: 0x1910

Characteristic UUID 属性 说明
Write Characteristic 0x2B11 Write Without Response App 向蓝牙低功耗设备发送数据
Notify Characteristic 0x2B10 Notify 蓝牙低功耗设备向 App 发送通知信息

使用示例

本文档演示了 Linux 通用平台 TKL 接口的实现,以供参考。

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"                  // 替换成自己的产品 ID
#define UUID      "tuya461dbc63aeeb991f"              // 替换成自己的 UUID
#define AUTHKEY   "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug"  // 替换成自己的 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_wr_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));
    
    return OPRT_COM_ERROR;
#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;
}

tkl_wired.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <net/if.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#include "uni_log.h"
#include "tkl_wired.h"

#define WIRED_NAME "eth0"

TKL_WIRED_STATUS_CHANGE_CB g_link_status_change_cb;

STATIC VOID *link_status_thread(VOID *arg)
{
    INT_T old_status = -1;
    TKL_WIRED_STAT_E new_status;

    while (1) {
        tkl_wired_get_status(&new_status);
        if (old_status != new_status) {
            g_link_status_change_cb(new_status);
            old_status = new_status;
        }
        sleep(1);
    }
}

/**
 * 获取网口 IP 地址接口
 */
OPERATE_RET tkl_wired_get_ip(OUT NW_IP_S *ip)
{
    int sock = 0;
    char ipaddr[50] = {0};

    struct sockaddr_in *sin;
    struct ifreq ifr;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
         PR_ERR("socket create failed");
         return OPRT_COM_ERROR;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, WIRED_NAME, sizeof(ifr.ifr_name) - 1);

    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0 ) {
         PR_ERR("ioctl failed");
         close(sock);
         return OPRT_COM_ERROR;
    }

    sin = (struct sockaddr_in *)&ifr.ifr_addr;
    strcpy(ip->ip, inet_ntoa(sin->sin_addr)); 
    close(sock);

    return OPRT_OK;
}

/**
 * 获取网线连接状态接口
 */
OPERATE_RET tkl_wired_get_status(TKL_WIRED_STAT_E *status)
{
    int sock;
    struct sockaddr_in *sin;
    struct ifreq ifr;
    NW_IP_S ip;

    *status = TKL_WIRED_LINK_DOWN;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
         PR_ERR("socket failed");
         return OPRT_COM_ERROR;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, WIRED_NAME, sizeof(ifr.ifr_name) - 1);

    if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
        PR_ERR("ioctl failed");
        close(sock);
        return OPRT_COM_ERROR;
    }

    close(sock);

    if ((ifr.ifr_flags & IFF_UP) == 0) {
        return OPRT_OK;
    }
    
    if (tkl_wired_get_ip(&ip) != OPRT_OK) {
    	return OPRT_OK;
    }
    
    *status = TKL_WIRED_LINK_UP;
    
    return OPRT_OK;
}

/* 设置有线状态回调 */
OPERATE_RET tkl_wired_set_status_cb(TKL_WIRED_STATUS_CHANGE_CB cb)
{
    pthread_t tid;

    g_link_status_change_cb = cb;

    return pthread_create(&tid, NULL, link_status_thread, NULL);
}

OPERATE_RET tkl_wired_get_mac(NW_MAC_S *mac)
{
    int i;
    int sock = -1;
    struct ifreq ifr;
    struct sockaddr *addr;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         PR_ERR("socket failed");
         return OPRT_SOCK_ERR;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, WIRED_NAME, sizeof(ifr.ifr_name) - 1);
    addr = (struct sockaddr *)&ifr.ifr_hwaddr;
    addr->sa_family = 1;

    /* get mac addr */
    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        PR_ERR("ioctl failed");
        close(sock);
        return OPRT_COM_ERROR;
    }

    memcpy(mac->mac, addr->sa_data, MAC_ADDR_LEN);

    close(sock);
    return OPRT_OK;
}

/* 无需实现 */
OPERATE_RET tkl_wired_set_mac(CONST NW_MAC_S *mac)
{
    return OPRT_OK;
}

tkl_wifi.c

#include "tuya_iot_config.h"

#if defined(WIFI_GW) && (WIFI_GW==1)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include "tuya_os_adapter.h"
#include "tkl_wifi.h"
#include "tkl_system.h"
#include "uni_log.h"

#define WLAN_DEV              "wlan0"

#define UDHCPD_CONF           "/etc/udhcpd.conf"
#define HOSTAPD_CONF          "/etc/hostapd/hostapd.conf"
#define WPA_SUPPLICANT_CONF   "/etc/wpa_supplicant/wpa_supplicant.conf"

STATIC NW_IP_S g_ap_ip = {0};
STATIC WF_WK_MD_E g_wifi_mode = WWM_STATION;
STATIC WIFI_EVENT_CB  __wifi_event_cb = NULL;

STATIC OPERATE_RET save_conf(CONST CHAR_T *filename, VOID *data, UINT32_T len)
{
    OPERATE_RET wbyte = 0;
    FILE *fp = NULL;

    fp = fopen(filename, "w");
    if (fp == NULL) {
        PR_ERR("open %s failed", filename);
        return OPRT_OPEN_FILE_FAILED;
    }

    wbyte = fwrite(data, 1, len, fp);
    if (wbyte != len) {
        PR_ERR("write error");
        fclose(fp);
        return OPRT_WRITE_FILE_FAILED;
    }

    fflush(fp);
    fsync(fileno(fp));
    fclose(fp);

    return OPRT_OK;
}

STATIC OPERATE_RET exec_command(CONST CHAR_T *cmd, CHAR_T *data, INT_T len)
{
    FILE *fp = NULL;

    fp = popen(cmd, "r");
    if (fp == NULL) {
        return OPRT_COM_ERROR;
    }

    if (data != NULL) {
        memset(data, 0, len);

        fread(data, len, 1, fp);
        len = strlen(data);
        if (data[len - 1] == '\n') {
            data[len - 1] = '\0';
        } 
    }

    pclose(fp);

    return OPRT_OK;
}

STATIC BOOL_T __wifi_status(VOID)
{
    FILE *fp = NULL;
    CHAR_T buf[512] = {0};
    WF_STATION_STAT_E stat = 0;

    fp = popen("wpa_cli -i " WLAN_DEV " status", "r");
    if (fp == NULL) {
        return FALSE;
    }

    while (fgets(buf, SIZEOF(buf), fp)) {
        if (!strstr(buf, "wpa_state"))
            continue;

        char *k = strtok(buf, "=");
        char *v = strtok(NULL, "=");
        if (v && !strncmp(v, "COMPLETED", strlen("COMPLETED"))) {
            tkl_wifi_station_get_status(&stat);
            if (stat == WSS_GOT_IP) {
                return TRUE;
            }
        }
    }

    pclose(fp);

    return FALSE;
}

STATIC VOID *__wifi_status_thread(VOID *arg)
{
    BOOL_T cur_status = FALSE, lst_status = FALSE;

    while (1) {
        if (g_wifi_mode != WWM_STATION) {
            tkl_system_sleep(500);
            continue;
        }

        cur_status = __wifi_status();

        if (cur_status != lst_status) {
            PR_DEBUG("wifi connection status changed, %d -> %d", lst_status, cur_status);
            if (__wifi_event_cb) {
                __wifi_event_cb(cur_status ? WFE_CONNECTED : WFE_DISCONNECTED, NULL);
            }
            lst_status = cur_status;
        }
        tkl_system_sleep(1000);
    }
}

OPERATE_RET tkl_wifi_init(WIFI_EVENT_CB cb)
{
    pthread_t tid;

    __wifi_event_cb = cb;

    pthread_create(&tid, NULL, __wifi_status_thread, NULL);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_get_ip(CONST WF_IF_E wf, NW_IP_S *ip)
{
    struct ifreq ifr;
    int sock = 0;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        PR_ERR("create socket failed");
        return OPRT_COM_ERROR;
    }

    strncpy(ifr.ifr_name, WLAN_DEV, strlen(WLAN_DEV) + 1);

    if (ioctl(sock, SIOCGIFADDR, &ifr) == 0)
        strncpy(ip->ip, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), sizeof(ip->ip));

    if (ioctl(sock, SIOCGIFBRDADDR, &ifr) == 0)
        strncpy(ip->gw, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr), sizeof(ip->gw));

    if (ioctl(sock, SIOCGIFNETMASK, &ifr) == 0)
        strncpy(ip->mask, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), sizeof(ip->mask));

    close(sock);

    PR_DEBUG("WiFi ip->ip: %s", ip->ip);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_station_get_status(WF_STATION_STAT_E *stat)
{
    OPERATE_RET op_ret = OPRT_OK;
    NW_IP_S ip = {0};

    *stat = WSS_IDLE;

    op_ret = tkl_wifi_get_ip(WF_STATION, &ip);
    if (op_ret != OPRT_OK) {
        return op_ret;
    }

    if ((strlen(ip.ip) > 0) && (strncmp(g_ap_ip.ip, ip.ip, strlen(ip.ip)) != 0)) {
        *stat = WSS_GOT_IP;
    }

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_get_mac(CONST WF_IF_E wf, NW_MAC_S *mac)
{
    int i;
    int fd = -1;
    struct ifreq ifr;
    struct sockaddr *addr;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
         PR_ERR("socket failed");
         return OPRT_SOCK_ERR;
    }

    memset(&ifr, 0, SIZEOF(ifr));
    strncpy(ifr.ifr_name, WLAN_DEV, SIZEOF(ifr.ifr_name) - 1);
    addr = (struct sockaddr *)&ifr.ifr_hwaddr;
    addr->sa_family = 1;

    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
        PR_ERR("ioctl failed");
        close(fd);
        return OPRT_COM_ERROR;
    }

    memcpy(mac->mac, addr->sa_data, MAC_ADDR_LEN);
    PR_DEBUG("WiFi mac->mac: %02X-%02X-%02X-%02X-%02X-%02X", mac->mac[0], mac->mac[1], mac->mac[2], \
                                                             mac->mac[3],mac->mac[4],mac->mac[5]);

    close(fd);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_work_mode(CONST WF_WK_MD_E mode)
{
    PR_DEBUG("WiFi set mode: %d", mode);

    g_wifi_mode = mode;

    switch (mode) {
        case WWM_STATION:
            exec_command("iwconfig " WLAN_DEV " mode managed", NULL, 0);
            break;
        case WWM_SOFTAP:
            // exec_command("iwconfig " WLAN_DEV " mode master", NULL, 0);
            break;
        default:
            break;
    }

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_get_work_mode(WF_WK_MD_E *mode)
{
#if 0
    CHAR_T buf[256] = {0};
    CHAR_T *pstart = NULL;
    CHAR_T mode_buf[16] = {0};
    FILE *fp = NULL;

    fp = popen("iwconfig " WLAN_DEV, "r");
    if (fp == NULL) {
        return OPRT_COM_ERROR;
    }

    while (fgets(buf, SIZEOF(buf), fp) != NULL) {
        pstart = strstr(buf, "Mode:");
        if (pstart != NULL) {
            sscanf(pstart + strlen("Mode:"), "%s ", mode_buf);
            break;
        }
    }
    pclose(fp);

    if (!strncasecmp(mode_buf, "Managed", strlen("Managed"))) {
        *mode = WWM_STATION;
    } else if (!strncasecmp(mode_buf, "Master", strlen("Master"))) {
        *mode = WWM_SOFTAP;
    }
#endif
    *mode = g_wifi_mode;

    PR_DEBUG("WiFi got mode: %d", *mode);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_start_ap(CONST WF_AP_CFG_IF_S *cfg)
{
    OPERATE_RET op_ret = OPRT_OK;
    INT_T len = 0;
    CHAR_T buf[512] = {0};
    CHAR_T cmd[128] = {0};
    CHAR_T *ap_conf_fmt = 
"interface=%s\n"
"ssid=%s\n"
"country_code=CN\n"
"channel=%d\n"
"beacon_int=100\n"
"max_num_sta=%d\n"
"auth_algs=3\n"
"wpa=%d\n"
"wpa_key_mgmt=WPA-PSK\n"
"wpa_pairwise=TKIP CCMP\n"
"rsn_pairwise=CCMP\n";

    CHAR_T *udhcpd_conf_fmt = 
"interface %s\n"
"start %s.100\n"
"end %s.200\n"
"opt subnet %s\n"
"opt lease 28800\n"
"opt router %s\n"
"opt dns %s\n"
"opt domain SmartLift\n";

    INT_T seg1 = 0, seg2 = 0, seg3 = 0, seg4 = 0;
    CHAR_T ip_prefix[12] = {0};

    tkl_wifi_station_disconnect();

    PR_DEBUG("start ap, ssid: %s, ip: %s", cfg->ssid, cfg->ip.ip);

    memcpy(&g_ap_ip, &(cfg->ip), SIZEOF(NW_IP_S));

    sscanf(cfg->ip.ip, "%d.%d.%d.%d", &seg1, &seg2, &seg3, &seg4);
    snprintf(ip_prefix, SIZEOF(ip_prefix), "%d.%d.%d", seg1, seg2, seg3);

    if (cfg->p_len > 0) {
        len = snprintf(buf, SIZEOF(buf), ap_conf_fmt, WLAN_DEV, cfg->ssid, cfg->chan, cfg->max_conn, 2);
        len += snprintf(buf + len, SIZEOF(buf) - len, "wpa_passphrase=%s\n", cfg->passwd);
    } else {
        len = snprintf(buf, SIZEOF(buf), ap_conf_fmt, WLAN_DEV, cfg->ssid, cfg->chan, cfg->max_conn, 0);
    }

    op_ret = save_conf(HOSTAPD_CONF, buf, len);
    if (op_ret != OPRT_OK) {
        PR_ERR("fail to write %s", HOSTAPD_CONF);
    }

    len = snprintf(buf, SIZEOF(buf), udhcpd_conf_fmt, WLAN_DEV, ip_prefix, ip_prefix, cfg->ip.mask, cfg->ip.gw, cfg->ip.gw);
    op_ret = save_conf(UDHCPD_CONF, buf, len);
    if (op_ret != OPRT_OK) {
        PR_ERR("fail to write %s", UDHCPD_CONF);
    }

    snprintf(cmd, SIZEOF(cmd), "ifconfig %s %s netmask %s", WLAN_DEV, cfg->ip.ip, cfg->ip.mask);
    exec_command(cmd, NULL, 0);
    exec_command("ifconfig " WLAN_DEV " up", NULL, 0);
    tkl_system_sleep(1000);
    exec_command("hostapd -B -P /run/hostapd.pid " HOSTAPD_CONF, NULL, 0);
    exec_command("killall udhcpd", NULL, 0);
    exec_command("udhcpd " UDHCPD_CONF, NULL, 0);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_stop_ap(VOID_T)
{
    exec_command("killall udhcpd", NULL, 0);
    exec_command("killall hostapd", NULL, 0);
    exec_command("ifconfig " WLAN_DEV " down", NULL, 0);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_station_connect(CONST SCHAR_T *ssid, CONST SCHAR_T *passwd)
{
    OPERATE_RET op_ret = OPRT_OK;
    INT_T len = 0;
    CHAR_T buf[512] = {0};
    CHAR_T *wpa_conf_fmt = 
"ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n"
"update_config=1\n"
"country=CN\n"
"\n"
"network={\n"
        "\tssid=\"%s\"\n"
        "\tpairwise=CCMP TKIP\n"
        "\tkey_mgmt=WPA-PSK\n"
        "\tgroup=CCMP TKIP\n"
        "\tpsk=\"%s\"\n"
"}\n";

    if (!ssid || !passwd) {
        PR_WARN("ssid or passwd is null");
        return OPRT_INVALID_PARM;
    }

    tkl_wifi_stop_ap();

    PR_DEBUG("ssid: %s, passwd: %s", ssid, passwd);

    len = snprintf(buf, SIZEOF(buf), wpa_conf_fmt, ssid, passwd);
    op_ret = save_conf(WPA_SUPPLICANT_CONF, buf, len);
    if (op_ret != OPRT_OK) {
        PR_ERR("fail to write %s", UDHCPD_CONF);
    }

    exec_command("wpa_supplicant -B -Dnl80211 -i" WLAN_DEV " -c" WPA_SUPPLICANT_CONF, NULL, 0);
    exec_command("udhcpc -i " WLAN_DEV " -s /etc/udhcpc/default.script -p /run/udhcpc_wlan0.pid -b", NULL, 0);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_station_disconnect(VOID_T)
{
    exec_command("wpa_cli -i " WLAN_DEV " terminate", NULL, 0);
    exec_command("[ -f /run/udhcpc_wlan0.pid ] && (cat /run/udhcpc_wlan0.pid | xargs sudo kill)", NULL, 0);
    exec_command("ifconfig " WLAN_DEV " down", NULL, 0);

    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_mac(CONST WF_IF_E wf, CONST NW_MAC_S *mac)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_scan_ap(CONST SCHAR_T *ssid, AP_IF_S **aps, UINT_T *num)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_release_ap(AP_IF_S *ap)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_cur_channel(CONST UCHAR_T chan)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_get_cur_channel(UCHAR_T *chan)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_sniffer(CONST BOOL_T en, CONST SNIFFER_CALLBACK cb)
{
    return OPRT_OK;
}

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

OPERATE_RET tkl_wifi_get_bssid(UCHAR_T *mac)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_country_code(CONST COUNTRY_CODE_E ccode)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_send_mgnt(CONST UCHAR_T *buf, CONST UINT_T len)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_register_recv_mgnt_callback(CONST BOOL_T enable, CONST WIFI_REV_MGNT_CB recv_cb)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_set_lp_mode(CONST BOOL_T enable, CONST UCHAR_T dtim)
{
    return OPRT_OK;
}

OPERATE_RET tkl_wifi_get_connected_ap_info(FAST_WF_CONNECTED_AP_INFO_T **fast_ap_info)
{
    return OPRT_COM_ERROR;
}

OPERATE_RET tkl_wifi_station_fast_connect(CONST FAST_WF_CONNECTED_AP_INFO_T *fast_ap_info)
{
    return OPRT_COM_ERROR;
}

OPERATE_RET tkl_wifi_set_rf_calibrated(VOID_T)
{
    return OPRT_OK;
}

OPERATE_RET tkl_hostap_ioctl_inet(INT_T dev, INT_T vif_index, UINT_T cmd, ULONG_T arg)
{
    return OPRT_OK;
}
#endif

tkl_ble.c

#include "tkl_bluetooth.h"

OPERATE_RET tkl_ble_stack_init(UCHAR_T role)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_stack_deinit(UCHAR_T role)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_callback_register(CONST TKL_BLE_GAP_EVT_FUNC_CB gap_evt)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatt_callback_register(CONST TKL_BLE_GATT_EVT_FUNC_CB gatt_evt)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_adv_start(TKL_BLE_GAP_ADV_PARAMS_T CONST *p_adv_params)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_adv_stop(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_adv_rsp_data_set(TKL_BLE_DATA_T CONST *p_adv, TKL_BLE_DATA_T CONST *p_scan_rsp)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_adv_rsp_data_update(TKL_BLE_DATA_T CONST *p_adv, TKL_BLE_DATA_T CONST *p_scan_rsp)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_value_notify(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_stack_gatt_link(USHORT_T *p_link)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_addr_set(TKL_BLE_GAP_ADDR_T CONST *p_peer_addr)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_address_get(TKL_BLE_GAP_ADDR_T *p_peer_addr)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_scan_start(TKL_BLE_GAP_SCAN_PARAMS_T CONST *p_scan_params)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_scan_stop(VOID)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_connect(TKL_BLE_GAP_ADDR_T CONST *p_peer_addr, TKL_BLE_GAP_SCAN_PARAMS_T CONST *p_scan_params, TKL_BLE_GAP_CONN_PARAMS_T CONST *p_conn_params)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_disconnect(USHORT_T conn_handle, UCHAR_T hci_reason)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_conn_param_update(USHORT_T conn_handle, TKL_BLE_GAP_CONN_PARAMS_T CONST *p_conn_params)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_tx_power_set(UCHAR_T role, INT_T tx_power)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gap_rssi_get(USHORT_T conn_handle)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_service_add(TKL_BLE_GATTS_PARAMS_T *p_service)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_value_set(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_value_get(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_value_indicate(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gatts_exchange_mtu_reply(USHORT_T conn_handle, USHORT_T server_rx_mtu)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_all_service_discovery(USHORT_T conn_handle)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_all_char_discovery(USHORT_T conn_handle, USHORT_T start_handle, USHORT_T end_handle)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_char_desc_discovery(USHORT_T conn_handle, USHORT_T start_handle, USHORT_T end_handle)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_write_without_rsp(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_write(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_read(USHORT_T conn_handle, USHORT_T char_handle)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_gattc_exchange_mtu_request(USHORT_T conn_handle, USHORT_T client_rx_mtu)
{
    return OPRT_OK;
}

OPERATE_RET tkl_ble_vendor_command_control(USHORT_T opcode, VOID_T *user_data, USHORT_T data_len)
{
    return OPRT_NOT_SUPPORTED;
}

注意事项

TKL 接口不允许阻塞,耗时的任务建议做异步处理。