惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
I
InfoQ
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
T
The Blog of Author Tim Ferriss
N
Netflix TechBlog - Medium
GbyAI
GbyAI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
B
Blog RSS Feed
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
L
LINUX DO - 热门话题
Security Latest
Security Latest
月光博客
月光博客
U
Unit 42
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threat Research - Cisco Blogs
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
Engineering at Meta
Engineering at Meta
罗磊的独立博客
Attack and Defense Labs
Attack and Defense Labs
MongoDB | Blog
MongoDB | Blog
Microsoft Azure Blog
Microsoft Azure Blog
S
Securelist
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
PCI Perspectives
PCI Perspectives
S
Security Affairs
Schneier on Security
Schneier on Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
LangChain Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
H
Hacker News: Front Page
C
Cyber Attacks, Cyber Crime and Cyber Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Privacy & Cybersecurity Law Blog
W
WeLiveSecurity
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
V
Vulnerabilities – Threatpost
Recorded Future
Recorded Future

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
【YFIOs】用C#开发硬件之设备上云
刘洪峰AIoT · 2026-06-16 · via 博客园_首页

概述

将YF3300-ESP32S3设备数据上传到叶帆物联网平台需要使用 YFLink 协议通过 MQTT 进行通信。本章以完整项目 YeFanIoTTest 为例,介绍从设备初始化到数据上传的完整实现。

NuGet 软件包

包名 版本 用途
nanoFramework.CoreLibrary 1.17.11 基础运行时(System 命名空间)
nanoFramework.Hardware.Esp32 1.6.37 ESP32 引脚功能配置(I2C/SPI/UART)
nanoFramework.Iot.Device.DhcpServer 1.2.938 AP 模式 DHCP 服务器
nanoFramework.Logging 1.1.161 日志记录(ILogger)
nanoFramework.M2Mqtt 5.1.212 MQTT 客户端(连接叶帆物联平台)
nanoFramework.Networking.Sntp 1.6.42 NTP 时间同步
nanoFramework.System.Collections 1.5.67 Hashtable / ArrayList(属性上传)
nanoFramework.System.Device.Gpio 1.1.57 GPIO 控制(LED/按钮/继电器/数字输入)
nanoFramework.System.Device.I2c 1.1.29 I2C 通信(SHT30 温湿度传感器)
nanoFramework.System.Device.Wifi 1.5.141 WiFi STA/AP 管理
nanoFramework.System.Math 1.5.116 数学运算(指数退避重连)
nanoFramework.System.Net 1.11.50 DNS 解析(网络可达性检测)
nanoFramework.System.Net.Http.Server 1.5.204 Web 服务器(配网页面)
nanoFramework.System.Text 1.3.42 StringBuilder / UTF-8 编码
nanoFramework.System.Threading 1.1.52 线程/定时器
nanoFramework.WebServer 1.2.140 WebServer 路由框架(属性路由)

系统架构

┌─────────────────────────────────────────────────────────┐
│                   YF3300-ESP32S3 设备                     │
│                                                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │  SHT30   │  │ 继电器   │  │ 数字输入  │  │  BOOT   │ │
│  │ 温湿度   │  │  GPIO48  │  │ GPIO21/47│  │ GPIO0   │ │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬────┘ │
│       │ I2C          │ GPIO        │ GPIO         │ GPIO   │
│  ┌────▼──────────────▼────────────▼──────────────▼─────┐ │
│  │                  驱动层 (Drivers/)                   │ │
│  │  Sht30Sensor │ RelayDriver │ DigitalInputDriver      │ │
│  │  ButtonDriver│ LedManager                           │ │
│  └────────────────────────┬────────────────────────────┘ │
│                           │                              │
│  ┌────────────────────────▼────────────────────────────┐ │
│  │               管理层 (Managers/)                     │ │
│  │  MqttClientManager ←→ WifiManager ←→ APConfigManager│ │
│  │  NtpTimeManager  │  ConfigurationManager             │ │
│  └────────────────────────┬────────────────────────────┘ │
│                           │ MQTT (YFLink协议)            │
└───────────────────────────┼──────────────────────────────┘
                            │
                   ┌────────▼────────┐
                   │ 叶帆物联网平台   │
                   │ iot.yfios.net   │
                   │   MQTT:1883     │
                   └─────────────────┘

启动流程(12步初始化)

程序在 Main() 中按顺序执行12个步骤完成系统初始化:

步骤 组件 GPIO/接口 说明
1 Logger - 初始化 DebugLoggerFactory 日志系统
2 GPIO - 创建 GpioController 实例
3 LED GPIO39/40 黄色(网络状态) + 绿色(配网状态)
4 Relay GPIO48 1路继电器输出驱动
5 Digital Input GPIO21/47 2路开关量输入,回调模式
6 SHT30 I2C(17/18) 温湿度传感器,地址0x44
7 Button GPIO0 BOOT按钮,短按切换继电器/长按配网
8 Configuration - WiFi凭证读写的配置管理器
9 WiFi Manager - STA/AP双模式WiFi管理
10 WiFi Connect - 加载配置连接WiFi,成功后初始化NTP
11 AP Config - AP配网管理器,含Web服务器
12 MQTT Connect - 连接叶帆物联网平台

主循环逻辑

┌──────────────┐
│  counter++   │
└──────┬───────┘
       │
       ▼
┌──────────────┐    每5秒读取一次
│ 读取传感器    │◄── NTP时间 / 温湿度 / 数字输入 / 继电器状态
└──────┬───────┘
       │
       ▼
┌──────────────┐    counter % 6 == 0 (约30秒)
│ 是否上传?    │─── NO ──► Sleep(5000ms) ──► 循环
└──────┬───────┘
       │ YES
       ▼
┌──────────────┐
│ UploadData   │    构建属性Hashtable → MQTT发布
│ ToCloud()    │    属性: H(湿度) T(温度) I1 I2 Q1
└──────────────┘

核心代码详解

1. 硬件引脚定义 (Hardware/YF3300_ESP32S3.cs)

Hardware/YF3300_ESP32S3.cs

using System;

namespace YFSoft.Hardware.YF3300_ESP32S3
{
    public static class CPU
    {
        public static class Pins
        {
            // GPIO 0-48 全部定义
            public const int GPIO0  = 0;
            public const int GPIO9  = 9;   // RS485 TX
            public const int GPIO10 = 10;  // RS485 RX
            public const int GPIO11 = 11;  // RS232 TX
            public const int GPIO12 = 12;  // RS232 RX
            public const int GPIO17 = 17;  // I2C SDA
            public const int GPIO18 = 18;  // I2C SCL
            public const int GPIO21 = 21;  // 开关量输入1
            public const int GPIO39 = 39;  // 绿色LED
            public const int GPIO40 = 40;  // 黄色LED
            public const int GPIO47 = 47;  // 开关量输入2
            public const int GPIO48 = 48;  // 继电器1
        }
    }

    public static class Mainboard
    {
        public static class Pins
        {
            public const int YellowLED = CPU.Pins.GPIO40;   // 网络状态
            public const int GreenLED  = CPU.Pins.GPIO39;   // 配网状态
            public const int BOOT      = CPU.Pins.GPIO0;
            public const int I1        = CPU.Pins.GPIO21;
            public const int I2        = CPU.Pins.GPIO47;
            public const int Q1        = CPU.Pins.GPIO48;
        }

        public static class RS485
        {
            public const string PortName = "COM1";
            public const int TxPin = CPU.Pins.GPIO9;
            public const int RxPin = CPU.Pins.GPIO10;
        }

        public static class I2C
        {
            public const int BusId   = 1;
            public const int SdaPin  = CPU.Pins.GPIO17;
            public const int SclPin  = CPU.Pins.GPIO18;
        }
    }

    // LED闪烁时序定义(毫秒)
    public static class LEDTiming
    {
        // 黄色LED - 网络状态
        public const int NetworkConnecting_On  = 0;     // 常亮
        public const int NetworkNormal_On      = 500;   // 慢闪
        public const int NetworkNormal_Off     = 1500;
        public const int NetworkError_On       = 200;   // 快闪
        public const int NetworkError_Off      = 200;

        // 绿色LED - 配网状态
        public const int ConfigAP_On        = 0;        // 常亮(配网中)
        public const int ConfigSuccess_On   = 500;      // 慢闪(成功)
        public const int ConfigFailed_On    = 200;      // 快闪(失败)
    }

    // 系统配置常量
    public static class SystemConfig
    {
        public const int WiFiConnectTimeout    = 15000;  // WiFi连接超时(ms)
        public const int WiFiReconnectInterval = 5000;   // 重连间隔(ms)
        public const int MqttKeepAliveInterval = 60;     // MQTT心跳(s)
        public const string APSSID             = "YF3300_ESP32S3";
        public const string APPassword         = "yf123456";
        public const string APIP               = "192.168.4.1";
        public const int APConfigTimeout       = 600000; // 配网超时(10分钟)
        public const int SensorReadInterval    = 30000;  // 传感器读取间隔
        public const int DataUploadInterval    = 30000;  // 数据上传间隔
    }
}

YFLink协议使用JSON格式通过MQTT通信,所有请求都包含 idver(版本号1.3.0)、timestamp 三个基础字段。

Models/YFLinkModels.cs

using System.Collections;

namespace YeFanIoTTest.Models
{
    // 基础请求/响应
    public class YFLinkRequest
    {
        public int id { get; set; }                  // 消息ID
        public string ver { get; set; } = "1.3.0";   // 协议版本
        public long timestamp { get; set; }           // Unix毫秒时间戳
    }

    public class YFLinkResponse
    {
        public int id { get; set; }
        public int code { get; set; }                 // 200=成功
        public string message { get; set; }
    }

    // ★ 属性上传
    public class PropertyPostRequest : YFLinkRequest
    {
        public Hashtable parameters { get; set; }     // 属性键值对
    }

    // 事件上传
    public class EventPostRequest : YFLinkRequest
    {
        public ArrayList parameters { get; set; }     // 事件数据列表
    }

    public class EventData
    {
        public int type { get; set; }                 // 0-信息 1-告警 2-故障
        public int code { get; set; }                 // 事件编码
        public string content { get; set; }           // 事件内容(≤1024字节)
        public long time { get; set; }                // 事件时间戳
    }

    // 服务下发(云端→设备)
    public class ServiceSendRequest : YFLinkRequest
    {
        public int serviceType { get; set; }          // 0-命令 1-参数
        public ServiceParams parameters { get; set; }
    }

    public class ServiceParams
    {
        public string command { get; set; }
        public string parameter { get; set; }
    }

    // NTP校时
    public class NtpRequest : YFLinkRequest
    {
        public NtpParams parameters { get; set; }
    }

    public class NtpParams
    {
        public long deviceSendTime { get; set; }
    }

    public class NtpResponse : YFLinkResponse
    {
        public NtpResponseParams parameters { get; set; }
    }

    public class NtpResponseParams
    {
        public long deviceSendTime { get; set; }
        public long serverRecvTime { get; set; }
        public long serverSendTime { get; set; }
    }
}

3. MQTT 连接与YFLink认证 (Managers/MqttClientManager.cs)

这是与叶帆物联网平台通信的核心代码,包含YFLink协议的三元组认证(HMAC-SHA1签名)。

3.1 MQTT连接参数

// MQTT服务器
private const string MqttServer = "iot.yfios.net";
private const int MqttPort = 1883;

// YFLink三元组
private const string ProjectId  = "YFIoT_TEST";
private const string ProductId  = "YF3300_ESP32S3";
private const string DeviceId   = "YF3300_ESP32S301";
private const string DeviceKey  = "dxR99LCS7Uldc7KUnurFBeBi";

// MQTT主题(V1.3.0格式)
private const string PropertyPostTopic = "{0}/{1}/{2}/property/post";   // 属性上传
private const string EventPostTopic    = "{0}/{1}/{2}/event/post";      // 事件上传
private const string ServiceSendTopic  = "{0}/{1}/{2}/service/send";    // 服务下发

3.2 HMAC-SHA1 认证算法

认证公式

clientId = "{项目ID}-{产品ID}-{设备ID}"
userName = "{项目ID}&{产品ID}&{设备ID}"
password = HMAC-SHA1(DeviceKey, clientId + userName).toLowerCase(hex)

Managers/MqttClientManager.cs - Connect方法

public bool Connect()
{
    // clientId: YFIoT_TEST-YF3300_ESP32S3-YF3300_ESP32S301
    // userName: YFIoT_TEST&YF3300_ESP32S3&YF3300_ESP32S301
    // password: HMAC-SHA1签名 → 小写十六进制
    string clientId = $"{ProjectId}-{ProductId}-{DeviceId}";
    string username = $"{ProjectId}&{ProductId}&{DeviceId}";
    string password = CalculateHmacSha1(clientId + username, DeviceKey).ToLower();

    _mqttClient = new MqttClient(MqttServer, MqttPort, false, null, null, MqttSslProtocols.None);

    // 注册回调
    _mqttClient.MqttMsgPublishReceived += OnMessageReceived;
    _mqttClient.ConnectionClosed += OnConnectionClosed;

    var result = _mqttClient.Connect(clientId, username, password, false, 60);
    if (result == MqttReasonCode.Success)
    {
        SubscribeServiceTopic();  // 订阅服务下发主题
        return true;
    }
    return false;
}

3.3 属性上传

属性上传使用自定义JSON序列化(nanoFramework的 System.Text.Json 功能有限):

public bool PublishProperties(Hashtable properties)
{
    var request = new PropertyPostRequest
    {
        id = GenerateMessageId(),
        timestamp = GetCurrentTimestamp(),
        parameters = properties   // { "H":36.2, "T":29.3, "I1":0, "I2":0, "Q1":0 }
    };

    string json = SerializeToJson(request);
    string topic = string.Format(PropertyPostTopic, ProjectId, ProductId, DeviceId);
    _mqttClient.Publish(topic, Encoding.UTF8.GetBytes(json), null, null,
        MqttQoSLevel.AtLeastOnce, false);
    return true;
}

上传的JSON格式

{
  "id": 1234578,
  "timestamp": 1716355200000,
  "params": {
    "H": 36.2,
    "T": 29.3,
    "I1": 0,
    "I2": 0,
    "Q1": 0
  }
}

3.4 HMAC-SHA1 完整实现

Managers/MqttClientManager.cs - HMAC-SHA1自实现

// HMAC-SHA1算法(RFC 2104)
private byte[] HmacSha1(byte[] key, byte[] message)
{
    const int blockSize = 64;  // SHA1块大小

    // 1. 规范化密钥(>64字节则先SHA1哈希)
    byte[] normalizedKey = new byte[blockSize];
    if (key.Length > blockSize)
        Array.Copy(Sha1(key), normalizedKey, 20);
    else
        Array.Copy(key, normalizedKey, key.Length);

    // 2. 计算inner/outer padding
    byte[] innerPadding = new byte[blockSize];
    byte[] outerPadding = new byte[blockSize];
    for (int i = 0; i < blockSize; i++)
    {
        innerPadding[i] = (byte)(normalizedKey[i] ^ 0x36);
        outerPadding[i] = (byte)(normalizedKey[i] ^ 0x5C);
    }

    // 3. HMAC = SHA1(outer_padding + SHA1(inner_padding + message))
    byte[] innerHash = Sha1(Concat(innerPadding, message));
    return Sha1(Concat(outerPadding, innerHash));
}

// SHA1算法(FIPS 180-4)
private byte[] Sha1(byte[] data)
{
    uint h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE,
         h3 = 0x10325476, h4 = 0xC3D2E1F0;

    // 填充 → 分块(64字节) → 80轮压缩 → 输出20字节
    // ... (完整实现见项目源码,共130行)
    return hash; // 20字节
}

纯I2C通信实现,不依赖外部传感器库。

Drivers/Sht30Sensor.cs - 核心读取

public class Sht30Sensor : IDisposable
{
    private I2cDevice _i2cDevice;
    private const byte CMD_MEASURE_HIGH_REP   = 0x2C;  // 单次测量(高重复性)
    private const byte CMD_MEASURE_HIGH_REP_2 = 0x06;

    public Sht30Sensor()
    {
        // 配置I2C引脚:SDA=GPIO17, SCL=GPIO18
        Configuration.SetPinFunction(17, DeviceFunction.I2C1_DATA);
        Configuration.SetPinFunction(18, DeviceFunction.I2C1_CLOCK);

        var settings = new I2cConnectionSettings(1, 0x44); // BusId=1, 地址0x44
        _i2cDevice = I2cDevice.Create(settings);
    }

    public Sht30Data ReadMeasurement()
    {
        // 1. 发送测量命令
        _i2cDevice.Write(new byte[] { 0x2C, 0x06 });

        // 2. 等待测量完成(高重复性≈15ms)
        Thread.Sleep(20);

        // 3. 读取6字节:[温度高, 温度低, CRC, 湿度高, 湿度低, CRC]
        byte[] buf = new byte[6];
        _i2cDevice.Read(buf);

        // 4. 计算公式
        int rawT = (buf[0] << 8) | buf[1];
        int rawH = (buf[3] << 8) | buf[4];

        double temperature = -45.0 + (175.0 * rawT / 65535.0);
        double humidity    = 100.0 * rawH / 65535.0;

        // 5. CRC-8校验(多项式0x31)
        CheckCRC(buf[0], buf[1], buf[2]);
        CheckCRC(buf[3], buf[4], buf[5]);

        return new Sht30Data { Temperature = temperature, Humidity = humidity };
    }

    // CRC-8校验 (x^8 + x^5 + x^4 + 1)
    private bool CheckCRC(byte d1, byte d2, byte crc)
    {
        byte val = 0xFF;
        val ^= d1;
        for (int i = 0; i < 8; i++)
            val = (byte)((val & 0x80) != 0 ? (val << 1) ^ 0x31 : val << 1);
        val ^= d2;
        for (int i = 0; i < 8; i++)
            val = (byte)((val & 0x80) != 0 ? (val << 1) ^ 0x31 : val << 1);
        return val == crc;
    }
}

温度公式T = -45 + 175 × (raw / 65535) °C

湿度公式RH = 100 × (raw / 65535) %


5. 数据采集与上传 (Program.cs 主循环)

Program.cs - UploadDataToCloud()

private static void UploadDataToCloud()
{
    if (_mqttClientManager == null || !_mqttClientManager.IsConnected) return;

    // 采集数据
    double temperature = 0, humidity = 0;
    bool relayState = false;

    if (_sht30Sensor != null)
    {
        var data = _sht30Sensor.ReadMeasurement();
        if (data != null)
        {
            // 四舍五入保留一位小数
            temperature = (int)(data.Temperature * 10 + 0.5) / 10.0;
            humidity    = (int)(data.Humidity * 10 + 0.5) / 10.0;
        }
    }

    if (_relayDriver != null)
        relayState = _relayDriver.GetState(0);

    // 构建YFLink属性(属性ID需与云端物模型一致)
    var properties = new Hashtable
    {
        { "H",  humidity },                 // 湿度
        { "T",  temperature },              // 温度
        { "I1", 0 },                        // 开关量输入1
        { "I2", 0 },                        // 开关量输入2
        { "Q1", relayState ? 1 : 0 }        // 继电器输出1
    };

    _mqttClientManager.PublishProperties(properties);
}

6. 数据模型 (Models/DeviceModels.cs)

namespace YeFanIoTTest.Models
{
    public class DeviceConfig
    {
        public string ProjectID { get; set; }    // 项目ID
        public string ProductID { get; set; }    // 产品ID
        public string DeviceID { get; set; }     // 设备ID
        public string DeviceKey { get; set; }    // 设备密钥(32位)
        public string MqttServer { get; set; }   // MQTT服务器
        public int MqttPort { get; set; }        // MQTT端口
    }

    public class WifiConfig
    {
        public string SSID { get; set; }
        public string Password { get; set; }
    }

    public class SensorData
    {
        public double Temperature { get; set; }
        public double Humidity { get; set; }
        public DateTime Timestamp { get; set; }
    }
}

7. 枚举定义 (Enums/Enums.cs)

namespace YeFanIoTTest.Enums
{
    public enum DeviceState    { Initializing, CheckingConfig, APConfiguring,
                                 ConnectingWifi, ConnectingCloud, NormalRunning, Error }
    public enum NetworkStatus  { Connecting, Connected, Disconnected, Error }
    public enum ConfigStatus   { Configuring, Success, Failed, Normal }
    public enum LedBlinkMode   { Off, On, SlowBlink, FastBlink }
    public enum EventType      { Info = 0, Warning = 1, Fault = 2 }
    public enum ServiceType    { Command = 0, Parameter = 1 }
}

8. 按钮与配网交互

Program.cs - 按钮事件处理

_buttonDriver.OnButtonEvent += (sender, e) =>
{
    if (e.EventType == ButtonEventType.ShortPress)
    {
        // 短按:切换继电器
        _relayDriver.Toggle(0);
    }
    else if (e.EventType == ButtonEventType.LongPress)
    {
        // 长按(3秒):启动AP配网模式
        // SSID: YF3300_ESP32S3  密码: yf123456
        // 配网页面: http://192.168.4.1
        new Thread(() => StartAPConfigMode()).Start();
    }
};

// 配网完成回调
_apConfigManager.OnConfigCompleted += (sender, e) =>
{
    if (e.Success)
    {
        _ledManager.SetNetworkStatus(NetworkStatus.Connected);
        InitializeNtpTime();  // 配网成功后同步时间
    }
};

项目文件结构

YeFanIoTTest/
├── Program.cs                          # 主入口,12步初始化 + 主循环
├── YeFanIoTTest.nfproj                 # nanoFramework项目文件
├── packages.config                     # NuGet依赖(22个包)
│
├── Hardware/
│   └── YF3300_ESP32S3.cs              # 硬件引脚映射 + 系统配置常量
│
├── Drivers/                            # 硬件驱动层
│   ├── Sht30Sensor.cs                 # SHT30温湿度传感器(I2C + CRC-8校验)
│   ├── ButtonDriver.cs                # BOOT按钮(短按/长按检测)
│   ├── DigitalInputDriver.cs          # 2路开关量输入(回调模式)
│   ├── RelayDriver.cs                 # 1路继电器输出
│   ├── LedManager.cs                  # 双色LED状态指示
│   └── SerialPortManager.cs           # [预留] 串口管理器
│
├── Managers/                           # 业务逻辑层
│   ├── MqttClientManager.cs           # ★ YFLink协议MQTT通信(含HMAC-SHA1自实现)
│   ├── WifiManager.cs                 # WiFi STA/AP模式管理
│   ├── APConfigManager.cs             # AP配网流程控制
│   ├── WebServer.cs                   # 配网Web页面服务
│   ├── NtpTimeManager.cs              # NTP时间同步(5个服务器)
│   ├── ConfigurationManager.cs        # WiFi凭证持久化
│   ├── CloudCommunicationManager.cs   # [预留] 云端通信管理器
│   └── DataCollectorManager.cs        # [预留] 数据采集管理器
│
├── Models/                             # 数据模型层
│   ├── YFLinkModels.cs                # YFLink协议请求/响应模型
│   └── DeviceModels.cs                # 设备配置/传感器数据模型
│
├── Enums/
│   └── Enums.cs                       # 6个枚举定义
│
├── Core/
│   └── DeviceManager.cs              # [预留] 设备管理器
│
└── Properties/
    └── AssemblyInfo.cs                # 程序集元数据

全部代码

Program.cs

using System;
using System.Device.Gpio;
using System.Threading;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.Logging.Debug;
using YeFanIoTTest.Drivers;
using YeFanIoTTest.Enums;
using YeFanIoTTest.Managers;
using YeFanIoTTest.Models;

namespace YeFanIoTTest
{
    public class Program
    {
        private static LedManager _ledManager;
        private static GpioController _gpioController;
        private static ButtonDriver _buttonDriver;
        private static WifiManager _wifiManager;
        private static APConfigManager _apConfigManager;
        private static RelayDriver _relayDriver;
        private static DigitalInputDriver _digitalInputDriver;
        private static Sht30Sensor _sht30Sensor;
        private static NtpTimeManager _ntpTimeManager;
        private static ConfigurationManager _configurationManager;
        private static MqttClientManager _mqttClientManager;

        public static void Main()
        {
            Console.WriteLine("========================================");
            Console.WriteLine("  YF3300-ESP32S3 - Iteration 4 Test");
            Console.WriteLine("  NTP Time Sync + Cloud Communication");
            Console.WriteLine("========================================");
            
            Console.WriteLine("\n[Step 1] Initializing Logger...");
            try
            {
                var factory = new DebugLoggerFactory();
                if (factory != null)
                {
                    LogDispatcher.LoggerFactory = factory;
                    Console.WriteLine("[Step 1] Logger - PASS");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 1] Logger - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 2] Initializing GPIO...");
            try
            {
                _gpioController = new GpioController();
                Console.WriteLine("[Step 2] GPIO Controller - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 2] GPIO - FAIL: {ex.Message}");
                Thread.Sleep(Timeout.Infinite);
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 3] Initializing LED...");
            try
            {
                _ledManager = new LedManager(_gpioController);
                _ledManager.SetNetworkStatus(NetworkStatus.Connecting);
                _ledManager.SetConfigStatus(ConfigStatus.Normal);  // 绿色LED默认熄灭(正常运行)
                Console.WriteLine("[Step 3] LED Manager - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 3] LED - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 4] Initializing Relay...");
            try
            {
                _relayDriver = new RelayDriver(_gpioController);
                Console.WriteLine("[Step 4] Relay Driver - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 4] Relay - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 5] Initializing Digital Input...");
            try
            {
                // 使用回调委托,实时响应数字输入变化
                _digitalInputDriver = new DigitalInputDriver(_gpioController, OnDigitalInputChanged);
                Console.WriteLine("[Step 5] Digital Input Driver - PASS");
                Console.WriteLine("[Step 5] Callback mode enabled (real-time response)");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 5] Digital Input - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 6] Initializing SHT30 Sensor...");
            try
            {
                _sht30Sensor = new Sht30Sensor();
                Console.WriteLine("[Step 6] SHT30 Sensor - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 6] SHT30 - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 7] Initializing Button...");
            try
            {
                _buttonDriver = new ButtonDriver(
                    _gpioController,
                    YFSoft.Hardware.YF3300_ESP32S3.Mainboard.Pins.BOOT,
                    debounceTime: 20,
                    longPressTime: 3000
                );
                Console.WriteLine("[Step 7] Button Driver - PASS");
                
                _buttonDriver.OnButtonEvent += (sender, e) =>
                {
                    Console.WriteLine($"[Button] Event: {e.EventType}, Pin: {e.PinNumber}");
                    
                    if (e.EventType == ButtonEventType.ShortPress)
                    {
                        if (_relayDriver != null)
                        {
                            _relayDriver.Toggle(0);
                        }
                        Console.WriteLine("[Button] Toggled Relay 1");
                    }
                    else if (e.EventType == ButtonEventType.LongPress)
                    {
                        Console.WriteLine("[Button] Long press detected - Starting AP config mode...");
                        
                        Thread apThread = new Thread(() =>
                        {
                            StartAPConfigMode();
                        });
                        apThread.Start();
                    }
                };
                Console.WriteLine("[Step 7] Event Handler Registered");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 7] Button - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 8] Initializing Configuration Manager...");
            try
            {
                _configurationManager = new ConfigurationManager();
                Console.WriteLine("[Step 8] Configuration Manager - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 8] Configuration Manager - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 9] Initializing WiFi Manager...");
            try
            {
                _wifiManager = new WifiManager();
                Console.WriteLine("[Step 9] WiFi Manager - PASS");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 9] WiFi Manager - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 10] Checking WiFi Configuration...");
            bool hasWifiConfig = false;
            try
            {
                hasWifiConfig = _configurationManager.HasWifiConfig();
                if (hasWifiConfig)
                {
                    Console.WriteLine("[Step 10] WiFi configuration found");
                    
                    // 加载WiFi配置
                    WifiConfig wifiConfig;
                    if (_configurationManager.LoadWifiConfig(out wifiConfig))
                    {
                        Console.WriteLine($"[Step 10] SSID: {wifiConfig.SSID}");
                        
                        // 连接WiFi
                        Console.WriteLine("[Step 10] Connecting to WiFi...");
                        bool connected = _wifiManager.ConnectSTA(wifiConfig.SSID, wifiConfig.Password);
                        if (connected)
                        {
                            Console.WriteLine("[Step 10] WiFi connected successfully");
                            _ledManager.SetNetworkStatus(NetworkStatus.Connected);
                            
                            // 初始化NTP并同步时间
                            InitializeNtpTime();
                        }
                        else
                        {
                            Console.WriteLine("[Step 10] WiFi connection failed");
                            _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                        }
                    }
                }
                else
                {
                    Console.WriteLine("[Step 10] No WiFi configuration found");
                    Console.WriteLine("[Step 10] Long press BOOT button to start AP config mode");
                    _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 10] WiFi Check - FAIL: {ex.Message}");
            }
            Thread.Sleep(500);
            
            Console.WriteLine("\n[Step 11] Initializing AP Config Manager...");
            try
            {
                _apConfigManager = new APConfigManager(_wifiManager);
                
                // 设置WebServerController的APConfigManager实例引用
                WebServerController.SetAPConfigManager(_apConfigManager);
                
                Console.WriteLine("[Step 11] AP Config Manager - PASS");
                
                _apConfigManager.OnConfigCompleted += (sender, e) =>
                {
                    Console.WriteLine($"[AP Config] Result: {(e.Success ? "SUCCESS" : "FAILED")}");
                    Console.WriteLine($"[AP Config] SSID: {e.SSID}");
                    Console.WriteLine($"[AP Config] Message: {e.Message}");
                    
                    // 检查是否网络可达
                    bool networkReachable = e.Message.Contains("网络可达");
                    
                    if (e.Success && networkReachable)
                    {
                        // ========== 网络可达:配网真正成功 ==========
                        Console.WriteLine("[AP Config] Network is reachable - Configuration successful!");
                        
                        // ========== 暂时注释:不保存WiFi配置 ==========
                        // if (_configurationManager != null)
                        // {
                        //     var wifiConfig = new WifiConfig { SSID = e.SSID, Password = e.Password };
                        //     _configurationManager.SaveWifiConfig(wifiConfig);
                        //     Console.WriteLine("[AP Config] WiFi configuration saved");
                        // }
                        
                        if (_ledManager != null)
                        {
                            _ledManager.SetNetworkStatus(NetworkStatus.Connected);
                        }
                        
                        // 配网成功后,初始化NTP并同步时间
                        InitializeNtpTime();
                    }
                    else if (e.Success && !networkReachable)
                    {
                        // ========== 网络不可达:配网失败 ==========
                        Console.WriteLine("[AP Config] Network is NOT reachable - Configuration failed!");
                        Console.WriteLine("[AP Config] Please check your router internet connection");
                        
                        if (_ledManager != null)
                        {
                            _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                        }
                    }
                    else
                    {
                        // ========== WiFi连接失败 ==========
                        Console.WriteLine("[AP Config] WiFi connection failed");
                        
                        if (_ledManager != null)
                        {
                            _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                        }
                    }
                };
                Console.WriteLine("[Step 11] Event Handler Registered");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 11] AP Config Manager - FAIL: {ex.Message}");
            }
            
            Console.WriteLine("\n========================================");
            Console.WriteLine("  Iteration 4 Test Complete!");
            Console.WriteLine("========================================");
            Console.WriteLine("\nTest Instructions:");
            Console.WriteLine("1. Short press BOOT button to toggle relay");
            Console.WriteLine("2. Long press BOOT button to start AP config mode");
            Console.WriteLine("3. Digital inputs use real-time callback mode");
            Console.WriteLine("4. Monitor temperature, humidity and time every 5 seconds");
            Console.WriteLine("5. Upload data to cloud every 30 seconds");
            Console.WriteLine("\nPress CTRL+C to exit\n");
            
            // ========== 步骤12:连接MQTT服务器 ==========
            Console.WriteLine("\n[Step 12] Connecting to MQTT Server...");
            try
            {
                _mqttClientManager = new MqttClientManager();
                bool mqttConnected = _mqttClientManager.Connect();
                
                if (mqttConnected)
                {
                    Console.WriteLine("[Step 12] MQTT Connected successfully!");
                }
                else
                {
                    Console.WriteLine("[Step 12] MQTT Connection failed!");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Step 12] MQTT Error: {ex.Message}");
            }
            Thread.Sleep(500);
            
            if (_ledManager != null)
            {
                _ledManager.SetNetworkStatus(NetworkStatus.Connected);
            }
            
            int counter = 0;
            while (true)
            {
                try
                {
                    counter++;
                    Console.WriteLine($"\n--- Reading #{counter} ---");
                    
                    // 显示时间
                    if (_ntpTimeManager != null)
                    {
                        try
                        {
                            var localTime = _ntpTimeManager.GetLocalTime();
                            Console.WriteLine($"Time: {localTime:yyyy-MM-dd HH:mm:ss}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Time: Error - {ex.Message}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Time: NTP not initialized");
                    }
                    
                    // 读取温湿度
                    if (_sht30Sensor != null)
                    {
                        try
                        {
                            var sht30Data = _sht30Sensor.ReadMeasurement();
                            if (sht30Data != null)
                            {
                                Console.WriteLine($"Temperature: {sht30Data.Temperature:F1}°C, Humidity: {sht30Data.Humidity:F1}%");
                            }
                            else
                            {
                                Console.WriteLine("Temperature: N/A, Humidity: N/A");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Temperature: Error - {ex.Message}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Temperature: Sensor not initialized");
                    }
                    
                    // 读取数字输入状态(轮询模式,作为备份)
                    if (_digitalInputDriver != null)
                    {
                        try
                        {
                            for (int i = 0; i < 2; i++)
                            {
                                var state = _digitalInputDriver.ReadState(i);
                                Console.WriteLine($"Digital Input {i + 1}: {(state ? "Triggered" : "Not triggered")}");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Digital Input: Error - {ex.Message}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Digital Input: Driver not initialized");
                    }
                    
                    // 读取继电器状态
                    if (_relayDriver != null)
                    {
                        try
                        {
                            var relayState = _relayDriver.GetState(0);
                            Console.WriteLine($"Relay 1: {(relayState ? "ON" : "OFF")}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Relay: Error - {ex.Message}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Relay: Driver not initialized");
                    }
                    
                    Thread.Sleep(5000);
                    
                    // ========== 上传数据到云端(每30秒) ==========
                    if (counter % 6 == 0)  // 每6次读取(约30秒)上传一次
                    {
                        UploadDataToCloud();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error in monitoring loop: {ex.Message}");
                    Thread.Sleep(1000);
                }
            }
        }
        
        // 初始化NTP并同步时间
        private static void InitializeNtpTime()
        {
            try
            {
                Console.WriteLine("\n[NTP] Initializing NTP Time Manager...");
                _ntpTimeManager = new NtpTimeManager();
                
                Console.WriteLine("[NTP] Starting NTP client...");
                bool ntpInitSuccess = _ntpTimeManager.Initialize();
                if (ntpInitSuccess)
                {
                    Console.WriteLine("[NTP] NTP Client started");
                    
                    Console.WriteLine("[NTP] Syncing time...");
                    Thread.Sleep(2000); // 等待2秒让NTP同步
                    
                    bool syncSuccess = _ntpTimeManager.SyncNow();
                    if (syncSuccess)
                    {
                        var utcTime = _ntpTimeManager.GetCurrentTime();
                        var localTime = _ntpTimeManager.GetLocalTime();
                        Console.WriteLine($"[NTP] UTC Time: {utcTime:yyyy-MM-dd HH:mm:ss}");
                        Console.WriteLine($"[NTP] Local Time: {localTime:yyyy-MM-dd HH:mm:ss}");
                        Console.WriteLine("[NTP] Time sync successful");
                    }
                    else
                    {
                        Console.WriteLine("[NTP] Time sync failed");
                    }
                }
                else
                {
                    Console.WriteLine("[NTP] NTP Client start failed");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[NTP] Error: {ex.Message}");
            }
        }
        
        // 上传数据到云端
        private static void UploadDataToCloud()
        {
            if (_mqttClientManager == null || !_mqttClientManager.IsConnected)
            {
                Console.WriteLine("[Cloud] MQTT not connected, skipping data upload");
                return;
            }
            
            try
            {
                // 收集数据
                double temperature = 0;
                double humidity = 0;
                bool relayState = false;
                
                // 读取温湿度(保留一位小数)
                if (_sht30Sensor != null)
                {
                    var sht30Data = _sht30Sensor.ReadMeasurement();
                    if (sht30Data != null)
                    {
                        // 使用整数运算实现四舍五入
                        temperature = (int)(sht30Data.Temperature * 10 + 0.5) / 10.0;
                        humidity = (int)(sht30Data.Humidity * 10 + 0.5) / 10.0;
                    }
                }
                
                // 读取继电器状态
                if (_relayDriver != null)
                {
                    relayState = _relayDriver.GetState(0);
                }
                
                // 构建属性数据(使用YFLink协议规定的属性ID)
                // H - 湿度(百分比)
                // T - 温度(摄氏度)
                // I1 - 开关量输入1
                // I2 - 开关量输入2
                // Q1 - 继电器输出1
                var properties = new System.Collections.Hashtable
                {
                    { "H", humidity },           // 湿度
                    { "T", temperature },        // 温度
                    { "I1", 0 },                 // 开关量输入1(待实现)
                    { "I2", 0 },                 // 开关量输入2(待实现)
                    { "Q1", relayState ? 1 : 0 } // 继电器输出1
                };
                
                // 上传属性
                bool uploadSuccess = _mqttClientManager.PublishProperties(properties);
                if (uploadSuccess)
                {
                    Console.WriteLine($"[Cloud] Data uploaded: H={humidity:F1}%, T={temperature:F1}°C, I1=0, I2=0, Q1={(relayState ? 1 : 0)}");
                }
                else
                {
                    Console.WriteLine("[Cloud] Data upload failed");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[Cloud] Upload error: {ex.Message}");
            }
        }
        
        // 数字输入状态变化回调
        // 【重要】此方法在GPIO中断上下文中执行,必须遵循BUG1.md的规则
        // channel: 通道号(从0开始)
        // isTriggered: 是否触发(true=低电平触发,false=高电平未触发)
        // pinValue: 引脚电平值(0=低电平,1=高电平)
        private static void OnDigitalInputChanged(int channel, bool isTriggered, int pinValue)
        {
            // 在中断上下文中,只能做最简单的操作
            // 使用Console.WriteLine输出,不创建对象
            Console.WriteLine($"[Digital Input] Channel {channel + 1}: {(isTriggered ? "Triggered" : "Not triggered")} (Pin: {pinValue})");
        }
        
        private static void StartAPConfigMode()
        {
            try
            {
                Console.WriteLine("\n[AP Config] Starting AP configuration mode...");
                if (_ledManager != null)
                {
                    _ledManager.SetNetworkStatus(NetworkStatus.Connecting);
                }
                
                Thread.Sleep(100);
                
                bool success = _apConfigManager.StartAPConfig();
                
                if (success)
                {
                    Console.WriteLine("[AP Config] AP mode started successfully");
                    Console.WriteLine("[AP Config] SSID: YF3300_ESP32S3");
                    Console.WriteLine("[AP Config] Password: yf123456");
                    Console.WriteLine("[AP Config] IP: 192.168.4.1");
                }
                else
                {
                    Console.WriteLine("[AP Config] Failed to start AP mode");
                    if (_ledManager != null)
                    {
                        _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[AP Config] Error: {ex.Message}");
                Console.WriteLine($"[AP Config] Exception Type: {ex.GetType().Name}");
                if (_ledManager != null)
                {
                    _ledManager.SetNetworkStatus(NetworkStatus.Disconnected);
                }
            }
        }
    }
}

Hardware/YF3300_ESP32S3.cs

using System;


namespace YFSoft.Hardware.YF3300_ESP32S3
{
    public static class CPU
    {
        public static class Pins
        {
            // GPIO 0-47
            public const int GPIO0 = 0;
            public const int GPIO1 = 1;
            public const int GPIO2 = 2;
            public const int GPIO3 = 3;
            public const int GPIO4 = 4;
            public const int GPIO5 = 5;
            public const int GPIO6 = 6;
            public const int GPIO7 = 7;
            public const int GPIO8 = 8;
            public const int GPIO9 = 9;
            public const int GPIO10 = 10;
            public const int GPIO11 = 11;
            public const int GPIO12 = 12;
            public const int GPIO13 = 13;
            public const int GPIO14 = 14;
            public const int GPIO15 = 15;
            public const int GPIO16 = 16;
            public const int GPIO17 = 17;
            public const int GPIO18 = 18;
            public const int GPIO19 = 19;
            public const int GPIO20 = 20;
            public const int GPIO21 = 21;
            public const int GPIO22 = 22;
            public const int GPIO23 = 23;
            public const int GPIO24 = 24;
            public const int GPIO25 = 25;
            public const int GPIO26 = 26;
            public const int GPIO27 = 27;
            public const int GPIO28 = 28;
            public const int GPIO29 = 29;
            public const int GPIO30 = 30;
            public const int GPIO31 = 31;
            public const int GPIO32 = 32;
            public const int GPIO33 = 33;
            public const int GPIO34 = 34;
            public const int GPIO35 = 35;
            public const int GPIO36 = 36;
            public const int GPIO37 = 37;
            public const int GPIO38 = 38;
            public const int GPIO39 = 39;
            public const int GPIO40 = 40;
            public const int GPIO41 = 41;
            public const int GPIO42 = 42;
            public const int GPIO43 = 43;
            public const int GPIO44 = 44;
            public const int GPIO45 = 45;
            public const int GPIO46 = 46;
            public const int GPIO47 = 47;
            public const int GPIO48 = 48;
        }
    }

    public static class Mainboard
    {
        // 主板引脚定义
        public static class Pins
        {
            // LED 指示灯(根据实际硬件连接)
            public const int YellowLED = CPU.Pins.GPIO40;   // 黄色LED - 网络状态指示
            public const int GreenLED = CPU.Pins.GPIO39;    // 绿色LED - 配网状态指示

            // 兼容旧命名
            public const int CommLED = YellowLED;           // 通信指示灯(黄色)
            public const int UserLED = GreenLED;            // 用户指示灯(绿色)

            // 按钮
            public const int BOOT = CPU.Pins.GPIO0;

            // 开关量输入
            public const int I1 = CPU.Pins.GPIO21;           // 输入1
            public const int I2 = CPU.Pins.GPIO47;           // 输入2

            // 继电器输出
            public const int Q1 = CPU.Pins.GPIO48;           // 继电器1

        }

        // RS485 串口定义
        public static class RS485
        {
            public const string PortName = "COM1";
            public const int DefaultBaudRate = 9600;
            public const int TxPin = CPU.Pins.GPIO9;        // UART1 TX (电路图: TX1=IO9)
            public const int RxPin = CPU.Pins.GPIO10;       // UART1 RX (电路图: RX1=IO10)
        }

        // RS232 串口定义
        public static class RS232
        {
            public const string PortName = "COM2";
            public const int DefaultBaudRate = 9600;
            public const int TxPin = CPU.Pins.GPIO11;       // UART2 TX (电路图: TX2=IO11)
            public const int RxPin = CPU.Pins.GPIO12;       // UART2 RX (电路图: RX2=IO12)
        }

        // I2C 总线定义
        public static class I2C
        {
            public const int BusId = 1;
            public const int SdaPin = CPU.Pins.GPIO17;
            public const int SclPin = CPU.Pins.GPIO18;
            public const int DefaultSpeed = 100000; // 100kHz
        }

        // 开关量输入通道
        public static class DigitalInputs
        {
            public const int Count = 2;
            public static readonly int[] Channels = { Mainboard.Pins.I1, Mainboard.Pins.I2 };
        }

        // 继电器输出通道
        public static class Relays
        {
            public const int Count = 1;
            public static readonly int[] Channels = { Mainboard.Pins.Q1 };
        }

        // LED 闪烁时间定义(毫秒)
        public static class LEDTiming
        {
            // 黄色LED - 网络状态指示
            public const int NetworkConnecting_On = 0;          // 常亮(正在连接)
            public const int NetworkConnecting_Off = int.MaxValue;

            public const int NetworkNormal_On = 500;            // 慢闪(正常)
            public const int NetworkNormal_Off = 1500;

            public const int NetworkError_On = 200;            // 快闪(异常)
            public const int NetworkError_Off = 200;

            // 绿色LED - 配网状态指示
            public const int ConfigAP_On = 0;                   // 常亮(配网中)
            public const int ConfigAP_Off = int.MaxValue;

            public const int ConfigSuccess_On = 500;           // 慢闪(配网成功)
            public const int ConfigSuccess_Off = 1500;

            public const int ConfigFailed_On = 200;             // 快闪(配网失败)
            public const int ConfigFailed_Off = 200;

            public const int ConfigNormal_On = 0;               // 熄灭(正常运行)
            public const int ConfigNormal_Off = 0;
        }
    }

    // 设备信息定义
    public static class DeviceInfo
    {
        public const string DeviceName = "YF3300-ESP32S3";      // 设备名称
        public const string Manufacturer = "YFSoft";            // 制造商
        public const string HardwareVersion = "1.0.0";          // 硬件版本
        public const string FirmwareVersion = "1.0.0";          // 固件版本
        public const string Model = "YF3300-ESP32S3";           // 设备型号
    }

    // 系统配置常量
    public static class SystemConfig
    {
        // WiFi 配置
        public const int WiFiConnectTimeout = 15000;            // WiFi连接超时(毫秒)
        public const int WiFiReconnectInterval = 5000;          // WiFi重连间隔(毫秒)

        // MQTT 配置
        public const string DefaultMqttServer = "mqtt.yfiot.com"; // 默认MQTT服务器
        public const int DefaultMqttPort = 1883;                // 默认MQTT端口
        public const int MqttKeepAliveInterval = 60;            // MQTT心跳间隔(秒)
        public const int MqttReconnectInterval = 5000;          // MQTT重连间隔(毫秒)

        // AP 配网配置
        public const string APSSID = "YF3300_ESP32S3";         // AP热点名称
        public const string APPassword = "yf123456";           // AP热点密码
        public const string APIP = "192.168.4.1";              // AP网关IP
        public const int APConfigTimeout = 600000;              // 配网超时(10分钟)

        // 数据采集配置
        public const int SensorReadInterval = 30000;            // 传感器读取间隔(毫秒)
        public const int DataUploadInterval = 30000;            // 数据上传间隔(毫秒)

        // 看门狗配置
        public const int WatchdogTimeout = 30000;               // 看门狗超时(毫秒)

        // NTP 配置
        public const string DefaultNtpServer = "ntp.aliyun.com"; // 默认NTP服务器
        public const int NtpSyncInterval = 3600000;             // NTP同步间隔(1小时)

        // 按钮配置
        public const int ButtonLongPressDuration = 5000;        // 长按时间(毫秒)
        public const int ButtonDebounceTime = 50;               // 按钮防抖时间(毫秒)
    }
}

Models/YFLinkModels.cs

using System;
using System.Collections;

namespace YeFanIoTTest.Models
{
    // YFLink协议基础请求模型
    public class YFLinkRequest
    {
        public int id { get; set; }                  // 消息ID(32位整数)
        public string ver { get; set; } = "1.3.0";  // 协议版本
        public long timestamp { get; set; }          // 时间戳(1970年1月1日以来的毫秒数)
    }

    // YFLink协议基础响应模型
    public class YFLinkResponse
    {
        public int id { get; set; }                  // 消息ID(与请求ID对应)
        public int code { get; set; }                // 返回结果编码(200表示成功)
        public string message { get; set; }          // 返回结果描述
    }

    // 属性上传请求模型
    public class PropertyPostRequest : YFLinkRequest
    {
        public Hashtable parameters { get; set; }    // 属性键值对集合
    }

    // 属性上传响应模型
    public class PropertyPostResponse : YFLinkResponse
    {
        public ArrayList data { get; set; }          // 验证不通过的属性标识列表
    }

    // 事件上传请求模型
    public class EventPostRequest : YFLinkRequest
    {
        public ArrayList parameters { get; set; }     // 事件数据列表
    }

    // 事件数据模型
    public class EventData
    {
        public int type { get; set; }                // 事件类型(0-信息,1-告警,2-故障)
        public int code { get; set; }                // 事件编码(32位整数)
        public string content { get; set; }          // 事件内容(不超过1024字节)
        public long time { get; set; }               // 事件时间戳
    }

    // 服务下发请求模型
    public class ServiceSendRequest : YFLinkRequest
    {
        public int serviceType { get; set; }         // 服务类型(0-命令,1-参数)
        public ServiceParams parameters { get; set; } // 服务参数
    }

    // 服务参数模型
    public class ServiceParams
    {
        public string command { get; set; }          // 服务命令
        public string parameter { get; set; }        // 服务参数
    }

    // 服务响应模型
    public class ServiceSendResponse : YFLinkRequest
    {
        public ServiceResultParams parameters { get; set; } // 服务响应参数
    }

    // 服务响应参数模型
    public class ServiceResultParams
    {
        public int code { get; set; }                // 服务响应标识符
        public string content { get; set; }          // 服务响应内容
    }

    // NTP校时请求模型
    public class NtpRequest : YFLinkRequest
    {
        public NtpParams parameters { get; set; }     // NTP参数
    }

    // NTP参数模型
    public class NtpParams
    {
        public long deviceSendTime { get; set; }     // 设备发送请求的时间
    }

    // NTP校时响应模型
    public class NtpResponse : YFLinkResponse
    {
        public NtpResponseParams parameters { get; set; } // NTP响应参数
    }

    // NTP响应参数模型
    public class NtpResponseParams
    {
        public long deviceSendTime { get; set; }     // 设备发送请求的时间
        public long serverRecvTime { get; set; }     // 服务器接收到该请求的时间
        public long serverSendTime { get; set; }     // 服务器发起发送该响应的时间
    }
}

Models/DeviceModels.cs

using System;

namespace YeFanIoTTest.Models
{
    // 设备配置模型 - 包含YFLink协议四元组及MQTT服务器配置
    public class DeviceConfig
    {
        public string ProjectID { get; set; }      // 项目ID
        public string ProductID { get; set; }      // 产品ID
        public string DeviceID { get; set; }       // 设备ID
        public string DeviceKey { get; set; }      // 设备密钥(32位随机字符)
        public string MqttServer { get; set; }     // MQTT服务器地址
        public int MqttPort { get; set; }          // MQTT服务器端口

        // 验证设备配置是否有效
        public bool IsValid()
        {
            return !string.IsNullOrEmpty(ProjectID) &&
                   !string.IsNullOrEmpty(ProductID) &&
                   !string.IsNullOrEmpty(DeviceID) &&
                   !string.IsNullOrEmpty(DeviceKey);
        }
    }

    // WiFi配置模型
    public class WifiConfig
    {
        public string SSID { get; set; }           // WiFi名称
        public string Password { get; set; }       // WiFi密码

        // 验证WiFi配置是否有效
        public bool IsValid()
        {
            return !string.IsNullOrEmpty(SSID) && !string.IsNullOrEmpty(Password);
        }
    }

    // 传感器数据模型 - 存储温湿度传感器采集的数据
    public class SensorData
    {
        public double Temperature { get; set; }    // 温度值(摄氏度)
        public double Humidity { get; set; }       // 湿度值(百分比)
        public DateTime Timestamp { get; set; }    // 数据采集时间戳

        // 格式化输出传感器数据
        public override string ToString()
        {
            return $"Temperature: {Temperature:F1}°C, Humidity: {Humidity:F1}%";
        }
    }

    // 继电器状态模型
    public class RelayState
    {
        public int RelayId { get; set; }           // 继电器ID(1, 2, 3...)
        public bool IsOn { get; set; }             // 继电器状态(true=打开,false=关闭)
        public DateTime Timestamp { get; set; }    // 状态更新时间戳
    }

    // 开关量输入状态模型
    public class DigitalInputState
    {
        public int InputId { get; set; }           // 输入通道ID(1, 2, 3...)
        public bool IsHigh { get; set; }           // 输入状态(true=高电平,false=低电平)
        public DateTime Timestamp { get; set; }    // 状态更新时间戳
    }
}

Enums/Enums.cs

using System;

namespace YeFanIoTTest.Enums
{
    // 设备状态枚举 - 用于表示设备的生命周期状态
    public enum DeviceState
    {
        Initializing,       // 初始化中
        CheckingConfig,      // 检查配置
        APConfiguring,       // AP配网中
        ConnectingWifi,      // 连接WiFi中
        ConnectingCloud,     // 连接云端中
        NormalRunning,       // 正常运行
        Error                // 错误状态
    }

    // 网络状态枚举 - 用于表示WiFi和MQTT连接状态
    public enum NetworkStatus
    {
        Connecting,          // 连接中
        Connected,           // 已连接
        Disconnected,        // 已断开
        Error                // 错误
    }

    // 配网状态枚举 - 用于表示AP配网过程的状态
    public enum ConfigStatus
    {
        Configuring,         // 配网中
        Success,             // 配网成功
        Failed,              // 配网失败
        Normal               // 正常运行
    }

    // LED闪烁模式枚举
    public enum LedBlinkMode
    {
        Off,                 // 关闭
        On,                  // 常亮
        SlowBlink,           // 慢闪
        FastBlink            // 快闪
    }

    // 事件类型枚举 - 对应YFLink协议中的事件类型定义
    public enum EventType
    {
        Info = 0,            // 信息事件
        Warning = 1,         // 告警事件
        Fault = 2            // 故障事件
    }

    // 服务类型枚举 - 对应YFLink协议中的服务类型定义
    public enum ServiceType
    {
        Command = 0,         // 命令服务
        Parameter = 1        // 参数服务
    }
}

Drivers/Sht30Sensor.cs

using System;
using System.Device.I2c;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.Hardware.Esp32;
using YFSoft.Hardware.YF3300_ESP32S3;

namespace YeFanIoTTest.Drivers
{
    // SHT30传感器数据模型
    public class Sht30Data
    {
        public double Temperature { get; set; }     // 温度值(摄氏度)
        public double Humidity { get; set; }        // 湿度值(百分比)
        public DateTime Timestamp { get; set; }     // 数据读取时间戳

        // 格式化输出传感器数据
        public override string ToString()
        {
            return $"Temperature: {Temperature:F1}°C, Humidity: {Humidity:F1}%, Time: {Timestamp:HH:mm:ss}";
        }
    }

    // SHT30温湿度传感器驱动类
    // 使用纯I2C通信协议实现,不依赖外部库
    public class Sht30Sensor : IDisposable
    {
        private readonly ILogger _logger;
        private I2cDevice _i2cDevice;                       // I2C 设备实例
        private bool _disposed = false;

        // SHT30 I2C命令定义
        private const byte CMD_MEASURE_HIGH_REP = 0x2C;      // 单次测量命令(高重复性)
        private const byte CMD_MEASURE_HIGH_REP_2 = 0x06;    // 单次测量命令第二字节
        private const byte CMD_SOFT_RESET = 0x30;            // 软复位命令
        private const byte CMD_SOFT_RESET_2 = 0xA2;          // 软复位命令第二字节
        private const byte CMD_READ_STATUS = 0xF3;           // 读状态寄存器命令
        private const byte CMD_READ_STATUS_2 = 0x2D;         // 读状态寄存器命令第二字节

        // 默认构造函数:使用默认I2C配置
        public Sht30Sensor()
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("Sht30Sensor");
            
            try
            {
                // 配置I2C引脚
                Configuration.SetPinFunction(Mainboard.I2C.SdaPin, DeviceFunction.I2C1_DATA);
                Configuration.SetPinFunction(Mainboard.I2C.SclPin, DeviceFunction.I2C1_CLOCK);

                // 创建I2C设备(默认地址0x44)
                var i2cSettings = new I2cConnectionSettings(Mainboard.I2C.BusId, 0x44);
                _i2cDevice = I2cDevice.Create(i2cSettings);

                _logger.LogInformation($"SHT30 sensor initialized on I2C bus {Mainboard.I2C.BusId}, address: 0x44");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to initialize SHT30 sensor: {ex.Message}");
                throw;
            }
        }

        // 带参数构造函数
        // i2cBusId: I2C总线ID
        // sensorAddress: 传感器地址(0x44或0x45)
        public Sht30Sensor(int i2cBusId, byte sensorAddress)
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("Sht30Sensor");
            
            try
            {
                // 配置I2C引脚
                Configuration.SetPinFunction(Mainboard.I2C.SdaPin, DeviceFunction.I2C1_DATA);
                Configuration.SetPinFunction(Mainboard.I2C.SclPin, DeviceFunction.I2C1_CLOCK);

                // 创建I2C设备
                var i2cSettings = new I2cConnectionSettings(i2cBusId, sensorAddress);
                _i2cDevice = I2cDevice.Create(i2cSettings);

                _logger.LogInformation($"SHT30 sensor initialized on I2C bus {i2cBusId}, address: 0x{sensorAddress:X2}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to initialize SHT30 sensor: {ex.Message}");
                throw;
            }
        }

        // 读取传感器数据
        // 返回:Sht30Data对象,读取失败返回null
        public Sht30Data ReadMeasurement()
        {
            if (_disposed)
            {
                _logger.LogWarning("Sensor has been disposed");
                return null;
            }

            try
            {
                // 发送测量命令(单次测量,高重复性)
                byte[] writeBuffer = new byte[] { CMD_MEASURE_HIGH_REP, CMD_MEASURE_HIGH_REP_2 };
                _i2cDevice.Write(writeBuffer);

                // 等待测量完成(高重复性测量需要约15ms)
                System.Threading.Thread.Sleep(20);

                // 读取6字节数据:温度高字节、温度低字节、温度CRC、湿度高字节、湿度低字节、湿度CRC
                byte[] readBuffer = new byte[6];
                _i2cDevice.Read(readBuffer);

                // 解析温度数据(前2字节)
                int rawTemperature = (readBuffer[0] << 8) | readBuffer[1];
                
                // 解析湿度数据(后2字节,跳过CRC)
                int rawHumidity = (readBuffer[3] << 8) | readBuffer[4];

                // 计算实际温度和湿度
                // 温度公式:T = -45 + 175 * (raw / 65535.0)
                // 湿度公式:RH = 100 * (raw / 65535.0)
                double temperature = -45.0 + (175.0 * rawTemperature / 65535.0);
                double humidity = 100.0 * rawHumidity / 65535.0;

                // CRC校验(可选)
                if (!CheckCRC(readBuffer[0], readBuffer[1], readBuffer[2]))
                {
                    _logger.LogWarning("Temperature CRC check failed");
                }
                
                if (!CheckCRC(readBuffer[3], readBuffer[4], readBuffer[5]))
                {
                    _logger.LogWarning("Humidity CRC check failed");
                }

                return new Sht30Data
                {
                    Temperature = temperature,
                    Humidity = humidity,
                    Timestamp = DateTime.UtcNow
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to read SHT30 sensor: {ex.Message}");
                return null;
            }
        }

        // 重置传感器
        public void Reset()
        {
            if (_disposed)
            {
                _logger.LogWarning("Sensor has been disposed");
                return;
            }

            try
            {
                // 发送软复位命令
                byte[] writeBuffer = new byte[] { CMD_SOFT_RESET, CMD_SOFT_RESET_2 };
                _i2cDevice.Write(writeBuffer);

                // 等待复位完成
                System.Threading.Thread.Sleep(10);

                _logger.LogInformation("SHT30 sensor reset successfully");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to reset SHT30 sensor: {ex.Message}");
            }
        }

        // 读取状态寄存器
        public ushort ReadStatus()
        {
            if (_disposed)
            {
                _logger.LogWarning("Sensor has been disposed");
                return 0;
            }

            try
            {
                // 发送读状态命令
                byte[] writeBuffer = new byte[] { CMD_READ_STATUS, CMD_READ_STATUS_2 };
                _i2cDevice.Write(writeBuffer);

                // 读取3字节:状态高字节、状态低字节、CRC
                byte[] readBuffer = new byte[3];
                _i2cDevice.Read(readBuffer);

                // 组合状态字
                ushort status = (ushort)((readBuffer[0] << 8) | readBuffer[1]);

                return status;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to read SHT30 status: {ex.Message}");
                return 0;
            }
        }

        // CRC校验函数
        // data1: 数据字节1
        // data2: 数据字节2
        // crc: CRC校验字节
        // 返回:校验是否通过
        private bool CheckCRC(byte data1, byte data2, byte crc)
        {
            // SHT30使用CRC-8校验
            // 多项式:0x31 (x^8 + x^5 + x^4 + 1)
            // 初始值:0xFF
            byte crcValue = 0xFF;
            
            // 计算第一个字节的CRC
            crcValue ^= data1;
            for (int i = 0; i < 8; i++)
            {
                if ((crcValue & 0x80) != 0)
                {
                    crcValue = (byte)((crcValue << 1) ^ 0x31);
                }
                else
                {
                    crcValue = (byte)(crcValue << 1);
                }
            }
            
            // 计算第二个字节的CRC
            crcValue ^= data2;
            for (int i = 0; i < 8; i++)
            {
                if ((crcValue & 0x80) != 0)
                {
                    crcValue = (byte)((crcValue << 1) ^ 0x31);
                }
                else
                {
                    crcValue = (byte)(crcValue << 1);
                }
            }
            
            return crcValue == crc;
        }

        // 释放资源
        public void Dispose()
        {
            if (_disposed) return;

            try
            {
                if (_i2cDevice != null)
                {
                    _i2cDevice.Dispose();
                    _i2cDevice = null;
                }

                _disposed = true;
                _logger.LogInformation("SHT30 sensor disposed");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error disposing SHT30 sensor: {ex.Message}");
            }
        }
    }
}

Drivers/ButtonDriver.cs

using System;
using System.Device.Gpio;
using System.Threading;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;

namespace YeFanIoTTest.Drivers
{
    // 按钮事件类型
    public enum ButtonEventType
    {
        ShortPress,     // 短按(按下并释放)
        LongPress       // 长按(按住超过指定时间)
    }

    // 按钮事件参数
    public class ButtonEventArgs : EventArgs
    {
        public ButtonEventType EventType { get; set; }
        public int PinNumber { get; set; }
        public TimeSpan PressDuration { get; set; }
    }

    // 按钮事件委托
    public delegate void ButtonEventHandler(object sender, ButtonEventArgs e);

    // 按钮驱动类(安全稳定版本)
    // 支持短按、长按检测,带软件防抖
    internal class ButtonDriver : IDisposable
    {
        // 日志记录器
        private readonly ILogger _logger;

        // GPIO引脚
        private readonly GpioPin _pin;
        private readonly bool _activeLow;       // 是否低电平有效
        private bool _disposed;                 // 是否已释放资源

        // 防抖和长按检测
        private Timer _initTimer;               // 初始化延迟定时器
        private Timer _debounceTimer;           // 防抖定时器
        private Timer _longPressTimer;          // 长按检测定时器
        private DateTime _pressStartTime;       // 按下时间
        private bool _isPressed;                // 当前是否按下
        private bool _longPressTriggered;       // 长按是否已触发
        private bool _isInitialized;            // 是否已初始化完成(稳定期结束)
        private DateTime _initCompleteTime;     // 初始化完成时间

        // 配置参数
        private readonly int _debounceTime;     // 防抖时间(毫秒)
        private readonly int _longPressTime;    // 长按触发时间(毫秒)

        // 事件
        public event ButtonEventHandler OnButtonEvent;

        // 属性
        public int PinNumber => _pin.PinNumber;
        public bool IsPressed => _activeLow ? _pin.Read() == PinValue.Low 
                                           : _pin.Read() == PinValue.High;

        // 构造函数
        public ButtonDriver(GpioController controller, int pinNumber, 
            int debounceTime = 20, int longPressTime = 3000, 
            bool activeLow = true, PinMode pinMode = PinMode.InputPullUp)
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("ButtonDriver");
            _activeLow = activeLow;
            _debounceTime = debounceTime;
            _longPressTime = longPressTime;

            // 打开GPIO引脚
            _pin = controller.OpenPin(pinNumber, pinMode);

            // 延迟订阅事件,等待引脚状态稳定(避免初始化时触发误事件)
            _initTimer = new Timer(InitCallback, null, 100, Timeout.Infinite);

            _logger.LogInformation($"Button driver initialized on pin {pinNumber}");
        }

        // 延迟初始化回调(等待引脚状态稳定)
        private void InitCallback(object state)
        {
            if (_disposed) return;

            try
            {
                // 订阅引脚值变化事件
                _pin.ValueChanged += OnPinValueChanged;
                _initTimer?.Dispose();
                _initTimer = null;

                // 标记初始化完成时间
                _initCompleteTime = DateTime.UtcNow;
                _isInitialized = true;

                _logger.LogInformation("Button event subscription enabled");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to subscribe button event: {ex.Message}");
            }
        }

        // GPIO引脚值变化事件处理(中断回调)
        // 【重要】不要在此方法中执行耗时操作,避免阻塞中断
        private void OnPinValueChanged(object sender, PinValueChangedEventArgs e)
        {
            if (_disposed || !_isInitialized) return;

            // 判断按下/释放状态
            bool isPressed = _activeLow ? (e.ChangeType == PinEventTypes.Falling) 
                                       : (e.ChangeType == PinEventTypes.Rising);

            // 软件防抖:使用定时器延迟处理
            if (_debounceTimer != null)
            {
                _debounceTimer.Dispose();
                _debounceTimer = null;
            }

            // 防抖延迟后处理
            _debounceTimer = new Timer(DebounceCallback, isPressed, _debounceTime, Timeout.Infinite);
        }

        // 防抖回调(在防抖时间后执行实际逻辑)
        private void DebounceCallback(object state)
        {
            if (_disposed) return;

            bool isPressed = (bool)state;

            try
            {
                if (isPressed)
                {
                    // 按钮按下
                    _isPressed = true;
                    _longPressTriggered = false;
                    _pressStartTime = DateTime.UtcNow;

                    // 启动长按检测定时器
                    if (_longPressTimer != null)
                    {
                        _longPressTimer.Dispose();
                    }
                    _longPressTimer = new Timer(LongPressCallback, null, _longPressTime, Timeout.Infinite);

                    _logger.LogDebug($"Button pressed on pin {PinNumber}");
                }
                else
                {
                    // 按钮释放
                    _isPressed = false;

                    // 停止长按检测定时器
                    if (_longPressTimer != null)
                    {
                        _longPressTimer.Dispose();
                        _longPressTimer = null;
                    }

                    // 如果长按未触发,则触发短按事件
                    if (!_longPressTriggered)
                    {
                        var duration = DateTime.UtcNow - _pressStartTime;
                        
                        // 合理性检查:过滤异常的持续时间(超过10秒的短按视为异常)
                        if (duration.TotalMilliseconds > 10000)
                        {
                            _logger.LogWarning($"Ignored abnormal button press, duration: {duration.TotalMilliseconds}ms");
                            return;
                        }
                        
                        _logger.LogInformation($"Button short press detected, duration: {duration.TotalMilliseconds}ms");

                        // 触发短按事件
                        OnButtonEvent?.Invoke(this, new ButtonEventArgs
                        {
                            EventType = ButtonEventType.ShortPress,
                            PinNumber = PinNumber,
                            PressDuration = duration
                        });
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error in button debounce callback: {ex.Message}");
            }
        }

        // 长按检测回调
        private void LongPressCallback(object state)
        {
            if (_disposed || !_isPressed) return;

            try
            {
                _longPressTriggered = true;
                var duration = DateTime.UtcNow - _pressStartTime;

                _logger.LogInformation($"Button long press detected, duration: {duration.TotalMilliseconds}ms");

                // 触发长按事件
                OnButtonEvent?.Invoke(this, new ButtonEventArgs
                {
                    EventType = ButtonEventType.LongPress,
                    PinNumber = PinNumber,
                    PressDuration = duration
                });
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error in long press callback: {ex.Message}");
            }
        }

        // 释放资源
        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;

            try
            {
                // 停止所有定时器
                _initTimer?.Dispose();
                _debounceTimer?.Dispose();
                _longPressTimer?.Dispose();

                // 取消订阅事件
                _pin.ValueChanged -= OnPinValueChanged;

                // 释放GPIO引脚
                _pin.Dispose();

                _logger.LogInformation("Button driver disposed");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error disposing button driver: {ex.Message}");
            }
        }
    }
}

Drivers/RelayDriver.cs

using System;
using System.Device.Gpio;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using YFSoft.Hardware.YF3300_ESP32S3;

namespace YeFanIoTTest.Drivers
{
    // 继电器驱动类
    // 支持多路继电器控制,提供开关、切换、状态查询等功能
    public class RelayDriver : IDisposable
    {
        private readonly ILogger _logger;
        private readonly GpioController _gpioController;
        private readonly GpioPin[] _relayPins;             // 继电器引脚数组
        private readonly bool[] _relayStates;              // 继电器状态数组
        private readonly int _relayCount;                  // 继电器数量
        private bool _disposed = false;

        // 构造函数:使用默认继电器配置
        public RelayDriver(GpioController gpioController)
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("RelayDriver");
            _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController));
            
            _relayCount = Mainboard.Relays.Count;
            _relayPins = new GpioPin[_relayCount];
            _relayStates = new bool[_relayCount];

            InitializeRelays();
            _logger.LogInformation($"RelayDriver initialized with {_relayCount} relay(s)");
        }

        // 初始化所有继电器引脚
        private void InitializeRelays()
        {
            for (int i = 0; i < _relayCount; i++)
            {
                int pinNumber = Mainboard.Relays.Channels[i];
                _relayPins[i] = _gpioController.OpenPin(pinNumber, PinMode.Output);
                _relayPins[i].Write(PinValue.Low);  // 默认关闭
                _relayStates[i] = false;
                _logger.LogInformation($"Relay {i + 1} initialized on GPIO{pinNumber}, initial state: OFF");
            }
        }

        // 打开指定继电器
        // channel: 继电器通道号(从0开始)
        public void TurnOn(int channel)
        {
            if (!IsValidChannel(channel)) return;

            _relayPins[channel].Write(PinValue.High);
            _relayStates[channel] = true;
            _logger.LogInformation($"Relay {channel + 1} turned ON");
        }

        // 关闭指定继电器
        // channel: 继电器通道号(从0开始)
        public void TurnOff(int channel)
        {
            if (!IsValidChannel(channel)) return;

            _relayPins[channel].Write(PinValue.Low);
            _relayStates[channel] = false;
            _logger.LogInformation($"Relay {channel + 1} turned OFF");
        }

        // 切换指定继电器状态
        // channel: 继电器通道号(从0开始)
        public void Toggle(int channel)
        {
            if (!IsValidChannel(channel)) return;

            if (_relayStates[channel])
            {
                TurnOff(channel);
            }
            else
            {
                TurnOn(channel);
            }
        }

        // 获取指定继电器状态
        // channel: 继电器通道号(从0开始)
        // 返回:true=吸合,false=释放
        public bool GetState(int channel)
        {
            if (!IsValidChannel(channel)) return false;
            return _relayStates[channel];
        }

        // 获取所有继电器状态
        // 返回:状态数组,true=吸合,false=释放
        public bool[] GetAllStates()
        {
            bool[] states = new bool[_relayCount];
            Array.Copy(_relayStates, states, _relayCount);
            return states;
        }

        // 打开所有继电器
        public void TurnOnAll()
        {
            for (int i = 0; i < _relayCount; i++)
            {
                TurnOn(i);
            }
        }

        // 关闭所有继电器
        public void TurnOffAll()
        {
            for (int i = 0; i < _relayCount; i++)
            {
                TurnOff(i);
            }
        }

        // 验证通道号是否有效
        private bool IsValidChannel(int channel)
        {
            if (channel < 0 || channel >= _relayCount)
            {
                _logger.LogWarning($"Invalid relay channel: {channel}, valid range: 0-{_relayCount - 1}");
                return false;
            }
            return true;
        }

        // 释放资源
        public void Dispose()
        {
            if (_disposed) return;

            // 关闭所有继电器
            TurnOffAll();

            // 释放引脚资源
            for (int i = 0; i < _relayCount; i++)
            {
                if (_relayPins[i] != null)
                {
                    _relayPins[i].Dispose();
                    _relayPins[i] = null;
                }
            }

            _disposed = true;
            _logger.LogInformation("RelayDriver disposed");
        }
    }
}

Drivers/DigitalInputDriver.cs

using System;
using System.Device.Gpio;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using YFSoft.Hardware.YF3300_ESP32S3;

namespace YeFanIoTTest.Drivers
{
    // 数字输入回调委托 - 只传递基本类型参数
    // channel: 通道号(从0开始)
    // isTriggered: 是否触发(true=低电平触发,false=高电平未触发)
    // pinValue: 引脚电平值(0=低电平,1=高电平)
    public delegate void DigitalInputCallback(int channel, bool isTriggered, int pinValue);

    // 数字输入驱动类
    // 支持多路数字输入检测,使用回调委托代替事件
    public class DigitalInputDriver : IDisposable
    {
        private readonly ILogger _logger;
        private readonly GpioController _gpioController;
        private readonly GpioPin[] _inputPins;             // 输入引脚数组
        private readonly int _inputCount;                  // 输入数量
        private readonly DigitalInputCallback _callback;   // 回调委托
        private bool _disposed = false;

        // 构造函数:使用默认数字输入配置
        // gpioController: GPIO控制器
        // callback: 状态变化回调委托(可选)
        public DigitalInputDriver(GpioController gpioController, DigitalInputCallback callback = null)
        {
            // 构造函数中可以使用Logger
            _logger = LogDispatcher.LoggerFactory.CreateLogger("DigitalInputDriver");
            
            _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController));
            _callback = callback;
            
            _inputCount = Mainboard.DigitalInputs.Count;
            _inputPins = new GpioPin[_inputCount];

            InitializeInputs();
            
            _logger.LogInformation($"DigitalInputDriver initialized with {_inputCount} input(s)");
        }

        // 初始化所有数字输入引脚
        private void InitializeInputs()
        {
            for (int i = 0; i < _inputCount; i++)
            {
                int pinNumber = Mainboard.DigitalInputs.Channels[i];
                
                // 采用参考项目07的方式:先OpenPin,再SetPinMode
                _inputPins[i] = _gpioController.OpenPin(pinNumber);
                _inputPins[i].SetPinMode(PinMode.Input);
                
                // 如果提供了回调委托,订阅ValueChanged事件
                if (_callback != null)
                {
                    // 捕获变量,避免闭包问题
                    int channel = i;
                    
                    // 订阅事件
                    _inputPins[i].ValueChanged += (sender, e) =>
                    {
                        // 事件回调 - 只传递基本类型,不创建对象
                        OnPinValueChanged(channel);
                    };
                }
                
                _logger.LogInformation($"Digital input {i + 1} initialized on GPIO{pinNumber}");
            }
        }

        // 引脚值变化事件处理
        // 【重要】此方法在GPIO中断上下文中执行,必须遵循以下规则:
        // 1. 不能创建托管对象(不使用new关键字)
        // 2. 不能使用DateTime.UtcNow(会创建对象)
        // 3. 只传递基本类型参数
        // 4. 只调用简单方法
        private void OnPinValueChanged(int channel)
        {
            if (_disposed) return;
            
            try
            {
                // 读取引脚状态
                PinValue value = _inputPins[channel].Read();
                bool isTriggered = (value == PinValue.Low);
                int pinValueInt = (int)value;
                
                // 调用回调,只传递基本类型
                // 不创建任何对象,不使用DateTime.UtcNow
                _callback?.Invoke(channel, isTriggered, pinValueInt);
            }
            catch
            {
                // 中断上下文中不能使用Logger,只能忽略错误
            }
        }

        // 读取指定通道状态
        // channel: 通道号(从0开始)
        // 返回:true=触发(低电平),false=未触发(高电平)
        public bool ReadState(int channel)
        {
            if (channel < 0 || channel >= _inputCount)
            {
                _logger.LogWarning($"Invalid input channel: {channel}, valid range: 0-{_inputCount - 1}");
                return false;
            }

            PinValue value = _inputPins[channel].Read();
            return (value == PinValue.Low);
        }

        // 读取所有通道状态
        // 返回:状态数组,true=触发,false=未触发
        public bool[] ReadAllStates()
        {
            bool[] states = new bool[_inputCount];
            for (int i = 0; i < _inputCount; i++)
            {
                states[i] = ReadState(i);
            }
            return states;
        }

        // 释放资源
        public void Dispose()
        {
            if (_disposed) return;

            // 释放引脚资源
            for (int i = 0; i < _inputCount; i++)
            {
                if (_inputPins[i] != null)
                {
                    _inputPins[i].Dispose();
                    _inputPins[i] = null;
                }
            }

            _disposed = true;
            _logger.LogInformation("DigitalInputDriver disposed");
        }
    }
}

Drivers/LedManager.cs

using System;
using System.Device.Gpio;
using System.Threading;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using YeFanIoTTest.Enums;
using YFSoft.Hardware.YF3300_ESP32S3;

namespace YeFanIoTTest.Drivers
{
    // LED管理器 - 管理黄色LED(网络状态)和绿色LED(配网状态)
    public class LedManager : IDisposable
    {
        // 日志记录器
        private readonly ILogger _logger;

        // GPIO控制器(外部注入)
        private readonly GpioController _gpioController;

        // LED引脚
        private readonly GpioPin _yellowLedPin;      // 黄色LED引脚(网络状态指示)
        private readonly GpioPin _greenLedPin;       // 绿色LED引脚(配网状态指示)

        // 闪烁定时器
        private Timer _yellowLedTimer;               // 黄色LED闪烁定时器
        private Timer _greenLedTimer;                // 绿色LED闪烁定时器

        // 资源释放标志
        private bool _disposed;

        // 构造函数:注入GPIO控制器
        public LedManager(GpioController gpioController)
        {
            _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController));
            _logger = LogDispatcher.LoggerFactory.CreateLogger("LedManager");

            // 初始化LED引脚
            _yellowLedPin = _gpioController.OpenPin(Mainboard.Pins.YellowLED, PinMode.Output);
            _greenLedPin = _gpioController.OpenPin(Mainboard.Pins.GreenLED, PinMode.Output);

            // 初始化时关闭所有LED
            TurnOffAll();
            _logger.LogInformation("LED manager initialized");
        }

        // 设置网络状态指示(黄色LED)
        public void SetNetworkStatus(NetworkStatus status)
        {
            StopYellowBlink();

            switch (status)
            {
                case NetworkStatus.Connecting:
                    // 常亮 - 正在连接网络
                    _logger.LogDebug("Yellow LED: ON - Connecting to network");
                    _yellowLedPin.Write(PinValue.High);
                    break;

                case NetworkStatus.Connected:
                    // 慢闪 - 网络正常
                    _logger.LogDebug("Yellow LED: Slow blink - Network normal");
                    StartYellowBlink(
                        Mainboard.LEDTiming.NetworkNormal_On,
                        Mainboard.LEDTiming.NetworkNormal_Off);
                    break;

                case NetworkStatus.Disconnected:
                case NetworkStatus.Error:
                    // 快闪 - 网络异常
                    _logger.LogDebug("Yellow LED: Fast blink - Network error");
                    StartYellowBlink(
                        Mainboard.LEDTiming.NetworkError_On,
                        Mainboard.LEDTiming.NetworkError_Off);
                    break;
            }
        }

        // 设置配网状态指示(绿色LED)
        public void SetConfigStatus(ConfigStatus status)
        {
            StopGreenBlink();

            switch (status)
            {
                case ConfigStatus.Configuring:
                    // 常亮 - 正在配网
                    _logger.LogDebug("Green LED: ON - Configuring");
                    _greenLedPin.Write(PinValue.High);
                    break;

                case ConfigStatus.Success:
                    // 慢闪 - 配网成功
                    _logger.LogDebug("Green LED: Slow blink - Config success");
                    StartGreenBlink(
                        Mainboard.LEDTiming.ConfigSuccess_On,
                        Mainboard.LEDTiming.ConfigSuccess_Off);
                    break;

                case ConfigStatus.Failed:
                    // 快闪 - 配网失败
                    _logger.LogDebug("Green LED: Fast blink - Config failed");
                    StartGreenBlink(
                        Mainboard.LEDTiming.ConfigFailed_On,
                        Mainboard.LEDTiming.ConfigFailed_Off);
                    break;

                case ConfigStatus.Normal:
                    // 熄灭 - 正常运行
                    _logger.LogDebug("Green LED: OFF - Normal operation");
                    _greenLedPin.Write(PinValue.Low);
                    break;
            }
        }

        // 启动黄色LED闪烁
        private void StartYellowBlink(int onMs, int offMs)
        {
            StopYellowBlink();
            bool isOn = false;
            _yellowLedTimer = new Timer(_ =>
            {
                isOn = !isOn;
                _yellowLedPin.Write(isOn ? PinValue.High : PinValue.Low);
            }, null, 0, isOn ? onMs : offMs);
        }

        // 停止黄色LED闪烁
        private void StopYellowBlink()
        {
            if (_yellowLedTimer != null)
            {
                _yellowLedTimer.Dispose();
                _yellowLedTimer = null;
            }
        }

        // 启动绿色LED闪烁
        private void StartGreenBlink(int onMs, int offMs)
        {
            StopGreenBlink();
            bool isOn = false;
            _greenLedTimer = new Timer(_ =>
            {
                isOn = !isOn;
                _greenLedPin.Write(isOn ? PinValue.High : PinValue.Low);
            }, null, 0, isOn ? onMs : offMs);
        }

        // 停止绿色LED闪烁
        private void StopGreenBlink()
        {
            if (_greenLedTimer != null)
            {
                _greenLedTimer.Dispose();
                _greenLedTimer = null;
            }
        }

        // 关闭所有LED
        public void TurnOffAll()
        {
            _yellowLedPin.Write(PinValue.Low);
            _greenLedPin.Write(PinValue.Low);
        }

        // 释放资源
        public void Dispose()
        {
            if (!_disposed)
            {
                StopYellowBlink();
                StopGreenBlink();
                _yellowLedPin?.Dispose();
                _greenLedPin?.Dispose();
                _disposed = true;
                _logger.LogInformation("LED manager disposed");
            }
        }
    }
}

Managers/MqttClientManager.cs

using System;
using System.Collections;
using System.Text;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.M2Mqtt;
using nanoFramework.M2Mqtt.Messages;
using YeFanIoTTest.Models;

namespace YeFanIoTTest.Managers
{
    // MQTT客户端管理器
    // 负责与叶帆物联网平台通信,实现YFLink协议
    internal class MqttClientManager
    {
        private readonly ILogger _logger;
        private MqttClient _mqttClient;
        private bool _isConnected = false;

        // MQTT连接参数
        private const string MqttServer = "iot.yfios.net";
        private const int MqttPort = 1883;
        private const string ProjectId = "YFIoT_TEST";
        private const string ProductId = "YF3300_ESP32S3";
        private const string DeviceId = "YF3300_ESP32S301";
        private const string DeviceKey = "dxR99LCS7Uldc7KUnurFBeBi";

        // MQTT主题(V1.3.0 去掉前导"/"以支持共享订阅)
        private const string PropertyPostTopic = "{0}/{1}/{2}/property/post";           // 属性上传
        private const string EventPostTopic = "{0}/{1}/{2}/event/post";                // 事件上传
        private const string ServiceSendTopic = "{0}/{1}/{2}/service/send";            // 服务下发
        private const string ServiceResultTopic = "{0}/{1}/{2}/service/result";        // 服务响应

        // 事件:收到服务下发
        public event ServiceReceivedEventHandler OnServiceReceived;

        // 服务下发事件委托
        public delegate void ServiceReceivedEventHandler(object sender, ServiceSendRequest request);

        // 属性:是否已连接
        public bool IsConnected => _isConnected;

        // 构造函数
        public MqttClientManager()
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("MqttClientManager");
        }

        // 连接到MQTT服务器
        public bool Connect()
        {
            try
                       {
                _logger.LogInformation("Connecting to MQTT server...");

                // 创建MQTT客户端
                _mqttClient = new MqttClient(MqttServer, MqttPort, false, null, null, MqttSslProtocols.None);

                // 设置回调
                _mqttClient.MqttMsgPublishReceived += OnMessageReceived;
                _mqttClient.MqttMsgSubscribed += OnSubscribed;
                _mqttClient.ConnectionClosed += OnConnectionClosed;

                // 连接参数(YFLink协议格式)
                // clientId - 项目ID + "-" + 产品ID + "-" + 设备ID
                // userName - 项目ID + "&" + 产品ID + "&" + 设备ID
                // password - HMACSHA1(DeviceKey, clientId + userName) 转为小写十六进制
                string clientId = $"{ProjectId}-{ProductId}-{DeviceId}";
                string username = $"{ProjectId}&{ProductId}&{DeviceId}";
                
                // 计算HMACSHA1密码
                string content = clientId + username;
                string password = CalculateHmacSha1(content, DeviceKey).ToLower();
                
                _logger.LogInformation($"MQTT ClientId: {clientId}");
                _logger.LogInformation($"MQTT Username: {username}");
                _logger.LogInformation($"MQTT Password: {password}");

                // 连接服务器
                var result = _mqttClient.Connect(clientId, username, password, false, 60);

                if (result == MqttReasonCode.Success)
                {
                    _isConnected = true;
                    _logger.LogInformation($"MQTT connected successfully, ClientId: {clientId}");

                    // 订阅服务下发主题
                    SubscribeServiceTopic();

                    return true;
                }
                else
                {
                    _logger.LogError($"MQTT connection failed, reason: {result}");
                    return false;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to connect MQTT: {ex.Message}");
                return false;
            }
        }

        // 断开连接
        public void Disconnect()
        {
            try
            {
                if (_mqttClient != null && _mqttClient.IsConnected)
                {
                    _mqttClient.Disconnect();
                    _logger.LogInformation("MQTT disconnected");
                }

                _isConnected = false;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to disconnect MQTT: {ex.Message}");
            }
        }

        // 上传属性
        public bool PublishProperties(Hashtable properties)
        {
            if (!_isConnected)
            {
                _logger.LogWarning("MQTT not connected, cannot publish properties");
                return false;
            }

            try
            {
                // 构建属性上传请求
                var request = new PropertyPostRequest
                {
                    id = GenerateMessageId(),
                    timestamp = GetCurrentTimestamp(),
                    parameters = properties
                };

                // 序列化为JSON
                string json = SerializeToJson(request);

                // 发布消息
                string topic = string.Format(PropertyPostTopic, ProjectId, ProductId, DeviceId);
                _mqttClient.Publish(topic, Encoding.UTF8.GetBytes(json), null, null, MqttQoSLevel.AtLeastOnce, false);

                _logger.LogInformation($"Properties published: {json}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to publish properties: {ex.Message}");
                return false;
            }
        }

        // 上传事件
        public bool PublishEvent(int eventType, int eventCode, string content)
        {
            if (!_isConnected)
            {
                _logger.LogWarning("MQTT not connected, cannot publish event");
                return false;
            }

            try
            {
                // 构建事件数据
                var eventData = new EventData
                {
                    type = eventType,
                    code = eventCode,
                    content = content,
                    time = GetCurrentTimestamp()
                };

                // 构建事件上传请求
                var request = new EventPostRequest
                {
                    id = GenerateMessageId(),
                    timestamp = GetCurrentTimestamp(),
                    parameters = new ArrayList { eventData }
                };

                // 序列化为JSON
                string json = SerializeToJson(request);

                // 发布消息
                string topic = string.Format(EventPostTopic, ProjectId, ProductId, DeviceId);
                _mqttClient.Publish(topic, Encoding.UTF8.GetBytes(json), null, null, MqttQoSLevel.AtLeastOnce, false);

                _logger.LogInformation($"Event published: {json}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to publish event: {ex.Message}");
                return false;
            }
        }

        // 订阅服务下发主题
        private void SubscribeServiceTopic()
        {
            try
            {
                string topic = string.Format(ServiceSendTopic, ProjectId, ProductId, DeviceId);
                _mqttClient.Subscribe(new string[] { topic }, new MqttQoSLevel[] { MqttQoSLevel.AtLeastOnce });
                _logger.LogInformation($"Subscribed to service topic: {topic}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to subscribe service topic: {ex.Message}");
            }
        }

        // 消息接收回调
        private void OnMessageReceived(object sender, MqttMsgPublishEventArgs e)
        {
            try
            {
                string topic = e.Topic;
                string message = Encoding.UTF8.GetString(e.Message, 0, e.Message.Length);

                _logger.LogInformation($"Message received, Topic: {topic}, Message: {message}");

                // 检查是否为服务下发主题
                string serviceTopic = string.Format(ServiceSendTopic, ProjectId, ProductId, DeviceId);
                if (topic == serviceTopic)
                {
                    // 解析服务下发请求
                    var request = DeserializeFromJson<ServiceSendRequest>(message);
                    if (request != null)
                    {
                        // 触发事件
                        OnServiceReceived?.Invoke(this, request);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error processing received message: {ex.Message}");
            }
        }

        // 订阅成功回调
        private void OnSubscribed(object sender, MqttMsgSubscribedEventArgs e)
        {
            _logger.LogInformation($"Subscribed successfully, MessageId: {e.MessageId}");
        }

        // 连接关闭回调
        private void OnConnectionClosed(object sender, EventArgs e)
        {
            _isConnected = false;
            _logger.LogWarning("MQTT connection closed");
        }

        // 生成消息ID
        private int GenerateMessageId()
        {
            var random = new Random();
            return random.Next();
        }

        // 获取当前时间戳(毫秒)
        private long GetCurrentTimestamp()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
        }

        // 序列化为JSON(YFLink协议格式)
        private string SerializeToJson(object obj)
        {
            // YFLink属性上传格式:
            // {
            //   "id": 1234578,
            //   "timestamp": xxxxx,
            //   "params": {
            //     "H": 36.2,
            //     "T": 29.3,
            //     "I1": 0,
            //     "I2": 0,
            //     "Q1": 0
            //   }
            // }
            if (obj is PropertyPostRequest propReq)
            {
                var sb = new StringBuilder();
                sb.Append("{");
                sb.Append($"\"id\":{propReq.id},");
                sb.Append($"\"timestamp\":{propReq.timestamp},");
                sb.Append("\"params\":{");
                
                bool first = true;
                foreach (DictionaryEntry entry in propReq.parameters)
                {
                    if (!first) sb.Append(",");
                    // 根据值类型决定是否需要引号和格式
                    if (entry.Value is string)
                    {
                        sb.Append($"\"{entry.Key}\":\"{entry.Value}\"");
                    }
                    else if (entry.Value is double)
                    {
                        // 对double类型保留一位小数
                        double value = (double)entry.Value;
                        sb.Append($"\"{entry.Key}\":{value:F1}");
                    }
                    else
                    {
                        sb.Append($"\"{entry.Key}\":{entry.Value}");
                    }
                    first = false;
                }
                
                sb.Append("}}");
                return sb.ToString();
            }
            
            return "{}";
        }

        // 从JSON反序列化(简化版)
        private T DeserializeFromJson<T>(string json) where T : class
        {
            // 这里使用简化的JSON反序列化
            // 实际项目中应使用nanoFramework.Json库
            return null;
        }
        
        // 计算HMACSHA1(YFLink协议要求的密码计算方法)
        // content: ClientID + UserName
        // key: DeviceKey
        // 注意:nanoFramework只支持HMACSHA256 和 HMACSHA512,这里使用自定义HMACSHA1实现
        private string CalculateHmacSha1(string content, string key)
        {
            try
            {
                // 将key和content转换为字节数组
                byte[] keyBytes = Encoding.UTF8.GetBytes(key);
                byte[] contentBytes = Encoding.UTF8.GetBytes(content);
                
                // 使用自定义HMACSHA1实现
                byte[] hashBytes = HmacSha1(keyBytes, contentBytes);
                
                // 转换为十六进制字符串
                return BytesToHexString(hashBytes);
            }
            catch (Exception ex)
            {
                _logger.LogError($"HMACSHA1 calculation failed: {ex.Message}");
                return string.Empty;
            }
        }
        
        // 自定义HMACSHA1实现(因为nanoFramework不支持HMACSHA1)
        private byte[] HmacSha1(byte[] key, byte[] message)
        {
            // HMAC算法步骤:
            // 1. 如果key长度大于64字节,先对key进行SHA1哈希
            // 2. 如果key长度小于64字节,用0填充到64字节
            // 3. 计算inner padding: key XOR 0x36
            // 4. 计算outer padding: key XOR 0x5C
            // 5. 计算hash = SHA1(outer_padding + SHA1(inner_padding + message))
            
            const int blockSize = 64; // SHA1的块大小是64字节
            
            // 处理key
            byte[] normalizedKey = new byte[blockSize];
            if (key.Length > blockSize)
            {
                // Key太长,先进行SHA1哈希
                byte[] keyHash = Sha1(key);
                Array.Copy(keyHash, normalizedKey, keyHash.Length);
            }
            else
            {
                Array.Copy(key, normalizedKey, key.Length);
            }
            
            // 创建inner padding (key XOR 0x36)
            byte[] innerPadding = new byte[blockSize];
            for (int i = 0; i < blockSize; i++)
            {
                innerPadding[i] = (byte)(normalizedKey[i] ^ 0x36);
            }
            
            // 创建outer padding (key XOR 0x5C)
            byte[] outerPadding = new byte[blockSize];
            for (int i = 0; i < blockSize; i++)
            {
                outerPadding[i] = (byte)(normalizedKey[i] ^ 0x5C);
            }
            
            // 计算inner hash: SHA1(inner_padding + message)
            byte[] innerData = new byte[blockSize + message.Length];
            Array.Copy(innerPadding, innerData, blockSize);
            Array.Copy(message, 0, innerData, blockSize, message.Length);
            byte[] innerHash = Sha1(innerData);
            
            // 计算outer hash: SHA1(outer_padding + inner_hash)
            byte[] outerData = new byte[blockSize + innerHash.Length];
            Array.Copy(outerPadding, outerData, blockSize);
            Array.Copy(innerHash, 0, outerData, blockSize, innerHash.Length);
            byte[] outerHash = Sha1(outerData);
            
            return outerHash;
        }
        
        // 自定义SHA1实现
        private byte[] Sha1(byte[] data)
        {
            // SHA1算法实现
            // 初始化哈希值
            uint h0 = 0x67452301;
            uint h1 = 0xEFCDAB89;
            uint h2 = 0x98BADCFE;
            uint h3 = 0x10325476;
            uint h4 = 0xC3D2E1F0;
            
            // 预处理:填充数据
            int originalLength = data.Length;
            int paddedLength = ((originalLength + 8) / 64 + 1) * 64;
            byte[] paddedData = new byte[paddedLength];
            Array.Copy(data, paddedData, originalLength);
            paddedData[originalLength] = 0x80; // 添加1后面跟着0
            
            // 添加原始长度(位)
            ulong bitLength = (ulong)originalLength * 8;
            for (int i = 0; i < 8; i++)
            {
                paddedData[paddedLength - 8 + i] = (byte)(bitLength >> (56 - i * 8));
            }
            
            // 处理每个512位(64字节)块
            for (int blockStart = 0; blockStart < paddedLength; blockStart += 64)
            {
                // 将块分成16个32位字
                uint[] w = new uint[80];
                for (int i = 0; i < 16; i++)
                {
                    w[i] = (uint)(paddedData[blockStart + i * 4] << 24 |
                                   paddedData[blockStart + i * 4 + 1] << 16 |
                                   paddedData[blockStart + i * 4 + 2] << 8 |
                                   paddedData[blockStart + i * 4 + 3]);
                }
                
                // 扩展为80个字
                for (int i = 16; i < 80; i++)
                {
                    w[i] = LeftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
                }
                
                // 初始化工作变量
                uint a = h0;
                uint b = h1;
                uint c = h2;
                uint d = h3;
                uint e = h4;
                
                // 主循环
                for (int i = 0; i < 80; i++)
                {
                    uint f, k;
                    if (i < 20)
                    {
                        f = (b & c) | ((~b) & d);
                        k = 0x5A827999;
                    }
                    else if (i < 40)
                    {
                        f = b ^ c ^ d;
                        k = 0x6ED9EBA1;
                    }
                    else if (i < 60)
                    {
                        f = (b & c) | (b & d) | (c & d);
                        k = 0x8F1BBCDC;
                    }
                    else
                    {
                        f = b ^ c ^ d;
                        k = 0xCA62C1D6;
                    }
                    
                    uint temp = LeftRotate(a, 5) + f + e + k + w[i];
                    e = d;
                    d = c;
                    c = LeftRotate(b, 30);
                    b = a;
                    a = temp;
                }
                
                // 添加工作变量到哈希值
                h0 += a;
                h1 += b;
                h2 += c;
                h3 += d;
                h4 += e;
            }
            
            // 生成最终哈希值(20字节)
            byte[] hash = new byte[20];
            hash[0] = (byte)(h0 >> 24);
            hash[1] = (byte)(h0 >> 16);
            hash[2] = (byte)(h0 >> 8);
            hash[3] = (byte)h0;
            hash[4] = (byte)(h1 >> 24);
            hash[5] = (byte)(h1 >> 16);
            hash[6] = (byte)(h1 >> 8);
            hash[7] = (byte)h1;
            hash[8] = (byte)(h2 >> 24);
            hash[9] = (byte)(h2 >> 16);
            hash[10] = (byte)(h2 >> 8);
            hash[11] = (byte)h2;
            hash[12] = (byte)(h3 >> 24);
            hash[13] = (byte)(h3 >> 16);
            hash[14] = (byte)(h3 >> 8);
            hash[15] = (byte)h3;
            hash[16] = (byte)(h4 >> 24);
            hash[17] = (byte)(h4 >> 16);
            hash[18] = (byte)(h4 >> 8);
            hash[19] = (byte)h4;
            
            return hash;
        }
        
        // 32位左循环移位
        private uint LeftRotate(uint value, int bits)
        {
            return (value << bits) | (value >> (32 - bits));
        }
        
        // 字节数组转十六进制字符串
        private string BytesToHexString(byte[] bytes)
        {
            var sb = new StringBuilder();
            foreach (byte b in bytes)
            {
                sb.Append(b.ToString("x2"));
            }
            return sb.ToString();
        }
    }
}

Managers/WifiManager.cs

using System;
using System.Device.Wifi;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.Networking;
using Iot.Device.DhcpServer;

namespace YeFanIoTTest.Managers
{
    public enum WifiState 
    { 
        Idle,               // 空闲状态
        Connecting,         // 连接中
        Connected,          // 已连接
        Reconnecting,       // 重连中   
        APMode              // AP模式
    }

    // internal:仅本程序集可见,封装内部实现
    // 不对外暴露具体实现细节,保证API稳定性
    internal class WifiManager
    {
        // 日志记录器
        private ILogger _logger;
        
        // WIFI硬件适配器(系统唯一)
        private WifiAdapter _wifiAdapter;
        
        // 当前连接的网络凭证
        private string _currentSSID = null;
        private string _currentPassword = null;

        // ========== 状态机与同步工具 ==========
        // 当前WIFI状态(线程安全的状态机)
        public WifiState CurrentState { get; private set; } = WifiState.Idle;
        
        // 是否已连接(封装IsSTAConnected方法)
        public bool IsConnected => IsSTAConnected();
        
        // 云端通信唤醒事件(连接成功时通知上云线程)
        public AutoResetEvent CloudWakeUpEvent { get; private set; }
        
        // 重连任务取消令牌(用于安全终止重连线程)
        private CancellationTokenSource _reconnectCts;
        
        // 网络状态防抖定时器(避免频繁触发重连)
        private Timer _debounceTimer;
        
        // DHCP服务器实例(AP模式下使用)
        private DhcpServer _dhcpServer;

        // 构造函数:初始化WIFI管理器
        // 1. 创建日志记录器
        // 2. 初始化同步事件
        // 3. 获取系统WIFI适配器
        public WifiManager()
        {
            // 初始化云端唤醒事件(非信号状态)
            CloudWakeUpEvent = new AutoResetEvent(false);
            
            // 获取系统中的第一个WIFI适配器
            var adapters = WifiAdapter.FindAllAdapters();
            if (adapters == null || adapters.Length == 0)
            {
                throw new Exception("未找到 WIFI 硬件适配器");
            }
            _wifiAdapter = adapters[0];

            // 创建日志记录器(放在最后,因为需要确保其他初始化完成)
            try
            {
                _logger = LogDispatcher.LoggerFactory.CreateLogger("WifiManager");
            }
            catch
            {
                // 如果日志创建失败,不影响程序运行
                //Debug.WriteLine("WifiManager: 日志记录器创建失败");
                _logger = null;
                return;
            }
        }

        #region STA模式连接

        // 连接到指定的WIFI网络(STA模式)
        public bool ConnectSTA(string ssid, string password, bool enableReconnect = true)
        {
            // 保存连接凭证
            _currentSSID = ssid;
            _currentPassword = password;

            // ========== 连接前检查 ==========
            // 检查是否已经连接(避免重复连接)
            if (IsSTAConnected())
            {
                _logger.LogInformation($"[WifiManager] 已连接到网络,无需重复连接");
                CurrentState = WifiState.Connected;
                
                if (enableReconnect)
                {
                    RegisterNetworkEvents();
                }
                return true;
            }

            // 更新状态为连接中
            CurrentState = WifiState.Connecting;

            // 首次连接,超时15秒
            var firstTryCts = new CancellationTokenSource(15000);
            bool connectResult = ConnectWithHelper(ssid, password, firstTryCts.Token);

            // 根据连接结果更新状态
            CurrentState = connectResult ? WifiState.Connected : WifiState.Reconnecting;

            // 如果启用自动重连
            if (enableReconnect)
            {
                // 注册网络状态变化事件
                RegisterNetworkEvents();

                // 如果首次连接失败,立即启动重连任务
                if (!connectResult)
                {
                    StartReconnectTask();
                }
            }

            return connectResult;
        }

        // 重新连接WiFi
        public bool Reconnect()
        {
            if (string.IsNullOrEmpty(_currentSSID) || string.IsNullOrEmpty(_currentPassword))
            {
                _logger.LogWarning("[WifiManager] No WiFi credentials saved, cannot reconnect");
                return false;
            }

            _logger.LogInformation($"[WifiManager] Reconnecting to {_currentSSID}...");
            
            // 使用保存的凭证重新连接
            return ConnectSTA(_currentSSID, _currentPassword, enableReconnect: true);
        }

        // 获取IP地址
        public string GetIPAddress()
        {
            try
            {
                foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
                {
                    if (nic.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                    {
                        string ip = nic.IPv4Address.ToString();
                        if (ip != "0.0.0.0" && ip != "127.0.0.1")
                        {
                            return ip;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError($"[WifiManager] Failed to get IP address: {ex.Message}");
            }
            
            return "0.0.0.0";
        }

        // 注册网络状态变化事件(事件驱动)
        private void RegisterNetworkEvents()
        {
            // 订阅网络地址变化事件
            NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

            // 初始化防抖定时器(3秒防抖窗口)
            if (_debounceTimer == null)
            {
                _debounceTimer = new Timer(DebounceAction, null, Timeout.Infinite, Timeout.Infinite);
            }

            _logger.LogInformation("[WifiManager] 已注册防抖网络事件");
        }

        // 取消注册网络事件
        public void UnregisterNetworkEvents()
        {
            NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
            _logger.LogInformation("[WifiManager] 已取消网络事件");
        }
        #endregion

        #region 防抖核心逻辑
        // 网络状态变化事件处理函数(防抖入口)
        // 当检测到网络波动时,重置防抖计时器
        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
        {
            // AP模式下忽略网络事件
            if (CurrentState == WifiState.APMode)
            {
                return;
            }

            _logger.LogInformation("[WifiManager] 检测到波动,重置3秒计时...");

            // 重置防抖定时器,3秒后执行实际检测
            // 如果3秒内再次触发事件,计时器将被重新设置
            if (_debounceTimer != null)
            {
                _debounceTimer.Change(3000, Timeout.Infinite);
            }
        }

        // 防抖动作 - 防抖窗口结束后执行的实际检测逻辑
        private void DebounceAction(object state)
        {
            // 获取实际连接状态
            bool actuallyConnected = IsSTAConnected();

            // 状态流转判断
            // 场景1:已连接 -> 断开
            if (CurrentState == WifiState.Connected && !actuallyConnected)
            {
                CurrentState = WifiState.Reconnecting;
                _logger.LogWarning("[WifiManager] 确认断开,触发重连...");
                StartReconnectTask();
            }
            // 场景2:连接中/重连中 -> 已连接
            else if ((CurrentState == WifiState.Reconnecting || CurrentState == WifiState.Connecting) && actuallyConnected)
            {
                CurrentState = WifiState.Connected;
                _logger.LogInformation("[WifiManager] 确认恢复!唤醒上云线程...");
                CloudWakeUpEvent.Set(); // 唤醒等待中的云端通信线程
            }
        }
        #endregion

        #region 安全重连核心逻辑
        // 启动安全重连任务
        // 取消之前的重连任务(如果存在),启动新的重连任务
        private void StartReconnectTask()
        {
            // 取消之前的重连任务(如果正在运行)
            if (_reconnectCts != null)
            {
                _reconnectCts.Cancel();
            }

            // 创建新的取消令牌源
            _reconnectCts = new CancellationTokenSource();

            // 在新线程中启动重连任务(nanoFramework不支持ThreadPool和IsBackground)
            Thread reconnectThread = new Thread(() => AttemptReconnect(_reconnectCts.Token));
            reconnectThread.Start();
        }

        // 执行重连尝试(带指数退避策略)
        private void AttemptReconnect(CancellationToken ct)
        {
            int retryCount = 0;                           // 重试次数
            const int maxRetryDelay = 60000;              // 最大重试延迟(60秒)

            // 循环重连,直到连接成功或被取消
            while (CurrentState == WifiState.Reconnecting)
            {
                // 检查是否被取消
                if (ct.IsCancellationRequested)
                {
                    _logger.LogInformation("[WifiManager] 重连任务已被取消");
                    break;
                }

                // 尝试连接
                if (ConnectWithHelper(_currentSSID, _currentPassword, ct))
                {
                    // 连接成功,更新状态并退出循环
                    CurrentState = WifiState.Connected;
                    _logger.LogInformation("[WifiManager] 重连成功!");
                    CloudWakeUpEvent.Set(); // 唤醒云端通信线程
                    break;
                }

                // 计算指数退避延迟:2^n * 2000ms,最大60秒
                int delay = Math.Min((int)Math.Pow(2, retryCount) * 2000, maxRetryDelay);

                _logger.LogWarning($"[WifiManager] 第 {retryCount + 1} 次失败,等待 {delay / 1000} 秒后重试...");

                // 分段等待,每500ms检查一次取消信号
                for (int elapsed = 0; elapsed < delay; elapsed += 500)
                {
                    if (ct.IsCancellationRequested)
                    {
                        _logger.LogInformation("[WifiManager] 等待期间收到取消信号");
                        return;
                    }
                    Thread.Sleep(500);
                }

                retryCount++;
            }
        }
        #endregion

        #region 底层连接辅助
        // 底层连接辅助函数 使用 nanoFramework 内置的 WifiNetworkHelper 进行连接
        private bool ConnectWithHelper(string ssid, string password, CancellationToken ct)
        {
            try
            {
                // 使用内置助手类连接,禁用日期时间检查(嵌入式设备可能没有RTC)
                bool success = WifiNetworkHelper.ConnectDhcp(
                    ssid, 
                    password, 
                    token: ct                 // 取消令牌
                );

                if (success)
                {
                    // 获取并记录分配的IP地址
                    var ip = NetworkInterface.GetAllNetworkInterfaces()[0].IPv4Address;
                    _logger.LogInformation($"[WifiManager] 连接成功,IP: {ip}");
                }

                return success;
            }
            catch (OperationCanceledException)
            {
                // 连接被取消
                _logger.LogInformation("[WifiManager] 连接操作已取消");
                return false;
            }
            catch (Exception ex)
            {
                // ConnectDhcp可能在物理连接成功后因DNS/NTP检查失败而抛异常
                // 需要检查实际连接状态
                _logger.LogWarning($"[WifiManager] ConnectDhcp异常: {ex.Message}");
                
                // 检查实际连接状态
                if (IsSTAConnected())
                {
                    var ip = NetworkInterface.GetAllNetworkInterfaces()[0].IPv4Address;
                    _logger.LogInformation($"[WifiManager] 实际已连接,IP: {ip}");
                    return true;
                }
                
                return false;
            }
        }

        // 检查STA模式是否已连接(带网络可达性检测)
        // 判断条件:
        // 1. 无线STA接口 + 非零IP + 接口状态正常(基本连接)
        // 2. DNS解析成功(网络可达性,确保NTP/MQTT能正常工作)
        private bool IsSTAConnected()
        {
            try
            {
                string deviceIp = null;
                
                // 步骤1:检查基本连接状态
                foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
                {
                    if (nic.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                    {
                        string ip = nic.IPv4Address.ToString();
                        // nanoFramework中NetworkInterface没有OperationalStatus属性
                        // 仅通过IP地址判断连接状态
                        bool hasValidIP = (ip != "0.0.0.0" && ip != "127.0.0.1");
                        
                        if (hasValidIP)
                        {
                            deviceIp = ip;
                            break;
                        }
                    }
                }

                // 如果基本连接都不满足,直接返回false
                if (string.IsNullOrEmpty(deviceIp))
                {
                    return false;
                }

                // 步骤2:网络可达性检测(DNS解析)
                // 通过解析域名验证网络是否真正可达
                // 这确保后续NTP校时、MQTT等网络操作能正常工作
                if (IsNetworkReachable())
                {
                    _logger.LogInformation($"[WifiManager] STA已连接,IP: {deviceIp},网络可达");
                    return true;
                }
                else
                {
                    _logger.LogWarning($"[WifiManager] STA有IP但网络不可达");
                    return false;
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"[WifiManager] 检查连接状态异常: {ex.Message}");
            }
            return false;
        }

        // 网络可达性检测(通过DNS解析验证)
        // 返回true表示网络可达,可以进行NTP校时、MQTT等操作
        public bool IsNetworkReachable()
        {
            try
            {
                // 尝试解析公共DNS域名
                // 使用多个域名提高可靠性
                string[] testDomains = { "www.baidu.com", "time.windows.com", "pool.ntp.org" , "ntp.ntsc.ac.cn", "ntp.aliyun.com" };
                
                foreach (string domain in testDomains)
                {
                    try
                    {
                        _logger.LogInformation($"[WifiManager] 正在检测网络可达性: {domain}");
                        var hostEntry = System.Net.Dns.GetHostEntry(domain);
                        
                        if (hostEntry != null && hostEntry.AddressList != null && hostEntry.AddressList.Length > 0)
                        {
                            _logger.LogInformation($"[WifiManager] DNS解析成功: {domain} -> {hostEntry.AddressList[0]}");
                            return true;
                        }
                    }
                    catch { /* 继续尝试下一个域名 */ }
                }
                
                _logger.LogWarning("[WifiManager] 所有域名解析失败,网络不可达");
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"[WifiManager] 网络可达性检测异常: {ex.Message}");
                return false;
            }
        }
        #endregion

        #region AP模式(热点模式)
        // 启动AP热点模式(参考官方WifiAP示例实现)
        // 【配置AP必需的7个关键步骤】:
        // 1. 取消STA相关任务和事件(STA/AP互斥)
        // 2. 彻底关闭STA模式(使用Wireless80211Configuration.Options = None | SmartConfig)
        // 3. 查找WirelessAP网络接口
        // 4. 配置静态IP地址
        // 5. 通过SpecificConfigId获取正确的配置槽
        // 6. 设置AP参数(SSID/密码/认证类型/Options)
        // 7. 启动DHCP服务器(关键!否则AP不会广播SSID)
        // 热点密码(WPA2模式下最少8位)
        // AP静态IP地址(默认192.168.4.1)
        public bool StartAP(string ssid, string password, string ipAddress = "192.168.4.1")
        {
            _logger.LogInformation($"[WifiManager] AP准备启动...");

            // ========== 步骤1:互斥保护 - 取消STA相关任务 ==========
            // STA模式与AP模式互斥,必须先清理STA相关资源
            if (_reconnectCts != null) _reconnectCts.Cancel();
            Thread.Sleep(200);           // 等待任务取消完成
            UnregisterNetworkEvents();   // 取消网络状态监听
            CurrentState = WifiState.APMode;  // 更新状态机

            // ========== 步骤2:彻底关闭STA模式(关键!否则射频冲突) ==========
            // 参考官方Wireless80211.Disable()实现
            // 使用 None | SmartConfig 确保STA完全禁用
            DisableSTAMode();
            Thread.Sleep(300); // 等待射频切换完成

            // ========== 步骤3:查找WirelessAP网络接口 ==========
            NetworkInterface apInterface = null;
            foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (nic.NetworkInterfaceType == NetworkInterfaceType.WirelessAP)
                {
                    apInterface = nic;
                    break;
                }
            }
            if (apInterface == null)
            {
                _logger.LogError("[WifiManager] 未找到AP网络接口");
                return false;
            }

            // ========== 步骤4:配置AP静态IP地址 ==========
            // 参数:IP地址、子网掩码、网关(AP自身作为网关)
            apInterface.EnableStaticIPv4(ipAddress, "255.255.255.0", ipAddress);
            _logger.LogInformation($"[WifiManager] AP接口IP配置完成: {ipAddress}");
            
            // 【关键】等待网络接口完全初始化
            // 配置IP后需要等待一段时间,否则后续Socket操作会失败
            Thread.Sleep(1000);

            // ========== 步骤5:获取AP配置对象(关键!使用SpecificConfigId) ==========
            // 【重要】必须通过接口的SpecificConfigId获取配置对象
            // 直接new WirelessAPConfiguration(1)可能修改错误的配置槽
            WirelessAPConfiguration apConfig = null;
            try
            {
                WirelessAPConfiguration[] apConfigs = WirelessAPConfiguration.GetAllWirelessAPConfigurations();
                if (apConfigs != null && apConfigs.Length > 0)
                {
                    uint configIdUInt = apInterface.SpecificConfigId;
                    int configId = (int)configIdUInt;
                    apConfig = (configId >= 0 && configId < apConfigs.Length) ? apConfigs[configId] : apConfigs[0];
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"[WifiManager] 获取AP配置失败: {ex.Message}");
            }
            if (apConfig == null) apConfig = new WirelessAPConfiguration(0);

            // ========== 步骤6:配置AP热点参数 ==========
            apConfig.Ssid = ssid;                          // 热点名称
            apConfig.Password = password;                  // 热点密码(WPA2需≥8位)
            apConfig.Authentication = System.Net.NetworkInformation.AuthenticationType.WPA2;  // 认证类型:WPA2
            apConfig.Encryption = EncryptionType.WPA2_PSK;           // 加密类型:WPA2-PSK
            apConfig.Channel = 6;                          // WIFI信道(信道6兼容性最好)
            apConfig.MaxConnections = 8;                   // 最大连接数
            // 【关键】Options必须同时包含AutoStart和Enable
            // AutoStart: 配置保存后立即启动AP
            // Enable: 启用AP模式
            apConfig.Options = WirelessAPConfiguration.ConfigurationOptions.AutoStart 
                            | WirelessAPConfiguration.ConfigurationOptions.Enable;

            // 保存配置到Flash
            apConfig.SaveConfiguration();
            _logger.LogInformation($"[WifiManager] AP配置已保存,SSID: {ssid}");

            // ========== 步骤7:(关键)启动DHCP服务器 ==========
            // DHCP服务器为连接的设备分配IP地址
            // 同时配置Captive Portal URL、DNS服务器和网关
            try
            {
                _logger.LogInformation("[WifiManager] 正在启动DHCP服务器...");
                _dhcpServer = new DhcpServer
                {
                    CaptivePortalUrl = "http://" + ipAddress,
                    DnsServer = IPAddress.Parse(ipAddress),
                    Gateway = IPAddress.Parse(ipAddress)
                };
                bool dhcpStarted = _dhcpServer.Start(IPAddress.Parse(ipAddress), new IPAddress(new byte[] { 255, 255, 255, 0 }));
                if (dhcpStarted)
                {
                    _logger.LogInformation("[WifiManager] DHCP服务器启动成功");
                }
                else
                {
                    _logger.LogError("[WifiManager] DHCP服务器启动失败!");
                    _logger.LogError("[WifiManager] 可能原因:网络接口未就绪或端口被占用");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"[WifiManager] 启动DHCP服务器异常: {ex.Message}");
                _logger.LogError($"[WifiManager] 异常类型: {ex.GetType().Name}");
            }

            // 等待AP启动(增加等待时间确保射频模块初始化完成)
            Thread.Sleep(2000);

            // 验证AP是否真正启动
            bool apStarted = VerifyAPStarted(apInterface, ipAddress);
            if (!apStarted)
            {
                // nanoFramework.Runtime.Native.Power.RebootDevice();
                _logger.LogWarning("[WifiManager] AP配置已保存,但射频可能未立即启动");
                _logger.LogWarning("[WifiManager] 建议重启设备以确保AP完全生效");
                // 不强制重启,让用户决定是否重启
            }

            _logger.LogInformation($"[WifiManager] AP启动成功,IP: {ipAddress}");
            return true;

            // 验证AP是否真正启动
            bool VerifyAPStarted(NetworkInterface ni, string expectedIP)
            {
                try
                {
                    // 检查IP是否配置正确
                    if (ni.IPv4Address != expectedIP)
                    {
                        _logger.LogWarning($"[WifiManager] AP IP不匹配: 期望 {expectedIP}, 实际 {ni.IPv4Address}");
                        return false;
                    }

                    // 获取当前AP配置检查Options
                    var currentConfigs = WirelessAPConfiguration.GetAllWirelessAPConfigurations();
                    if (currentConfigs != null && currentConfigs.Length > 0)
                    {
                        uint configId = ni.SpecificConfigId;
                        if (configId < currentConfigs.Length)
                        {
                            var currentConfig = currentConfigs[configId];
                            bool hasAutoStart = (currentConfig.Options & WirelessAPConfiguration.ConfigurationOptions.AutoStart) != 0;
                            bool hasEnable = (currentConfig.Options & WirelessAPConfiguration.ConfigurationOptions.Enable) != 0;
                            
                            if (!hasAutoStart || !hasEnable)
                            {
                                _logger.LogWarning("[WifiManager] AP Options未正确设置");
                                return false;
                            }
                        }
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"[WifiManager] 验证AP状态失败: {ex.Message}");
                    return false;
                }
            }
        }

        // 禁用STA模式(参考官方示例Wireless80211.Disable())
        // 使用System.Net.NetworkInformation中的Wireless80211Configuration
        private void DisableSTAMode()
        {
            try
            {
                // 获取STA网络接口
                NetworkInterface staInterface = null;
                foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
                {
                    if (nic.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                    {
                        staInterface = nic;
                        break;
                    }
                }

                if (staInterface != null)
                {
                    // 通过接口的SpecificConfigId获取STA配置(与官方一致)
                    var staConfigs = Wireless80211Configuration.GetAllWireless80211Configurations();
                    if (staConfigs != null && staConfigs.Length > 0)
                    {
                        uint configId = staInterface.SpecificConfigId;
                        Wireless80211Configuration staConfig = staConfigs[configId];
                        
                        // 禁用STA模式(与官方一致:None | SmartConfig)
                        staConfig.Options = Wireless80211Configuration.ConfigurationOptions.None | 
                                           Wireless80211Configuration.ConfigurationOptions.SmartConfig;
                        staConfig.SaveConfiguration();
                        _logger.LogInformation("[WifiManager] STA模式已禁用");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"[WifiManager] 禁用STA模式失败: {ex.Message}");
                // 降级方案:断开连接
                try { _wifiAdapter.Disconnect(); } catch { }
            }
        }

        // 关闭AP热点模式(参考官方示例WirelessAP.Disable())
        public void StopAP()
        {
            // 获取当前AP配置
            var configs = WirelessAPConfiguration.GetAllWirelessAPConfigurations();

            if (configs != null && configs.Length > 0)
            {
                // 禁用AP模式(与官方一致:使用None而非Disable)
                configs[0].Options = WirelessAPConfiguration.ConfigurationOptions.None;
                // 保存配置以应用更改
                configs[0].SaveConfiguration();
            }

            // 更新状态为空闲
            CurrentState = WifiState.Idle;
            _logger.LogInformation("[WifiManager] AP 已关闭");
        }
        #endregion

        #region 断开连接
        // 断开STA连接并安全地取消所有相关任务
        public void DisconnectSTA()
        {
            // 取消重连任务
            if (_reconnectCts != null)
            {
                _reconnectCts.Cancel();
            }

            // 取消注册网络事件
            UnregisterNetworkEvents();

            // 断开WIFI连接
            _wifiAdapter.Disconnect();

            // 更新状态为空闲
            CurrentState = WifiState.Idle;

            _logger.LogInformation("[WifiManager] STA 已断开连接");
        }
        #endregion

        #region WiFi凭证存储(官方方式)

        // 获取无线网络接口(私有方法)
        private NetworkInterface GetWirelessInterface()
        {
            var interfaces = NetworkInterface.GetAllNetworkInterfaces();
            foreach (var iface in interfaces)
            {
                if (iface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                {
                    return iface;
                }
            }
            return null;
        }

        // 保存WiFi凭证到Flash(使用官方Wireless80211Configuration)
        // 官方推荐方式:WiFi配置存储在设备的Configuration分区,重启后固件自动连接
        public bool SaveCredentials()
        {
            try
            {
                // 检查当前是否有凭证
                if (string.IsNullOrEmpty(_currentSSID))
                {
                    _logger.LogWarning("[WifiManager] 没有WiFi凭证可保存");
                    return false;
                }

                // 获取无线网络接口
                var iface = GetWirelessInterface();
                if (iface == null)
                {
                    _logger.LogError("[WifiManager] 未找到无线网络接口");
                    return false;
                }

                // 获取WiFi配置
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();
                var config = configs[iface.SpecificConfigId];

                // 设置WiFi参数
                config.Ssid = _currentSSID;
                config.Password = _currentPassword;
                config.Authentication = System.Net.NetworkInformation.AuthenticationType.WPA2;
                config.Encryption = EncryptionType.WPA2_PSK;
                config.Options = Wireless80211Configuration.ConfigurationOptions.Enable;

                // 保存配置(重启后固件会自动连接)
                config.SaveConfiguration();
                _logger.LogInformation($"[WifiManager] WiFi凭证已保存: {_currentSSID}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"[WifiManager] 保存凭证失败: {ex.Message}");
                return false;
            }
        }

        // 保存指定的WiFi凭证到Flash
        // 参数:ssid - WiFi名称,password - WiFi密码
        public bool SaveCredentials(string ssid, string password)
        {
            // 更新当前凭证
            _currentSSID = ssid;
            _currentPassword = password;

            // 调用无参版本保存
            return SaveCredentials();
        }

        // 从Flash读取WiFi凭证(使用官方Wireless80211Configuration)
        // 返回:true表示读取成功,false表示读取失败或无配置
        public bool LoadCredentials(out string ssid, out string password)
        {
            ssid = null;
            password = null;

            try
            {
                // 获取所有WiFi配置
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();

                foreach (var config in configs)
                {
                    // 查找第一个有效的配置
                    if (!string.IsNullOrEmpty(config.Ssid))
                    {
                        ssid = config.Ssid;
                        password = config.Password;

                        // 更新当前凭证
                        _currentSSID = ssid;
                        _currentPassword = password;

                        _logger.LogInformation($"[WifiManager] 读取凭证成功: {ssid}");
                        return true;
                    }
                }

                _logger.LogInformation("[WifiManager] 未找到已保存的WiFi配置");
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError($"[WifiManager] 读取凭证失败: {ex.Message}");
                return false;
            }
        }

        // 检查是否有已保存的WiFi配置
        public bool HasSavedConfig()
        {
            try
            {
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();
                foreach (var config in configs)
                {
                    if (!string.IsNullOrEmpty(config.Ssid))
                    {
                        return true;
                    }
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        // 清除保存的WiFi配置
        public bool ClearCredentials()
        {
            try
            {
                // 获取无线网络接口
                var iface = GetWirelessInterface();
                if (iface == null)
                {
                    _logger.LogError("[WifiManager] 未找到无线网络接口");
                    return false;
                }

                // 获取WiFi配置并清除
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();
                var config = configs[iface.SpecificConfigId];

                config.Ssid = "";
                config.Password = "";
                config.Options = Wireless80211Configuration.ConfigurationOptions.None;
                config.SaveConfiguration();

                // 清空内存中的凭证
                _currentSSID = null;
                _currentPassword = null;

                _logger.LogInformation("[WifiManager] WiFi凭证已清除");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"[WifiManager] 清除凭证失败: {ex.Message}");
                return false;
            }
        }

        #endregion
    }
}

Managers/APConfigManager.cs

using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.WebServer;
using System;
using System.Net;
using System.Threading;

namespace YeFanIoTTest.Managers
{
    // AP配网状态枚举
    public enum APConfigState
    {
        Idle,               // 空闲
        APConfiguring,      // 正在配网
        Connecting,         // 正在连接
        Configured,         // 配网成功
        Failed              // 配网失败
    }

    // 配网完成事件参数
    public class APConfigResultEventArgs : EventArgs
    {
        public bool Success { get; set; }
        public string SSID { get; set; }
        public string Message { get; set; }
    }

    internal class APConfigManager
    {
        // 日志记录器
        private ILogger _logger;

        // WIFI管理器(外部注入,不内部创建)
        private WifiManager _wifiManager;

        // Web服务器实例
        private WebServer _webServer;

        // 当前配网状态
        public APConfigState CurrentState { get; private set; } = APConfigState.Idle;

        // 配网完成事件(通知外部配网结果)
        public event APConfigResultEventHandler OnConfigCompleted;
        public delegate void APConfigResultEventHandler(object sender, APConfigResultEventArgs e);

        // AP配网参数
        private const string AP_SSID = "YF3300_ESP32S3";    // 热点名称
        private const string AP_PASSWORD = "yf123456";      // 热点密码(WPA2需>=8)
        private const string AP_IP = "192.168.4.1";         // AP网关IP

        // Web服务器参数
        private const int WEB_PORT = 80;                    // Web服务器端口
        private const int CONFIG_TIMEOUT = 600000;          // 配网超时时间(10分钟)

        // 配网任务取消令牌
        private CancellationTokenSource _configCts;

        // 配网超时定时器
        private Timer _timeoutTimer;

        // 当前配网的SSID和密码(用于传递给WebServerController)
        public static string PendingSSID { get; set; }
        public static string PendingPassword { get; set; }
        public static bool ConfigSubmitted { get; set; } = false;
        
        // WifiManager实例(供WebServerController访问)
        private static WifiManager _staticWifiManager;
        
        // 获取WifiManager实例
        public static WifiManager GetWifiManager()
        {
            return _staticWifiManager;
        }
        
        // 触发配网完成事件(由外部调用)
        public void RaiseConfigCompleted(bool success, string ssid, string message)
        {
            if (OnConfigCompleted != null)
            {
                OnConfigCompleted(this, new APConfigResultEventArgs()
                {
                    Success = success,
                    SSID = ssid,
                    Message = message
                });
            }
        }

        // 构造函数:注入WIFI管理器实例
        public APConfigManager(WifiManager wifiManager)
        {
            if (wifiManager == null)
            {
                throw new ArgumentNullException(nameof(wifiManager));
            }
            _wifiManager = wifiManager;
            _staticWifiManager = wifiManager; // 保存到静态字段
            _logger = LogDispatcher.LoggerFactory.CreateLogger("APConfigManager");
        }

        #region 公共API

        // 检查是否已有保存的WIFI配置
        public bool HasSavedWifiConfig()
        {
            return _wifiManager.HasSavedConfig();
        }

        // 尝试使用已保存的配置连接WIFI
        public bool TryConnectSavedWifiConfig()
        {
            if (_wifiManager.LoadCredentials(out string ssid, out string password))
            {
                _logger.LogInformation($"[APConfigManager] 尝试使用已保存的WIFI配置进行连接: {ssid}");
                return _wifiManager.ConnectSTA(ssid, password, enableReconnect: true);
            }
            return false;
        }

        // 开始配网
        public bool StartAPConfig()
        {
            if (CurrentState != APConfigState.Idle)
            {
                _logger.LogWarning("[APConfigManager] 当前AP状态不是空闲状态,无法开始配网");
                return false;
            }

            _logger.LogInformation("[APConfigManager] 开始配网");

            // 步骤一:启动AP热点
            bool apStarted = _wifiManager.StartAP(AP_SSID, AP_PASSWORD, AP_IP);
            if (!apStarted)
            {
                _logger.LogError("[APConfigManager] 启动AP热点失败");
                CurrentState = APConfigState.Failed;
                return false;
            }

            CurrentState = APConfigState.APConfiguring;
            _logger.LogInformation($"[APConfigManager] AP热点已启动,热点名称:{AP_SSID}, IP:{AP_IP}");

            // 步骤二:启动Web服务器
            // 【重要】等待网络接口完全初始化后再启动Web服务器
            Thread.Sleep(1000);
            try
            {
                // 重置配网提交标志
                ConfigSubmitted = false;

                // 创建Web服务器实例
                // 【重要】在AP模式下,必须显式指定绑定的IP地址,否则会绑定到错误的网络接口
                _webServer = new WebServer(WEB_PORT, HttpProtocol.Http, IPAddress.Parse(AP_IP), new Type[] { typeof(WebServerController) });
                _webServer.Start();
                _logger.LogInformation($"[APConfigManager] Web服务器已启动,端口:{WEB_PORT},绑定IP:{AP_IP}");
                
                // ========== 诊断信息:验证网络接口状态 ==========
                try
                {
                    var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
                    foreach (var ni in interfaces)
                    {
                        if (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.WirelessAP)
                        {
                            _logger.LogInformation($"[APConfigManager] AP接口状态: {ni.IPv4Address}");
                            _logger.LogInformation($"[APConfigManager] AP接口类型: {ni.NetworkInterfaceType}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"[APConfigManager] 获取网络接口信息失败: {ex.Message}");
                }
            }
            catch (Exception e)
            {
                _logger.LogError($"[APConfigManager] 启动Web服务器失败: {e.Message}");
                StopAPConfig();
                return false;
            }

            // 步骤三:启动配网超时定时器
            _configCts = new CancellationTokenSource();
            _timeoutTimer = new Timer(OnTimeoutCallback, null, CONFIG_TIMEOUT, Timeout.Infinite);

            _logger.LogInformation($"[APConfigManager] 请连接WIFI {AP_SSID},并访问 http://{AP_IP}");
            _logger.LogInformation($"[APConfigManager] 配网超时定时器已启动,超时时间:{CONFIG_TIMEOUT / 1000} 秒");

            return true;
        }

        // 停止AP配网(清理资源)
        public void StopAPConfig()
        {
            _logger.LogInformation("[APConfigManager] 停止AP配网");

            // 停止超时定时器
            if (_timeoutTimer != null)
            {
                _timeoutTimer.Dispose();
                _timeoutTimer = null;
            }

            // 取消配网任务
            if (_configCts != null)
            {
                _configCts.Cancel();
                _configCts.Dispose();
                _configCts = null;
            }

            // 关闭Web服务器
            if (_webServer != null)
            {
                _webServer.Stop();
                _webServer.Dispose();
                _webServer = null;
            }

            // 关闭AP热点
            _wifiManager.StopAP();

            CurrentState = APConfigState.Idle;
            _logger.LogInformation("[APConfigManager] AP配网已停止");
        }

        // 处理配网提交(由WebServerController调用)
        public void HandleConfigSubmit(string ssid, string password)
        {
            _logger.LogInformation($"[APConfigManager] 收到配网请求:{ssid}");

            // 停止超时定时器
            if (_timeoutTimer != null)
            {
                _timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }

            // 停止Web服务器
            if (_webServer != null)
            {
                _webServer.Stop();
            }

            CurrentState = APConfigState.Connecting;

            // 尝试连接WIFI
            bool connected = _wifiManager.ConnectSTA(ssid, password, enableReconnect: true);

            if (connected)
            {
                // ========== 验证网络可达性 ==========
                // 检查WiFi是否真的可以上网(通过DNS解析验证)
                _logger.LogInformation("[APConfigManager] 正在验证网络可达性...");
                bool networkReachable = _wifiManager.IsNetworkReachable();
                
                if (!networkReachable)
                {
                    _logger.LogWarning("[APConfigManager] WiFi已连接但网络不可达,请检查路由器是否连接外网");
                    // 仍然保存凭证,但提示用户网络可能不可用
                }
                else
                {
                    _logger.LogInformation("[APConfigManager] 网络可达性验证成功,可以正常上网");
                }

                // ========== 保存WIFI凭证到Flash ==========
                // 【测试时可注释以下2行,关闭持久化存储功能】
                // _wifiManager.SaveCredentials(ssid, password);
                _logger.LogInformation($"[APConfigManager] 已保存WIFI凭证:{ssid}");

                CurrentState = APConfigState.Configured;
                _logger.LogInformation($"[APConfigManager] 配网成功!SSID:{ssid}");

                // 触发配网完成事件
                if (OnConfigCompleted != null)
                {
                    OnConfigCompleted(this, new APConfigResultEventArgs()
                    {
                        Success = true,
                        SSID = ssid,
                        Message = networkReachable ? "配网成功!网络可达" : "配网成功!但网络不可达,请检查路由器"
                    });
                }
            }
            else
            {
                CurrentState = APConfigState.Failed;
                _logger.LogError($"[APConfigManager] 配网失败!SSID:{ssid}");

                // 触发配网失败事件
                if (OnConfigCompleted != null)
                {
                    OnConfigCompleted(this, new APConfigResultEventArgs()
                    {
                        Success = false,
                        SSID = ssid,
                        Message = "配网失败!"
                    });
                }

                // 重新启动AP配网
                Thread.Sleep(2000);
                CurrentState = APConfigState.Idle;
                StartAPConfig();
            }
        }

        #endregion

        #region 私有方法

        // 超时回调
        private void OnTimeoutCallback(object state)
        {
            _logger.LogWarning("[APConfigManager] 配网超时!");

            // 触发配网超时事件
            if (OnConfigCompleted != null)
            {
                OnConfigCompleted(this, new APConfigResultEventArgs()
                {
                    Success = false,
                    SSID = null,
                    Message = "配网超时!"
                });
            }

            // 停止AP配网
            StopAPConfig();
        }

        #endregion
    }
}

Managers/WebServer.cs

using nanoFramework.WebServer;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace YeFanIoTTest.Managers
{
    // Web服务器控制器
    // 处理AP配网的HTTP请求
    // 【重要】必须是public类,否则WebServer无法实例化
    public class WebServerController
    {
        // 静态APConfigManager实例引用
        private static APConfigManager _apConfigManager;
        
        // 设置APConfigManager实例(internal,因为APConfigManager是internal)
        internal static void SetAPConfigManager(APConfigManager apConfigManager)
        {
            _apConfigManager = apConfigManager;
        }

        // ========== 配网页面路由 ==========

        // GET / - 配网主页
        [Route("")]
        [Method("GET")]
        public void GetIndex(WebServerEventArgs e)
        {
            Debug.WriteLine("[WebServer] ========== Received GET request ==========");
            Debug.WriteLine($"[WebServer] Remote endpoint: {e.Context.Request.RemoteEndPoint}");
            Debug.WriteLine($"[WebServer] Request URL: {e.Context.Request.RawUrl}");
            
            // 直接输出HTML,不进行任何检查
            string html = GetConfigPageHtml();
            e.Context.Response.ContentType = "text/html";
            WebServer.OutputAsStream(e.Context.Response, html);
            
            Debug.WriteLine("[WebServer] Response sent successfully");
        }

        // POST /config - 处理配网表单提交
        [Route("config")]
        [Method("POST")]
        public void PostConfig(WebServerEventArgs e)
        {
            try
            {
                // 解析表单数据
                string body = GetRequestBody(e);

                // 解析SSID和密码
                string ssid = ParseFormField(body, "ssid");
                string password = ParseFormField(body, "password");

                if (string.IsNullOrEmpty(ssid) || string.IsNullOrEmpty(password))
                {
                    string errorHtml = GetErrorPageHtml("SSID和密码不能为空!");
                    e.Context.Response.ContentType = "text/html";
                    WebServer.OutputAsStream(e.Context.Response, errorHtml);
                    return;
                }

                // ========== 调用APConfigManager处理配网 ==========
                if (_apConfigManager == null)
                {
                    string errorHtml = GetErrorPageHtml("系统错误:APConfigManager未初始化");
                    e.Context.Response.ContentType = "text/html";
                    WebServer.OutputAsStream(e.Context.Response, errorHtml);
                    return;
                }
                
                // 调用HandleConfigSubmit方法处理配网
                // 该方法会:连接WiFi、检查网络可达性、触发OnConfigCompleted事件
                _apConfigManager.HandleConfigSubmit(ssid, password);
                
                // 根据配网结果显示页面
                // 注意:HandleConfigSubmit已经处理了所有逻辑,这里只需要显示结果页面
                // 由于HandleConfigSubmit是同步的,我们可以直接检查CurrentState
                
                // 等待配网完成(最多等待10秒)
                int waitCount = 0;
                while (_apConfigManager.CurrentState == APConfigState.Connecting && waitCount < 100)
                {
                    System.Threading.Thread.Sleep(100);
                    waitCount++;
                }
                
                // 根据配网结果显示页面
                if (_apConfigManager.CurrentState == APConfigState.Configured)
                {
                    // 配网成功
                    string successHtml = GetSuccessPageHtml(ssid);
                    e.Context.Response.ContentType = "text/html";
                    WebServer.OutputAsStream(e.Context.Response, successHtml);
                }
                else if (_apConfigManager.CurrentState == APConfigState.Failed)
                {
                    // 配网失败
                    string errorHtml = GetErrorPageHtml("WiFi连接失败,请检查SSID和密码是否正确");
                    e.Context.Response.ContentType = "text/html";
                    WebServer.OutputAsStream(e.Context.Response, errorHtml);
                }
                else
                {
                    // 其他状态(超时等)
                    string errorHtml = GetErrorPageHtml("配网超时,请重试");
                    e.Context.Response.ContentType = "text/html";
                    WebServer.OutputAsStream(e.Context.Response, errorHtml);
                }
            }
            catch (Exception ex)
            {
                string errorHtml = GetErrorPageHtml("配网失败:" + ex.Message);
                e.Context.Response.ContentType = "text/html";
                WebServer.OutputAsStream(e.Context.Response, errorHtml);
            }
        }

        // GET /scan - 扫描WiFi网络(AP模式下不支持)
        [Route("scan")]
        [Method("GET")]
        public void GetScan(WebServerEventArgs e)
        {
            // AP模式下无法扫描WiFi网络
            string jsonResult = "{\"success\":false,\"message\":\"AP模式下不支持WiFi扫描,请手动输入WiFi名称\"}";
            e.Context.Response.ContentType = "application/json";
            WebServer.OutputAsStream(e.Context.Response, jsonResult);
        }

        // ========== 辅助方法 ==========

        // 获取请求体
        private string GetRequestBody(WebServerEventArgs e)
        {
            try
            {
                var request = e.Context.Request;
                
                // 获取请求体长度
                long contentLength = request.ContentLength64;
                if (contentLength == 0)
                {
                    return string.Empty;
                }

                // 读取请求体
                byte[] buffer = new byte[(int)contentLength];
                request.InputStream.Read(buffer, 0, (int)contentLength);
                return Encoding.UTF8.GetString(buffer, 0, (int)contentLength);
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        // 解析表单字段
        private static string ParseFormField(string body, string fieldName)
        {
            try
            {
                // 格式: ssid=MyWiFi&password=12345678
                string searchPattern = fieldName + "=";
                int startIndex = body.IndexOf(searchPattern);
                if (startIndex < 0)
                {
                    return string.Empty;
                }

                startIndex += searchPattern.Length;
                int endIndex = body.IndexOf('&', startIndex);
                if (endIndex < 0)
                {
                    endIndex = body.Length;
                }

                string value = body.Substring(startIndex, endIndex - startIndex);

                // URL解码
                return UrlDecode(value);
            }
            catch
            {
                return string.Empty;
            }
        }

        // URL解码
        private static string UrlDecode(string value)
        {
            // 处理 %XX 格式的编码和 + 号
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < value.Length; i++)
            {
                if (value[i] == '+')
                {
                    // + 号转换为空格
                    result.Append(' ');
                }
                else if (value[i] == '%' && i + 2 < value.Length)
                {
                    // 解析十六进制值
                    string hex = value.Substring(i + 1, 2);
                    byte b = HexToByte(hex);
                    if (b > 0)
                    {
                        result.Append((char)b);
                        i += 2;
                    }
                    else
                    {
                        result.Append(value[i]);
                    }
                }
                else
                {
                    result.Append(value[i]);
                }
            }
            return result.ToString();
        }

        // 十六进制字符串转字节
        private static byte HexToByte(string hex)
        {
            try
            {
                byte high = HexCharToValue(hex[0]);
                byte low = HexCharToValue(hex[1]);
                return (byte)((high << 4) | low);
            }
            catch
            {
                return 0;
            }
        }

        // 十六进制字符转数值
        private static byte HexCharToValue(char c)
        {
            if (c >= '0' && c <= '9')
            {
                return (byte)(c - '0');
            }
            else if (c >= 'A' && c <= 'F')
            {
                return (byte)(c - 'A' + 10);
            }
            else if (c >= 'a' && c <= 'f')
            {
                return (byte)(c - 'a' + 10);
            }
            return 0;
        }

        // ========== HTML页面模板 ==========

        // 配网主页HTML
        private static string GetConfigPageHtml()
        {
            string html = "<!DOCTYPE html>";
            html += "<html><head>";
            html += "<meta charset=\"UTF-8\">";
            html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
            html += "<title>YF3300-ESP32S3 WiFi配网</title>";
            html += "<style>";
            html += "body{font-family:Arial;margin:20px;background:#f0f0f0;}";
            html += ".container{max-width:400px;margin:0 auto;background:white;padding:20px;border-radius:10px;}";
            html += "h1{color:#333;text-align:center;}";
            html += ".form-group{margin-bottom:15px;}";
            html += "label{display:block;margin-bottom:5px;color:#666;}";
            html += "input{width:100%;padding:10px;border:1px solid #ddd;border-radius:5px;box-sizing:border-box;}";
            html += "button{width:100%;padding:12px;background:#007bff;color:white;border:none;border-radius:5px;font-size:16px;cursor:pointer;}";
            html += ".footer{text-align:center;margin-top:20px;color:#999;font-size:12px;}";
            html += ".tip{background:#fff3cd;border:1px solid #ffc107;padding:10px;border-radius:5px;margin-bottom:15px;font-size:14px;}";
            html += "</style></head><body>";
            html += "<div class=\"container\">";
            html += "<h1>YF3300-ESP32S3 WiFi配网</h1>";
            html += "<div class=\"tip\">请输入您要连接的WiFi名称和密码</div>";
            html += "<form action=\"/config\" method=\"POST\">";
            html += "<div class=\"form-group\">";
            html += "<label>WiFi名称 (SSID)</label>";
            html += "<input type=\"text\" name=\"ssid\" placeholder=\"请输入WiFi名称\" required>";
            html += "</div>";
            html += "<div class=\"form-group\">";
            html += "<label>WiFi密码</label>";
            html += "<input type=\"password\" name=\"password\" placeholder=\"请输入WiFi密码\" required>";
            html += "</div>";
            html += "<button type=\"submit\">开始配网</button>";
            html += "</form>";
            html += "<div class=\"footer\">YF3300-ESP32S3 v1.0</div>";
            html += "</div></body></html>";
            return html;
        }

        // 成功页面HTML
        private static string GetSuccessPageHtml(string ssid)
        {
            string html = "<!DOCTYPE html>";
            html += "<html><head>";
            html += "<meta charset=\"UTF-8\">";
            html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
            html += "<title>配网成功</title>";
            html += "<style>";
            html += "body{font-family:Arial;margin:20px;background:#f0f0f0;}";
            html += ".container{max-width:400px;margin:0 auto;background:white;padding:20px;border-radius:10px;text-align:center;}";
            html += "h1{color:#28a745;}";
            html += ".info{margin:20px 0;padding:15px;background:#f8f9fa;border-radius:5px;}";
            html += "</style></head><body>";
            html += "<div class=\"container\">";
            html += "<h1>配网成功!</h1>";
            html += "<div class=\"info\">";
            html += "<p>WiFi: <strong>" + ssid + "</strong></p>";
            html += "<p>网络连接正常,可以访问互联网</p>";
            html += "</div>";
            html += "<p>设备已成功同步时间</p>";
            html += "</div></body></html>";
            return html;
        }

        // 错误页面HTML
        private static string GetErrorPageHtml(string message)
        {
            string html = "<!DOCTYPE html>";
            html += "<html><head>";
            html += "<meta charset=\"UTF-8\">";
            html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
            html += "<title>配网失败</title>";
            html += "<style>";
            html += "body{font-family:Arial;margin:20px;background:#f0f0f0;}";
            html += ".container{max-width:400px;margin:0 auto;background:white;padding:20px;border-radius:10px;text-align:center;}";
            html += "h1{color:#dc3545;}";
            html += ".error{margin:20px 0;padding:15px;background:#f8d7da;border-radius:5px;color:#721c24;}";
            html += "a{display:inline-block;margin-top:20px;padding:10px 20px;background:#007bff;color:white;text-decoration:none;border-radius:5px;}";
            html += "</style></head><body>";
            html += "<div class=\"container\">";
            html += "<h1>配网失败</h1>";
            html += "<div class=\"error\">";
            html += "<p>" + message + "</p>";
            html += "</div>";
            html += "<a href=\"/\">重新配网</a>";
            html += "</div></body></html>";
            return html;
        }

        // 警告页面HTML(WiFi连接成功但网络不可达)
        private static string GetWarningPageHtml(string ssid, string message)
        {
            string html = "<!DOCTYPE html>";
            html += "<html><head>";
            html += "<meta charset=\"UTF-8\">";
            html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
            html += "<title>配网警告</title>";
            html += "<style>";
            html += "body{font-family:Arial;margin:20px;background:#f0f0f0;}";
            html += ".container{max-width:400px;margin:0 auto;background:white;padding:20px;border-radius:10px;text-align:center;}";
            html += "h1{color:#ffc107;}";
            html += ".warning{margin:20px 0;padding:15px;background:#fff3cd;border-radius:5px;color:#856404;}";
            html += ".info{margin:10px 0;color:#666;}";
            html += "</style></head><body>";
            html += "<div class=\"container\">";
            html += "<h1>⚠️ 配网警告</h1>";
            html += "<div class=\"info\">";
            html += "<p>已连接WiFi: " + ssid + "</p>";
            html += "</div>";
            html += "<div class=\"warning\">";
            html += "<p>" + message + "</p>";
            html += "</div>";
            html += "<p style=\"color:#999;font-size:12px;\">设备将自动重启...</p>";
            html += "</div></body></html>";
            return html;
        }
    }
}

Managers/NtpTimeManager.cs

using System;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.Networking;

namespace YeFanIoTTest.Managers
{
    // NTP时间同步管理器
    // 负责与NTP服务器同步系统时间
    internal class NtpTimeManager
    {
        private readonly ILogger _logger;
        private bool _isSynced = false;

        // NTP服务器列表(按优先级排序)
        private static readonly string[] NtpServers = 
        {
            "cn.pool.ntp.org",      // 中国NTP池
            "ntp.aliyun.com",       // 阿里云NTP(推荐)
            "ntp.ntsc.ac.cn",       // 国家授时中心(推荐)
            "time.windows.com",     // Windows时间服务器
            "pool.ntp.org",         // NTP池
        };
        
        private int _currentServerIndex = 0;  // 当前使用的服务器索引

        // 属性:是否已同步
        public bool IsSynced => _isSynced;

        // 构造函数
        public NtpTimeManager()
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("NtpTimeManager");
        }

        // 初始化并启动NTP客户端
        public bool Initialize()
        {
            try
            {
                _logger.LogInformation("Initializing NTP time manager");

                // 使用第一个NTP服务器
                ConfigureNtpServer(_currentServerIndex);

                // 启动SNTP客户端
                Sntp.Start();

                _logger.LogInformation("SNTP client started");

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to initialize NTP manager: {ex.Message}");
                return false;
            }
        }
        
        // 配置NTP服务器
        private void ConfigureNtpServer(int index)
        {
            if (index < NtpServers.Length)
            {
                // 使用局部变量存储服务器地址
                string server1 = NtpServers[index];
                string server2 = (index + 1 < NtpServers.Length) ? NtpServers[index + 1] : NtpServers[0];
                
                // 设置服务器
                Sntp.Server1 = server1;
                Sntp.Server2 = server2;
                
                // 使用变量记录日志,避免读取可能为null的属性
                _logger.LogInformation($"NTP servers configured: {server1}, {server2}");
            }
        }

        // 立即同步时间(带重试机制)
        public bool SyncNow()
        {
            int maxRetries = NtpServers.Length;  // 最多尝试所有服务器
            
            for (int retry = 0; retry < maxRetries; retry++)
            {
                try
                {
                    _logger.LogInformation($"Requesting time sync (attempt {retry + 1}/{maxRetries})");
                    _logger.LogInformation($"Using NTP server: {NtpServers[_currentServerIndex]}");

                    // 执行立即同步
                    Sntp.UpdateNow();

                    // 检查时间是否合理(大于2020年)
                    var now = DateTime.UtcNow;
                    if (now.Year > 2020)
                    {
                        _isSynced = true;
                        _logger.LogInformation($"Time synchronized successfully: {now.ToString()}");
                        return true;
                    }
                    else
                    {
                        _logger.LogWarning($"Time sync failed, invalid time: {now.ToString()}");
                        
                        // 尝试下一个服务器
                        if (retry < maxRetries - 1)
                        {
                            _logger.LogInformation("Trying next NTP server...");
                            _currentServerIndex = (_currentServerIndex + 1) % NtpServers.Length;
                            
                            // 重新配置NTP服务器
                            ConfigureNtpServer(_currentServerIndex);
                            
                            // 等待1秒再重试
                            System.Threading.Thread.Sleep(1000);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Failed to sync time: {ex.Message}");
                    
                    // 尝试下一个服务器
                    if (retry < maxRetries - 1)
                    {
                        _logger.LogInformation("Trying next NTP server...");
                        _currentServerIndex = (_currentServerIndex + 1) % NtpServers.Length;
                        
                        // 重新配置NTP服务器
                        ConfigureNtpServer(_currentServerIndex);
                        
                        // 等待1秒再重试
                        System.Threading.Thread.Sleep(1000);
                    }
                }
            }
            
            _logger.LogError("All NTP servers failed");
            return false;
        }

        // 获取当前UTC时间
        public DateTime GetCurrentTime()
        {
            return DateTime.UtcNow;
        }

        // 获取当前本地时间(UTC+8)
        public DateTime GetLocalTime()
        {
            return DateTime.UtcNow.AddHours(8);
        }

        // 停止NTP客户端
        public void Stop()
        {
            try
            {
                if (Sntp.IsStarted)
                {
                    Sntp.Stop();
                    _logger.LogInformation("SNTP client stopped");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to stop SNTP client: {ex.Message}");
            }
        }

        // 检查SNTP客户端状态
        public bool IsRunning()
        {
            return Sntp.IsStarted;
        }
    }
}

Managers/ConfigurationManager.cs

using System;
using System.Net.NetworkInformation;
using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using YeFanIoTTest.Models;

namespace YeFanIoTTest.Managers
{
    // 配置管理器 - 负责设备配置和WiFi配置的加载、保存和持久化
    public class ConfigurationManager
    {
        // 日志记录器
        private readonly ILogger _logger;

        // 内存中的设备配置(临时存储)
        private DeviceConfig _deviceConfig;

        // 构造函数
        public ConfigurationManager()
        {
            _logger = LogDispatcher.LoggerFactory.CreateLogger("ConfigurationManager");
        }

        // 加载设备配置(从内存)
        public bool LoadDeviceConfig(out DeviceConfig config)
        {
            config = _deviceConfig ?? new DeviceConfig();
            
            if (_deviceConfig != null && _deviceConfig.IsValid())
            {
                _logger.LogInformation($"Device config loaded successfully: {_deviceConfig.DeviceID}");
                return true;
            }
            
            _logger.LogWarning("Device config not found in memory");
            return false;
        }

        // 保存设备配置(到内存)
        public bool SaveDeviceConfig(DeviceConfig config)
        {
            try
            {
                _deviceConfig = config;
                _logger.LogInformation("Device config saved to memory successfully");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to save device config: {ex.Message}");
                return false;
            }
        }

        // 加载WiFi配置(使用官方Wireless80211Configuration)
        public bool LoadWifiConfig(out WifiConfig config)
        {
            config = new WifiConfig();
            try
            {
                // 获取所有WiFi配置
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();

                foreach (var cfg in configs)
                {
                    // 查找第一个有效的配置
                    if (!string.IsNullOrEmpty(cfg.Ssid))
                    {
                        config.SSID = cfg.Ssid;
                        config.Password = cfg.Password;

                        _logger.LogInformation($"WiFi config loaded successfully: {cfg.Ssid}");
                        return config.IsValid();
                    }
                }

                _logger.LogWarning("WiFi config not found");
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to load WiFi config: {ex.Message}");
                return false;
            }
        }

        // 保存WiFi配置(使用官方Wireless80211Configuration)
        public bool SaveWifiConfig(WifiConfig config)
        {
            try
            {
                // 获取所有WiFi配置
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();
                
                // 使用第一个配置槽位
                var cfg = configs[0];
                
                // 设置WiFi参数
                cfg.Ssid = config.SSID;
                cfg.Password = config.Password;
                cfg.Authentication = AuthenticationType.WPA2;
                cfg.Encryption = EncryptionType.WPA2_PSK;
                cfg.Options = Wireless80211Configuration.ConfigurationOptions.Enable;

                // 保存配置(重启后固件会自动连接)
                cfg.SaveConfiguration();
                
                _logger.LogInformation($"WiFi config saved successfully: {config.SSID}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to save WiFi config: {ex.Message}");
                return false;
            }
        }

        // 检查设备配置是否存在
        public bool HasDeviceConfig()
        {
            return _deviceConfig != null && _deviceConfig.IsValid();
        }

        // 检查WiFi配置是否存在
        public bool HasWifiConfig()
        {
            try
            {
                var configs = Wireless80211Configuration.GetAllWireless80211Configurations();
                foreach (var cfg in configs)
                {
                    if (!string.IsNullOrEmpty(cfg.Ssid))
                    {
                        return true;
                    }
                }
                return false;
            }
            catch
            {
                return false;
            }
        }
    }
}

Properties/AssemblyInfo.cs

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CSharp.BlankApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharp.BlankApplication")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]