讲师中心 微信公众号
AI工具推荐 视频效率加速

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

冬晨同学_2039

冬晨同学_2039

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

|

838人浏览过

|

来源于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#对象转换成字节数组并拆分成数据包发送到设备。

ClickHouse GitHub Forensics
ClickHouse GitHub Forensics

通过ClickHouse查询GitHub事件数据,用于供应链调查、行为者画像和异常检测。适用于调查基于GitHub的攻击,...

下载

代码语言: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工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

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

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

8285

2026.01.21

GitHub官网入口版本汇总
GitHub官网入口版本汇总

本专题整合了GitHub入口版本地址汇总,阅读专题下面的文章了解更多详细内容。

758

2026.04.02

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

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

5290

2024.08.16

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

0

2026.09.30

LLVM RISC-V参数配置教程
LLVM RISC-V参数配置教程

本专题介绍LLVM对RISC-V基础ISA和扩展的支持方式,涵盖RV32、RV64、标准扩展、实验性扩展、厂商扩展、-menable-experimental-extensions和版本差异。

0

2026.09.30

LLVM IR中间表示入门指南
LLVM IR中间表示入门指南

本专题整理LLVM IR的核心概念,包括中间表示作用、模块结构、函数、基本块、SSA形式、类型系统和常见语法,帮助新手理解LLVM编译流程中的关键层。

0

2026.09.30

PDF转图片方法
PDF转图片方法

需要把 PDF 页面用于上传、预览、分享或图片归档时,PDF 转图片方法专题整理 JPG/PNG 格式选择、逐页导出、清晰度设置、批量下载和结果检查等流程,帮助用户稳定完成 PDF 图片化处理。

0

2026.09.30

PixTV AI视频生成与无限画布创作
PixTV AI视频生成与无限画布创作

PixTV专题整理AI视频与视觉内容创作相关功能使用教程,涵盖AI生图、视频生成、无限画布、多模型创作、素材管理、声音音乐及视频剪辑等功能,帮助用户快速掌握PixTV从创意到成片的完整制作方法。

0

2026.09.29

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

200

2026.09.23

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Conan 包创建教程
Conan 包创建教程

共0课时 | 0人学习

GDB 当前开发版文档
GDB 当前开发版文档

共0课时 | 0人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn