【YFIOs】用C#开发硬件之设备上云

概述

将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字节
}

4. SHT30 温湿度传感器 (Drivers/Sht30Sensor.cs)

纯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")]
posted on 2026-06-16 09:20  刘洪峰AIoT  阅读(0)  评论(0)    收藏  举报