前言
近期我在社区中表达了想要制作稚晖君的瀚文键盘的意愿,幸运的是,有两位朋友慷慨相助,一位赠送了我电路板,另一位则送来了已经焊接好元件的电路板。既然大家如此大方,我也决定全力投入到这把客制化键盘的制作中。为了节省成本,我特意重新设计了外壳模型,并使用3D打印机打印了整个外壳,这样就省下了八九百元的CNC加工费。
关于键盘的基本介绍这里就不赘述了,它的主要特色在于左侧的扩展模块,配备了墨水屏和手感极佳的旋钮,当然也支持自定义开发,这也是我撰写这篇文章的动机之一,因为我想开发一些功能。以下是效果图:

技术选型
我查阅了一些社区的键盘资料,发现社区的固件有多个版本。稚晖君原版的固件年代久远,不太好用,而赠送我键盘的那位朋友的版本我觉得很方便,且用户量也很多。因此,我基于这个版本的固件进行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
最终效果如下图所示:

代码讲解
我这次将项目代码提交到了电子脑壳的仓库,因为我要将功能集成到电子脑壳中,所以放在了这个仓库的helloworld-keyboard分支,未来可能会合并到主分支。
仓库地址:https://www.php.cn/link/7048c496a6cf49699109089b743c2bf6

通讯协议实现
通讯的核心部分是Hw75DynamicDevice的Call方法,它包含了将protobuf生成的C#对象转换成字节数组并拆分成数据包发送到设备。
代码语言:j*ascript
刺鸟创客
一款专业高效稳定的AI内容创作平台
110
查看详情
代码运行次数: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[]时需要特别注意,代码如下:
代码语言:j*ascript
代码运行次数: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;
}
}
重点部分是HID设备每次发送64字节,第一字节是固定数字1,第二字节是数据长度,后面是数据内容。

数据传输测试
在完成SDK编写和测试后,我使用控制台项目进行测试,包括图片的合成和文字的绘制,以及将绘制好的图片转换成设备可用的字节数据。
首先使用ImageSharp加载图片,再加载字体文件,将文字和图片绘制到图片上,为后续制作动态数据做准备,代码如下:
代码语言:j*ascript
代码运行次数: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.S*e("test.jpg");
byteArray = image.EnCodeImageToBytes();
}然后将ImageSharp合成的图片转换成01矩阵,再组装成byte[]。如果大家有更好的方法,欢迎推荐给我。我的逻辑写在了EnCodeImageToBytes这个扩展方法中。
代码语言:j*ascript
代码运行次数: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;
}全部代码如下:
代码语言:j*ascript
代码运行次数: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.S*e("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 与硬件设备能碰撞出怎么样的火花的详细内容,更多请关注其它相关文章!
# 中国
# seo网站运营加盟
# 快手推广北方市场营销
# 学seo好还是sem
# 排名seo建议易 速达
# 天心区品质网站建设
# 网站信息流推广什么意思
# 桂城网站优化广告
# 番禺外贸网站推广
# 河北网站建设价格套餐
# 武汉哪有网站架设推广
# 都是
# 的是
# 远程控制
# 测试
# 已成
# 立竿见影
# 已有
# 不太
# 固件
# 转换成
# 为什么
# c#
# 电脑
# git
# python
# vue
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
AI 大模型重塑软件开发,有哪些落地前景和痛点?| ArchSummit
新华三集团总裁兼首席执行官于英涛:人工智能时代需要想象力,更需要精耕务实
当人工智能开始写高考作文?作家陈崇正、朱山坡谈文学与未来
V社悄悄封禁使用AI生成美术素材的游戏
生成式人工智能来了,如何保护未成年人? | 社会科学报
Meta 推出 Quest 超级分辨率技术,让 VR 画面更清晰
为AI而服务设计:构建以人为本的AI创新方法
海南科技职业大学第25届中国机器人及人工智能大赛海南赛区荣获一等奖等114项
一文看懂被英伟达看中的九号机器人移动底盘
280万条多模态指令-响应对,八种语言通用,首个涵盖视频内容的指令数据集MIMIC-IT来了
码刻 | 48小时Hackathon,源码见证新生代AI创新的发生
百川智能发布Baichuan-13B AI模型,号称“130亿参数开源可商用”
日媒关注中国推进鸟类识别 AI 普及,除监测保护外还可预防传染性疾病
可按用户语气自动回复消息,Zoom 推出基于生成式 AI 的新功能
以分布式网络串联闲置GPU,这家创企称可将AI模型训练成本降低90%
马斯克WAIC2025演讲全文:AI将对人类文明产生深远影响
基于预训练模型的金融事件分析及应用
英媒:硅谷有些人太鼓吹AI,宣扬“学习无用”
昇腾AI大模型训推一体化解决方案将在WAIC发布
利用AI探索抗体“钥匙”、加速药物研发——访百图生科团队
微软Bing聊天机器人电脑端即将支持语音提问
【趋势周报】全球人工智能产业发展趋势:OpenAI向美国专利局提交“GPT-5”商标申请
360发布认知型通用大模型“360智脑4.0” 全面接入360全家桶
拓普龙7188ML:轻便壁挂式工控机箱,为人工智能应用场景提供有力保障
“直击”AI新世界,智能机器人再次“火出圈”了
GPT-4 模型架构泄露:包含 1.8 万亿参数、采用混合专家模型
IBM与NASA联手开源地理空间AI基础模型,促进气候科学领域进步
优地网络助力新媒体拥抱人工智能时代
AIGC 风潮刮到游戏产业,巨人网络与阿里云达成“游戏 +AI ”合作
PS AI修图免费平替来了!Stability AI又放大招,核弹级更新一键扩图
抖音在Android平台获得VR|直播|软件著作权
鸿蒙生态带来了哪些新的流量可能性,包括AI、服务分发和原生智能等方面?
人才智能平台转型中的人工智能的关键角色
AI 作画工具 Midjourney 推出“pan”功能,可平移扩展图片外场景
高质量数据推动AI场景化应用快速发展及落地
商业智能决策技术助力降本增效,世界人工智能大会举办商业AI高峰论坛
微软和谷歌面临的人工智能困境:需要投入大量资金才能获得盈利
大型无人机FH-98国内首次夜航转场成功
AI和ML推动联网设备的增长
探展WAIC |万向区块链杜宇:不存在单一技术的iPhone时刻,Web3.0核心将基于AI+区块链+物联网
华为余承东表示:鸿蒙可能拥有强大的人工智能大模型能力
看懂AI,找到增长新势能 | 笔记侠AI峰会等你来
上新7款产品,美图继续“蹭”AI
华为昇腾AI原生支持30多种基础大模型,包括GPT
大厂出品!这个AI网站太顶了,所有功能免费用
美图第二届影像节发布七款AI影像创作工具
机智云AI离线语音识别模组,让家电变得更加智能便捷
梦想实现!硬核科幻大片VR智能头盔即将问世
华为盘古AI模型实现秒级全球气象预报时间缩短
字节、网易相继入局,AI之后大厂又找到下一个风口?
2025-04-26
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。