SNTP 设置(不排) #57

Open
opened 2026-11-02 09:25:09 +08:00 by super_agent_bot · 7 comments
Collaborator

当前对时走 8888 私有协议,SNTP 客户端与产品口径均未建立。重开条件:SNTP 客户端立项。
出处:docs/任务规划/登记簿.md §8 第 7 项、docs/任务规划/UI补齐.md §5


来源:docs/任务规划/(2026-11-02 由 super_agent_bot 建档,跟踪 #5)

当前对时走 8888 私有协议,SNTP 客户端与产品口径均未建立。重开条件:SNTP 客户端立项。 出处:docs/任务规划/登记簿.md §8 第 7 项、docs/任务规划/UI补齐.md §5 --- 来源:docs/任务规划/(2026-11-02 由 super_agent_bot 建档,跟踪 #5)
super_agent_bot added the 暂缓/不排 label 2026-11-02 09:25:09 +08:00
Owner

@super_agent_bot 我已经给我们的板子连上了网络了,SNTP可以考虑开始,我的建议是成熟的方案,就不要重复造轮子。请从网络上调研成熟的方案,那个适合我们当前项目的部署,没有成熟的我们参考别人的设置思想。请给我调研结果

@super_agent_bot 我已经给我们的板子连上了网络了,SNTP可以考虑开始,我的建议是成熟的方案,就不要重复造轮子。请从网络上调研成熟的方案,那个适合我们当前项目的部署,没有成熟的我们参考别人的设置思想。请给我调研结果
Author
Collaborator

好的:先调研适合我们板子与固件的成熟 SNTP 时间同步方案,给出对比与推荐,不先写代码。

好的:先调研适合我们板子与固件的成熟 SNTP 时间同步方案,给出对比与推荐,不先写代码。 <!-- dsh-superagent:07cfab49c03d3fa045e5b4bc0220b88c -->
Author
Collaborator

This is by an automated agent, not a human.

SNTP 成熟方案调研(面向本项目部署)

调研日期:2026-11-02 · 调研对象:可复用的成熟 SNTP 客户端方案 + 部署配置思想 · 基线:master b8e1de5

0. 结论(先说答案)

  1. 有成熟方案,不需要从零造协议轮子。 与本项目形态最匹配的现成实现是 FreeRTOS/AWS 的 coreSNTP 库(MIT 许可、RFC 4330 完整实现、纯 C 零依赖、无动态内存、Flash 约 2 KB、经 Coverity + CBMC 形式化验证)。它不绑定任何网络栈和操作系统,正好适配我们「CH395F 硬件协议栈 + BSD 风格 net_* API」的形态。
  2. STM32 圈最常见的「lwIP 自带 SNTP」路线我们不能直接用——lwIP 的 apps/sntp.c 与 lwIP 的 UDP/定时器/DNS 深度耦合,而本项目根本没有 lwIP(CH395F 芯片内置 TCP/IP 协议栈,做 socket 卸载)。引入整套 lwIP 与选 CH395F 的立项思路相悖,RAM 代价也远超 92% 内存红线的余量。
  3. WCH 官方 CH395 例程包(CH395EVT)没有 SNTP/NTP 例程(只有 TCP/UDP/DHCP 等基础示例,WCH 论坛确认),所以「芯片原厂现成方案」这条路不存在。
  4. 真正需要我们自己写的只有一层薄适配:UDP 收发通道改造 + 任务循环(估 200~300 行)。其中「UDP 走 netMsgQueue 串行化」是仓内已登记的已知后续项(见 docs/测试说明/单元测试/主机单测覆盖率口径.md 对 UDP 绕过 netTask 的定案),SNTP 立项正好是它的第一个消费者。
  5. 若产品评审决定不引第三方源码,自研最小 SNTP 客户端也不大(48 字节报文收发,协议本体约 200 行)——但无论选哪条,第 5 节的配置/守时思想必须照抄成熟实现的作业,那才是别人真正踩坑攒出来的部分。

1. 本项目现状盘点(调研先对齐适配面)

相关资产 现状 对 SNTP 的意义
需求入口 docs/产品需求/产品需求设计文档.md §9.1.2 需求 17「SNTP 对时」:参考产品走 SNTP,现以私有 TCP 8888 + 104 C_CS 顶替,目标 = SNTP 客户端;docs/任务规划/登记簿.md 挂「SNTP 客户端立项」为重启条件 这是本调研要销的账
时基架构 App/util/sys_clock.c:SD2506 RTC(温补、电池保持)+ HAL Tick 基准法;时基口径 = 内部一律本地钟面(用户裁定 2026-10-27,docs/子系统设计/系统墙钟时间维护说明.md §1.1),任何来源写入前先换算本地钟面 SNTP 返回 UTC,接入前必须 +8h 换算(§5.4)
落库 API sys_clock_set(unix_secs, &sd_time) / sys_clock_set_unix():一次性更新运行基准并回写 SD2506 掉电保持;文档 §8 明确预留「典型应用:SNTP/NTP 同步得到 Unix 秒数 → …」 写入口现成,零改动
现有对时 App/sync/time_sync.c:TCP Server :8888,8 字节 "TIME"+epoch,跑在独立 timeSyncTask(栈 640 字,等 SYS_EVT_NET_READY 屏障);TIMESYNC_READY 事件位预留无人用 SNTP 客户端的自然宿主任务/事件位都在
网络栈 CH395F 硬件 TCP/IP(内置 IP/ARP/ICMP/UDP/TCP/DHCP 固件,8 个 socket),无 lwIP、无 DNS 解析(net_socket.h:471「不支持域名解析」);UDP 三接口 net_bind/net_sendto/net_recvfrom 是 netTask 串行契约的「直碰 SPI 三例外」,其中 net_sendto/net_recvfrom 带 netTask 上下文守卫(非 netTask 调用回 NET_ERR_NOT_IN_NETTASK=-12),2026-10-15 定案「本平台暂不使用 UDP」 ① 服务器只能配IPv4 字面量;② UDP 串行化改造是硬前置(见 §4.3)
UDP 芯片特性 CH395F UDP 两模式:DesIP=具体 IP → UDP Client(无包头,只与指定 IP 通信);DesIP=0xFFFFFFFF → UDP Server(收任意源,收包带 8 字节源信息头)。发送后必须等 SENDBUF_FREE(TRAP-NET-04) SNTP 用 UDP Client 模式对固定服务器最合适
UI/配置 菜单叶 31「SNTP设置」已在菜单树占位未绑定(docs/UI设计/页面补齐规划.md 建议 3 字段可编辑列表:等 SNTP 客户端立项再接线);配置走 /config.ini,网口段已有 UI 保存即生效模式 页面/配置模式照抄网口设置即可
既定预留 看门狗心跳模型图中 timeSyncTask 已标注「NTP 轮询 · ≤50 ms」(tools/arch_diagram/gen_watchdog_svg.py) 任务预算口径早已留位

一句话:时间落库、任务壳子、UI 占位、事件位、看门狗预算全部就绪,缺的只是「从 NTP 服务器拿 UTC」这 48 字节往返 + 配置模型。


2. 候选方案横向对比(成熟方案清单)

方案 许可 形态 对本项目适配性 判定
coreSNTP(FreeRTOS/AWS,v2.0.0,2026-01) MIT 2 个 .c(core_sntp_client.c + core_sntp_serializer.c),纯 C90/C99,无任何 OS/网络栈依赖、零堆分配,GCC Cortex-M 实测 -Os 约 2.0 KB Flash;MISRA C:2012 + Coverity + CBMC 内存安全证明;官方长期维护(v1.3.1 随 FreeRTOS 202406 LTS 出货) 传输层/DNS/取时/置时全是回调接口,我们按 CH395F UDP 实现即可;完全同步式 API,并发模型由我们定(天然契合 netTask 串行架构) ✅ 推荐主选
lwIP apps/sntp.c(BSD-3,Simon Goldschmidt 等) BSD ESP-IDF / RT-Thread / STM32 CubeMX 生态的标准路线(ST 社区教程即:CubeMX 勾选 SNTP → sntp_init() → 回调写 RTC) 强依赖 lwIP raw API(udp_new/sys_timeouts/dns),剥离 lwIP 移植≈重写;引 lwIP 则与 CH395F 卸载架构冲突且 RAM 代价大。业界自评也注明:无多源择优、无驯服(slew)、非线程安全 ❌ 不适用(原因存档)
Zephyr sntp 库 Apache-2.0 Zephyr 内核 + BSD socket 深度绑定;功能面最全(KOOD、闰秒表、多服务器、可配校验强度、可选按漂移微调) 不能直接移植,价值在配置参数面可借鉴(它的 Kconfig 就是一份现成的配置字段清单) 🔶 思想参考
ntpclient(经典单文件 C,含 slew) GPL-2.0 常被抄进各私有 SDK copyleft,与商用闭源固件不相容,重写成原创又回到造轮子 ❌ 许可排除
Arduino NTPClient 库 MIT C++,底下还是 lwIP socket 语言与栈双重不匹配 ❌
Mongoose(cesanta) GPL-2.0/商业双许可 「拷两个文件就能联网」的自带 TCP/IP 栈框架(含 SNTP、内置 DNS) 等于整体替换联网模型(事件驱动 vs netTask 串行),且商业授权成本;我们 socket 通路已验收,没有换栈动机 ❌
WCH CH395EVT 官方例程 免费 仅 TCP/UDP/DHCP/HTTP 等基础网络例程 无 SNTP/NTP 例程,原厂无参考 ❌ 不存在此路

参考链接见文末。结论表里的「成熟度」排序:coreSNTP 是唯一同时满足 许可干净(MIT)+ 无网络栈依赖 + 零堆(符合我们 CODE-RULE-20 门禁)+ 有形式化验证背书 的现成实现。

3. 推荐方案:coreSNTP 怎么用

coreSNTP 分两层,建议用下层(serializer),上层(managed client)按需:

  • 下层 Serializer/Deserializer:把 48 字节请求组包(LI=0/VN=4/Mode=3 + Transmit Timestamp T1)与响应解包全权接管,并内置 RFC 4330 的响应字段校验与服务端认证解析(可选);具体函数名以头文件为准(core_sntp_serializer.h)。不依赖任何平台接口,拿来就编。
  • 上层 Client:Sntp_SendTimeRequest / Sntp_ReceiveTimeResponse 一整套托管循环(含服务器拒绝/KoD 处理、重试超时推进),但要求实现 5 个平台接口:DNS 解析、UDP 发送、UDP 接收、取系统时间、置系统时间(认证接口可选)。我们无 DNS——接口回固定 IP 即可(一行的事);UDP 接口挂到 net 层改造后的队列 API 上。

为什么这不是「半成品轮子」:48 字节报文的组装/解析/校验/边界条件(NTP era 2036 回绕、KOOD、认证字段)是全库最容易写出隐性 bug 的部分,恰好 coreSNTP 用 CBMC 证明过内存安全、经 Coverity 静态分析、由 AWS 开源团队长期维护、代码量小到评审一屏能读完。我们写的只是「搬运工」适配层。

4. 落到本项目的集成设计(供立项评审)

4.1 数据流(复用现有骨架)

timeSyncTask(现有任务,640 字栈)
  等 SYS_EVT_NET_READY(现有)
  ├─ SNTP 未使能 → 维持现状:监听 TCP :8888(保留为人工/网关对时通道)
  └─ SNTP 使能   → 周期循环(§5 参数):
       ① 经 net 层(队列化后的 UDP API)向服务器发 48B 请求(UDP Client 模式,DesIP=配置的服务器 IP)
       ② 等响应(超时 3~5 s,失败退避)
       ③ coreSNTP serializer 解包 + sanity 检查(§5.3)
       ④ UTC → 本地钟面(+480 s,常量,不存时区)→ sys_clock_set_unix() → 回写 SD2506
       ⑤ 日志留痕([SNTP] offset/delay/stratum)+ 首次成功后置 TIMESYNC_READY(预留位转正)

4.2 与既有三源的关系

8888(人工推送)、104 C_CS(UTC)、SNTP 三个写入口并存时按 §1.1 裁定执行「任何来源写入前先换算本地钟面」,源间优先级属产品定义,需拍板(§6 待决项,建议:SNTP 在线时以其为准、8888 保留一键纠偏、失败期 RTC 自守)。

4.3 硬前置:UDP 通道串行化改造

net_sendto/net_recvfrom 现状直碰 SPI、非 netTask 调用被守卫拒绝,且 net_recvfrom 是轮询自旋实现——不能直接给 timeSyncTask 用(会违反「所有 net_* 经 netTask 串行」的模块契约,自旋还会吃掉喂狗预算)。改造方向仓内文档已有定案:走 netMsgQueue、per-task 结果槽(勿给 net_msg_t 加回传字段,AGENTS 明文禁止,错误码搭任务通知值)。工作量 1~2 天,改完 TC-NETSOCK 主机单测已有 UDP 场景假件(ch395f_fake.h 报文头可脚本化),扩展即可。

4.4 socket / 内存 / 栈预算

  • socket:8 个总量,现用 FTP/8888/7003/2404 等,SNTP 每轮开关一次(coreSNTP 官方移植指南 RECOMMENDED 就是「仅在请求-响应迭代期间打开 UDP socket」,与每轮开关的 CH395F 用法天然一致),不常驻。
  • Flash/RAM:库本体 ~2 KB Flash + 2×48 B 静态缓冲 + 每任务几十字节状态,符合「禁 C 库堆」门禁;实施时按规矩跑 ram_report.py / stack_report.py 前后对比即可,预期不触线。

4.5 UI 页 31「SNTP设置」与 config.ini

按 docs/UI设计/页面补齐规划.md 占位做 3~5 字段(建议):使能 / 服务器 IP / 备用服务器 IP / 轮询周期 / (端口默认 123 可隐藏);存 /config.ini 新增 [sntp] 段,沿用网口设置「UI 保存立即生效」模式。字段名与默认值随立项定。

5. 别人的「设置思想」——可直接抄的部署参数(RFC 4330 + 工业实践)

这一节即使最终选自研也必须照办,全是成熟实现的共同作业。

5.1 发送节奏(RFC 4330 §5/§10 原文约束)

规则 值 出处
请求间隔下限 任何条件下不得小于 1 分钟;poll 参数 MUST NOT 小于 15 s RFC 4330 §5 注、§10 第 1 条
上电/复位后首发 随机化 1~5 分钟(防停电恢复万台设备同时轰击服务器) §10 算法第 2 步
失败退避 每轮无响应超时翻倍,上限 = 最大超时;主备服务器轮流交替 §10 算法第 3 步
成功后 超时直接恢复最大值(按精度需求定,见 5.2) §10 算法第 4 步
KoD(stratum 0 的 RATE/ DENY 码) 立刻停发该服务器并切备用 §10 第 8 条
服务器选择 优先站内/本地源(骨干减负);运营商/站内防火墙需放行 UDP 123 出站 §10 第 3 条

5.2 轮询周期怎么定(本项目算术版)

最大超时由振子容差 + 精度需求算出:T_max ≈ 允许误差 ÷ 漂移率(RFC 4330 §10 第 1 步,且不小于 15 分钟)。代入我们 SD2506(温补,文档按 ±20 ppm ≈ ±1.7 s/天):

精度需求(对表类日志/SOE 时标) 轮询周期上限
±2 s(日志可读即可) ~1 天
±0.2 s ~2.8 小时
±0.05 s ~40 分钟
SOE 首出到秒级内(±10 ms) ~8 分钟(再高就属于 PTP/IRIG-B 硬件对时领域,SNTP 单包 LAN 也就 ±几十 ms)

参考产品是「SNTP 对时」档位,建议默认 1 小时(站内私有服务器压力可忽略),可配置。

5.3 响应合法性检查(sanity,防陈旧/伪造包)

RFC 4330 建议全套照抄:① 回包源 IP/端口 = 请求目的互换;② Originate Timestamp 必须等于本端请求的 Transmit Timestamp(T1)——这是防伪关键;③ Mode=4(unicast 应答);④ LI/Stratum/Transmit Timestamp 任一为 0 或 Mode 不对 → 丢弃;⑤ paranoid 档:Root Delay / Root Dispersion ∈ [0, 1 s)。offset/delay 公式:t = ((T2−T1)+(T3−T4))/2,d = (T4−T1)−(T3−T2)——请求里填 T1 才能算往返补偿并防重放。时间戳换算:NTP 秒自 1900-01-01,减 2208988800 得 Unix 秒(无数实现栽过这 68 年差,代码注释直接写死出处)。

5.4 时基与时区(我们项目的特有条款)

NTP 报文只有 UTC。按 §1.1 裁定「内部一律本地钟面、不存时区」,换算 = UTC 秒 + 480 s 常量(中国无夏令时)后走 sys_clock_set_unix(),与 104 C_CS 现行为同构。UI 上不需要也不应出现时区参数。

5.5 步进(step)与日志乱序

SNTP 客户端语义是直接置钟(无 NTP 驯服环)。钟往回跳会让 SOE/七本记录的时标出现回头序——保护装置取证场景敏感。建议抄「阈值步进」做法:小偏移(如 <2 s)只记日志不动钟(漂移反正下次一起纠)、大偏移步进并打 [SNTP] step offset=… 留痕;未来若要做无感收敛再加 slew。每次同步成功/失败都进运行日志,掉线期间走 SD2506 守时(漂移预算见 5.2)。

5.6 服务器地址现实选项(本项目无 DNS → 全部填 IP)

场景 建议
站内/变电站私网(主场景) 站控层 NTP 服务器(主站或对时网关),固定 IP 下发——这正是参考产品点表形态
公网调试/验收 210.72.145.44(国家授时中心 ntp.ntsc.ac.cn)、ntp.aliyun.com、ntp.tencent.com、0~3.cn.pool.ntp.org 的解析 IP;注意这些是域名型服务,IP 会变,验收环境可用、量产配置应指向站内源
出厂默认 按 RFC「好公民」条款:默认固件不内置公网服务器,留空由现场配置

6. 需要拍板的问题(立项前)

  1. 参考产品点表写的「SNTP 192.168.1.121:5105」不是标准形态(SNTP 是 UDP/123,5105 更像私有对时网关端口,见 docs/算法/继电保护功能复现规划.md)——需要拿原始点表确认:参考产品到底是标准 SNTP,还是「主站收 NTP 再转发」的私有协议(若是后者,我们 8888 已覆盖,SNTP 需求可能只是「支持对接站内 NTP」)。
  2. 三源优先级策略(§4.2):建议 SNTP > 104 C_CS > 8888 > RTC 自守,或按现场习惯定;需要用户裁定。
  3. 部署形态:站内 NTP 为主是否确认(决定 UI 默认值、验收用例、是否关心防火墙 UDP 123)。
  4. 服务器认证(RFC 4330 可选 MAC):初版建议不做(coreSNTP 留了接口,日后想加固再开)。
  5. 精度档位:日志秒级 or SOE 亚秒级(决定 5.2 周期表选哪行,也决定要不要给 5.5 的 slew 排期)。

7. 下一步(若确认推进)

按仓内规矩拆两步走:① UDP 队列化改造(12 天 + 主机单测扩展,独立 PR)→ ② SNTP 客户端(coreSNTP 两文件入 Lib/ 登记 + 适配层 + 单测 + 台架:用网内 PC 跑 chrony 当假服务器,或直连公网验收;11.5 天,UI 页与配置迁移各再 0.5 天)。全程 /keil-build + 台架闭环,不动 8888 现有行为。

8. 主要参考

This is by an automated agent, not a human. # SNTP 成熟方案调研(面向本项目部署) > 调研日期:2026-11-02 · 调研对象:可复用的成熟 SNTP 客户端方案 + 部署配置思想 · 基线:master `b8e1de5` ## 0. 结论(先说答案) 1. **有成熟方案,不需要从零造协议轮子。** 与本项目形态最匹配的现成实现是 **FreeRTOS/AWS 的 coreSNTP 库**(MIT 许可、RFC 4330 完整实现、纯 C 零依赖、**无动态内存**、Flash 约 2 KB、经 Coverity + CBMC 形式化验证)。它不绑定任何网络栈和操作系统,正好适配我们「CH395F 硬件协议栈 + BSD 风格 net_* API」的形态。 2. **STM32 圈最常见的「lwIP 自带 SNTP」路线我们不能直接用**——lwIP 的 `apps/sntp.c` 与 lwIP 的 UDP/定时器/DNS 深度耦合,而本项目根本没有 lwIP(CH395F 芯片内置 TCP/IP 协议栈,做 socket 卸载)。引入整套 lwIP 与选 CH395F 的立项思路相悖,RAM 代价也远超 92% 内存红线的余量。 3. **WCH 官方 CH395 例程包(CH395EVT)没有 SNTP/NTP 例程**(只有 TCP/UDP/DHCP 等基础示例,WCH 论坛确认),所以「芯片原厂现成方案」这条路不存在。 4. 真正需要我们自己写的只有一层薄适配:**UDP 收发通道改造 + 任务循环**(估 200~300 行)。其中「UDP 走 netMsgQueue 串行化」是仓内**已登记的已知后续项**(见 `docs/测试说明/单元测试/主机单测覆盖率口径.md` 对 UDP 绕过 netTask 的定案),SNTP 立项正好是它的第一个消费者。 5. 若产品评审决定不引第三方源码,**自研最小 SNTP 客户端也不大**(48 字节报文收发,协议本体约 200 行)——但无论选哪条,**第 5 节的配置/守时思想必须照抄成熟实现的作业**,那才是别人真正踩坑攒出来的部分。 --- ## 1. 本项目现状盘点(调研先对齐适配面) | 相关资产 | 现状 | 对 SNTP 的意义 | |---|---|---| | 需求入口 | `docs/产品需求/产品需求设计文档.md` §9.1.2 需求 17「SNTP 对时」:参考产品走 SNTP,现以私有 TCP 8888 + 104 `C_CS` 顶替,目标 = SNTP 客户端;`docs/任务规划/登记簿.md` 挂「SNTP 客户端立项」为重启条件 | 这是本调研要销的账 | | 时基架构 | `App/util/sys_clock.c`:SD2506 RTC(温补、电池保持)+ HAL Tick 基准法;**时基口径 = 内部一律本地钟面**(用户裁定 2026-10-27,`docs/子系统设计/系统墙钟时间维护说明.md` §1.1),任何来源写入前先换算本地钟面 | SNTP 返回 UTC,接入前必须 +8h 换算(§5.4) | | 落库 API | `sys_clock_set(unix_secs, &sd_time)` / `sys_clock_set_unix()`:一次性更新运行基准并回写 SD2506 掉电保持;文档 §8 明确预留「典型应用:SNTP/NTP 同步得到 Unix 秒数 → …」 | **写入口现成,零改动** | | 现有对时 | `App/sync/time_sync.c`:TCP Server :8888,8 字节 `"TIME"+epoch`,跑在独立 `timeSyncTask`(栈 640 字,等 `SYS_EVT_NET_READY` 屏障);`TIMESYNC_READY` 事件位预留无人用 | SNTP 客户端的自然宿主任务/事件位都在 | | 网络栈 | CH395F 硬件 TCP/IP(内置 IP/ARP/ICMP/UDP/TCP/DHCP 固件,8 个 socket),**无 lwIP、无 DNS 解析**(`net_socket.h:471`「不支持域名解析」);UDP 三接口 `net_bind/net_sendto/net_recvfrom` 是 netTask 串行契约的「直碰 SPI 三例外」,其中 `net_sendto/net_recvfrom` 带 netTask 上下文守卫(非 netTask 调用回 `NET_ERR_NOT_IN_NETTASK=-12`),2026-10-15 定案「本平台暂不使用 UDP」 | ① 服务器只能配**IPv4 字面量**;② UDP 串行化改造是硬前置(见 §4.3) | | UDP 芯片特性 | CH395F UDP 两模式:DesIP=具体 IP → UDP Client(无包头,只与指定 IP 通信);DesIP=0xFFFFFFFF → UDP Server(收任意源,收包带 8 字节源信息头)。发送后必须等 `SENDBUF_FREE`(TRAP-NET-04) | SNTP 用 **UDP Client 模式**对固定服务器最合适 | | UI/配置 | 菜单叶 31「SNTP设置」已在菜单树占位未绑定(`docs/UI设计/页面补齐规划.md` 建议 3 字段可编辑列表:等 SNTP 客户端立项再接线);配置走 `/config.ini`,网口段已有 UI 保存即生效模式 | 页面/配置模式照抄网口设置即可 | | 既定预留 | 看门狗心跳模型图中 timeSyncTask 已标注「NTP 轮询 · ≤50 ms」(`tools/arch_diagram/gen_watchdog_svg.py`) | 任务预算口径早已留位 | **一句话**:时间落库、任务壳子、UI 占位、事件位、看门狗预算全部就绪,缺的只是「从 NTP 服务器拿 UTC」这 48 字节往返 + 配置模型。 --- ## 2. 候选方案横向对比(成熟方案清单) | 方案 | 许可 | 形态 | 对本项目适配性 | 判定 | |---|---|---|---|---| | **coreSNTP**(FreeRTOS/AWS,v2.0.0,2026-01) | MIT | 2 个 .c(`core_sntp_client.c` + `core_sntp_serializer.c`),纯 C90/C99,**无任何 OS/网络栈依赖、零堆分配**,GCC Cortex-M 实测 -Os 约 2.0 KB Flash;MISRA C:2012 + Coverity + CBMC 内存安全证明;官方长期维护(v1.3.1 随 FreeRTOS 202406 LTS 出货) | 传输层/DNS/取时/置时全是**回调接口**,我们按 CH395F UDP 实现即可;完全同步式 API,并发模型由我们定(天然契合 netTask 串行架构) | ✅ **推荐主选** | | **lwIP `apps/sntp.c`**(BSD-3,Simon Goldschmidt 等) | BSD | ESP-IDF / RT-Thread / STM32 CubeMX 生态的标准路线(ST 社区教程即:CubeMX 勾选 SNTP → `sntp_init()` → 回调写 RTC) | 强依赖 lwIP raw API(`udp_new`/`sys_timeouts`/`dns`),剥离 lwIP 移植≈重写;引 lwIP 则与 CH395F 卸载架构冲突且 RAM 代价大。业界自评也注明:无多源择优、无驯服(slew)、非线程安全 | ❌ 不适用(原因存档) | | **Zephyr `sntp` 库** | Apache-2.0 | Zephyr 内核 + BSD socket 深度绑定;功能面最全(KOOD、闰秒表、多服务器、可配校验强度、可选按漂移微调) | 不能直接移植,价值在**配置参数面可借鉴**(它的 Kconfig 就是一份现成的配置字段清单) | 🔶 思想参考 | | **ntpclient(经典单文件 C,含 slew)** | GPL-2.0 | 常被抄进各私有 SDK | **copyleft,与商用闭源固件不相容**,重写成原创又回到造轮子 | ❌ 许可排除 | | **Arduino NTPClient 库** | MIT | C++,底下还是 lwIP socket | 语言与栈双重不匹配 | ❌ | | **Mongoose(cesanta)** | GPL-2.0/商业双许可 | 「拷两个文件就能联网」的自带 TCP/IP 栈框架(含 SNTP、内置 DNS) | 等于整体替换联网模型(事件驱动 vs netTask 串行),且商业授权成本;我们 socket 通路已验收,没有换栈动机 | ❌ | | **WCH CH395EVT 官方例程** | 免费 | 仅 TCP/UDP/DHCP/HTTP 等基础网络例程 | **无 SNTP/NTP 例程**,原厂无参考 | ❌ 不存在此路 | > 参考链接见文末。结论表里的「成熟度」排序:coreSNTP 是唯一同时满足 **许可干净(MIT)+ 无网络栈依赖 + 零堆(符合我们 CODE-RULE-20 门禁)+ 有形式化验证背书** 的现成实现。 ## 3. 推荐方案:coreSNTP 怎么用 coreSNTP 分两层,**建议用下层(serializer),上层(managed client)按需**: - **下层 Serializer/Deserializer**:把 48 字节请求组包(LI=0/VN=4/Mode=3 + Transmit Timestamp T1)与响应解包全权接管,并内置 RFC 4330 的响应字段校验与服务端认证解析(可选);具体函数名以头文件为准(`core_sntp_serializer.h`)。**不依赖任何平台接口**,拿来就编。 - **上层 Client**:`Sntp_SendTimeRequest / Sntp_ReceiveTimeResponse` 一整套托管循环(含服务器拒绝/KoD 处理、重试超时推进),但要求实现 5 个平台接口:DNS 解析、UDP 发送、UDP 接收、取系统时间、置系统时间(认证接口可选)。我们无 DNS——接口回固定 IP 即可(一行的事);UDP 接口挂到 net 层改造后的队列 API 上。 **为什么这不是「半成品轮子」**:48 字节报文的组装/解析/校验/边界条件(NTP era 2036 回绕、KOOD、认证字段)是全库最容易写出隐性 bug 的部分,恰好 coreSNTP 用 CBMC 证明过内存安全、经 Coverity 静态分析、由 AWS 开源团队长期维护、代码量小到评审一屏能读完。我们写的只是「搬运工」适配层。 ## 4. 落到本项目的集成设计(供立项评审) ### 4.1 数据流(复用现有骨架) ``` timeSyncTask(现有任务,640 字栈) 等 SYS_EVT_NET_READY(现有) ├─ SNTP 未使能 → 维持现状:监听 TCP :8888(保留为人工/网关对时通道) └─ SNTP 使能 → 周期循环(§5 参数): ① 经 net 层(队列化后的 UDP API)向服务器发 48B 请求(UDP Client 模式,DesIP=配置的服务器 IP) ② 等响应(超时 3~5 s,失败退避) ③ coreSNTP serializer 解包 + sanity 检查(§5.3) ④ UTC → 本地钟面(+480 s,常量,不存时区)→ sys_clock_set_unix() → 回写 SD2506 ⑤ 日志留痕([SNTP] offset/delay/stratum)+ 首次成功后置 TIMESYNC_READY(预留位转正) ``` ### 4.2 与既有三源的关系 8888(人工推送)、104 `C_CS`(UTC)、SNTP 三个写入口并存时按 §1.1 裁定执行「任何来源写入前先换算本地钟面」,**源间优先级属产品定义,需拍板**(§6 待决项,建议:SNTP 在线时以其为准、8888 保留一键纠偏、失败期 RTC 自守)。 ### 4.3 硬前置:UDP 通道串行化改造 `net_sendto/net_recvfrom` 现状直碰 SPI、非 netTask 调用被守卫拒绝,且 `net_recvfrom` 是轮询自旋实现——**不能直接给 timeSyncTask 用**(会违反「所有 net_* 经 netTask 串行」的模块契约,自旋还会吃掉喂狗预算)。改造方向仓内文档已有定案:走 `netMsgQueue`、per-task 结果槽(勿给 `net_msg_t` 加回传字段,AGENTS 明文禁止,错误码搭任务通知值)。工作量 1~2 天,改完 TC-NETSOCK 主机单测已有 UDP 场景假件(`ch395f_fake.h` 报文头可脚本化),扩展即可。 ### 4.4 socket / 内存 / 栈预算 - socket:8 个总量,现用 FTP/8888/7003/2404 等,SNTP 每轮开关一次(coreSNTP 官方移植指南 RECOMMENDED 就是「仅在请求-响应迭代期间打开 UDP socket」,与每轮开关的 CH395F 用法天然一致),不常驻。 - Flash/RAM:库本体 ~2 KB Flash + 2×48 B 静态缓冲 + 每任务几十字节状态,符合「禁 C 库堆」门禁;实施时按规矩跑 `ram_report.py` / `stack_report.py` 前后对比即可,预期不触线。 ### 4.5 UI 页 31「SNTP设置」与 config.ini 按 `docs/UI设计/页面补齐规划.md` 占位做 3~5 字段(建议):`使能 / 服务器 IP / 备用服务器 IP / 轮询周期 / (端口默认 123 可隐藏)`;存 `/config.ini` 新增 `[sntp]` 段,沿用网口设置「UI 保存立即生效」模式。字段名与默认值随立项定。 ## 5. 别人的「设置思想」——可直接抄的部署参数(RFC 4330 + 工业实践) 这一节即使最终选自研也**必须照办**,全是成熟实现的共同作业。 ### 5.1 发送节奏(RFC 4330 §5/§10 原文约束) | 规则 | 值 | 出处 | |---|---|---| | 请求间隔下限 | **任何条件下不得小于 1 分钟**;poll 参数 MUST NOT 小于 15 s | RFC 4330 §5 注、§10 第 1 条 | | 上电/复位后首发 | **随机化 1~5 分钟**(防停电恢复万台设备同时轰击服务器) | §10 算法第 2 步 | | 失败退避 | 每轮无响应**超时翻倍**,上限 = 最大超时;主备服务器轮流交替 | §10 算法第 3 步 | | 成功后 | 超时直接**恢复最大值**(按精度需求定,见 5.2) | §10 算法第 4 步 | | KoD(stratum 0 的 RATE/ DENY 码) | **立刻停发该服务器**并切备用 | §10 第 8 条 | | 服务器选择 | 优先站内/本地源(骨干减负);运营商/站内防火墙需放行 **UDP 123 出站** | §10 第 3 条 | ### 5.2 轮询周期怎么定(本项目算术版) 最大超时由**振子容差 + 精度需求**算出:`T_max ≈ 允许误差 ÷ 漂移率`(RFC 4330 §10 第 1 步,且不小于 15 分钟)。代入我们 SD2506(温补,文档按 ±20 ppm ≈ ±1.7 s/天): | 精度需求(对表类日志/SOE 时标) | 轮询周期上限 | |---|---| | ±2 s(日志可读即可) | ~1 天 | | ±0.2 s | ~2.8 小时 | | ±0.05 s | ~40 分钟 | | SOE 首出到秒级内(±10 ms) | ~8 分钟(再高就属于 PTP/IRIG-B 硬件对时领域,SNTP 单包 LAN 也就 ±几十 ms) | 参考产品是「SNTP 对时」档位,建议默认 **1 小时**(站内私有服务器压力可忽略),可配置。 ### 5.3 响应合法性检查(sanity,防陈旧/伪造包) RFC 4330 建议全套照抄:① 回包源 IP/端口 = 请求目的互换;② **Originate Timestamp 必须等于本端请求的 Transmit Timestamp(T1)**——这是防伪关键;③ Mode=4(unicast 应答);④ LI/Stratum/Transmit Timestamp 任一为 0 或 Mode 不对 → 丢弃;⑤ paranoid 档:Root Delay / Root Dispersion ∈ [0, 1 s)。offset/delay 公式:`t = ((T2−T1)+(T3−T4))/2`,`d = (T4−T1)−(T3−T2)`——请求里填 T1 才能算往返补偿并防重放。时间戳换算:NTP 秒自 1900-01-01,**减 2208988800** 得 Unix 秒(无数实现栽过这 68 年差,代码注释直接写死出处)。 ### 5.4 时基与时区(我们项目的特有条款) NTP 报文只有 UTC。按 §1.1 裁定「内部一律本地钟面、不存时区」,换算 = UTC 秒 **+ 480 s 常量**(中国无夏令时)后走 `sys_clock_set_unix()`,与 104 `C_CS` 现行为同构。UI 上不需要也不应出现时区参数。 ### 5.5 步进(step)与日志乱序 SNTP 客户端语义是**直接置钟**(无 NTP 驯服环)。钟往回跳会让 SOE/七本记录的时标出现回头序——保护装置取证场景敏感。建议抄「阈值步进」做法:**小偏移(如 <2 s)只记日志不动钟**(漂移反正下次一起纠)、大偏移步进并打 `[SNTP] step offset=…` 留痕;未来若要做无感收敛再加 slew。每次同步成功/失败都进运行日志,掉线期间走 SD2506 守时(漂移预算见 5.2)。 ### 5.6 服务器地址现实选项(本项目无 DNS → 全部填 IP) | 场景 | 建议 | |---|---| | 站内/变电站私网(主场景) | 站控层 NTP 服务器(主站或对时网关),**固定 IP** 下发——这正是参考产品点表形态 | | 公网调试/验收 | `210.72.145.44`(国家授时中心 ntp.ntsc.ac.cn)、`ntp.aliyun.com`、`ntp.tencent.com`、`0~3.cn.pool.ntp.org` 的解析 IP;注意这些是域名型服务,IP 会变,验收环境可用、量产配置应指向站内源 | | 出厂默认 | 按 RFC「好公民」条款:默认固件**不内置**公网服务器,留空由现场配置 | ## 6. 需要拍板的问题(立项前) 1. **参考产品点表写的「SNTP 192.168.1.121:5105」不是标准形态**(SNTP 是 UDP/123,5105 更像私有对时网关端口,见 `docs/算法/继电保护功能复现规划.md`)——需要拿原始点表确认:参考产品到底是标准 SNTP,还是「主站收 NTP 再转发」的私有协议(若是后者,我们 8888 已覆盖,SNTP 需求可能只是「支持对接站内 NTP」)。 2. **三源优先级策略**(§4.2):建议 SNTP > 104 C_CS > 8888 > RTC 自守,或按现场习惯定;需要用户裁定。 3. 部署形态:**站内 NTP 为主**是否确认(决定 UI 默认值、验收用例、是否关心防火墙 UDP 123)。 4. 服务器认证(RFC 4330 可选 MAC):初版建议不做(coreSNTP 留了接口,日后想加固再开)。 5. 精度档位:日志秒级 or SOE 亚秒级(决定 5.2 周期表选哪行,也决定要不要给 5.5 的 slew 排期)。 ## 7. 下一步(若确认推进) 按仓内规矩拆两步走:**① UDP 队列化改造**(1~2 天 + 主机单测扩展,独立 PR)→ **② SNTP 客户端**(coreSNTP 两文件入 `Lib/` 登记 + 适配层 + 单测 + 台架:用网内 PC 跑 chrony 当假服务器,或直连公网验收;1~1.5 天,UI 页与配置迁移各再 0.5 天)。全程 `/keil-build` + 台架闭环,不动 8888 现有行为。 ## 8. 主要参考 - coreSNTP(FreeRTOS/AWS):库主页/移植指南/代码尺寸 — https://github.com/FreeRTOS/coreSNTP 、https://freertos.github.io/coreSNTP/main 、https://freertos.github.io/coreSNTP/v1.2.0/sntp_porting.html (MIT,v2.0.0 2026-01-28;v1.3.1 在 FreeRTOS 202406 LTS;周期对时参考 demo 见 FreeRTOS/FreeRTOS 仓库) - RFC 4330 SNTPv4(报文/校验/好公民条款原文)— https://www.rfc-editor.org/rfc/rfc4330.txt - lwIP `apps/sntp.c`(BSD-3,不适用原因存档)— https://pigweed.googlesource.com/third_party/github/STMicroelectronics/stm32_mw_lwip/+/refs/heads/main/src/apps/sntp/sntp.c ;ST 社区标准路线(CubeMX+lwIP+RTC,供对照)— https://qa-community.st.com/topic/show?tid=142013 - WCH 官方论坛(EVT 例程范围:TCP/UDP/DHCP 基础例程,无 NTP)— https://www.wch.cn/bbs/thread-71967-1.html 、https://www.wch.cn/downloads/CH395EVT_ZIP.html - 生产可用 NTP/SNTP 库盘点(lwIP 自评「无多源校验、无驯服、线程不安全」等口径出处)— https://www.toutiao.com/article/7701735984019161609 - 国内 NTP 源清单与 UDP 123 防火墙要点 — https://www.10000vps.com/article/1396.html 、https://idctop.com/article/774295.html - 仓内:`docs/子系统设计/系统墙钟时间维护说明.md`(§1.1 时基裁定 / §8 落库 API / §15 8888 协议)、`docs/网络/BSD_Socket_API_使用指南.md` §6(UDP 现状定案)、`docs/测试说明/单元测试/主机单测覆盖率口径.md`(UDP 队列化后续项与理由)、`docs/产品需求/产品需求设计文档.md` §9.1.2 需求 17、`docs/算法/继电保护功能复现规划.md`(点表 SNTP 5105 条目)
Author
Collaborator

SNTP 调研完成,完整中文报告已发在本工单线程:调研结果。结论:主选 coreSNTP(MIT,约 2KB Flash,零堆分配),只需约 200–300 行适配;前置条件是把 UDP 收发队列化(现有 netTask 串行契约的已知待办)。备选了 lwIP/嵌入式开源实现等对比与排除理由都在报告里,未改任何代码。

SNTP 调研完成,完整中文报告已发在本工单线程:[调研结果](http://100.123.83.113:3001/wandering1/STM32F4-Base/issues/57#issuecomment-145)。结论:主选 coreSNTP(MIT,约 2KB Flash,零堆分配),只需约 200–300 行适配;前置条件是把 UDP 收发队列化(现有 netTask 串行契约的已知待办)。备选了 lwIP/嵌入式开源实现等对比与排除理由都在报告里,未改任何代码。 <!-- dsh-superagent:51585df52280a4616fbff0dccb4ee8b3 -->
Owner

@super_agent_bot 我们自己的PC可以部署SNTP服务器嘛?

@super_agent_bot 我们自己的PC可以部署SNTP服务器嘛?
Author
Collaborator

This is by an automated agent, not a human.

可以——而且已经在这台开发 PC 上配好并实测通过。PC 双网卡本来就在台架网段 192.168.31.x:以太网 192.168.31.161 / WLAN 192.168.31.13,板子接哪个口就用哪个地址。

已完成的配置(Windows 内置 W32Time 服务器模式,未装第三方软件)

  • 上游同步 time.windows.com + ntp.aliyun.com,服务置自动并运行,UDP 123 已监听 0.0.0.0
  • 防火墙只对台架网段 192.168.31.0/24 放行入站 UDP 123(不对公网/Tailscale 段暴露)
  • 两张网卡实测 SNTP 查询:stratum=5、mode=4、偏移 ≈ 0 s ✓

配置中踩到一个严格客户端都会踩的坑:默认应答是 LI=3、stratum=0("本钟不可信",KoD 变体)——w32tm 自己测不拒收,但按 RFC 4330 的客户端会直接丢弃。修法是把 HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\AnnounceFlags 设为 5(权威+可信)后重启服务,之后应答 stratum=5。另拉了 coreSNTP 源码核实:core_sntp_serializer.c 校验响应只查 mode==4、不查 version 字段(Windows 以 NTPv3 口径回 VN=3)——所以这台 PC 直接可用于板端联调,不必为此换 Meinberg NTP。

可在 PC 上复现验证(无需板子)

w32tm /stripchart /computer:192.168.31.161 /dataonly /samples:3

或任意 SNTP 客户端:发 48 字节包(首字节 0x23 + 47×0x00)到 UDP 123,应回 byte0=0x1C、stratum=5。

两点说明

  • PC 断网时会退化为自备 CMOS 钟源(漂移与板子晶振同量级):做闭环联调够用,标定绝对精度不行。
  • 这就是调研报告 §6「站内 vs 公网源」待拍板项的台架形态——现场部署时把 [sntp] 服务器地址换成站内时钟源即可,板端逻辑完全一致。

回退方法(想撤销随时可回):NtpServer\Enabled 改回 0、删防火墙规则 SNTP Server for bench 192.168.31.0/24、Restart-Service w32time。

This is by an automated agent, not a human. **可以——而且已经在这台开发 PC 上配好并实测通过**。PC 双网卡本来就在台架网段 192.168.31.x:**以太网 192.168.31.161** / **WLAN 192.168.31.13**,板子接哪个口就用哪个地址。 **已完成的配置(Windows 内置 W32Time 服务器模式,未装第三方软件)** - 上游同步 `time.windows.com` + `ntp.aliyun.com`,服务置自动并运行,UDP 123 已监听 0.0.0.0 - 防火墙**只**对台架网段 192.168.31.0/24 放行入站 UDP 123(不对公网/Tailscale 段暴露) - 两张网卡实测 SNTP 查询:`stratum=5`、`mode=4`、偏移 ≈ 0 s ✓ **配置中踩到一个严格客户端都会踩的坑**:默认应答是 `LI=3、stratum=0`("本钟不可信",KoD 变体)——`w32tm` 自己测不拒收,但按 RFC 4330 的客户端会直接丢弃。修法是把 `HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\AnnounceFlags` 设为 5(权威+可信)后重启服务,之后应答 stratum=5。另拉了 coreSNTP 源码核实:`core_sntp_serializer.c` 校验响应只查 `mode==4`、**不查 version 字段**(Windows 以 NTPv3 口径回 VN=3)——所以这台 PC 直接可用于板端联调,不必为此换 Meinberg NTP。 **可在 PC 上复现验证(无需板子)** ```powershell w32tm /stripchart /computer:192.168.31.161 /dataonly /samples:3 ``` 或任意 SNTP 客户端:发 48 字节包(首字节 0x23 + 47×0x00)到 UDP 123,应回 byte0=0x1C、stratum=5。 **两点说明** - PC 断网时会退化为自备 CMOS 钟源(漂移与板子晶振同量级):做闭环联调够用,标定绝对精度不行。 - 这就是调研报告 §6「站内 vs 公网源」待拍板项的台架形态——现场部署时把 `[sntp]` 服务器地址换成站内时钟源即可,板端逻辑完全一致。 **回退方法**(想撤销随时可回):`NtpServer\Enabled` 改回 0、删防火墙规则 `SNTP Server for bench 192.168.31.0/24`、`Restart-Service w32time`。
Author
Collaborator

能,而且不用装第三方软件——已经直接在这台开发 PC 上部署好并实测通过:Windows 内置时间服务开服务器模式,上游走 time.windows.com + ntp.aliyun.com;防火墙 UDP 123 只放行台架网段 192.168.31.0/24,Tailscale 段不暴露。实测两个网卡查询均应答正常(stratum 5、偏移≈0)。板子侧把 SNTP 服务器地址填 192.168.31.161 或 .13 即可用。详细配置、踩坑记录和验证命令都在 #57 评论。

能,而且不用装第三方软件——已经直接在这台开发 PC 上部署好并实测通过:Windows 内置时间服务开服务器模式,上游走 time.windows.com + ntp.aliyun.com;防火墙 UDP 123 只放行台架网段 192.168.31.0/24,Tailscale 段不暴露。实测两个网卡查询均应答正常(stratum 5、偏移≈0)。板子侧把 SNTP 服务器地址填 192.168.31.161 或 .13 即可用。详细配置、踩坑记录和验证命令都在 [#57 评论](http://100.123.83.113:3001/wandering1/STM32F4-Base/issues/57#issuecomment-207)。 <!-- dsh-superagent:7f2fd7dc19a786069d331051394942a1 -->
wandering1 added reference master 2026-11-03 15:24:03 +08:00
wandering1 moved this to 进行中 in 项目跟踪 on 2026-11-03 15:25:09 +08:00
wandering1 added 进行中 and removed 暂缓/不排 labels 2026-11-03 15:25:43 +08:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wandering1/STM32F4-Base#57