时间服务

更新时间:2026-01-29 07:21:57下载pdf

本文介绍蓝牙设备支持的时间服务。

概述

概念介绍

蓝牙设备支持本地时间服务,设备上电后时间从 0 开始,时间精度为 1 s,误差取决于不同芯片内部低频晶振的精度或者外部低频晶振的精度。

当设备和 涂鸦 App 每次连接成功时(包括配网、重连),默认设备会向 App 请求时间同步。根据设备请求时间类型的不同,App 返回不同类型的时间(支持云端实时时间、App 本地时间等),之后设备会按照获取到的时间标准执行本地时间服务。

功能描述

如下图所示,时间服务主要包含 RTC 计时、UTC 时间转换和时间同步三个模块。

其中,RTC 计时主要由 tal_rtc.c 实现,包含芯片平台相关的 RTC 计时服务。UTC 时间转换模块由 tal_utc 组件实现,包含时间戳和通用时间格式转换等内容。时间同步模块的主要功能是从云端/App 同步实时时间。

时间服务

数据结构

tuya_ble_timestamp_data_t

typedef struct {
    UINT8_T timestamp_string[14];
    INT16_T time_zone;   // Multiply actual time zone by 100.
} tuya_ble_timestamp_data_t;
  • timestamp_string:13 字节字符串,Unix ms 级时间。

  • time_zone:有符号型,实际时区的 100 倍,例如北京东八区为 8 × 100 = 800,西 7.5 区为 -750。

tuya_ble_time_noraml_data_t

typedef struct {
    UINT16_T nYear;
    UINT8_T nMonth;
    UINT8_T nDay;
    UINT8_T nHour;
    UINT8_T nMin;
    UINT8_T nSec;
    UINT8_T DayIndex; 	// 0 = Sunday
    INT16_T time_zone;  // Multiply actual time zone by 100.
} tuya_ble_time_noraml_data_t;
  • nYear:年。
  • nMonth:月。
  • nDay:日。
  • nHour:时。
  • nMin:分。
  • nSec:秒。
  • DayIndex:星期,其中周日 = 0,周一至周六依次为 1 - 6
  • time_zone:有符号型,实际时区的 100 倍,例如北京东八区为 8 × 100 = 800,西 7.5 区为 -750。

tuya_ble_timestamp_with_dst_data_t

typedef struct {
    UINT32_T timestamp;
    INT16_T time_zone;   /**< Multiply actual time zone by 100. */
    UINT8_T n_years_dst; /**< how many years of daylight saving time. */
    UINT8_T *p_data;     /**< the point of dst data. */
    UINT16_T data_len;   /**< dst data length. */
} tuya_ble_timestamp_with_dst_data_t;
  • timestamp:Unix 时间戳。
  • time_zone:有符号型,实际时区的 100 倍,例如北京东八区为 8 × 100 = 800,西 7.5 区为 -750。
  • n_years_dst:表示后面有几年的夏令时数据,0 表示没有夏令时数据,1 表示后面只有 1 年的夏令时数据。
  • p_data:夏令时数据。
  • data_len:夏令时数据长度。
序号 长度 字段 说明
1~10 10 start_1 第 1 年的夏令时起始时间,10 字节 Unix 时间戳字符串。
11~20 10 end_1 第 1 年的夏令时结束时间,10 字节 Unix 时间戳字符串(必须大于当前时间 timestamp)。
…… …… …… ……
~ 10 start_n 第 n 年的夏令时起始时间,10 字节 Unix 时间戳字符串。
~ 10 end_n 第 n 年的夏令时结束时间,10 字节 Unix 时间戳字符串。
  • 如果 App 从云端获取不到时间,App 会回复全 0 数据(二进制 0)。
  • 响应包中的第一组夏令时数据(start_1 : end_1)有可能是当年的,也有可能是下一年的,App 判断条件是如果当前时间点 timestamp 还没进入当年的夏令时时间段或者已经处于当年夏令时时间段内,第一组的夏令时数据是当年的,否则为下一年的。

tal_utc_date_t

typedef struct {
    UINT16_T year;
    UINT8_T month;
    UINT8_T day;
    UINT8_T hour;
    UINT8_T min;
    UINT8_T sec;
    UINT8_T dayIndex; // 0 = Sunday
} tal_utc_date_t;
  • year:年。
  • month:月。
  • day:日。
  • hour:时。
  • min:分。
  • sec:秒。
  • dayIndex:星期,其中周日 = 0,周一至周六依次为 1 - 6

接口说明

RTC 初始化

RTC 上电后从 0 开始计时,RTC 的精度根据不同的芯片或者时钟源的选取会有不同。

接口说明

OPERATE_RET tal_rtc_init(VOID_T);

设置 RTC

接口说明

OPERATE_RET tal_rtc_time_set(TIME_T time_sec);

参数说明

参数 说明
time_sec 4 字节 Unix 时间戳

获取 RTC

接口说明

OPERATE_RET tal_rtc_time_get(TIME_T *time_sec);

参数说明

参数 说明
time_sec 4 字节 Unix 时间戳的指针

设置时区

接口说明

OPERATE_RET tal_utc_set_time_zone(INT16_T time_zone);

参数说明

参数 说明
time_zone 有符号型,实际时区的 100 倍,例如北京东八区为 8 × 100 = 800,西 7.5 区为 -750。

获取时区

接口说明

INT16_T tal_utc_get_time_zone(VOID_T);

参数说明

参数 说明
返回值 有符号型,实际时区的 100 倍,例如北京东八区为 8 × 100 = 800,西 7.5 区为 -750。

时间戳转日期

将标准的 Unix 时间戳转换成易于理解的日期时间结构类型。

接口说明

OPERATE_RET tal_utc_timestamp2date(UINT32_T timestamp, tal_utc_date_t* date, BOOL_T daylightSaving)

参数说明

参数 说明
timestamp 4 字节 Unix 时间戳(从 1970-01-01 00:00:00 开始的秒数)
date 指向转换后日期时间信息的结构体指针,结构体定义见 tal_utc_date_t
daylightSaving 夏令时标志位:
  • 0x00/FALSE:非夏令时
  • 0x01/TRUE:夏令时

日期转时间戳

将日期时间结构体转换为标准的 Unix 时间戳。

接口说明

UINT32_T tal_utc_date2timestamp(tal_utc_date_t *date, BOOL_T daylightSaving);

参数说明

参数 说明
date 指向待转换的日期时间结构体指针,结构体定义见 tal_utc_date_t
daylightSaving 夏令时标志位:
  • 0x00/FALSE: 非夏令时
  • 0x01 / TRUE:夏令时
返回值 4 字节 Unix 时间戳(从 1970-01-01 00:00:00 开始的秒数)

自动时间同步(连接后)

设备与 App 建立连接后,SDK 会自动请求一次时间同步(默认行为)。您可以根据需要,修改宏定义 TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE 来配置请求的时间类型。

配置值 请求类型与数据格式
1(SDK 默认值) 请求云端实时时间
  • 回调事件:TUYA_BLE_CB_EVT_TIME_STAMP
  • 数据结构:tuya_ble_timestamp_data_t
2 请求 App 本地实时时间
  • 回调事件:TUYA_BLE_CB_EVT_APP_LOCAL_TIME_NORMAL
  • 数据结构:tuya_ble_time_noraml_data_t
3 请求云端实时时间(含夏令时)
  • 回调事件:TUYA_BLE_CB_EVT_TIME_STAMP_WITH_DST
  • 数据结构:tuya_ble_timestamp_with_dst_data_t

主动请求实时时间

当设备需要额外获取时间时(例如在定时器中定期同步),开发者可以主动调用本接口。请注意,此操作仅在设备与 App 处于连接状态时有效

接口说明

tuya_ble_status_t tuya_ble_time_req(UINT8_T time_type);

参数说明

参数值 说明
0x00 请求云端实时时间
  • 回调事件:TUYA_BLE_CB_EVT_TIME_STAMP
  • 数据结构:tuya_ble_timestamp_data_t
0x01 请求云端实时时间(年月日时分秒格式)
  • 回调事件:TUYA_BLE_CB_EVT_TIME_NORMAL
  • 数据结构:tuya_ble_time_noraml_data_t
0x02 请求 App 本地实时时间
  • 回调事件:TUYA_BLE_CB_EVT_APP_LOCAL_TIME_NORMAL
  • 数据结构:tuya_ble_time_noraml_data_t

使用方法

交互流程

设备每次上电后,tal_rtc_init 接口会初始化设备本地时间,从 0 开始计时。此时设备时间和网络时间不同步,使用 tal_rtc_time_gettal_utc_get_time_zone 查询到的时间为从上电开始的秒数。

每次连接(配网/重连)设备均会请求一次时间同步,可通过 TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE 配置请求的时间类型。不同类型时间的说明请参考上文中对该宏的介绍。

此后还可以通过调用接口 tuya_ble_time_req 请求时间同步。如果需要定时获取实时时间,可以在定时器中调用该函数。注意只有在连接的状态下才能成功获取实时时间,否则会报状态错误。

当同步的时间下发给设备后,设备会依次调用 tal_rtc_time_settal_utc_set_time_zone 更新本地的时间戳和时区。此时设备时间和网络时间同步,使用 tal_rtc_time_gettal_utc_get_time_zone 查询到的时间为实际的网络时间。

时间服务

代码开发

  1. app_config.h 中配置每次连接请求的时间类型(SDK 默认配置 = 1,如需更改类型请重定义配置)。

    /*
     * Whether to automatically perform a time request after the connection is established.
     * 1: Request cloud time.
     * 2: Request phone local time.
     * 3: Request cloud time with Dst.
     */
    #define TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE            1
    
  2. tuya_ble_protocol_callback.c 里的 回调事件处理函数 下添加如下代码,即可实现基本功能。

    STATIC VOID_T tuya_ble_protocol_callback(tuya_ble_cb_evt_param_t* event)
    {
        switch (event->evt) {
                ...
    
    #if (TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE == 1)
            case TUYA_BLE_CB_EVT_TIME_STAMP: {
                UINT32_T timestamp_s = 0;
                UINT32_T timestamp_ms = 0;
                tal_util_str_intstr2int((VOID_T*)event->timestamp_data.timestamp_string, 10, &timestamp_s);
                tal_util_str_intstr2int((VOID_T*)(event->timestamp_data.timestamp_string+10), 3, &timestamp_ms);
    
                // It should be noted that the SDK already handles called tuya_ble_rtc_set_timestamp api to setting the RTC time and time zone.
                // tal_rtc_time_set(timestamp_s);
                // tal_utc_set_time_zone(event->timestamp_data.time_zone);
    
                TAL_PR_INFO("TUYA_BLE_CB_EVT_TIME_STAMP - time_zone: %d", event->timestamp_data.time_zone);
                TAL_PR_INFO("TUYA_BLE_CB_EVT_TIME_STAMP - timestamp: %d", timestamp_s);
            } break;
    #endif
            case TUYA_BLE_CB_EVT_TIME_NORMAL: {
                // It should be noted that the SDK already handles called tuya_ble_rtc_set_timestamp api to setting the RTC time and time zone.
    
                TAL_PR_INFO("TUYA_BLE_CB_EVT_APP_LOCAL_TIME_NORMAL, [%d-%d-%d %d:%d:%d w=%d] time_zone:%d", \
                    event->time_normal_data.nYear, event->time_normal_data.nMonth, event->time_normal_data.nDay,
                    event->time_normal_data.nHour, event->time_normal_data.nMin, event->time_normal_data.nSec,
                    event->time_normal_data.DayIndex, event->time_normal_data.time_zone);
            } break;
                
    #if (TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE == 2)
            case TUYA_BLE_CB_EVT_APP_LOCAL_TIME_NORMAL: {
                // It should be noted that the SDK already handles called tuya_ble_rtc_set_timestamp api to setting the RTC time and time zone.
    
                TAL_PR_INFO("TUYA_BLE_CB_EVT_APP_LOCAL_TIME_NORMAL, [%d-%d-%d %d:%d:%d w=%d] time_zone:%d", \
                    event->time_normal_data.nYear, event->time_normal_data.nMonth, event->time_normal_data.nDay,
                    event->time_normal_data.nHour, event->time_normal_data.nMin, event->time_normal_data.nSec,
                    event->time_normal_data.DayIndex, event->time_normal_data.time_zone);
            } break;
    #endif
    
    #if (TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE == 3)
            case TUYA_BLE_CB_EVT_TIME_STAMP_WITH_DST: {
                // It should be noted that the SDK already handles called tuya_ble_rtc_set_timestamp api to setting the RTC time and time zone.
                // tal_rtc_time_set(event->timestamp_with_dst_data.timestamp);
                // tal_utc_set_time_zone(event->timestamp_with_dst_data.time_zone);
    
                TAL_PR_INFO("TUYA_BLE_CB_EVT_TIME_STAMP_WITH_DST - time_zone: %d", event->timestamp_with_dst_data.time_zone);
                TAL_PR_INFO("TUYA_BLE_CB_EVT_TIME_STAMP_WITH_DST - timestamp: %d", event->timestamp_with_dst_data.timestamp);
                TAL_PR_INFO("TUYA_BLE_CB_EVT_TIME_STAMP_WITH_DST - n_years_dst: %d", event->timestamp_with_dst_data.n_years_dst);
                for (UINT32_T idx=0 ; idx<event->timestamp_with_dst_data.n_years_dst; idx++) {
                    UINT32_T timestamp_1 = 0;
                    UINT32_T timestamp_2 = 0;
                    tal_util_str_intstr2int(event->timestamp_with_dst_data.p_data + idx*20, 10, &timestamp_1);
                    tal_util_str_intstr2int(event->timestamp_with_dst_data.p_data + idx*20 + 10, 10, &timestamp_2);
                    TAL_PR_INFO("DST Year %d, From %d to %d", idx, timestamp_1, timestamp_2);
                }
            } break;
    #endif
    
            ...
        }
    }
    

功能测试

测试 Case

TEST_CID_GET_TIME
TEST_CID_REQ_TIME
TEST_CID_SET_RTC_TIME
TEST_CID_GET_RTC_TIME

前置条件

  • 您已经下载了 涂鸦 App。
  • 请确认您的设备处于配网状态。

操作步骤

通过上位机(模拟实际产品)和手机 App 进行数据交互。

  1. 查询设备本地时间。调用 tal_rtc_time_gettal_utc_get_time_zone 获取设备本地时间戳和时区,上电后从 0 开始。

    时间服务
  2. 配网成功后,默认请求实时时间。通过宏 TUYA_BLE_AUTO_REQUEST_TIME_CONFIGURE 控制请求的时间类型。

    时间服务
  3. 手动请求实时时间。实际是调用接口 tuya_ble_time_req 请求时间同步。

    时间服务
  4. 查询设备本地时间。调用 tal_rtc_time_gettal_utc_get_time_zone 获取设备本地时间戳和时区,配网成功后同步网络时间。

    时间服务

    上位机使用的相关问题,请访问 Logic 上位机使用指南

支持与帮助

在开发过程遇到问题,您可以登录 TuyaOS 开发者论坛 TuyaOS-蓝牙设备开发 版块进行沟通咨询。

咨询前建议首先查阅 官方资料 或参考已有帖子,并认真阅读 发帖规范