How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Initial Motivation

Friends who like electronics and DIY hardware should be familiar with Zhihui Jun, who regularly shares some fun hardware he has made. I was very interested in a desktop robot he previously made called ElectronBot, so I made one myself. How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)At first, I only developed an upper computer software called Electronic Brain Shell, which you may have seen in previous blogs; it is a Windows application developed using WinUI (WASDK).

The ElectronBot robot needs to be constantly connected to the computer for control through the application, and many users want to use ElectronBot independently of the computer. Thus, I thought of a good solution, which led to the idea of this title.

Project code address: https://github.com/maker-community/ElectronBot.DotNet

Technology Selection

When people think of Raspberry Pi, they often think of Python. I used Raspberry Pi a lot with Python during college, but as a .NET developer, I wanted to try out the cross-platform capabilities of .NET.

Since I already had the upper computer software, Electronic Brain Shell, I thought it would be natural to have the previously encapsulated SDK work directly on the Raspberry Pi, so choosing .NET was a logical decision.

  • Application development using the .NET framework

  • Communication method using GRPC

  • USB operation using LibUsbDotNet to call libusb

The overall solution is shown in the figure below:

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Environment Configuration

1. Raspberry Pi System Installation and Network Configuration

There are many systems supported by Raspberry Pi, such as the official Debian, Ubuntu, etc. I usually use Debian, but it seems there were some issues with USB operations during testing, likely due to the .NET interop library not supporting Debian well, so I chose the Ubuntu system.

  • Raspberry Pi Imager, the official system burning tool for Raspberry Pi

  • Ubuntu system download address Download Ubuntu Server

Install the burning tool, download the system, and then burn the system. As shown in the figure below: How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Currently, the Ubuntu version for Raspberry Pi Zero 2 W only has Server and Core versions, requiring command line operations.

Network configuration can refer to the article below for configuration.

Complete Process of Deploying SSH Server on Raspberry Pi 4B Ubuntu Server (Internal Network Penetration)

As long as you can connect to the Internet and install software, you are good to go.

2. .NET6 Runtime Environment

Since .NET6 Arm64 official package management installation is not provided, you need to install it through scripts or downloaded files. If the network is good, you can install it via script, which is simple; this article provides an example of script installation.

Refer to the following documentation:

Install .NET on Linux using installation scripts or by extracting binaries

Download the installation script dotnet-install.sh to the user home directory

Execute the following command to add execution permissions

sudo chmod +x ./dotnet-install.sh

Execute the command to install

./dotnet-install.sh -c Current

After downloading and extracting, we need to configure environment variables, just like installing software on Windows; after configuring the environment variables, you can use dotnet directly, otherwise, it will prompt that the command cannot be found.

The following command is for editing environment variables; I chose ~/.bashrc and added the environment variable content at the bottom of the document.

sudo vim ~/.bashrc

Content

export DOTNET_ROOT=$HOME/.dotnet
export PATH=$PATH:$HOME/.dotnet:$HOME/.dotnet/tools

Save and exit, then execute dotnet; if it runs normally, you can proceed with the following operations.

Record of the Practice Process

1. Development and Debugging of GRPC Service

First, create a grpc service

Microsoft’s official documentation on writing protocol files

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Writing protocol files and service

Using my protocol content as an example, it includes float, bool, bytes types

float corresponds to c# float

bool corresponds to c# bool

bytes corresponds to c# byte[]

syntax = "proto3";

option csharp_namespace = "Verdure.ElectronBot.GrpcService";

package electronbotactiongrpc;

// The electronbotaction service definition.
service ElectronBotActionGrpc {
  // Sends a greeting
  rpc PlayEmoticonAction (EmoticonActionFrameRequest) returns (EbHelloReply);
}

// The request message containing the user's name.
message EmoticonActionFrameRequest {
 float J1 = 1;
 float J2 = 2;
 float J3 = 3;
 float J4 = 4;
 float J5 = 5;
 float J6 = 6;
 bool Enable = 7;
 bytes FrameBuffer = 8;
}

// The response message containing the greetings.
message EbHelloReply {
  string message = 9;
}

Then generate the corresponding service code

The main point to note is the conversion of bytes type

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

After the server is written, you can write the client code. For convenience, I added the calling code to the Electronic Brain Shell code.

WinUI GRPC calling precautions

Directly putting grpc-related things into the winui project seems to have some issues, so I put it into a library project for operation.

Configure the protocol file to generate client code

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Inject grpc-client into Electronic Brain Shell

            services.AddGrpcClient<ElectronBotActionGrpc.ElectronBotActionGrpcClient>(o =>
            {
                o.Address = new Uri("http://192.168.3.236:5241");
            });

The data sender also needs to pay attention to the conversion of bytes type

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Packaging and publishing the grpc service

When packaging, choose the target runtime to be portable (can run on both Windows and Linux) How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Project code address

2. Deployment of the Service and Effect Verification

Use an FTP tool to place the grpc service on the Raspberry Pi; first, connect to ElectronBot and then run the service

Navigate to the project directory and execute the command as follows:

 dotnet Verdure.ElectronBot.GrpcService.dll --urls="http://*:5241"

If communication is normal, it should report an error indicating that libusb cannot be found. The system has already installed libusb; it is just that the directory was not found during the .NET call, so we need to create a soft link.

LibUsbDotNet README mentions this at the bottom

The actual operation on Raspberry Pi is as follows:

  sudo find / -name "libusb-1.0*.so*" // List the directory location of the library
  cd /usr/lib/aarch64-linux-gnu/ // Enter the library directory
  sudo ln -s libusb-1.0.so.0 libusb-1.0.so // Create a link

After the above operation, there should be no issues with libusb operation.

The running effect is shown in the figure below:

How to Operate USB Read and Write with .NET6 and LibUsb on Raspberry Pi Zero 2 W (Ubuntu 22.04)

Special Issues and Insights

The biggest issue was that the sdk tested on the Linux system had abnormal data writing.

  • The sdk needs to handle the Linux environment separately

  • The sdk needs to supplement some kernel driver uninstallation logic

  • Be especially careful not to use a USB OTG cable that splits into multiple ports; just use one port, or the data will not be sent properly

Since the logic above using LibUsbDotNet was not exposed to the upper layer, I had to create a branch and make a modification, but no one responded to my PR.

The internally specially handled code for the sdk is as follows:

Executed a device’s SetAutoDetachKernelDriver

if (wholeUsbDevice.DriverMode == UsbDevice.DriverModeType.MonoLibUsb)
{
_logger.LogInformation("MonoLibUsb DetachKernelDriver");

var retDetach = wholeUsbDevice.SetAutoDetachKernelDriver(true);

_logger.LogInformation(retDetach.ToString());
}

Through this attempt, I understand the meaning of “the ideal is beautiful, but reality is harsh”. Initially, I thought that the Raspberry Pi Zero 2 W was small and portable, and I was only using it to receive and send data. However, the screen refresh was very slow when sending data. I don’t know if it is due to poor performance or if USB transmission is a bottleneck. Next time, I will test with a Raspberry Pi 4B.

That concludes the sharing of the entire process. This project basically covers many aspects of .NET technology. If ElectronBot and Raspberry Pi can work well independently in the future, I think it would be logical to develop a mobile management tool using MAUI. I look forward to it, and those who can see this can leave some comments in the comment area.

Leave a Comment

Your email address will not be published. Required fields are marked *