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. 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:
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:
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
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
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
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
Packaging and publishing the grpc service
When packaging, choose the target runtime to be portable (can run on both Windows and Linux)
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:
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 *