use core::net::SocketAddr; use embassy_executor::Spawner; use embassy_net::{ Stack, udp::{PacketMetadata, UdpSocket}, }; use embassy_time::{Duration, Timer}; use portable_atomic::{AtomicU64, Ordering}; use sntpc::{NtpContext, get_time}; use sntpc_net_embassy::UdpSocketWrapper; use sntpc_time_embassy::EmbassyTimestampGenerator; // NTP server (pool.ntp.org is the usual choice) const NTP_SERVER: &str = "pool.ntp.org"; const NTP_PORT: u16 = 123; pub fn now() -> embassy_time::Instant { embassy_time::Instant::now() + Duration::from_secs(NTP_BOOT_OFFSET.load(Ordering::Relaxed)) } pub fn start_ntp_sync(spawner: Spawner, net_stack: Stack<'static>) { spawner.spawn(handler(net_stack).unwrap()); } #[embassy_executor::task] async fn handler(net_stack: Stack<'static>) { loop { match fetch_ntp_time(net_stack).await { Ok(unix_secs) => { NTP_BOOT_OFFSET.store( unix_secs - embassy_time::Instant::now().as_secs(), portable_atomic::Ordering::Relaxed, ); Timer::after(Duration::from_secs(6 * 60 * 60)).await } Err(err) => { defmt::error!("Failed to sync the clock from NTP: {}", err); Timer::after(Duration::from_secs(10)).await } } } } static NTP_BOOT_OFFSET: AtomicU64 = AtomicU64::new(0); pub async fn fetch_ntp_time(net_stack: Stack<'static>) -> Result { // Resolve the NTP server hostname let server_addr = net_stack .dns_query(NTP_SERVER, embassy_net::dns::DnsQueryType::A) .await .map_err(|_| "DNS resolution failed")? .first() .copied() .ok_or("No DNS result")?; let socket_addr = SocketAddr::new(server_addr.into(), NTP_PORT); // Allocate UDP buffers (can be small — SNTP packets are tiny) let mut rx_meta = [PacketMetadata::EMPTY; 16]; let mut rx_buf = [0u8; 48]; let mut tx_meta = [PacketMetadata::EMPTY; 16]; let mut tx_buf = [0u8; 48]; let mut socket = UdpSocket::new( net_stack, &mut rx_meta, &mut rx_buf, &mut tx_meta, &mut tx_buf, ); socket.bind(0).map_err(|_| "bind failed")?; let wrapped = UdpSocketWrapper::from(socket); let ctx = NtpContext::new(EmbassyTimestampGenerator::default()); let result = get_time(socket_addr, &wrapped, ctx) .await .map_err(|_| "SNTP request failed")?; // result.sec is the NTP timestamp in seconds since 1900-01-01. // Unix epoch is 1900-01-01 + 70 years = 2_208_988_800 seconds. const NTP_UNIX_OFFSET: u64 = 2_208_988_800; let unix_secs = result.sec().saturating_sub(NTP_UNIX_OFFSET); Ok(unix_secs) }