日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

.Net 8与硬件设备能碰撞出怎么样的火花(使用ImageSharp和Protobuf协议通过HidApi与设备通讯)

發(fā)布時(shí)間:2023/12/24 asp.net 48 coder
生活随笔 收集整理的這篇文章主要介紹了 .Net 8与硬件设备能碰撞出怎么样的火花(使用ImageSharp和Protobuf协议通过HidApi与设备通讯) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

本人最近在社區(qū)里說(shuō)想做稚暉君的那個(gè)瀚文鍵盤來(lái)著,結(jié)果遇到兩個(gè)老哥一個(gè)老哥送了我電路板,一個(gè)送了我焊接好元件的電路板,既然大家這么舍得,那我也就真的投入制作了這把客制化鍵盤,當(dāng)然我為了省錢也是特意把外殼模型重新切割,用3D打印機(jī)打印了整個(gè)外殼,不得不說(shuō)省了八九百的CNC費(fèi)用。鍵盤介紹我就不說(shuō)了,鍵盤主要特色是左邊的拓展模塊,有墨水屏和手感超好的旋鈕,當(dāng)然也支持自定義開(kāi)發(fā),能開(kāi)發(fā)也是我寫這篇文章的原因,畢竟是為了開(kāi)發(fā)功能,效果圖如下,大家可以關(guān)注我的b站賬號(hào)綠蔭阿廣,來(lái)學(xué)習(xí)交流一些有趣的東西。

技術(shù)選型

在我查閱了一些社區(qū)鍵盤資料發(fā)現(xiàn)社區(qū)固件有幾個(gè)版本,稚暉君原版的固件太老了不好用,送我鍵盤的老哥的版本我覺(jué)得挺方便而且用戶量應(yīng)該也很多,于是我就基于這個(gè)版本的固件進(jìn)行dotnet版本的sdk開(kāi)發(fā)了,目前有其他版本的sdk,有python版本的,vue版本的,我是可以拿來(lái)直接參考的。

1. 框架選擇

作為一名.Net開(kāi)發(fā),我肯定是想用.net進(jìn)行開(kāi)發(fā)的,理由是這個(gè)鍵盤用在PC上,用.Net實(shí)現(xiàn)SDK對(duì)接WPF,MAUI和WinUI可以做很多的任務(wù)型的功能。選擇采用最新版本的.Net8,然后在SDK測(cè)試編寫完成之后,對(duì)接到我之前的WinUI桌面程序里,大家肯定會(huì)問(wèn),為什么不選擇MAUI,我想說(shuō)當(dāng)然因?yàn)槲視簳r(shí)不想花時(shí)間重新寫,不過(guò)SDK是支持跨平臺(tái)的,這點(diǎn)問(wèn)題不大。

2. 設(shè)備通訊協(xié)議

鍵盤采用的固件是開(kāi)源的ZMK這個(gè)代碼編寫的,設(shè)備在電腦識(shí)別為hid設(shè)備,通訊格式使用的Protobuf協(xié)議,所以針對(duì).Net也需要使用這個(gè)Protobuf進(jìn)行數(shù)據(jù)的打包,這個(gè)地方花了我一些時(shí)間,主要是有些地方不太懂,坑主要是數(shù)據(jù)轉(zhuǎn)成字節(jié)數(shù)組的時(shí)候的一些問(wèn)題,這個(gè)在后面的代碼講解里有用到。

  • 設(shè)備固件地址:https://github.com/xingrz/zmk-config_helloword_hw-75
  • python SDK: https://github.com/xingrz/zmkx-sdk

3. 庫(kù)選擇

本來(lái)以為.Net可以用的hid庫(kù)有很多,在本人測(cè)試了一圈以后發(fā)現(xiàn)不錯(cuò)的也就這個(gè)HidApi.Net還可以,其他的什么Device.Net,HidLibrary都不是很滿意,在我測(cè)試以后選擇了HidApi.Net和設(shè)備通訊,Google.Protobuf和Grpc.Tools加工通訊數(shù)據(jù),SixLabors.ImageSharp進(jìn)行圖片數(shù)據(jù)的轉(zhuǎn)換。

  • HidApi.Net
  • Google.Protobuf
  • Grpc.Tools
  • SixLabors.ImageSharp
    最終效果如下圖:

代碼講解

項(xiàng)目代碼我這次提交到了電子腦殼的倉(cāng)庫(kù)里,因?yàn)槲乙獙⒐δ芗傻诫娮幽X殼里,所以放在了這個(gè)倉(cāng)庫(kù),目前所在分支為helloworld-keyboard,后期應(yīng)該會(huì)合并到主分支。
倉(cāng)庫(kù)地址:https://github.com/maker-community/ElectronBot.DotNet

通訊協(xié)議實(shí)現(xiàn)

通訊的核心部分是Hw75DynamicDevice的Call方法,包含了將protobuf生成的c#對(duì)象轉(zhuǎn)成byte[]并拆分成數(shù)據(jù)包發(fā)送到設(shè)備。

 private MessageD2H Call(MessageH2D h2d)
 {
     if (_device == null)
     {
         throw new Exception("設(shè)備為空");
     }
     var bytes = h2d.EnCodeProtoMessage();

     for (int i = 0; i < bytes.Length; i += PayloadSize)
     {
         var buf = new byte[PayloadSize];

         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;
         }
     }
     return MessageD2H.Parser.ParseFrom(byteList.ToArray());
 }
  • 數(shù)據(jù)打包有個(gè)重點(diǎn)問(wèn)題,就是在圖片數(shù)據(jù)進(jìn)行拼接的時(shí)候有個(gè)byte[]長(zhǎng)度需要采用protobuf編碼之后再組裝到數(shù)據(jù)byte[]的前面這個(gè)轉(zhuǎn)成byte[]需要注意,代碼如下:
    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;
        }
    }

  • 重點(diǎn)部分是hid設(shè)備要每次發(fā)送64字節(jié),第一字節(jié)是數(shù)字1,這個(gè)是固定的,第二字節(jié)是數(shù)據(jù)長(zhǎng)度,后面的是數(shù)據(jù)內(nèi)容。

數(shù)據(jù)傳輸測(cè)試

在sdk編寫測(cè)試完成之后,就可以進(jìn)行sdk的使用了,我使用控制臺(tái)項(xiàng)目進(jìn)行測(cè)試,包含圖片的合成和文字的繪制,以及將繪制好的圖片轉(zhuǎn)成設(shè)備能夠使用的byte數(shù)據(jù)。

  • 我先使用ImageSharp加載圖片,再加載字體文件將文字和圖片繪制到圖片上,這個(gè)為后面制作動(dòng)態(tài)數(shù)據(jù)做鋪墊,代碼如下:

    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;
    
    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);
    
    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("粉絲數(shù):", 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合成的圖片轉(zhuǎn)成01矩陣再組裝成byte[]這個(gè)不知道大家有沒(méi)有什么好的辦法,有的話可以推薦給我,我的邏輯寫在了EnCodeImageToBytes這個(gè)拓展方法里。

    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++)
            {
                matrix[y, x] = image[x, y].R > 128 ? 1 : 0;
            }
        }
    
        // Convert the matrix to a byte array
        byte[] byteArray = new byte[image.Height * image.Width / 8];
        for (int y = 0; y < image.Height; y++)
        {
            for (int x = 0; x < image.Width; x += 8)
            {
                for (int k = 0; k < 8; k++)
                {
                    byteArray[y * image.Width / 8 + x / 8] |= (byte)(matrix[y, x + k] << (7 - k));
                }
            }
        }
    
        return byteArray;
    }
    

全部代碼如下:

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;

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

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("粉絲數(shù):", 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();
}

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();

個(gè)人心得體會(huì)

這次功能的編寫讓我最有感悟的地方就是自己對(duì)Github Copilot的依賴更多了,我基本上很多的知識(shí)都是詢問(wèn)它,因?yàn)閺木W(wǎng)上搜索還要自己過(guò)濾那些數(shù)據(jù),比較耽誤時(shí)間。

還有個(gè)點(diǎn)就是這個(gè)HidApi.Net的庫(kù)是最近剛有人寫的,社區(qū)還是有新鮮的血液的,支持.net6,7,8很新,也算是個(gè)驚喜呢,希望社區(qū)的*越來(lái)越多呢!!!!

其他角度的照片展示:

參考推薦文檔項(xiàng)目如下:

  • 電子腦殼

  • 設(shè)備固件地址

  • python SDK

  • hidapi

  • HidApi.Net

  • LibUsbDotNet

  • 電子腦殼有在使用的得意黑字體

  • 項(xiàng)目模板——TemplateStudio

  • 社區(qū)工具集——CommunityToolkit

  • 控件庫(kù)展示demo——WinUI-Gallery

  • WASDK文檔地址

  • WinUI-Tutorial-Code WinUI測(cè)試學(xué)習(xí)代碼

總結(jié)

以上是生活随笔為你收集整理的.Net 8与硬件设备能碰撞出怎么样的火花(使用ImageSharp和Protobuf协议通过HidApi与设备通讯)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。