Pairing over Wired Connection

Last Updated on : 2022-11-24 09:20:23

This topic describes how to implement gateway pairing over a wired connection by using the integrated SDK.

Background

Connect the gateway to the router via an Ethernet cable. Connect the mobile app to the same router so they are on the same LAN. The gateway sends its encrypted PID and IP address using user datagram protocol (UDP) broadcast. After the mobile app receives and decrypts the packet, it displays the gateway to pair.

When users tap the gateway to add it to the app, the app creates a TCP persistent connection with the gateway and sends the pairing token to the gateway to complete the pairing process.

Implementation

The SDK provides a suite of standard APIs for pairing a gateway that accesses the internet via a wired connection. To enable wired gateways to be paired, you only need to achieve the function adaptation.

Adapt the functions that are used to detect the Ethernet cable connection and get the IP address of the wired network adapter.

  1. Initialize the SDK by using the function for wired connection.

    // ...
    
    int main(int argc, char **argv)
    {
        // ...
    
    #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)
        // The wireless connection SDK that does not support pairing over a wired connection.
        return OPRT_COM_ERROR;
    #else
        TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
    #endif
    
        // ...
    }
    
  2. Implement the function used to get the IP address of the wired network adapter. Typically, this function works for LAN communication. It allows the gateway to send UDP broadcast packets and the IP address via the specified network adapter.

    OPERATE_RET tuya_adapter_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, ETH_DEV, 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;
    }
    
  3. Implement the function used to detect the Ethernet cable connection and return the result to the SDK. This function cannot be blocked because the SDK calls it regularly to check for the wired connection. It is recommended that the time for task processing not exceed one second.

    BOOL_T tuya_adapter_wired_station_conn(VOID)
    {
        int sock;
        struct sockaddr_in *sin;
        struct ifreq ifr;
    
        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, ETH_DEV, sizeof(ifr.ifr_name) - 1);
    
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
            PR_ERR("ioctl failed");
            close(sock);
            return FALSE;
        }
    
        close(sock);
    
        if ((ifr.ifr_flags & IFF_UP) == 0)
            return FALSE;
    
        return TRUE;
    }
    
  4. If you use the wired and wireless combo connection SDK, you also need to implement the function used to get the wired internet connection status. For this combo connection SDK, the wired connection takes precedence when both types of connection are available. When the gateway requests to switch from the wireless to the wired connection, the SDK will call this function to get the internet connection status and switch to the wired connection only when the internet works fine.

    /**
     * By default, the wired internet is considered connected. As long as the gateway is connected via an Ethernet cable and assigned an IP address, the wired connection takes precedence over the wireless.
     */
    OPERATE_RET tuya_adapter_wired_if_connect_internet(BOOL_T *status)
    {
        *status = TRUE;
    
        return OPRT_OK;
    }
    

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"                  // Replace it with your own PID.
#define UUID      "tuya461dbc63aeeb991f"              // Replace it with your own UUID.
#define AUTHKEY   "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug"  // Replace it 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)
    // The wireless connection SDK that does not support pairing over a wired connection.
    return OPRT_COM_ERROR;
#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_wired.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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 "tuya_os_adapt_wired.h"

#define ETH_DEV "eth0"

/**
 * Get the IP address of the network adapter.
 */
OPERATE_RET tuya_adapter_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, ETH_DEV, 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;
}

/**
 * Get the status of the Ethernet cable connection.
 */
BOOL_T tuya_adapter_wired_station_conn(VOID)
{
    int sock;
    struct sockaddr_in *sin;
    struct ifreq ifr;

    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, ETH_DEV, sizeof(ifr.ifr_name) - 1);

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

    close(sock);

    if ((ifr.ifr_flags & IFF_UP) == 0)
        return FALSE;

    return TRUE;
}

/**
 * Get the wired internet connection status, applying to the wired and wireless combo connection SDK.
 */
OPERATE_RET tuya_adapter_wired_if_connect_internet(BOOL_T *status)
{
    *status = TRUE;

    return OPRT_OK;
}

/**
 * Get the signal strength of the wireless network, applying to the wired and wireless combo connection SDK. Report the signal strength to the cloud.
 *   a) If the gateway accesses the internet over a wired connection, use this function to get the signal strength of the wireless network.
 *   b) If the gateway accesses the internet over a wireless connection, use `tuya_adapter_wifi_station_get_conn_ap_rssi` to get the signal strength of the wireless network.
 */
OPERATE_RET tuya_adapter_wired_wifi_station_get_conn_ap_rssi(OUT SCHAR_T *rssi)
{
    *rssi = 99;

    return OPRT_OK;
}

/* Implementation is not needed. */
OPERATE_RET tuya_adapter_wired_get_mac(OUT NW_MAC_S *mac)
{
    return OPRT_OK;
}

/* Implementation is not needed. */
OPERATE_RET tuya_adapter_wired_set_mac(IN CONST NW_MAC_S *mac)
{
    return OPRT_OK;
}

/* Implementation is not needed. */
OPERATE_RET tuya_adapter_wired_wifi_set_station_connect(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd)
{
    return OPRT_OK;
}

/* Implementation is not needed. */
BOOL_T tuya_adapter_wired_wifi_need_cfg(VOID)
{
    return FALSE;
}

/* Implementation is not needed. */
OPERATE_RET tuya_adapter_wired_get_nw_stat(GW_BASE_NW_STAT_T *stat)
{
    return OPRT_OK;
}