产测授权

更新时间:2024-03-25 08:07:18下载pdf

产测授权是通过涂鸦生产工具把设备的授权信息写入到设备的不易失存储区,用于后续设备正常运行时的设备认证。TuyaOS 提供了产测授权功能,封装了设备与涂鸦生产上位机的通讯,让您快速实现产测授权的开发。

本文将介绍使用 TuyaOS 网关开发框架开发产测授权功能。

背景信息

设备接入到 涂鸦 IoT 开发平台 需要用到授权码作为接入凭证,每个设备的授权码都是唯一的,不能在不同设备上使用相同的授权码,否则无法正常接入涂鸦 IoT 开发平台。

授权信息一般是在设备生产测试阶段烧录写入的,为了帮助智能设备更好地完成量产,涂鸦提供了产测平台化的方案。把涂鸦产测上位机软件集成到产线,设备端实现产测授权功能,就能在生产测试阶段完成授权,提高生产效率。

环境准备

产测需要采用生产凭证和生产解决方案工具。对于如何申请生产凭证以及如何使用生产解决方案工具,参考 生产制造

网关产品的生产测试支持串口、有线和无线。其中串口方式的生产测试主要用于 FreeRTOS 网关产品,使用 TuyaOS 开发包开发的网关产品建议根据您的产品形态选择 带网口网关整机有线测试 或者 无网口网关整机无线测试

使用方法

TuyaOS 封装了业务级的产测接口,您只需要在网关预初始化之后调用 tuya_testframe_handle_init 接口就能开启产测功能。

修改端口

TuyaOS 产测服务与产测上位机软件通过 TCP 协议进行通讯,默认端口号为 12130,如果您本地占用了该端口,您可以通过 tuya_testframe_set_tcp_port 接口修改产测服务的端口号。

产测授权

TuyaOS 产测接口包含了一系列产测相关的回调接口,根据您选择的产测功能实现对应的回调接口即可。

TuyaOS 产测功能包含了非常丰富的通用产测项,如按键测试、指示灯测试、扬声器测试等。如果您现有的生产测试中已经包含了这些测试项,则在涂鸦产测中无需选择这些测试项,仅使用写入授权功能就行。

其中,产测授权必选的回调接口有:

回调接口 说明
TYTEST_FRAME_CBS_S -> master_firm_frame_cb 读取主固件版本信息
TYTEST_FRAME_CBS_S -> w_cfg_frame_cb 写入授权信息
TYTEST_FRAME_CBS_S -> r_cfg_frame_cb 读取授权信息

射频测试

TuyaOS 支持搭配涂鸦模组使用,您可以使用 TuyaOS 产测的射频测试功能对模组的射频性能进行测试。

射频测试的回调接口:

回调接口 说明
TYTEST_FRAME_CBS_S -> zigbee_rf_frame_cb Zigbee RF 测试,测试原理是 Zigbee 模组向 Zigbee 产测信标发送 N 个产测数据包,根据 Zigbee 模组接收到的产测数据包数量来判断其性能是否满足要求。
TYTEST_FRAME_CBS_S -> ble_rf_frame_cb 蓝牙 RF 测试,测试原理是手机作为蓝牙信标,蓝牙模组接收蓝牙信标的信号后返回 RSSI 信号强度,通过 RSSI 信号强度来判断其性能 是否满足要求。

使用示例

#include "tuya_testframe_handle.h"

#define M_ENABLE_ZIG_RFTEST 0
#define M_ENABLE_BLE_RFTEST 0

/**
 * @brief 读取主固件版本信息
 *
 * @param[out] out_buf 返回主固件版本信息,数据格式是 JSON string
 *                     字段      类型      说明
 *                     ret       Boolean   结果,true 表示成功,false 表示失败
 *                     firmName  String    固件名称,要求跟申请的生产工单信息一致
 *                     firmVer   String    固件版本,要求跟申请的生产工单信息一致
 *
 * @return 0 表示成功,非 0 表示失败
 */
STATIC INT_T __prod_test_r_master_firmware_info(CHAR_T *out_buf)
{
    if (NULL == out_buf) {
        return OPRT_INVALID_PARM;
    }

    // FIXME: 修改成您的产品信息
    sprintf(out_buf, "{\"ret\":true,\"firmName\":\"tuyaos-gw-integrated\",\"firmVer\":\"%s\"}", "9.9.9");

    return OPRT_OK;
}

/**
 * @brief 写入授权信息
 *
 * @param[in] auzkey   授权码 - AUTHKEY
 * @param[in] uuid     授权码 - UUID
 * @param[in] pid      产品 ID
 * @param[in] prodtest 该参数已废弃
 * @param[in] ap_ssid  设置无线产品热点 SSID
 * @param[in] ap_pwd   设置无线产品热点密码
 * @param[in] psk      预共享密钥
 *
 * @return 0 表示成功,非 0 表示失败
 */
STATIC INT_T __prod_test_w_auth_info(CHAR_T *auzkey,
                                     CHAR_T *uuid,
                                     CHAR_T *pid,
                                     INT_T  prodtest,
                                     CHAR_T *ap_ssid,
                                     CHAR_T *ap_pwd,
                                     CHAR_T *psk)
{
    /**
     * 如无特殊要求,只需要使用 auzkey 和 uuid 这两个参数,把它们写入到不易失存储区。产品阶段从不易失存储区读取,
     * 通过 `tuya_iot_set_gw_prod_info` 接口把授权信息设置到 TuyaOS
     */
    return OPRT_OK;
}

/**
 * @brief 读取授权信息
 * @note 为保证授权信息写入成功,需要进行读取验证
 *
 * @param[in] auzkey   授权码 - AUTHKEY
 * @param[in] uuid     授权码 - UUID
 * @param[in] pid      产品 ID
 * @param[in] prodtest 该参数已废弃
 * @param[in] ap_ssid  设置无线产品热点 SSID
 * @param[in] ap_pwd   设置无线产品热点密码
 * @param[in] psk      预共享密钥
 *
 * @return 0 表示成功,非 0 表示失败
 */
STATIC INT_T __prod_test_r_auth_info(CHAR_T *auzkey,
                                     CHAR_T *uuid,
                                     CHAR_T *pid,
                                     INT_T  *prodtest,
                                     CHAR_T *ap_ssid,
                                     CHAR_T *ap_pwd,
                                     CHAR_T *psk)
{
    // 从前面写入的不易失存储区把授权信息读取出来,赋值给 auzkey 和 uuid。
    return OPRT_OK;
}

#if defined(M_ENABLE_ZIG_RFTEST) && (M_ENABLE_ZIG_RFTEST == 1)
STATIC BOOL_T g_zigbee_rf_mode  = FALSE;
STATIC INT_T  g_zigbee_rf_index = 0;

/**
 * @brief Zigbee 射频测试回调
 *
 * @param[in] npacket 收到的产测数据包数量
 */
STATIC VOID __zigbee_rftest_result(USHORT_T npacket)
{
    if (g_zigbee_rf_mode) {
        // 异步上报测试结果
        tuya_testframe_rep_zigbeeRf_event(g_zigbee_rf_index, npacket);
        g_zigbee_rf_mode = FALSE;
    }
}

/**
 * @brief Zigbee 射频测试
 * @note Zigbee 射频测试会比较耗时,TuyaOS 采用了异步方式来处理,通过 `index` 值来标识异步事件,
 *       异步上报时需要携带上该 `index` 值
 *
 * @param[in] index   异步上报标志值
 * @param[in] channel 发送产测数据包使用的通道号
 * @param[in] num     发送产测数据包的数量
 *
 * @return 0 表示成功,非 0 表示失败
 */
STATIC INT_T __prod_test_zigbee_rf_test(INT_T index, INT_T channel, INT_T num)
{
    UCHAR_T rf_data[] = { 0x55, 0xaa, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; // 固定格式

    if (!g_zigbee_rf_mode) {
        PR_DEBUG("zigbee rf test, index: %d, channel: %d, num: %d", index, channel, num);

        g_zigbee_rf_index = index;

        tuya_zigbee_rftest(__zigbee_rftest_result, channel, 13, 12, rf_data, num);

        g_zigbee_rf_mode = TRUE;

        return 0;
    } else {
        PR_WARN("zigbee rf test has run");
        return -1;
    }
}
#endif

#if defined(M_ENABLE_BLE_RFTEST) && (M_ENABLE_BLE_RFTEST == 1)
typedef struct {
    BOOL_T state;
    SCHAR_T rssi;
} PROD_TEST_BT_RSSI_INFO;

STATIC PROD_TEST_BT_RSSI_INFO g_bt_rssi_info = {0};

/**
 * @brief 蓝牙 RSSI 信号强度
 *
 * @param rssi 信号强度
 */
STATIC VOID __bt_rssi_cb(SCHAR_T rssi)
{
    PR_DEBUG("rssi: %d", rssi);

    g_bt_rssi_info.state = TRUE;
    g_bt_rssi_info.rssi = rssi;
}

/**
 * @brief 蓝牙射频测试
 *
 * @param[in] ssid   蓝牙信标名称
 * @param[out] rssi  RSSI 信号强度结果
 */
STATIC INT_T __prod_test_ble_rf_test(CHAR_T *ssid, INT_T *rssi)
{
    INT_T i = 0;

    PR_ERR("Bluetooth rf test, ssid: %s", ssid);

    memset(&g_bt_rssi_info, 0, SIZEOF(PROD_TEST_BT_RSSI_INFO));

    // 开启扫描
    tuya_bt_rssi_get(TRUE, __bt_rssi_cb, ssid);
    for (i = 0; i < 100; i++) {
        if (g_bt_rssi_info.state) {
            *rssi = g_bt_rssi_info.rssi;

            // 关闭扫描
            tuya_bt_rssi_get(FALSE, NULL, NULL);

            return 0;
        }
        tal_system_sleep(100);
    }
    // 关闭扫描
    tuya_bt_rssi_get(FALSE, NULL, NULL);

    PR_ERR("Bluetooth rf test failed");

    return -1;
}
#endif

OPERATE_RET demo_prod_test(VOID)
{
    OPERATE_RET rt = OPRT_OK;
    TYTEST_FRAME_CBS_S prod_test_cbs = {
        .master_firm_frame_cb  = __prod_test_r_master_firmware_info,
        .w_cfg_frame_cb        = __prod_test_w_auth_info,
        .r_cfg_frame_cb        = __prod_test_r_auth_info,
        #if defined(M_ENABLE_ZIG_RFTEST) && (M_ENABLE_ZIG_RFTEST == 1)
        .zigbee_rf_frame_cb    = __prod_test_zigbee_rf_test,
        #endif
        #if defined(M_ENABLE_BLE_RFTEST) && (M_ENABLE_BLE_RFTEST == 1)
        .ble_rf_frame_cb       = __prod_test_ble_rf_test,
        #endif
    };

    TUYA_CALL_ERR_RETURN(tuya_testframe_handle_init(TPM_TCP, &prod_test_cbs));

    return OPRT_OK;
}

回调接口

读取主固件信息

/**
 * @brief 读取主固件版本信息
 *
 * @param[out] out_buf 返回主固件版本信息,数据格式是 JSON string
 *                     字段      类型      说明
 *                     ret       Boolean   结果,true 表示成功,false 表示失败
 *                     firmName  String    固件名称,要求跟申请的生产工单信息一致
 *                     firmVer   String    固件版本,要求跟申请的生产工单信息一致
 *
 * @return 0 表示成功,非 0 表示失败
 */
typedef int (*TYTEST_F_WR_HANDL)(char *buf);
typedef struct {
    TYTEST_F_WR_HANDL master_firm_frame_cb;
} TYTEST_FRAME_CBS_S;

写入授权信息

/**
 * @brief 写入授权信息
 *
 * @param[in] auzkey   授权码 - AUTHKEY
 * @param[in] uuid     授权码 - UUID
 * @param[in] pid      产品 ID
 * @param[in] prodtest 该参数已废弃
 * @param[in] ap_ssid  设置无线产品热点 SSID
 * @param[in] ap_pwd   设置无线产品热点密码
 * @param[in] psk      预共享密钥
 *
 * @return 0 表示成功,非 0 表示失败
 */
typedef int (*TYTEST_F_W_CFG_HANDL)(char *auzkey,
                                    char *uuid,
                                    char *pid,
                                    int prodtest,
                                    char *ap_ssid,
                                    char *ap_pwd,
                                    char *psk);
typedef struct {
    TYTEST_F_W_CFG_HANDL w_cfg_frame_cb;
} TYTEST_FRAME_CBS_S;

读取授权信息

/**
 * @brief 读取授权信息
 * @note 为保证授权信息写入成功,需要进行读取验证
 *
 * @param[in] auzkey   授权码 - AUTHKEY
 * @param[in] uuid     授权码 - UUID
 * @param[in] pid      产品 ID
 * @param[in] prodtest 该参数已废弃
 * @param[in] ap_ssid  设置无线产品热点 SSID
 * @param[in] ap_pwd   设置无线产品热点密码
 * @param[in] psk      预共享密钥
 *
 * @return 0 表示成功,非 0 表示失败
 */
typedef int (*TYTEST_F_R_CFG_HANDL)(char *auzkey,
                                    char *uuid,
                                    char *pid,
                                    int *prodtest,
                                    char *ap_ssid,
                                    char *ap_pwd,
                                    char *psk);
typedef struct {
    TYTEST_F_R_CFG_HANDL r_cfg_frame_cb;
} TYTEST_FRAME_CBS_S;

Zigbee 射频测试

/**
 * @brief Zigbee 射频测试
 * @note Zigbee 射频测试会比较耗时,TuyaOS 采用了异步方式来处理,通过 `index` 值来标识异步事件,
 *       异步上报时需要携带上该 `index` 值
 *
 * @param[in] index   异步上报标志值
 * @param[in] channel 发送产测数据包使用的通道号
 * @param[in] num     发送产测数据包的数量
 *
 * @return 0 表示成功,非 0 表示失败
 */
typedef int (*TYTEST_F_ZIGBEE_RF_HANDL)(int index, int channel, int num);
typedef struct {
    TYTEST_F_ZIGBEE_RF_HANDL zigbee_rf_frame_cb;
} TYTEST_FRAME_CBS_S;

蓝牙射频测试

/**
 * @brief 蓝牙射频测试
 *
 * @param[in] ssid   蓝牙信标名称
 * @param[out] rssi  RSSI 信号强度结果
 */
typedef int (*TYTEST_F_WIFIBLE_RF_HANDL)(char *ssid, int *rssi);
typedef struct {
    TYTEST_F_WIFIBLE_RF_HANDL ble_rf_frame_cb;
} TYTEST_FRAME_CBS_S;

API 接口

开启产测功能

/**
 * @brief 产测服务初始化
 *
 * @param[in] mode  选择产测协议,支持 TCP 和串口
 * @param[in] cbs   回调接口,参考 TYTEST_FRAME_CBS_S
 *
 * @return OPRT_OK 成功,其他错误码请参考 tuya_error_code.h
 */
int tuya_testframe_handle_init(TUYA_PRODTEST_MODE mode, TYTEST_FRAME_CBS_S *cbs);

设置产测端口

/**
 * @brief 设置产测端口
 *
 * @param[in] tcp_port 端口号
 *
 * @return OPRT_OK 成功,其他错误码请参考 tuya_error_code.h
 */
void tuya_testframe_set_tcp_port(int tcp_port);

退出产测模式

/**
 * @brief: production test frame handles deinit
 *
 * @return OPRT_OK 成功,其他错误码请参考 tuya_error_code.h
 */
int tuya_testframe_handle_deinit(void);

上报射频结果

/**
 * @brief 异步上报 Zigbee 射频测试结果
 *
 * @param[in] index       异步事件标志值
 * @param[in] receive_num 接收到的产测数据包数量
 *
 * @return OPRT_OK 成功,其他错误码请参考 tuya_error_code.h
 */
int tuya_testframe_rep_zigbeeRf_event(int index, unsigned int receive_num);