时间服务

更新时间:2024-04-08 08:18:04下载pdf

智能设备在很多场景下存在时间显示或使用系统时间做功能逻辑等需求,并需要保证时间准确。MCU 标准协议接入支持 MCU 通过 Wi-Fi 模组和 Wi-Fi & 蓝牙双模模组获取格林时间和本地时间,设备在连接网络后,MCU 通过指令获取当前的系统时间。

模组在网期间每次 HTTP 交互时会与云端校时,无交互时设备会间隔 6 小时定期到云端校时一次。模组自身带有软件 RTC,MCU 获取到的时间是模组自身软件 RTC 的时间,在模组离线期间 MCU 也可以获取到时间。同时,模组支持时间服务通知说明功能。Wi-Fi 模组和 Wi-Fi & 蓝牙双模模组上电重新入网后,时间与云端校准后主动通知 MCU。

  • 为了保证初次获取时间数据准确,需在模组入网后 5s 获取,此后获取时间校准间隔可依据使用场景决定,建议不要太过频繁,间隔时间至少大于 1 秒。
  • 模组软件 RTC 24 小时内时间误差小于 10s。
  • 模组软件 RTC 依赖连接云端后校时。如果在离线期间重启设备并依然无法连接云端,模组无法获取到准确的时间。

指令列表

时间服务涉及以下协议指令:

命令字 命令说明
0x0c 获取格林时间
0x1c 获取本地时间
0x34(子命令 0x01) 打开时间服务通知
0x34(子命令 0x02) 模组时间服务通知

获取格林时间(0x0c)

格林时间是一个国际标准的时间基准,不带有时区和夏令时。当模组连接上网络后,本地的时间戳校准完成才会返回成功,并携带有效的时间数据。

  • Wi-Fi & 蓝牙 LE 的模组,当接收模组的报告设备蓝牙连接状态的状态值为 0x03 后,也可以实现时间戳校准功能。设备与 App 连接建立时,App 需要连接外网。
  • 当接收到模组的 报告设备联网状态 状态值为 0x04 后,模组会执行时间戳校准功能。
  • 设备上电后,MCU 若不想通过轮询获取正确的时间,可通过 打开模组时间服务通知,等待模组时间戳校准完成后主动通知 MCU 时间信息。
  • 模组时间戳校准功能需要一定的时间消耗,MCU 在收到模组对应状态通知后立刻获取时间,可能会出现获取失败情况,此时请重试。
  • 如果使用 Wi-Fi & 蓝牙模组,请将固件更新到最新版本,才支持蓝牙连接后的时间校准功能。

MCU 发送

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x0c
数据长度 2 0x0000
数据 0
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例55 aa 03 0c 00 00 0e

模组返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x0c
数据长度 2 0x0007
数据 7 数据长度为 7 字节:
  • Data[0] 为是否获取时间成功标志,0 表示失败,1 表示成功
  • Data[1] 为年份,0x00 表示 2000 年
  • Data[2] 为月份,从 1 开始到 12 结束
  • Data[3] 为日期,从 1 开始到 31 结束
  • Data[4] 为时钟,从 0 开始到 23 结束
  • Data[5] 为分钟,从 0 开始到 59 结束
  • Data[6] 为秒钟,从 0 开始到 59 结束
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例:格林时间 2016 年 4 月 19 日 5 时 6 分 7 秒

55 aa 00 0c 00 07 01 10 04 13 05 06 07 4c

获取本地时间(0x1c)

本地时间是在格林时间的基础上加上当地(设备激活所在地)时区和夏令时的时间。当模组连接上网络后,本地的时间戳校准完成才会返回成功,并带有有效的时间数据。

当地时间是指配网时手机的系统时区。

  • Wi-Fi & 蓝牙 LE 的模组,当接收模组的报告设备蓝牙连接状态的状态值为 0x03 后,也可以实现时间戳校准功能。设备与 App 连接建立时,App 需要连接外网。
  • 当接收到模组的 报告设备联网状态 状态值为 0x04 后,模组会执行时间戳校准功能。
  • 设备上电后 MCU 若不想主动获取时间,可通过 打开模组时间服务通知,等待模组时间戳校准完成后主动通知 MCU 时间信息。
  • 模组时间戳校准功能需要一定的时间消耗,MCU 在收到模组对应状态通知后立刻获取时间,可能会出现获取失败情况,此时请重试。
  • 如果使用 Wi-Fi & 蓝牙模组,请将固件更新到最新版本,才支持蓝牙连接后的时间校准功能。

MCU 发送

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x1c
数据长度 2 0x0000
数据 0
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例55 aa 03 1c 00 00 1e

模组返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x1c
数据长度 2 0x0008
数据 8 数据长度为 8 字节:
  • Data[0] 为是否获取时间成功标志,0 表示失败,1 表示成功
  • Data[1] 为年份,0x00 表示 2000 年
  • Data[2] 为月份,从 1 开始到 12 结束
  • Data[3] 为日期,从 1 开始到 31 结束
  • Data[4] 为时钟,从 0 开始到 23 结束
  • Data[5] 为分钟,从 0 开始到 59 结束
  • Data[6] 为秒钟,从 0 开始到 59 结束
  • Data[7] 为星期,从 1 开始到 7 结束,1 代表星期一
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

打开时间服务通知(0x34 子命令 0x01)

模组上电并连接上云之后时间戳并不会马上校准,需要大约 10 秒才能正确校准时间,模组上电后该功能默认关闭,此时间周期内 MCU 不断地重试获取时间。开启时间服务通知功能后,Wi-Fi 和 Wi-Fi & 蓝牙模组上电、连接云,并与云端校准时间后主动通知 MCU。

上电后 MCU 发送打开时间服务通知指令,并选择格林时间或本地时间,模组收到后将开启 时间服务通知,并返回开启结果至 MCU。

  • 模组重新上电后,时间服务通知功能默认为关闭。
  • 服务开启后,对应的时间数据通过 模组时间服务通知 发送相关时间数据。
  • 模组未重新上电的情况下,服务开启后不可重复开启。模组不重启的情况下需要主动获取时间,执行时间相关获取协议。

MCU 发送

字段 长度 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x34
数据长度 2 0x0002
数据 1 0x01(子命令)
1
  • 0x00:格林时间
  • 0x01:本地时间
校验和 1 从帧头开始按字节求和得出的结果对 256 求余

示例55 aa 03 34 00 02 01 01 3a

模组返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x34
数据长度 2 0x0002
数据 1 0x01(子命令)
1
  • 0x00:服务开启成功
  • 0x01:服务开启失败
校验和 1 从帧头开始按字节求和得出的结果对 256 求余

示例55 aa 00 34 00 02 01 00 36

时间服务通知(0x34 子命令 0x02)

指令使用:模组时间与云端校准后,当前模组 时间服务通知 已打开,模组发送相对应的时间数据给至 MCU。

模组重新上电后,时间服务通知默认为关闭,需要 MCU 发送 打开时间服务通知(0x34 子命令 0x01) 指令。

模组发送

字段 长度 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x34
数据长度 2 0x0009
数据 1 0x02(子命令)
1
  • 0x00:格林时间
  • 0x01:本地时间
7 数据长度为 6 字节。
  • Data[0] 为年份,0x00 表示 2000 年
  • Data[1] 为月份,从 1 开始到 12 结束
  • Data[2] 为日期,从 1 开始到 31 结束
  • Data[3] 为时钟,从 0 开始到 23 结束
  • Data[4] 为分钟,从 0 开始到 59 结束
  • Data[5] 为秒钟,从 0 开始到 59 结束
  • Data[6] 为星期,从 1 开始到 7 结束,1 代表星期一
校验和 1 从帧头开始按字节求和得出的结果对 256 求余

示例:Type:1(本地时间),Time:2021/08/23 18:35:28 星期一

55 aa 00 34 00 09 02 01 15 08 17 12 23 1c 01 c5

MCU 返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x34
数据长度 2 0x0001
数据 1 0x02(子命令)
校验和 1 从帧头开始按字节求和得出的结果对 256 求余

示例55 aa 03 34 00 01 02 39

使用示例

获取格林时间

  1. 打开 SUPPORT_GREEN_TIME 宏定义,开启格林时间功能。

    #define         SUPPORT_GREEN_TIME                //开启格林时间功能
    
  2. 获取格林时间函数 mcu_get_green_time() 定义在 mcu_api.c 文件中,MCU 主动调用 mcu_get_green_time() 函数,发送格林时间获取指令。

    /**
     * @brief MCU 获取格林时间
     * @param Null
     * @return Null
     * @note   MCU 需要自行调用该功能
     */
    void mcu_get_green_time(void)
    {
        wifi_uart_write_frame(GET_ONLINE_TIME_CMD, MCU_TX_VER, 0);
    }
    
  3. 在串口接收数据处理函数 data_handle() 中,收到模组发来的获取结果后,将调用 mcu_get_greentime() 函数接收判断,并做下一步处理。

    void data_handle(unsigned short offset)
    {
        ......
            #ifdef SUPPORT_GREEN_TIME
            case GET_ONLINE_TIME_CMD:                               //获取格林时间
                    mcu_get_greentime((unsigned char *)(wifi_data_process_buf + offset + DATA_START));
            break;
            #endif
        ......
    }
    
  4. 获取到的格林时间,您需实现该函数

    /**
     * @brief 获取到的格林时间
     * @param[in] {time} 获取到的格林时间数据
     * @return Null
     * @note   MCU 需要自行实现该功能
     */
    void mcu_get_greentime(unsigned char time[])
    {
        #error "请自行完成相关代码,并删除该行"
        /*
        time[0] 为是否获取时间成功标志,0 表示失败,1 表示成功
        time[1] 为年份,0x00 表示 2000 年
        time[2] 为月份,从 1 开始到 12 结束
        time[3] 为日期,从 1 开始到 31 结束
        time[4] 为时钟,从 0 开始到 23 结束
        time[5] 为分钟,从 0 开始到 59 结束
        time[6] 为秒钟,从 0 开始到 59 结束
        */
        if(time[0] == 1) {
            //正确接收到 Wi-Fi 模组返回的格林数据
    
        }else {
            //获取格林时间出错,有可能是当前 Wi-Fi 模组未联网
        }
    }
    
  5. 调试助手示意图如下:

    时间服务

获取本地时间

  1. 打开 SUPPORT_MCU_RTC_CHECK 宏定义,开启本地时间功能。

    #define         SUPPORT_MCU_RTC_CHECK                //开启校时功能
    
  2. 获取本地时间函数 mcu_get_system_time() 定义在 mcu_api.c 文件中,MCU 主动调用 mcu_get_system_time() 函数,发送本地时间获取指令。

    /**
     * @brief MCU 获取系统时间,用于校对本地时钟
     * @param Null
     * @return Null
     * @note   MCU 主动调用完成后在 mcu_write_rtctime 函数内校对 rtc 时钟
     */
    void mcu_get_system_time(void)
    {
        wifi_uart_write_frame(GET_LOCAL_TIME_CMD, MCU_TX_VER, 0);
    }
    
  3. 在串口接收数据处理函数 data_handle() 中,收到模组发来的获取结果后,调用 mcu_write_rtctime() 函数接收判断,并做下一步处理。

    void data_handle(unsigned short offset)
    {
        ......
        #ifdef SUPPORT_MCU_RTC_CHECK
        case GET_LOCAL_TIME_CMD:               //获取本地时间
            mcu_write_rtctime((unsigned char *)(wifi_data_process_buf + offset + DATA_START));
        break;
        #endif
        ......
    }
    
  4. 获取到的本地时间,可校对本地 RTC 时钟,您需实现该函数

    /**
     * @brief MCU 校对本地 RTC 时钟
     * @param[in] {time} 获取到的格林时间数据
     * @return Null
     * @note   MCU 需要自行实现该功能
     */
    void mcu_write_rtctime(unsigned char time[])
    {
        #error "请自行完成 RTC 时钟写入代码,并删除该行"
       /*
        Time[0] 为是否获取时间成功标志,0 表示失败,1 表示成功
        Time[1] 为年份,0x00 表示 2000 年
        Time[2] 为月份,从 1 开始到 12 结束
        Time[3] 为日期,从 1 开始到 31 结束
        Time[4] 为时钟,从 0 开始到 23 结束
        Time[5] 为分钟,从 0 开始到 59 结束
        Time[6] 为秒钟,从 0 开始到 59 结束
        Time[7] 为星期,从 1 开始到 7 结束,1 代表星期一
    */
        if(time[0] == 1) {
            //正确接收到 Wi-Fi 模组返回的本地时钟数据
    
        }else {
            //获取本地时钟数据出错,有可能是当前 Wi-Fi 模组未联网
       }
    }
    
  5. 调试助手示意图如下:

    时间服务

打开时间服务通知

  1. 打开 MODULE_EXPANDING_SERVICE_ENABLE 宏定义,开启模组拓展服务功能。

    #define         MODULE_EXPANDING_SERVICE_ENABLE        //开启模组拓展服务功能
    
  2. 打开时间服务通知函数 open_module_time_serve() 定义在 mcu_api.c 文件中,MCU 主动调用 open_module_time_serve() 函数,发送打开模组时间服务通知指令,并选择服务时间类型。

    /**
     * @brief 打开模组时间服务通知
     * @param[in] {time_type} 时间类型
     * @ref       0x00: 格林时间
     * @ref       0x01: 本地时间
     * @return Null
     * @note   MCU 需要自行调用该功能
     */
    void open_module_time_serve(unsigned char time_type)
    {
        unsigned short send_len = 0;
        send_len = set_wifi_uart_byte(send_len, 0x01);
        send_len = set_wifi_uart_byte(send_len, time_type);
        wifi_uart_write_frame(MODULE_EXTEND_FUN_CMD, MCU_TX_VER, send_len);
    }
    
  3. 在串口接收数据处理函数 data_handle() 中,收到模组发来的获取结果后,调用模组拓展功能函数 open_module_time_serve_result() 接收判断,并做下一步处理。

     ```c
     void data_handle(unsigned short offset)
     {
         ......
         #ifdef MODULE_EXPANDING_SERVICE_ENABLE
         case MODULE_EXTEND_FUN_CMD:                             //模组拓展服务
             total_len = (wifi_data_process_buf[offset + LENGTH_HIGH] << 8) | wifi_data_process_buf[offset + LENGTH_LOW];
             open_module_time_serve_result((unsigned char *)(wifi_data_process_buf + offset + DATA_START), total_len);
             break;
         #endif
         ......
     }
     ```
    
  4. 打开模组时间服务通知结果,您需实现该函数

    /**
     * @brief 打开模组时间服务通知结果
     * @param[in] {value} 数据缓冲区
     * @param[in] {length} 数据长度
     * @return Null
     * @note   MCU 需要自行实现该功能
     */
    void open_module_time_serve_result(const unsigned char value[], unsigned short length)
    {
        ......
        case 0x01: { //子命令 打开模组时间服务通知
            if(0x02 != length) {
                //数据长度错误
                return;
                }
    
                if(value[1] == 0) {
                    //服务开启成功
                } else {
                    //服务开启失败
                }
        }
        break;
        ......
    }
    
  5. 调试助手示意图如下:

    时间服务

    时间服务

发送模组时间服务通知

  1. 打开 MODULE_EXPANDING_SERVICE_ENABLE 宏定义,开启模组拓展服务功能。

    #define         MODULE_EXPANDING_SERVICE_ENABLE        //开启模组拓展服务功能
    
  2. 在串口接收数据处理函数 data_handle() 中,收到模组发来的获取结果后,调用模组拓展功能函数 open_module_time_serve_result() 接收判断,并做下一步处理。

    void data_handle(unsigned short offset)
    {
        ......
        #ifdef MODULE_EXPANDING_SERVICE_ENABLE
            case MODULE_EXTEND_FUN_CMD:                             //模组拓展服务
                total_len = (wifi_data_process_buf[offset + LENGTH_HIGH] << 8) | wifi_data_process_buf[offset + LENGTH_LOW];
                open_module_time_serve_result((unsigned char *)(wifi_data_process_buf + offset + DATA_START), total_len);
                break;
        #endif
        ......
    }
    
  3. 模组时间服务通知结果,您需实现该函数

    /**
     * @brief 模组时间服务通知结果
     * @param[in] {value} 数据缓冲区
     * @param[in] {length} 数据长度
     * @return Null
     * @note   MCU 需要自行实现该功能
     */
    void open_module_time_serve_result(const unsigned char value[], unsigned short length)
    {
        ......
        case 0x02: { //子命令 模组时间服务通知
            if(0x09 != length) {
                //数据长度错误
                return;
            }
            unsigned char time_type = value[1]; //0x00:格林时间 0x01:本地时间
            unsigned char time_data[7];
            my_memcpy(time_data, value + 2, length - 2);
            /*
                Data[0]为年份, 0x00 表示 2000 年
                Data[1]为月份,从 1 开始到 12 结束
                Data[2]为日期,从 1 开始到 31 结束
                Data[3]为时钟,从 0 开始到 23 结束
                Data[4]为分钟,从 0 开始到 59 结束
                Data[5]为秒钟,从 0 开始到 15 结束
                Data[6]为星期,从 1 开始到 7 结束,1 代表星期一
            */
        //在此处添加时间数据处理代码,time_type 为时间类型
        unsigned short send_len = 0;
        send_len = set_wifi_uart_byte(send_len,sub_cmd);
            wifi_uart_write_frame(MODULE_EXTEND_FUN_CMD, MCU_TX_VER, send_len);
        }
        break;
        ......
    }
    
  4. 调试助手示意图如下:

    时间服务