电工插座 Demo

更新时间:2022-11-24 09:20:04下载pdf

本文以 SDK 内的智能单插 Demo 为例,介绍如何使用涂鸦标准模组 Wi-Fi SDK 开发,开发一款智能单插产品。

获取 Demo

涂鸦 IoT 平台,在 硬件开发 阶段选择 涂鸦标准模组 SDK 开发 下载的SDK 开发资料包内,有丰富的应用 Demo 可以用于参考开发应用代码。智能单插 Demo 就众多 Demo 中的其中之一。

电工插座 Demo

Demo 目录结构

解压下载到的 SDK 开发资料包后,您可以在 apps 文件夹下找到众多 Demo 例程。其中,插座 Demo 的目录结构如下:

电工插座 Demo

Demo 中包含的文件及相关说明如下所示:

文件名 文件类型 说明
tuya_device.h 头文件 包含设备相关初始化,功能代码,产测等函数接口
tuya_dp_upload.h 头文件 包含 DP 数据上报相关的函数接口
tuya_hard_table.h 头文件 包含硬件外设配置相关的函数
tuya_device.c 源文件 硬件控制接口等
tuya_dp_upload.c 源文件 其中包含关键固件 Key 参数
tuya_hard_table.c 源文件 硬件控制接口等

代码说明

本章节介绍 Demo 代码是如何实现插座的功能,以及您可以怎么参考 Demo,并进行应用代码的开发。

通信逻辑

设备与涂鸦云端交互的逻辑主要概括如下:

  1. 数据交互方式。

    涂鸦设备与云端使用 JSON 数据格式进行交互,数据的标识和定义是在平台第一步选择功能点处配置的,Demo 代码在 tuya_device_config 函数中写入 DP 信息。

    电工插座 Demo

    电工插座 Demo
  2. 设备唯一识别。

    在涂鸦 IoT 平台创建产品会产生 PID 信息,PID 是设备的唯一标识,产品的配置都是和 PID 绑定的,比如 App UI面板、云端配置、语音功能等,因此需要将 PID 信息写入到代码中,demo 代码在 tuya_device.h 中通过宏定义写入 PID 信息。

    电工插座 Demo
  3. 连接云端前授权。

    设备要连接到涂鸦云端,需要进行授权,授权码的获取和授权的流程请参考 通用 Wi-Fi SDK 烧录、授权和产测

Demo 功能

Demo 实现的功能概括如下:

功能名称 实现方式
本地控制 检测按键IO口状态,控制继电器IO口输出信号对继电器进行控制,如果设备联网,将状态上报到云端
进入配网状态 检测按键IO口状态,如果检测到长按按键超过一定时间后(5S),调用重置函数,使设备进入到配网状态
状态指示 判断设备当前的状态,并通过LED灯处于亮、灭、快闪、慢闪对不同状态进行指示
App配网 此部分代码涂鸦SDK已经封装完成,设备进入到配网状态后,使用App选择对应的配网方式进行配网即可,无需编写代码
App控制 解析App下发数据对硬件进行控制,并将状态上报到云端
App移除 调用移除接口,控制设备进入到配网状态
语音控制 此部分代码与App对设备进行控制原理相同
定时开关 启动软件定时器,间隔固定时间,更新倒计时,判断时间结束时,控制设备响应相应的动作
断电记忆 控制设备到一个状态时,启用一个定时器,设置时间,时间到后,将设备当前的状态写到Flash内,当重新上电时,去Flash中读取设备状态参数,对设备进行控制,保证设备处于断电前的状态
OTA升级 接收云端推送升级固件,并更新运行
产测 扫描产测SSID并判断信号强度是否满足要求,进行对应的指示

功能实现

在了解 Demo 代码是如何实现设备管控功能前,您需要了解 SDK 的初始化流程:

电工插座 Demo

本章节主要介绍功能相关的代码介绍,部分函数对应多个功能实现,所以在对应功能内展示相关部分代码,其他部分代码使用 ***** 替代。

常用函数

整个流程中,应用相关功能的实现,您需要重点关注 4 个函数:

user_main()         // 入口函数,通过调用下边三个函数完成 SDK 初始化
pre_device_init()  // 查看 SDK 版本,设置日志输出等级
app_init()         // 硬件相关初始化、设置 Wi-Fi 模组工作模式,注册产测回调函数、设置配网超时时间
device_init()      // 注册功能回调函数,完成初始化,运行 SDK主流程
  • 查看 SDK 版本

    //打印SDK相关信息、固件名称和版本、设置日志打印等级
    VOID pre_device_init(VOID)
    {
    	PR_DEBUG("%s",tuya_iot_get_sdk_info());
    	PR_DEBUG("%s:%s",APP_BIN_NAME,DEV_SW_VERSION);
    	PR_NOTICE("firmware compiled at %s %s",__DATE__,__TIME__);
    	PR_NOTICE("system reset reason:[%s]",tuya_hal_system_get_rst_info());
    	SetLogManageAttr(TY_LOG_LEVEL_DEBUG);                //打印等级设置
    }
    
  • 硬件功能及配网设置

    //初始化硬件外设、设置Wi-Fi工作模式及产测、设置配网超时时间
    VOID app_init(VOID)
    {
    	tuya_device_config();
    	app_cfg_set(GWCM_LOW_POWER,prod_test);
    	tuya_iot_wf_timeout_set(180);
    }
    
  • 完成硬件相关的初始化,注册功能回调函数

    // 完成硬件相关的初始化,注册功能回调函数,进入主循环
    OPERATE_RET device_init(VOID)
    {
    OPERATE_RET op_ret = OPRT_OK;
    tuya_hard_prod_flag(FALSE); // 设置产测标识
    // 功能代码相关的回调函数
    	TY_IOT_CBS_S wf_cbs = {
    		gw_status_cb,\
    		gw_ug_cb,\
    		gw_reset_cb,\
    		dev_obj_dp_cb,\
    		dev_raw_dp_cb,\
    		dev_dp_query_cb,\
    		NULL,
    };
    // IoT框架的初始化
    op_ret = tuya_iot_wf_soc_dev_init_param(func_select.gwcm_mode_user, WF_START_SMART_FIRST, &wf_cbs, FIRMWAIRE_KEY, PRODUCT_KEY, DEV_SW_VERSION);
    	if(OPRT_OK != op_ret) {
    		PR_ERR("tuya_iot_wf_soc_dev_init err:%d",op_ret);
    		return op_ret;
    }
    // 软件定时器的初始化
    op_ret = tuya_hard_soft_timer_init();
    if(OPRT_OK != op_ret)
    {
    	PR_ERR("tuya_hard_soft_timer_init fail,fail_num:%d",op_ret);
    	return op_ret;
    }
    // 查询 Wi-Fi 模组的状态回调函数
    op_ret = tuya_iot_reg_get_wf_nw_stat_cb(__get_wf_status);
    if(OPRT_OK != op_ret) {
    PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb err:%d",op_ret);
    	return op_ret;
    }
    PR_NOTICE("system free heap:%d",tuya_hal_system_getheapsize());
    return OPRT_OK;
    }
    

本地控制

//初始化相关硬件,并注册本地控制处理函数
VOID tuya_device_config(VOID)
{
	DEVICE_PARAM_S dev_param;
	/*开发者根据硬件信息的IO口进行设置*/
	dev_param.wifi_mode = GWCM_LOW_POWER;
	dev_param.wifi_led.io_pin = TY_GPIOA_9;
	dev_param.wifi_led.io_level = IO_LOW_LEVEL;
	dev_param.key_control.io_config.io_pin = TY_GPIOA_7;
	dev_param.key_control.io_config.io_level = IO_LOW_LEVEL;
	dev_param.key_control.press_time = 3000;
	dev_param.key_control.key_call_back = key_process;
	dev_param.relay.io_pin = TY_GPIOA_6;
	dev_param.relay.io_level = IO_HIGH_LEVEL;
	// 产品功能相关的 DP 配置
	dev_param.dev_dp.switch_dp.is_exist = TRUE;
	dev_param.dev_dp.switch_dp.dpid_num = 1;
	dev_param.dev_dp.switch_cd_dp.is_exist = TRUE;
	dev_param.dev_dp.switch_cd_dp.dpid_num = 9;
	dev_param.dev_dp.app_relay_stat.is_exist = TRUE;
	dev_param.dev_dp.app_relay_stat.dpid_num = 38;
	dev_param.relay_led.io_pin = TY_GPIOA_8;
	dev_param.relay_led.io_level = IO_LOW_LEVEL;
	tuya_hard_init(&dev_param);
}

VOID key_process(IN TY_GPIO_PORT_E port, IN PUSH_KEY_TYPE_E type, IN INT_T cnt)
{
	PR_DEBUG("port:%d,type:%d,cnt:%d",port,type,cnt);
	OPERATE_RET op_ret = OPRT_OK;
	if(tuya_hard_judge_key_pin(port))
	{
		if(LONG_KEY == type)//长按事件,进入配网状态
		{
			tuya_iot_wf_gw_unactive();//重置配网函数
		}
		else if(NORMAL_KEY == type) //短按事件,执行短按功能
		{
			tuya_hard_key_control();//短按按键操作控制
			op_ret = tuya_hard_channel_upload(DOUBLE_TYPE);//更新继电器状态
			if (OPRT_OK != op_ret)
			{
				PR_ERR("[%s] tuya_hard_channel_upload fail,fail_num:%d",__FUNCTION__,op_ret);
			}
			tuya_hard_channel_save();//保存继电器当前状态
		}
		else
		{
			PR_NOTICE("key type is no deal");
		}
	}

	return;
}
// 短按按键操作控制
VOID tuya_hard_key_control(VOID)
{
	g_dev_stat.relay_stat = !g_dev_stat.relay_stat;
	PR_DEBUG("key control channel status,channel_stat:%d",g_dev_stat.relay_stat);
	g_dev_stat.relay_cd_sec = 0;
	tuya_hard_set_channel_stat(g_dev_stat.relay_stat);
	tuya_hard_channel_upload(DOUBLE_TYPE);
}

进入配网状态

涂鸦模组 SDK 支持实现 Wi-Fi 快连配网和热点配网两种配网方式,对应涂鸦智能 App 中的 EZ模式AP模式

智能单插 Demo 通过控制按键长按控制设备进入到不同的配网状态。

  • tuya_device_config 中注册按键处理函数 key_process,判断按键长按。

  • 调用 tuya_iot_wf_gw_unactive 控制设备进入配网状态。

    配网状态的切换形式是循环机制:

    • 设备当前处于非配网状态时,调用 tuya_iot_wf_gw_unactive 会进入到 Wi-Fi 快连配网状态。
    • 如果设备在 Wi-Fi 快连配网状态,调用 tuya_iot_wf_gw_unactive 会进入到热点配网状态。
VOID tuya_device_config(VOID)
{
	DEVICE_PARAM_S dev_param;
	/*开发者根据硬件信息的IO口进行设置*/
	dev_param.wifi_mode = GWCM_LOW_POWER; //设置 Wi-Fi 工作模式
	dev_param.wifi_led.io_pin = TY_GPIOA_9;
	dev_param.wifi_led.io_level = IO_LOW_LEVEL;
	dev_param.key_control.io_config.io_pin = TY_GPIOA_7; //设置按键引脚
	dev_param.key_control.io_config.io_level = IO_LOW_LEVEL;//设置按键触发状态
	dev_param.key_control.press_time = 3000; //设置长按按键时间判断阈值
	dev_param.key_control.key_call_back = key_process;//注册按键回调函数
	..... //与配网无关信息,此处省略掉
	tuya_hard_init(&dev_param);
}

VOID key_process(IN TY_GPIO_PORT_E port, IN PUSH_KEY_TYPE_E type, IN INT_T cnt)
{
	...... //与配网无关代码,此处不做展示
	if(tuya_hard_judge_key_pin(port))
	{
		if(LONG_KEY == type)//长按按键事件,控制设备进入配网状态
		{
			tuya_iot_wf_gw_unactive();//配网重置函数
		}
		else if(NORMAL_KEY == type)
	...... //与配网无关代码,此处不做展示
}

查询网络状态

当网络状态变化时,device_init 函数中的 tuya_iot_reg_get_wf_nw_stat_cb 回调函数调用 nw_stat_cb,通过hw_set_wifi_led_stat 对查询设备的当前状态。

OPERATE_RET device_init(VOID)
{
.....  //与网络查询无关代码,此处不做展示
	op_ret = tuya_iot_reg_get_wf_nw_stat_cb(nw_stat_cb);
	if(OPRT_OK != op_ret) {
	PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb fail,fail_num:%d",op_ret);
		return op_ret;
		}
..... //与网络查询无关代码,此处不做展示
}

VOID nw_stat_cb(IN CONST GW_WIFI_NW_STAT_E stat)
{
	PR_NOTICE("cur_wifi_stat:%d",stat);
	OPERATE_RET op_ret = OPRT_OK;
	tuya_hard_wifi_status(stat); //Wi-Fi 状态对应指示灯显示
	if (STAT_CLOUD_CONN == stat || STAT_AP_CLOUD_CONN == stat)
	{
		op_ret = tuya_hard_upload_all_data();//初次连接到云端,将设备所有功能DP信息同步,用于面板展示
		if (OPRT_OK != op_ret)
		{
			PR_ERR("tuya_hard_upload_all_data fail,fail_num:%d",op_ret);
		}
	}
	return;
}

App 配网

根据设备当前的网络状态指示,App 选择对应的配网方式进行配网,此部分代码 SDK 内已实现,无需您再开发。当设备连接到云端时,需要将设备当前所有的DP信息同步到云端,用于App面板的显示。

// 设备连接到云端时,将设备当前所有 DP 的状态进行上报,用于 App 面板显示
VOID nw_stat_cb(IN CONST GW_WIFI_NW_STAT_E stat)
{
	.....  //与连接到云端
	if (STAT_CLOUD_CONN == stat || STAT_AP_CLOUD_CONN == stat)
	{
		**op_ret = tuya_hard_upload_all_data();//初次连接到云端,将设备所有功能DP信息同步,用于面板展示**
		if (OPRT_OK != op_ret)
		{
			PR_ERR("tuya_hard_upload_all_data fail,fail_num:%d",op_ret);
		}
	}
	return;
}

OPERATE_RET tuya_hard_upload_all_data(VOID)
{
	OPERATE_RET op_ret = OPRT_OK;
	DP_UPLOAD_S *upload_dp = NULL;
	UINT_T dp_cnt = DEV_DP_NUM;
	upload_dp = tuya_upload_alloc_space(dp_cnt);
	if (NULL == upload_dp)
	{
		PR_ERR("tuya_upload_alloc_space fail");
		return OPRT_COM_ERROR;
	}
	if (g_dev_info.dev_dp.switch_dp.is_exist)
	{
		tuya_upload_dp_bool_frame(upload_dp,g_dev_info.dev_dp.switch_dp.dpid_num,g_dev_stat.relay_stat);
	}
	if (g_dev_info.dev_dp.switch_cd_dp.is_exist)
	{
		tuya_upload_dp_value_frame(upload_dp,g_dev_info.dev_dp.switch_cd_dp.dpid_num,g_dev_stat.relay_cd_sec);
	}
	if (g_dev_info.dev_dp.app_relay_stat.is_exist)
	{
		tuya_upload_dp_enum_frame(upload_dp,g_dev_info.dev_dp.app_relay_stat.dpid_num,g_dev_stat.relay_power_stat);
	}
//            dev_report_dp_json_async(get_gw_cntl()->gw_if.id, upload_dp->dp_buf, upload_dp->cur_idx)
	op_ret = dev_report_dp_json_async(get_gw_cntl()->gw_if.id,upload_dp->dp_buf,upload_dp->cur_index);
	tuya_upload_delete_space(upload_dp);
	if (OPRT_OK != op_ret)
	{
		PR_ERR("dev_report_dp_json_async all_data is fail,fail_num:%d",op_ret);
		return op_ret;
	}
	PR_DEBUG("upload all dp data sucess");
	return OPRT_OK;
}

App 控制

device_init 函数中,注册了 DP 数据的处理回调函数 dev_obj_dp_cb。当设备接收到App发送的数据时,系统自动调用 dev_obj_dp_cb函数对接收到的 DP 数据处理,并控制相关硬件实现功能,然后调用 tuya_hard_upload_all_data 将状态返回给 App。

OPERATE_RET device_init(VOID)
{
...... //与控制无关代码,此处不做展示
	TY_IOT_CBS_S wf_cbs = {
		status_changed_cb,\
		gw_ug_inform_cb,\
		gw_reset_cb,\
		dev_obj_dp_cb,\
		dev_raw_dp_cb,\
		dev_dp_query_cb,\
		NULL,
	};
	...... //与控制无关代码,此处不做展示
}

VOID dev_obj_dp_cb(IN CONST TY_RECV_OBJ_DP_S *dp)
{
	PR_NOTICE("app_send dp_cnt:%d",dp->dps_cnt);
	OPERATE_RET op_ret = OPRT_OK;
	UINT_T dp_index = 0;
	UINT_T app_send_dp_num = dp->dps_cnt;
	DEV_DP_TYPE_E dpid_type = DP_NOT_EXIST;
	for (dp_index = 0;dp_index < app_send_dp_num;dp_index++)
	{
		dpid_type = tuya_hard_dpid_type(dp->dps[dp_index].dpid);
		PR_DEBUG("dpid_type:%d",dpid_type);
		switch (dpid_type)
		{
			case SW_TYPE:
				PR_DEBUG("value:%d",dp->dps[dp_index].value.dp_bool);
				tuya_hard_channel_control(dp->dps[dp_index].value.dp_bool);
			break;
			case SW_CD_TYPE:
				PR_DEBUG("value:%d",dp->dps[dp_index].value.dp_value);
				tuya_hard_channel_cd_control(dp->dps[dp_index].value.dp_value);
			break;
			case RELAY_ST_TYPE:
				PR_DEBUG("value:%d",dp->dps[dp_index].value.dp_enum);
				tuya_hard_relay_power_stat(dp->dps[dp_index].value.dp_enum);
			break;
			default:
			break;
		}
	}
	op_ret = tuya_hard_upload_all_data();//上报所有功能DP状态,用于面板展示
	if (OPRT_OK != op_ret)
	{
		PR_ERR("[%s] tuya_hard_upload_all_data fail,fail_num:%d",op_ret);
	}
	return;
}
//App 查询设备状态
VOID dev_dp_query_cb(IN CONST TY_DP_QUERY_S *dp_qry)
{
	PR_DEBUG("Recv DP Query Cmd");
	OPERATE_RET op_ret = OPRT_OK;
	op_ret = tuya_hard_upload_all_data();//上报所有功能DP状态,用于面板展示
	if (OPRT_OK != op_ret)
	{
		PR_ERR("tuya_hard_upload_all_data fail,fail_num: %d",op_ret);
	}
	return;
}

App 移除

在 App 移除设备时,设备接收到移除命令后,调用 gw_reset_cb 重置设备并进入配网状态。

VOID gw_reset_cb(GW_RESET_TYPE_E type)
{
	if (GW_REMOTE_RESET_FACTORY != type)
	{
		return;
	}
	/*clear falsh data*/
	tuya_hard_clear_flash_data();//清除 Flash 存储的所有设备相关的信息
	return;
}

//清除 Flash 存储的所有设备相关的信息。
VOID tuya_hard_clear_flash_data(VOID)
{
	OPERATE_RET op_ret = OPRT_OK;
	op_ret = iot_wd_common_delete(POWER_STAT_STORAGE);
	if (OPRT_OK != op_ret)
		{
		PR_ERR("[%s] delete power_stat fail",__FUNCTION__);
	}
	op_ret = iot_wd_common_delete(CHANNEL_STAT_STORAGE);
	if (OPRT_OK != op_ret)
		{
		PR_ERR("[%s] delete channel_stat fail",__FUNCTION__);
	}
	return;
}

语音控制

语音控制与 App 控制实现原理一样,详细请参考 App 配网App 控制App 移除

您需要在平台增值服务开通语音功能后,才能使用音响对设备进行控制,具体参考 第三方音响接入服务

倒计时开关

dev_obj_dp_cb 中接收到倒计时信息后,进入 tuya_hard_channel_cd_control 启动软件定时器注册 count_down_timer 回调函数,每隔 1 秒更新一次倒计时,并将更新后的倒计时信息上报到云端。

VOID dev_obj_dp_cb(IN CONST TY_RECV_OBJ_DP_S *dp)
{
	..... //与倒计时无关代码,此处不做展示
		switch (dpid_type)
		{
			..... //与倒计时无关代码,此处不做展示
			case SW_CD_TYPE:
				PR_DEBUG("value:%d",dp->dps[dp_index].value.dp_value);
				tuya_hard_channel_cd_control(dp->dps[dp_index].value.dp_value);
			break;
			..... //与倒计时无关代码,此处不做展示
			default:
			break;
		}
	..... //与倒计时无关代码,此处不做展示
}

VOID tuya_hard_channel_cd_control(UINT_T app_send_cd_sec)
{
	g_dev_stat.relay_cd_sec = app_send_cd_sec;
	if (app_send_cd_sec == 0)
	{
		g_dev_stat.relay_cd_sec = 0;
		tuya_hard_stop_soft_timer(g_dev_timer.count_down_timer);
	}
	else
	{
		tuya_hard_start_soft_timer(g_dev_timer.count_down_timer,1000,TIMER_CYCLE);
		tuya_hard_channel_upload(SINGLE_TYPE);
	}
}

断电记忆

每次设备状态改变时,都会调用tuya_hard_channel_save 保存设备当前的状态,当设备断电重新上电时,读取 Flash 保存的设备状态,实现断电记忆功能。

STATIC VOID key_process(tuya_pin_name_t port,PUSH_KEY_TYPE_E type,INT_T cnt)
{
	..... //与断电记忆无关代码,此处不做展示
	if(tuya_hard_judge_key_pin(port))
	{
		if(LONG_KEY == type)//press long enter linking network
		{
			tuya_iot_wf_gw_unactive();
		}
		else if(NORMAL_KEY == type)
		{
			tuya_hard_key_control();
			op_ret = tuya_hard_channel_upload(DOUBLE_TYPE);
			if (OPRT_OK != op_ret)
			{
				PR_ERR("[%s] tuya_hard_channel_upload fail,fail_num:%d",__FUNCTION__,op_ret);
			}
			tuya_hard_channel_save(); //保持本地控制后的设备状态
		}
	..... //与断电记忆无关代码,此处不做展示
	}
}

VOID tuya_hard_channel_save(VOID)
{
	if (g_dev_stat.relay_power_stat == RELAY_MEM)
	{
		tuya_hard_start_soft_timer(g_dev_timer.channel_save_timer,5000,TIMER_ONCE); //启动软件定时器,每隔5秒钟保存一次设备当前的状态
	}
	PR_DEBUG("relay_power_stat is not need save,stat:%d",g_dev_stat.relay_power_stat);
	return;
}

OTA 升级

在涂鸦 IoT 平台配置并对设备发起固件升级,设备收到推送的升级固件后调用 gw_ug_cb 回调函数进行固件更新,更新完成后跳转到升级后的固件运行

int gw_ug_cb(IN CONST FW_UG_S *fw)
{
	PR_DEBUG("Rev GW Upgrade Info");
	PR_DEBUG("fw->fw_url:%s", fw->fw_url);
	PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
	PR_DEBUG("fw->file_size:%d", fw->file_size);
	return tuya_iot_upgrade_gw(fw, get_file_data_cb, upgrade_notify_cb, NULL);
}

//下载升级固件回调函数
OPERATE_RET get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len, IN CONST UINT_T offset,
									IN CONST BYTE_T *data, IN CONST UINT_T len, OUT UINT_T *remain_len, IN PVOID_T pri_data)
{
	PR_DEBUG("Rev File Data");
	PR_DEBUG("Total_len:%d ", total_len);
	PR_DEBUG("Offset:%d Len:%d", offset, len);
	return OPRT_OK;
}
//升级状态回调函数
VOID upgrade_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data)
{
	PR_DEBUG("download  Finish");
	PR_DEBUG("download_result:%d", download_result);
}

产测

app_init 中通过 app_cfg_set 注册产测函数,扫描在 tuya_main.c 中定义的 TEST_SSID,判断信号强度。

  • 如果信号强度大于 -60dB,认为设备信号正常,启动软件定时器,调用 protest_switch_timer_cb 回调函数,进行功能测试。

  • 如果信号强度小于 -60dB,认为设备信号异常,做出相应指示,引导用户进行问题排查。

    #define TEST_SSID "tuya_mdev_test1"
    
    VOID app_init(VOID)
    {
    	.....
    	app_cfg_set(tuya_hard_get_wifi_mode(),prod_test);
    	.....
    }
    
    VOID prod_test(BOOL_T flag,CHAR_T rssi)
    {
    PR_NOTICE("rssi:%d,flag:%d",rssi,flag);
    if (flag == FALSE || rssi < -60)
    {
    	tuya_hard_set_led_state(PROD_TEST_FAIL);
    }
    tuya_hard_set_led_state(ENTER_PROD_TEST);
    tuya_hard_prod_flag(TRUE);
    }
    

固件编译

SDK 内提供编译脚本,您可以通过如下命令调用脚本可实现代码编译。

  • 编译指令:进入到编译脚本所在目录,执行 ./build_app.sh apps/<项目名称> <项目名称> <版本号>

  • 命令示例:

    ./build_app.sh apps/one_plug_demo one_plug_demo 1.0.0
    

编译成功后会在 apps/项目名称/output/版本号/xxx.bin 目录下生成烧录用的二进制(bin)文件,如下图片所示:

电工插座 Demo

编译出的 bin 文件,名称中尾缀为 QIO 的是生产固件,生产时使用,名称中尾缀为 UA 的是用户区固件,调试阶段使用,名称中尾缀为 UG 的是升级固件,OTA 时使用。

文件名 说明
tuya_demo_elp_1plug_QIO_1.0.0.bin 生产固件,用于模组Flash工作方式为QIO的模组
tuya_demo_elp_1plug_UA_1.0.0(2)_1.0.0.bin 用户区固件,用于芯片烧录工具使用
tuya_demo_elp_1plug_UG_1.0.0.bin 升级固件,用于上传涂鸦 IoT 平台用户区固件和 OTA 固件

烧录授权

将固件上传至涂鸦 IoT 平台,申请激活码,通过涂鸦 云模组烧录授权平台 写入授权信息,详情参考 通用 Wi-Fi 烧录、授权和产测

日志等级

在调试过程中可以通过添加打印信息,监控设备的运行状态,调试代码。

涂鸦通用 SDK 的日志等级有以下几种分类:

  • Notice
  • Warning
  • Err
  • Debug

您可以通过设置日志的等级来决定哪些日志信息输出,例如设置日志输出等级为TY_LOG_LEVEL_INFO,则ERRWARNNOTICEINFO相关信息会显示,DEBUGTRACE则不会显示。

VOID pre_device_init(VOID)
{
	.... //与日志无关代码,此处不做展示
	PR_DEBUG("%s",tuya_iot_get_sdk_info());
	PR_DEBUG("%s:%s",APP_BIN_NAME,DEV_SW_VERSION);
	PR_NOTICE("firmware compiled at %s %s",__DATE__,__TIME__);
	PR_NOTICE("system reset reason:[%s]",tuya_hal_system_get_rst_info());
	SetLogManageAttr(TY_LOG_LEVEL_DEBUG);                //打印等级设置
}

/* tuya sdk definition of log level */
typedef INT_T LOG_LEVEL;
#define TY_LOG_LEVEL_ERR       0  // 错误信息,程序正常运行不应发生的信息
#define TY_LOG_LEVEL_WARN      1  // 警告信息
#define TY_LOG_LEVEL_NOTICE    2  // 需要注意的信息
#define TY_LOG_LEVEL_INFO      3  // 通知信息
#define TY_LOG_LEVEL_DEBUG     4  // 程序运行调试信息,RELEASE版本中删除
#define TY_LOG_LEVEL_TRACE     5  // 程序运行路径信息,RELEASE版本中删除