更新时间:2023-09-06 10:41:15下载pdf
本文详细介绍 SDK 上有线配网功能,实现网关设备接入到涂鸦生态。
配网流程的业务逻辑已经在 SDK 内部实现,SDK 定义一套 TuyaOS Kernel Layer(简称 TKL)接口来屏蔽硬件和系统差异,TKL 接口由开发者实现,在 TKL 接口完成底层硬件的操作。本文档将提供 TKL 接口开发指导。
有线配网是把网关类设备链接到涂鸦 IoT 云的行为。与 Wi-Fi 无线配网不同,有线配网无需输入路由器热点名称与密码。
有线配网主要适用场景如下:
有线配网的工作原理是,网关设备和手机同时连接到路由器,使得设备和手机处于同一局域网下,设备把它的设备信息经过加密处理后定期在局域网上发送 UDP 广播数据包。手机 App 接收到 UDP 广播数据包后解密处理,成功获取到设备信息后会在 自动发现 页面显示待配网设备。
用户在手机 App 上单击 添加 激活待配网设备时,手机 App 与设备建立 TCP 连接,把配网授权信息发送给设备。设备收到配网授权信息后执行激活流程,直到配网完成。
有线配网不需要调用固定的 API 启动,但需要开发者适配以下 TKL(Tuya kernel layer)接口。
tkl_wired_set_status_cb
OPERATE_RET tkl_wired_set_status_cb(TKL_WIRED_STATUS_CHANGE_CB cb);
SDK 初始化会调用该接口,该接口的参数是网络接口状态变化的通知回调函数指针,需要应用实时监控网络接口的状态变化。当网络接口状态发生变化时,使用通知回调函数指针把其状态传递给 SDK。
举例,当断开以太网时,执行 cb(TKL_WIRED_LINK_DOWN)
。当连接以太网并分配到 IP 地址时,执行 cb(TKL_WIRED_LINK_UP)
。
tkl_wired_get_status
OPERATE_RET tkl_wired_get_status(TKL_WIRED_STAT_E *status);
当 SDK 需要获取当前网络接口的连接状态时,会调用该接口,SDK 关心的是网络接口是否有 IP 地址。如果网络接口是处于激活状态,并且有 IP 地址,则返回 TKL_WIRED_LINK_UP
,否则返回 TKL_WIRED_LINK_DOWN
。
tkl_wired_get_ip
OPERATE_RET tkl_wired_get_ip(NW_IP_S *ip);
当 SDK 需要获取当前网络接口的 IP 地址时,会调用该接口,用于 Socket 通讯绑定地址。对于多网口设备,返回的是哪个网络接口 IP 地址,就使用哪个网络接口通讯。一般使用连接外网的网络接口,根据自己的实际情况正确返回 IP 地址。
本文档演示了 Linux 通用平台有线配网 TKL 接口的实现,以供参考。
使用有线配网接口初始化 SDK。
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)
// 无线 SDK,不支持有线配网
return OPRT_COM_ERROR;
#else
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
#endif
// ...
}
实现 tkl_wired_get_ip
接口,在接口中实现获取网络接口的 IP 地址。
#define WIRED_NAME "eth0"
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;
}
实现 tkl_wired_get_status
接口,在接口中实现获取网络接口的连接状态。
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;
}
实现 tkl_wired_set_status_cb
接口,在接口中实现开启线程监控网络接口的状态,并把通知回调函数指针保存到全局变量。网络接口状态发生变化时,使用通知回调函数指针把状态传递给 SDK。
STATIC 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);
}
}
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);
}
实现 tkl_wired_get_mac
接口,在接口中实现获取网络接口的 MAC 地址。
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 接口不允许阻塞,耗时的任务建议做异步处理。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈