[Telink] SNTP get time from openthread network. (Auto-merged by platform-bot)
diff --git a/config/telink/chip-module/Kconfig.defaults b/config/telink/chip-module/Kconfig.defaults
index bd2f846..378a71c 100644
--- a/config/telink/chip-module/Kconfig.defaults
+++ b/config/telink/chip-module/Kconfig.defaults
@@ -386,6 +386,9 @@
config CHIP_OPENTHREAD_NETWORK_SWITCH_PATH
default y
+config OPENTHREAD_SNTP_CLIENT
+ default y if OPENTHREAD_SOURCES
+
endif # NET_L2_OPENTHREAD
config NET_TX_STACK_SIZE
diff --git a/examples/air-quality-sensor-app/telink/CMakeLists.txt b/examples/air-quality-sensor-app/telink/CMakeLists.txt
index f2d7d12..d521969 100644
--- a/examples/air-quality-sensor-app/telink/CMakeLists.txt
+++ b/examples/air-quality-sensor-app/telink/CMakeLists.txt
@@ -60,3 +60,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/all-clusters-app/telink/CMakeLists.txt b/examples/all-clusters-app/telink/CMakeLists.txt
index 6e290ff..3894d85 100644
--- a/examples/all-clusters-app/telink/CMakeLists.txt
+++ b/examples/all-clusters-app/telink/CMakeLists.txt
@@ -81,3 +81,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/all-clusters-minimal-app/telink/CMakeLists.txt b/examples/all-clusters-minimal-app/telink/CMakeLists.txt
index 5f47464..3577915 100644
--- a/examples/all-clusters-minimal-app/telink/CMakeLists.txt
+++ b/examples/all-clusters-minimal-app/telink/CMakeLists.txt
@@ -61,3 +61,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/all-devices-app/telink/CMakeLists.txt b/examples/all-devices-app/telink/CMakeLists.txt
index 7b9262b..65cc027 100644
--- a/examples/all-devices-app/telink/CMakeLists.txt
+++ b/examples/all-devices-app/telink/CMakeLists.txt
@@ -98,3 +98,8 @@
if (CONFIG_CHIP_PW_RPC)
include(${TELINK_COMMON}/Rpc.cmake)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/bridge-app/telink/CMakeLists.txt b/examples/bridge-app/telink/CMakeLists.txt
index 12a4712..4c16e8d 100644
--- a/examples/bridge-app/telink/CMakeLists.txt
+++ b/examples/bridge-app/telink/CMakeLists.txt
@@ -60,3 +60,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/chef/telink/CMakeLists.txt b/examples/chef/telink/CMakeLists.txt
index 1199843..8eed61d 100755
--- a/examples/chef/telink/CMakeLists.txt
+++ b/examples/chef/telink/CMakeLists.txt
@@ -90,3 +90,8 @@
if (CONFIG_CHIP_PW_RPC)
include(${TELINK_COMMON}/Rpc.cmake)
endif(CONFIG_CHIP_PW_RPC)
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/contact-sensor-app/telink/CMakeLists.txt b/examples/contact-sensor-app/telink/CMakeLists.txt
index 9889215..68c69a4 100755
--- a/examples/contact-sensor-app/telink/CMakeLists.txt
+++ b/examples/contact-sensor-app/telink/CMakeLists.txt
@@ -58,3 +58,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/light-switch-app/telink/CMakeLists.txt b/examples/light-switch-app/telink/CMakeLists.txt
index 3f252b9..776eb12 100755
--- a/examples/light-switch-app/telink/CMakeLists.txt
+++ b/examples/light-switch-app/telink/CMakeLists.txt
@@ -68,3 +68,8 @@
if (CONFIG_CHIP_PW_RPC)
include(${TELINK_COMMON}/Rpc.cmake)
endif(CONFIG_CHIP_PW_RPC)
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/lighting-app/telink/CMakeLists.txt b/examples/lighting-app/telink/CMakeLists.txt
index 8ffe1e4..f150610 100644
--- a/examples/lighting-app/telink/CMakeLists.txt
+++ b/examples/lighting-app/telink/CMakeLists.txt
@@ -100,3 +100,8 @@
if (CONFIG_CHIP_PW_RPC)
include(${TELINK_COMMON}/Rpc.cmake)
endif(CONFIG_CHIP_PW_RPC)
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/lock-app/telink/CMakeLists.txt b/examples/lock-app/telink/CMakeLists.txt
index 1fc084e..c418ef4 100755
--- a/examples/lock-app/telink/CMakeLists.txt
+++ b/examples/lock-app/telink/CMakeLists.txt
@@ -66,3 +66,8 @@
zephyr_library_link_libraries(MCUBOOT_BOOTUTIL)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/DFUOverSMP.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/ota-requestor-app/telink/CMakeLists.txt b/examples/ota-requestor-app/telink/CMakeLists.txt
index 9eec996..cad9e1e 100644
--- a/examples/ota-requestor-app/telink/CMakeLists.txt
+++ b/examples/ota-requestor-app/telink/CMakeLists.txt
@@ -58,3 +58,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/platform/telink/common/src/AppTaskCommon.cpp b/examples/platform/telink/common/src/AppTaskCommon.cpp
index 7743fa8..ad8df84 100644
--- a/examples/platform/telink/common/src/AppTaskCommon.cpp
+++ b/examples/platform/telink/common/src/AppTaskCommon.cpp
@@ -27,6 +27,9 @@
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include "ThreadUtil.h"
+#if CONFIG_OPENTHREAD_SNTP_CLIENT
+#include "ThreadTimeSync.h"
+#endif
#elif CHIP_DEVICE_CONFIG_ENABLE_WIFI
#include <platform/Zephyr/InetUtils.h>
#include <platform/telink/wifi/TelinkWiFiDriver.h>
@@ -812,6 +815,12 @@
#endif
break;
case DeviceEventType::kThreadStateChange:
+#if CONFIG_OPENTHREAD_SNTP_CLIENT
+ if (ConnectivityMgr().IsThreadAttached())
+ {
+ ThreadTimeSync::getInstance().sync_dns("pool.ntp.org");
+ }
+#endif
sIsNetworkProvisioned = ConnectivityMgr().IsThreadProvisioned();
sIsNetworkEnabled = ConnectivityMgr().IsThreadEnabled();
sIsNetworkAttached = ConnectivityMgr().IsThreadAttached();
diff --git a/examples/platform/telink/util/include/ThreadTimeSync.h b/examples/platform/telink/util/include/ThreadTimeSync.h
new file mode 100644
index 0000000..399f402
--- /dev/null
+++ b/examples/platform/telink/util/include/ThreadTimeSync.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2026 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <openthread/dns_client.h>
+#include <openthread/ip6.h>
+#include <openthread/sntp.h>
+
+class ThreadTimeSync
+{
+public:
+ static ThreadTimeSync & getInstance();
+
+ bool has_been_sync() { return static_cast<bool>(m_already_sync.load()); }
+ void sync_ip(const otIp6Address * server_ipv6, bool force = false);
+ void sync_dns(const char * server_name, bool force = false);
+
+ ThreadTimeSync(ThreadTimeSync const &) = delete;
+ void operator=(ThreadTimeSync const &) = delete;
+
+private:
+ static void on_ntp_ip_response_wrap(otError error, const otDnsAddressResponse * resp, void * ctx);
+ static void on_ntp_time_response_wrap(void * ctx, uint64_t time, otError error);
+
+ ThreadTimeSync() : m_in_progress(false), m_already_sync(false) {}
+ void sync_ip_internal(const otIp6Address * server_ipv6);
+
+ constexpr static const char * m_unit_name = "ThreadTimeSync";
+
+ std::atomic<int> m_in_progress;
+ std::atomic<int> m_already_sync;
+ otMessageInfo m_sntp_msg_info;
+ const otSntpQuery m_sntp_query = { .mMessageInfo = &m_sntp_msg_info };
+};
diff --git a/examples/platform/telink/util/src/ThreadTimeSync.cpp b/examples/platform/telink/util/src/ThreadTimeSync.cpp
new file mode 100644
index 0000000..858f3d8
--- /dev/null
+++ b/examples/platform/telink/util/src/ThreadTimeSync.cpp
@@ -0,0 +1,150 @@
+/*
+ *
+ * Copyright (c) 2026 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThreadTimeSync.h"
+#include <ctime>
+#include <system/SystemClock.h>
+#include <zephyr/net/openthread.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(ThreadTimeSync, CONFIG_CHIP_APP_LOG_LEVEL);
+
+ThreadTimeSync & ThreadTimeSync::getInstance()
+{
+ static ThreadTimeSync instance;
+
+ return instance;
+}
+
+void ThreadTimeSync::sync_ip(const otIp6Address * server_ipv6, bool force)
+{
+ if (m_already_sync.load() && !force)
+ {
+ return;
+ }
+
+ int expected_in_process = false;
+
+ if (!m_in_progress.compare_exchange_strong(expected_in_process, true))
+ {
+ LOG_DBG("[%s] operation in process", m_unit_name);
+ return;
+ }
+
+ sync_ip_internal(server_ipv6);
+}
+
+void ThreadTimeSync::sync_dns(const char * server_name, bool force)
+{
+ if (m_already_sync.load() && !force)
+ {
+ return;
+ }
+
+ int expected_in_process = false;
+
+ if (!m_in_progress.compare_exchange_strong(expected_in_process, true))
+ {
+ LOG_DBG("[%s] operation in process", m_unit_name);
+ return;
+ }
+
+ LOG_DBG("[%s] sync_dns %s", m_unit_name, server_name);
+ otError error =
+ otDnsClientResolveIp4Address(openthread_get_default_instance(), server_name, on_ntp_ip_response_wrap, this, nullptr);
+
+ if (error != OT_ERROR_NONE)
+ {
+ m_in_progress.store(false);
+ LOG_WRN("[%s] otDnsClientResolveIp4Address request failed %u", m_unit_name, error);
+ }
+}
+
+void ThreadTimeSync::sync_ip_internal(const otIp6Address * server_ipv6)
+{
+ char server_ipv6_str[OT_IP6_ADDRESS_STRING_SIZE];
+
+ otIp6AddressToString(server_ipv6, server_ipv6_str, sizeof(server_ipv6_str));
+ LOG_DBG("[%s] sync_ip %s", m_unit_name, server_ipv6_str);
+ memset(&m_sntp_msg_info, 0, sizeof(m_sntp_msg_info));
+ m_sntp_msg_info.mPeerAddr = *server_ipv6;
+ m_sntp_msg_info.mPeerPort = 123;
+
+ otError error = otSntpClientQuery(openthread_get_default_instance(), &m_sntp_query, on_ntp_time_response_wrap, this);
+
+ if (error != OT_ERROR_NONE)
+ {
+ m_in_progress.store(false);
+ LOG_WRN("[%s] otSntpClientQuery request failed %u", m_unit_name, error);
+ }
+}
+
+void ThreadTimeSync::on_ntp_ip_response_wrap(otError error, const otDnsAddressResponse * resp, void * ctx)
+{
+ ThreadTimeSync * thread_time_sync = static_cast<ThreadTimeSync *>(ctx);
+
+ if (error == OT_ERROR_NONE)
+ {
+ otIp6Address addr;
+ if (otDnsAddressResponseGetAddress(resp, 0, &addr, nullptr) == OT_ERROR_NONE)
+ {
+ thread_time_sync->sync_ip_internal(&addr);
+ }
+ else
+ {
+ thread_time_sync->m_in_progress.store(false);
+ LOG_WRN("[%s] otDnsClientResolveIp4Address response no address", m_unit_name);
+ }
+ }
+ else
+ {
+ thread_time_sync->m_in_progress.store(false);
+ LOG_WRN("[%s] otDnsClientResolveIp4Address response failed %u", m_unit_name, error);
+ }
+}
+
+void ThreadTimeSync::on_ntp_time_response_wrap(void * ctx, uint64_t time, otError error)
+{
+ ThreadTimeSync * thread_time_sync = static_cast<ThreadTimeSync *>(ctx);
+
+ if (error == OT_ERROR_NONE)
+ {
+ chip::System::Clock::Microseconds64 chip_time = chip::System::Clock::Seconds64(time);
+
+ if (chip::System::SystemClock().SetClock_RealTime(chip_time) != CHIP_NO_ERROR)
+ {
+ LOG_ERR("[%s] SetClock_RealTime failed", m_unit_name);
+ }
+ thread_time_sync->m_already_sync.store(true);
+ thread_time_sync->m_in_progress.store(false);
+ LOG_INF("[%s] SNTP time sync timestamp %llu", m_unit_name, time);
+
+ time_t ts = time;
+ struct tm info;
+ char tm_str[40];
+
+ localtime_r(&ts, &info);
+ strftime(tm_str, sizeof(tm_str), "%A %d %B %Y %H:%M:%S", &info);
+ LOG_INF("[%s] SNTP sync at %s", m_unit_name, tm_str);
+ }
+ else
+ {
+ thread_time_sync->m_in_progress.store(false);
+ LOG_WRN("[%s] otSntpClientQuery response failed %u", m_unit_name, error);
+ }
+}
diff --git a/examples/pump-app/telink/CMakeLists.txt b/examples/pump-app/telink/CMakeLists.txt
index 32d5a65..7f8900b 100755
--- a/examples/pump-app/telink/CMakeLists.txt
+++ b/examples/pump-app/telink/CMakeLists.txt
@@ -57,3 +57,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/pump-controller-app/telink/CMakeLists.txt b/examples/pump-controller-app/telink/CMakeLists.txt
index 136e14d..f5662e8 100755
--- a/examples/pump-controller-app/telink/CMakeLists.txt
+++ b/examples/pump-controller-app/telink/CMakeLists.txt
@@ -57,3 +57,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/smoke-co-alarm-app/telink/CMakeLists.txt b/examples/smoke-co-alarm-app/telink/CMakeLists.txt
index 6d11f0f..7622edb 100755
--- a/examples/smoke-co-alarm-app/telink/CMakeLists.txt
+++ b/examples/smoke-co-alarm-app/telink/CMakeLists.txt
@@ -58,3 +58,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/temperature-measurement-app/telink/CMakeLists.txt b/examples/temperature-measurement-app/telink/CMakeLists.txt
index 44e6655..0e1b90e 100644
--- a/examples/temperature-measurement-app/telink/CMakeLists.txt
+++ b/examples/temperature-measurement-app/telink/CMakeLists.txt
@@ -60,3 +60,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/thermostat/telink/CMakeLists.txt b/examples/thermostat/telink/CMakeLists.txt
index 114163f..262dbed 100755
--- a/examples/thermostat/telink/CMakeLists.txt
+++ b/examples/thermostat/telink/CMakeLists.txt
@@ -60,3 +60,8 @@
if(CONFIG_BOOTLOADER_MCUBOOT)
target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp)
endif()
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()
diff --git a/examples/window-app/telink/CMakeLists.txt b/examples/window-app/telink/CMakeLists.txt
index 8752594..ed8eb7d 100644
--- a/examples/window-app/telink/CMakeLists.txt
+++ b/examples/window-app/telink/CMakeLists.txt
@@ -67,3 +67,8 @@
if (CONFIG_CHIP_PW_RPC)
include(${TELINK_COMMON}/Rpc.cmake)
endif(CONFIG_CHIP_PW_RPC)
+
+if(CONFIG_OPENTHREAD_SNTP_CLIENT)
+ target_sources(app PRIVATE
+ ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp)
+endif()