应用开发

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

IoT 设备的基础功能通常包括网络连接、设备控制、设备状态上报、OTA 等。此外,还会涉及到生产相关的烧录授权、产测等功能。本文讲解如何使用涂鸦 SDK 调用相关接口函数实现相应功能,您可以参考此文档使用 SDK 进行产品功能开发。

概览

涂鸦 SDK 完整的运行的流程图如下,实现此流程中的每个环节后,即可完成一个 IoT 硬件产品的功能开发。

应用开发

预初始化

系统初始化之前,将系统运行可能会用到的一些参数或功能进行配置。

如下是一段预初始化应用示例代码,完成了日志的输出等级的设置,便于在系统启动后,查看系统运行的信息,确认系统运行情况。

VOID pre_device_init(VOID)
{
    PR_DEBUG("%s",tuya_iot_get_sdk_info());  //查看SDK版本
    PR_DEBUG("%s:%s",APP_BIN_NAME,DEV_SW_VERSION); //查看SDK名称及固件版本
    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(tuya_hard_get_wifi_mode(),prod_test); // 设置Wi-Fi 工作模式,注册产测回调
    tuya_iot_wf_timeout_set(180);         //设置Wi-Fi配网超时时间
}

设备初始化

涂鸦 Wi-Fi SDK 将操作系统相关的功能封装,配网激活及数据收发处理等 IoT 产品核心功能的任务调度都已经实现,您无需关心具体的任务调度逻辑,调用涂鸦的初始化接口并填入相关参数、注册相关功能的回调函数即可。

如下是一段示例代码,调用 tuya_iot_wf_soc_dev_init_param(cfg,start_mode,cbs,NULL,product_key,wf_sw_ver) 初始化函数,并注册回调函数、Wi-Fi 工作模式、PID、固件版本等信息。其中,回调函数一般包括配网、固件升级、下发数据处理等,PID 是设备连接涂鸦云端激活时的身份信息,固件版本信息用于固件升级时使用。

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
     };
    op_ret =             
    tuya_iot_wf_soc_dev_init_param(tuya_hard_get_wifi_mode(),WF_START_SMART_FIRST,&wf_cbs,NULL,PRODUCT_KEY,DEV_SW_VERSION);
    if (OPRT_OK != op_ret)
    {
        PR_ERR("tuya_iot_wf_soc_dev_init fail,fail_num:%d",op_ret);
        return op_ret;
    }
}

网络连接

设备连接网络的完整链路如下图所示:

应用开发

在网络连接应用中,通常设置一定的硬件触发条件,程序判断满足硬件触发条件时,调用配网函数接口,控制设备进入到配网状态。

如下是通过判断长按按键进入配网的示例代码,当检测到长按按键时,调用配网函数接口控制设备进入配网状态:

STATIC VOID key_process(tuya_pin_name_t port,PUSH_KEY_TYPE_E type,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); //将设备执行本地按键后的状态同步到App和云端
            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;
}

数据处理

完整的数据处理链路图下图所示,主要涉及到数据的上报和下发两部分应用。

应用开发

下发数据处理

下发数据处理的回调函数,需要在初始化中时注册,当云端或者 App 端有数据下发时,SDK 会自动找到相应回调函数并执行,您需要完成回调函数的具体逻辑,实现解析接收到的数据并执行相关的命令。

如下是当接收到开关数据的示例代码:

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) //判断接收到的DP点类型
        {
            case SW_TYPE:  //接收到的是开关DP点,执行开关功能
                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://接收到的是倒计时DP点,执行倒计时功能
                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://接收到的是上电状态设置DP点,执行状态设置功能
                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();//执行完控制命令后,将当前设备的状态同步到App和云端
    if (OPRT_OK != op_ret)
    {
        PR_ERR("[%s] tuya_hard_upload_all_data fail,fail_num:%d",op_ret);
    }
    return;
}

状态信息上报

设备状态改变时,根据设备状态对应的 DP 类型,调用相应的上报接口函数,将设备状态同步到云端和 App。

如下是开关、倒计时、上电状态改变时的状态信息上报函数的示例代码:

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)//判断开关 DP 状态是否改变
    {
        tuya_upload_dp_bool_frame(upload_dp,g_dev_info.dev_dp.switch_dp.dpid_num,g_dev_stat.relay_stat);//开关(bool型) DP 赋值
    }
    if (g_dev_info.dev_dp.switch_cd_dp.is_exist)//判断倒计时 DP 状态是否改变
    {  
        tuya_upload_dp_value_frame(upload_dp,g_dev_info.dev_dp.switch_cd_dp.dpid_num,g_dev_stat.relay_cd_sec);//倒计时(value型) DP 赋值
    }
    if (g_dev_info.dev_dp.app_relay_stat.is_exist)//判断上电状态 DP 是否改变
    {   tuya_upload_dp_enum_frame(upload_dp,g_dev_info.dev_dp.app_relay_stat.dpid_num,g_dev_stat.relay_power_stat);//上电状态(enmu型) DP 赋值
    }
//  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);//上报所有 DP 当前值
    tuya_upload_delete_space(upload_dp);//释放存储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;
}

固件编译

涂鸦 Wi-Fi SDK 提供标准化的编译脚本,您只需要执行 Shell 命令,即可进行编译即可自动生成生产用的 bin 文件:

  • 命令格式:

    sh build.sh 代码路径 代码名称 版本号
    
  • 命令示例:

    sh build.sh apps/tuya_demo_elp_1plug tuya_demo_elp_1plug 1.0.0
    

如下是编译脚本中的入参部分的示例代码,详细信息请参考 build.app.sh 文件。

APP_PATH=$1
APP_NAME=$2
APP_VERSION=$3
USER_CMD=$4
echo APP_PATH=$APP_PATH
echo APP_NAME=$APP_NAME
echo APP_VERSION=$APP_VERSION
echo USER_CMD=$USER_CMD

...

烧录授权

将编译生成的生产文件烧录到芯片内,并使用涂鸦授权工具进行授权,即可验证代码功能。

不同芯片的烧录方式不同,需要参考具体芯片的烧录说明文档,授权通过串口通信,将授权信息写入到芯片特定 Flash 区域,此部分功能由涂鸦 SDK 自动完成,您无需关心具体的逻辑。

产测

产品在批量生产时,为提高生产效率,会用代码实现批量的自动化测试,通常称为产测。

涂鸦结合多年工厂智能产品生产经验,在 SDK 内封装了扫描特定 SSID 路由器信号,通过判定信号强度确认产品射频性能的产测功能。在预初始化时,注册产测回调接口函数并完成产测逻辑代码,即可实现批量自动化测试。

如下是产测功能的示例代码:

app_cfg_set(tuya_hard_get_wifi_mode(),prod_test); // 预初始化时调用,设置Wi-Fi 工作模式,设置产测

//根据扫描到的 Wi-Fi 路由器信号强度,判定设备的射频性能是否符合要求,并执行功能测试代码。
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);
}

OTA

完整的 OTA 链路如下图所示:

应用开发

涂鸦 Wi-Fi SDK 已经实现了主芯片的升级功能,您无需关心具体逻辑,在初始化时注册 OTA 升级接口函数并完成相关回调函数即可。

如下是固件升级回调函数的示例代码:

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);
}