0

0

.NET 8.0 与硬件设备能碰撞出怎么样的火花

爱谁谁

爱谁谁

发布时间:2025-04-26 13:10:21

|

818人浏览过

|

来源于php中文网

原创

前言

近期我在社区中表达了想要制作稚晖君的瀚文键盘的意愿,幸运的是,有两位朋友慷慨相助,一位赠送了我电路板,另一位则送来了已经焊接好元件的电路板。既然大家如此大方,我也决定全力投入到这把客制化键盘的制作中。为了节省成本,我特意重新设计了外壳模型,并使用3D打印机打印了整个外壳,这样就省下了八九百元的CNC加工费。

关于键盘的基本介绍这里就不赘述了,它的主要特色在于左侧的扩展模块,配备了墨水屏和手感极佳的旋钮,当然也支持自定义开发,这也是我撰写这篇文章的动机之一,因为我想开发一些功能。以下是效果图:

.NET 8.0 与硬件设备能碰撞出怎么样的火花

技术选型

我查阅了一些社区的键盘资料,发现社区的固件有多个版本。稚晖君原版的固件年代久远,不太好用,而赠送我键盘的那位朋友的版本我觉得很方便,且用户量也很多。因此,我基于这个版本的固件进行dotnet版本的SDK开发。目前,社区还有其他版本的SDK,包括Python版本和Vue版本,我可以参考这些进行开发。

1、框架选择

作为一名.Net开发者,我自然希望使用.Net进行开发。理由是这个键盘主要用于PC上,使用.Net实现SDK可以与WPF、MAUI和WinUI无缝对接,完成很多任务型功能。我选择了最新的.Net8版本,在SDK测试编写完成后,将其集成到我之前的WinUI桌面程序中。大家可能会问,为什么不选择MAUI?因为我暂时不想花时间重新编写,但SDK是支持跨平台的,这点问题不大。

2、设备通讯协议

键盘使用的固件是基于开源的ZMK代码编写的,设备在电脑上被识别为HID设备,通讯格式采用Protobuf协议。因此,对于.Net也需要使用Protobuf来进行数据打包。这部分花费了我一些时间,主要是有些地方不太理解,坑主要是在将数据转换成字节数组时遇到的问题,这点在后面的代码讲解中会有详细说明。

设备固件地址:https://www.php.cn/link/729bea7aa9914689ae2a70fe8bb5cf27

Python SDK: https://www.php.cn/link/14d010488fdb86b7b84ad331943cbb35

3、库选择

我原本以为.Net可以使用的HID库有很多,但经过一番测试后发现,HidApi.Net的表现还算不错,其他如Device.Net和HidLibrary则不太满意。最终,我选择了HidApi.Net来与设备通讯,使用Google.Protobuf和Grpc.Tools处理通讯数据,使用SixLabors.ImageSharp进行图片数据转换。

HidApi.Net

Google.Protobuf

Grpc.Tools

SixLabors.ImageSharp

最终效果如下图所示:

.NET 8.0 与硬件设备能碰撞出怎么样的火花

代码讲解

我这次将项目代码提交到了电子脑壳的仓库,因为我要将功能集成到电子脑壳中,所以放在了这个仓库的helloworld-keyboard分支,未来可能会合并到主分支。

仓库地址:https://www.php.cn/link/7048c496a6cf49699109089b743c2bf6

.NET 8.0 与硬件设备能碰撞出怎么样的火花

通讯协议实现

通讯的核心部分是Hw75DynamicDevice的Call方法,它包含了将protobuf生成的C#对象转换成字节数组并拆分成数据包发送到设备。

Pokecut
Pokecut

AI图片编辑处理工具,拥有超过50多种AI功能

下载

代码语言:javascript

代码运行次数:0

private MessageD2H Call(MessageH2D h2d) {
    if (_device == null) {
        throw new Exception("设备为空");
    }
    var bytes = h2d.EnCodeProtoMessage();
    for (int i = 0; i < bytes.Length; i += PayloadSize) {
        byte[] buf;
        if (i + PayloadSize > bytes.Length) {
            buf = bytes[i..];
        } else {
            buf = bytes[i..(i + PayloadSize)];
        }
        var list = new byte[2] { 1, (byte)buf.Length };
        var result = list.Concat(buf).ToArray();
        _device.Write(result);
    }
    Task.Delay(20);
    var byteList = new List<byte>();
    while (true) {
        var read = _device.Read(RePortCount + 1);
        int cnt = read[1];
        byteList.AddRange(read[3..(cnt + 2)]);
        if (cnt < PayloadSize) {
            break;
        }
    }
    var resultMessage = new MessageD2H();
    resultMessage.MergeFrom(byteList.ToArray());
    return resultMessage;
}

数据打包时有一个关键问题,就是在拼接图片数据时,需要将byte[]长度使用protobuf编码后再组装到数据的byte[]前面。这个转换成byte[]时需要特别注意,代码如下:

代码语言:javascript

代码运行次数:0

public static byte[] EnCodeProtoMessage(this MessageH2D messageH2D) {
    var msgBytes = messageH2D.ToByteArray();
    using (MemoryStream ms = new MemoryStream()) {
        CodedOutputStream output = new CodedOutputStream(ms);
        output.WriteInt32(msgBytes.Length);
        output.Flush();
        byte[] byteList = ms.ToArray();
        var result = byteList.Concat(msgBytes).ToArray();
        return result;
    }
}

.NET 8.0 与硬件设备能碰撞出怎么样的火花

重点部分是HID设备每次发送64字节,第一字节是固定数字1,第二字节是数据长度,后面是数据内容。

.NET 8.0 与硬件设备能碰撞出怎么样的火花

数据传输测试

在完成SDK编写和测试后,我使用控制台项目进行测试,包括图片的合成和文字的绘制,以及将绘制好的图片转换成设备可用的字节数据。

首先使用ImageSharp加载图片,再加载字体文件,将文字和图片绘制到图片上,为后续制作动态数据做准备,代码如下:

代码语言:javascript

代码运行次数:0

using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.Diagnostics;
using System.Numerics;
<p>byte[] byteArray = new byte[128 * 296 / 8];
var list = new List<byte>();
var collection = new FontCollection();
var family = collection.Add("./SmileySans-Oblique.ttf");
var font = family.CreateFont(18, FontStyle.Bold);</p><p>using (var image = Image.Load<Rgba32>("face.jpg")) {
using var overlay = Image.Load<Rgba32>("bzhan.png");
overlay.Mutate(x => {
x.Resize(new Size(50,50));
});
// Convert the image to grayscale
image.Mutate(x => {
x.DrawImage(overlay, new Point(0, 64), opacity: 1);
x.DrawText("粉丝数:", font, Color.Black, new Vector2(20, 220));
x.DrawText("999999", font, Color.Black, new Vector2(20, 260));
x.Grayscale();
});
image.Save("test.jpg");
byteArray = image.EnCodeImageToBytes();
}

然后将ImageSharp合成的图片转换成01矩阵,再组装成byte[]。如果大家有更好的方法,欢迎推荐给我。我的逻辑写在了EnCodeImageToBytes这个扩展方法中。

代码语言:javascript

代码运行次数:0

public static byte[] EnCodeImageToBytes(this Image<Rgba32> image) {
// Create a 01 matrix
int[,] matrix = new int[image.Height, image.Width];
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x++) {
var pixel = image[x, y];
matrix[y, x] = pixel.R > 128 ? 1 : 0;
}
}
// Convert the matrix to a byte array
byte[] byteArray = new byte[image.Height <em> image.Width / 8];
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x += 8) {
byte b = 0;
for (int i = 0; i < 8; i++) {
if (x + i < image.Width) {
b |= (byte)(matrix[y, x + i] << (7 - i));
}
}
byteArray[y </em> (image.Width / 8) + x / 8] = b;
}
}
return byteArray;
}

全部代码如下:

代码语言:javascript

代码运行次数:0

using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using HelloWordKeyboard.DotNet;
using HidApi;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.Diagnostics;
using System.Numerics;</p><p>byte[] byteArray = new byte[128 * 296 / 8];
var list = new List<byte>();
var collection = new FontCollection();
var family = collection.Add("./SmileySans-Oblique.ttf");
var font = family.CreateFont(18, FontStyle.Bold);</p><p>using (var image = Image.Load<Rgba32>("face.jpg")) {
using var overlay = Image.Load<Rgba32>("bzhan.png");
overlay.Mutate(x => {
x.Resize(new Size(50,50));
});
// Convert the image to grayscale
image.Mutate(x => {
x.DrawImage(overlay, new Point(0, 64), opacity: 1);
x.DrawText("粉丝数:", font, Color.Black, new Vector2(20, 220));
x.DrawText("999999", font, Color.Black, new Vector2(20, 260));
x.Grayscale();
});
image.Save("test.jpg");
byteArray = image.EnCodeImageToBytes();
}</p><p>var hidDevice = new Hw75DynamicDevice();
hidDevice.Open();
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
var data111 = hidDevice.SetEInkImage(byteArray, 0, 0, 128, 296, false);
sw.Stop();
Console.WriteLine($"send data ms:{sw.ElapsedMilliseconds}");
Console.ReadKey();
Hid.Exit();

总结

这次功能的编写让我最有感悟的地方就是自己对Github Copilot的依赖更多了,我基本上很多的知识都是通过询问它获得的,因为从网上搜索还要自己过滤那些数据,比较耽误时间。

另一个值得一提的点是,HidApi.Net这个库是最近刚有人编写的,社区中还是有新鲜血液的,支持.net6,7,8,非常新,也算是个惊喜。希望社区的轮子越来越多!

其他角度的照片展示

.NET 8.0 与硬件设备能碰撞出怎么样的火花

.NET 8.0 与硬件设备能碰撞出怎么样的火花

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
github中文官网入口 github中文版官网网页进入
github中文官网入口 github中文版官网网页进入

github中文官网入口https://docs.github.com/zh/get-started,GitHub 是一种基于云的平台,可在其中存储、共享并与他人一起编写代码。 通过将代码存储在GitHub 上的“存储库”中,你可以: “展示或共享”你的工作。 持续“跟踪和管理”对代码的更改。

3782

2026.01.21

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2864

2024.08.16

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

45

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

113

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

229

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

90

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

137

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

29

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

79

2026.02.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML教程
HTML教程

共500课时 | 6.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号