将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);
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐