用 .NET 做一个跨平台的 Improv Wi-Fi 蓝牙配网项目

用一套协议层,同时跑通 Windows 和 Linux 的 BLE 配网流程,使用.NET 轻松实现嵌入式设备的首次联网引导。通过 NuGet 轻松集成到自己的项目里,支持 AOT 场景。

1. 前言

做嵌入式设备或者物联网设备时,配网几乎是绕不过去的一步。设备出厂后,用户总得先把它接到 Wi-Fi 上,后面的远程控制、设备发现、网页配置,才谈得上真正可用。

Improv Wi-Fi 正是为这件事准备的一套开放协议。它定义了设备如何通过 Bluetooth LESerial 接收 Wi-Fi 凭据,并在联网成功后返回一个 URL,让客户端继续完成后续配置。这套协议的边界很清晰,所以特别适合做"首次接入"这件事。当然,如果你的设备有经常移动更换网络的需求,那么这个配网方案也完全可以被复用。

我前面其实已经写过很多篇和 Improv 相关的文章,主要是介绍的使用.NET NanoFramework 在 ESP32 上实现设备端配网。

不过它们更多是围绕具体硬件项目展开。这一次我想单独聊聊我整理出来的这个 .NET 版本实现:Sang.ImprovWifi。它的目标不是只做一个能跑的示例,而是把协议层和平台层拆开,让 WindowsLinux 共用同一套核心逻辑。

这篇文章也会把重点放在"怎么用"上,主要回答两个问题:

  • 如果你只是想快速验证 Improv Wi-Fi 流程,怎么直接跑仓库里的 Demo
  • 如果你已经有自己的 .NET 项目,怎么通过 NuGet 包接入,尤其是在 Linux 场景下使用 ImprovWifi.Transport.Linux.Aot

官方资料放在这里,感兴趣的话可以先看一眼:

2. 这个项目解决了什么问题

如果只是临时做一个蓝牙配网服务,自己定义几个 Characteristic,约定一套数据格式,通常也能很快跑起来。

但这种方式有一个很明显的问题:协议、平台 API 和业务逻辑很容易写死在一起。等你后面想换平台、换蓝牙库,或者只是想把协议逻辑单独拿出来复用时,成本就会一下子高起来。

我做 Sang.ImprovWifi 时,最在意的就是不要把这几层揉成一团。所以现在这个仓库的设计目标很明确:

  • 用纯协议层处理 Improv 的编解码、状态与事件
  • 用平台宿主层分别对接 WindowsLinux 的 BLE 实现
  • 用最小 Demo 把整条配网链路跑通,方便联调和二次开发

目前它已经可以完成下面这些事情:

  • 接收并解析 Improv 的 Wi-Fi 配网命令
  • 处理 Identify、授权状态、错误状态和能力值读取
  • 在配网成功后生成并发送 URL 类型的 RPC Result
  • Windows 上作为真实的 BLE 外设运行
  • Linux / BlueZ 上作为真实的 BLE 外设运行

如果你正在做的是设备端配网、主机侧配网服务,或者只是想在 .NET 下完整跑一遍 Improv Wi-Fi 协议流程,这个仓库基本已经能拿来直接用。

3. 仓库里都有什么

当前仓库主要包含下面几个项目:

  • ImprovWifi.Protocol
  • ImprovWifi.Transport.Windows
  • ImprovWifi.Transport.Linux
  • ImprovWifi.Transport.Linux.Aot
  • ImprovWifi.Demo.Windows
  • ImprovWifi.Demo.Linux
  • ImprovWifi.Demo.Linux.Aot

其中最核心的是 ImprovWifi.Protocol。这一层不依赖 WindowsLinux 的蓝牙 API,里面主要包含:

  • ImprovService
  • ImprovPacketCodec
  • ImprovTransportServer
  • ImprovTransportOptions
  • 一组和协议状态相关的事件类型

平台层要做的事情,其实就是把各自的 BLE 读写动作接到协议层上。比如把客户端写入 RpcCommand 的数据交给 ImprovService,再把它生成的状态和结果同步到对应特征值中。

也正因为这样,WindowsLinux 的差异被限制在了宿主层,协议层本身不需要跟着平台变化一起改。

4. 最快的使用方式:直接跑仓库 Demo

如果你现在只是想验证整个配网流程,我建议先从仓库里的 Demo 开始。这样最直接,也最容易确认到底是代码问题、蓝牙环境问题,还是客户端工具的问题。

仓库地址:

先在仓库根目录构建:

bash 复制代码
dotnet build

4.1 运行 Windows Demo

bash 复制代码
dotnet run --project ImprovWifi.Demo.Windows/ImprovWifi.Demo.Windows.csproj

运行前需要确认:

  • 蓝牙已开启
  • 适配器支持 Peripheral Role
  • 系统允许本地 GATT Service 注册

Windows Demo 的默认行为很简单:

  1. 启动后输入一个 Redirect URL
  2. 等待支持 Improv 的客户端扫描到设备
  3. 收到 Identify 请求后自动授权
  4. 收到 Wi-Fi 凭据后输出 SSIDPassword
  5. 调用 CompleteProvisioning(url) 返回结果地址

它的核心处理逻辑大致就是下面这样:

csharp 复制代码
using ImprovWifi.Protocol;
using ImprovWifi.Transport.Windows;

var server = new WindowsImprovServer(new WindowsImprovServerOptions
{
    DeviceName = "improv-windows-demo",
    AdapterName = "default",
    AutoAuthorize = false
});

server.IdentifyRequested += (_, _) =>
{
    Console.WriteLine("identify requested");
    server.Authorize(true);
};

server.ProvisioningRequested += (_, e) =>
{
    Console.WriteLine($"ssid={e.Ssid}, password={e.Password}");
    server.CompleteProvisioning("http://192.168.1.10/");
};

server.Start();
Console.ReadLine();
server.Stop();

这个示例没有真正去连接系统 Wi-Fi,它只是把协议流程跑通。真实项目里,你通常会在 ProvisioningRequested 事件中接入自己的联网逻辑。

4.2 运行 Linux Demo

bash 复制代码
dotnet run --project ImprovWifi.Demo.Linux/ImprovWifi.Demo.Linux.csproj -f net10.0

运行前需要确认:

  • 系统已安装.Net 10.0
  • 系统已安装并启动 BlueZ
  • bluetoothd 正常运行
  • 可以通过 bluetoothctl 看到蓝牙适配器,例如 hci0
  • 当前用户具备蓝牙访问权限

Linux 版当前使用的是基于 BlueZ 的宿主实现,直接对接系统蓝牙栈。运行流程与 Windows 版一致,也是先广播、接收识别请求、接收配网信息,然后返回 RPC Result URL

如果你是在 WSL2 里跑,需要额外确认蓝牙透传本身已经成立。否则即便代码没有问题,外围设备广播和 GATT Server 注册也可能无法真正工作。

5. 如何集成到自己的项目里

跑通 Demo 之后,接下来的问题通常就变成了:我能不能不直接改仓库里的 Demo,而是把它接到自己的项目里?答案当然是可以。

5.1 NuGet 包直接引用

如果你更关心"在自己的程序里快速挂一个配网服务",那直接引用 NuGet 包会更方便。

这里可以使用这几个包:

如果你是在 Windows 上开发,直接引用 ImprovWifi.Windows 就可以了;如果你是在 Linux 上开发,推荐直接上 ImprovWifi.Transport.Linux.Aot 这个包。后者虽然名字里带了 Aot,但它的 API 和 Linux 包是完全一样的,只不过底层实现更适合 AOT 场景。底层通过 LibraryImport 调用原生实现,更适合需要 NativeAOT 或更轻量运行方式的场景。

下面是一段最小可用的示例代码:

csharp 复制代码
using ImprovWifi.Protocol;
using ImprovWifi.Transport.Linux.Aot;

using var server = new LinuxAotImprovServer(new LinuxAotImprovServerOptions
{
    DeviceName = "improv-linux-demo",
    AdapterName = "hci0",
    AutoAuthorize = false
});

server.IdentifyRequested += (_, _) =>
{
    Console.WriteLine("identify requested");

    // 演示场景下直接授权,生产环境建议改成用户可感知的确认流程
    server.Authorize(true);
};

server.ProvisioningRequested += (_, e) =>
{
    Console.WriteLine($"ssid={e.Ssid}, password={e.Password}");

    var connected = ConnectToWifi(e.Ssid, e.Password);
    if (!connected)
    {
        server.FailProvisioning();
        return;
    }

    server.CompleteProvisioning("http://192.168.1.10/");
};

server.Start();
Console.WriteLine("Improv service started. Press ENTER to stop.");
Console.ReadLine();
server.Stop();

static bool ConnectToWifi(string ssid, string password)
{
    // 这里替换成你自己的联网逻辑,比如调用 nmcli、NetworkManager 或系统 API
    return !string.IsNullOrWhiteSpace(ssid);
}

这段代码背后的思路其实很简单:

  1. LinuxAotImprovServerImprov 服务挂起来
  2. IdentifyRequested 中决定是否授权
  3. ProvisioningRequested 中接收 Wi-Fi 信息并执行实际联网
  4. 根据联网结果调用 CompleteProvisioning(...)FailProvisioning(...)

如果你只是想做一个"板端收到 Wi-Fi 信息后立即返回 URL"的最小版本,那么甚至连真实联网逻辑都可以先不接,先把 BLE 配网链路跑通再说。

5.2 什么时候该用 AOT 包

如果你只是本地开发和联调,直接用仓库里的 ImprovWifi.Demo.Linux 通常就够了。

但如果你的目标是部署到板端、做更轻量的 Linux 运行环境,或者你本身就希望往 NativeAOT 方向走,那么 ImprovWifi.Transport.Linux.Aot 会更适合。仓库里对应还有:

  • ImprovWifi.Demo.Linux.Aot
  • native/improvwifi_transport_linux_aot_native

其中原生部分是一个 Rust 动态库,负责底层 BlueZ 交互;上层依然保持 .NET 的调用方式。这种拆法的好处,是业务代码不用为了 AOT 场景重写一套接口。

6. 配网时可以用什么客户端

目前这个项目可以配合下面两种方式使用:

  • 微信小程序:Improv 蓝牙配网
  • 官方网页工具:improv-wifi.com

如果你是拿手机直接做联调,小程序会更顺手一些,可以微信搜索 "Improv 蓝牙配网";如果你在桌面环境里做调试,官方网页工具通常更方便观察流程。

配网界面示意如下:

7. 什么时候适合用它

如果你现在处在下面这些场景里,这个项目会比较适合:

  • 想在 .NET 中实现 Improv Wi-Fi 协议
  • 想做跨平台的 BLE 配网服务
  • 想先用 Demo 快速验证协议链路
  • 想把配网能力接到自己的业务程序中

当然,它也有明确边界。仓库里的 Demo 默认不会直接替你完成系统层面的联网,也不建议把自动授权逻辑原样带到生产环境。生产场景下,授权动作最好由用户显式触发,比如按钮确认、设备物理操作或者其他可感知的授权方式。

8. 总结

这次整理 Sang.ImprovWifi,我想解决的并不是"怎么随手写一个蓝牙配网 Demo",而是"怎么把 Improv Wi-Fi 做成一个结构清晰、可复用、能跨平台继续扩展的 .NET 项目"。

如果你只是想验证流程,可以直接运行仓库里的 WindowsLinux Demo;如果你已经有自己的 Linux 项目,那就可以直接试试 ImprovWifi.Transport.Linux.Aot 这个 NuGet 包,把配网能力接进去。

项目地址放在这里:

如果你也正好在做设备配网相关的事情,希望这份项目和这篇文章,能帮你少走一些弯路。

相关推荐
小邓的技术笔记3 小时前
.NET 进阶之路:异步、并发与内存管理的系统性认知
.net
Maybe_ch3 小时前
WPF的STA线程模型、APM与TAP:从线程约束到现代异步
c#·.net·wpf
我是唐青枫4 小时前
C#.NET Consul + Steeltoe 深入解析:服务注册发现、健康检查与微服务接入
c#·.net·consul
硅基喵14 小时前
.NET 进阶之路:异步、并发与内存管理的系统性认知
.net
俊俊谢15 小时前
LabVIEW如何排查和修复dll缺失问题
驱动开发·.net·labview·dll
武藤一雄19 小时前
C# 设计模式大全(第一弹|7种)
microsoft·设计模式·微软·c#·.net·.netcore
麦壳饼1 天前
JekyllNet .Net 版本的Jekyll , 你博客 文档的静态生成利器 。
.net
步步为营DotNet1 天前
.NET 11 中 ASP.NET Core 10 在分布式系统中的安全通信与性能调优
安全·asp.net·.net
xu_wenming1 天前
在 TinyML 场景下,如何将模型从 FP32 量化为 INT8?
arm开发·算法·iot