如何实现C# MAUI Android应用通过USB OTG与Arduino串口通信
解决MAUI Android与Arduino USB OTG串口通信问题
核心问题排查与修复步骤
- 修正CH340驱动匹配:原代码给CH340设备错误绑定了
CdcAcmSerialDriver,需替换为专用的Ch34xSerialDriver - 修复权限请求逻辑:权限请求是异步操作,需等待用户授权后再执行后续连接,不能直接返回
- 替换async void为async Task:避免异步操作异常无法捕获、流程无法正确等待的问题
- 完善资源释放:添加资源清理方法,确保串口和连接在使用后正确关闭
- 优化数据收发:给读写操作添加异常捕获,发送数据时增加换行符适配Arduino读取逻辑
修正后的完整代码
#if ANDROID using Android.App; using Android.Content; using Android.Content.PM; using Android.Hardware.Usb; using Android.OS; using Hoho.Android.UsbSerial.Driver; using System; using System.Threading.Tasks; #endif using System.Collections.Generic; using System.Text; namespace DroneBrainPlus; public static class ArduinoController { #if ANDROID public static UsbManager? _usbManager; private static IUsbSerialDriver? _usbSerialDriver; private static IUsbSerialPort? _usbSerialPort; private static UsbDeviceConnection? _connection; private static TaskCompletionSource<bool>? _permissionTcs; // 权限广播接收器 private class UsbPermissionReceiver : BroadcastReceiver { public const string ACTION_USB_PERMISSION = "com.dronebrainplus.USB_PERMISSION"; public override void OnReceive(Context? context, Intent? intent) { if (intent?.Action == ACTION_USB_PERMISSION) { bool granted = intent.GetBooleanExtra(UsbManager.ExtraPermissionGranted, false); _permissionTcs?.SetResult(granted); } } } public static async Task FindAndConnectToCH34x() { _usbManager = (UsbManager)Android.App.Application.Context.GetSystemService(Android.App.Application.UsbService); var permissionFilter = new IntentFilter(UsbPermissionReceiver.ACTION_USB_PERMISSION); Android.App.Application.Context.RegisterReceiver(new UsbPermissionReceiver(), permissionFilter); // 正确配置CH340驱动 var customTable = UsbSerialProber.DefaultProbeTable; customTable.AddProduct(0x1A86, 0x7523, Class.FromType(typeof(Ch34xSerialDriver))); var prober = new UsbSerialProber(customTable); var availableDrivers = prober.FindAllDrivers(_usbManager); if (availableDrivers.Count == 0) { await Shell.Current.DisplayAlert("USB", "未找到兼容的串口设备", "确定").ConfigureAwait(false); return; } var usbSerialDriver = availableDrivers.FirstOrDefault(); if (usbSerialDriver == null) { await Shell.Current.DisplayAlert("USB", "未找到串口驱动", "确定").ConfigureAwait(false); return; } var device = usbSerialDriver.Device; // 处理权限请求 if (!_usbManager.HasPermission(device)) { _permissionTcs = new TaskCompletionSource<bool>(); var permissionIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, new Intent(UsbPermissionReceiver.ACTION_USB_PERMISSION), PendingIntentFlags.Immutable); _usbManager.RequestPermission(device, permissionIntent); bool permissionGranted = await _permissionTcs.Task.ConfigureAwait(false); if (!permissionGranted) { await Shell.Current.DisplayAlert("USB", "未获取到USB设备权限", "确定").ConfigureAwait(false); return; } } // 打开设备连接 _connection = _usbManager.OpenDevice(device); if (_connection == null) { await Shell.Current.DisplayAlert("USB", "无法打开USB设备连接", "确定").ConfigureAwait(false); return; } var usbSerialPort = usbSerialDriver.Ports.FirstOrDefault(); if (usbSerialPort == null) { await Shell.Current.DisplayAlert("USB", "未找到可用串口", "确定").ConfigureAwait(false); return; } try { _usbSerialPort = usbSerialPort; _usbSerialPort.Open(_connection); _usbSerialPort.SetParameters(9600, 8, StopBits.One, Parity.None); await Shell.Current.DisplayAlert("USB", "设备连接成功", "确定").ConfigureAwait(false); } catch (Exception ex) { await Shell.Current.DisplayAlert("错误", $"连接失败:{ex.Message}", "确定").ConfigureAwait(false); CleanupResources(); } } public static async Task SendData(string message) { if (_usbSerialPort == null) { await Shell.Current.DisplayAlert("提示", "未连接到设备", "确定").ConfigureAwait(false); return; } try { byte[] buffer = Encoding.UTF8.GetBytes(message + "\n"); // 适配Arduino换行触发读取逻辑 int bytesWritten = _usbSerialPort.Write(buffer, 1000); await Shell.Current.DisplayAlert("USB", $"已发送:{message}({bytesWritten}字节)", "确定").ConfigureAwait(false); } catch (Exception ex) { await Shell.Current.DisplayAlert("错误", $"发送失败:{ex.Message}", "确定").ConfigureAwait(false); } } public static async Task ReadData() { if (_usbSerialPort == null) { await Shell.Current.DisplayAlert("提示", "未连接到设备", "确定").ConfigureAwait(false); return; } try { byte[] buffer = new byte[64]; int numBytesRead = _usbSerialPort.Read(buffer, 1000); if (numBytesRead > 0) { string receivedData = Encoding.UTF8.GetString(buffer, 0, numBytesRead).Trim(); await Shell.Current.DisplayAlert("USB", $"已接收:{receivedData}", "确定").ConfigureAwait(false); } else { await Shell.Current.DisplayAlert("提示", "未读取到数据", "确定").ConfigureAwait(false); } } catch (Exception ex) { await Shell.Current.DisplayAlert("错误", $"读取失败:{ex.Message}", "确定").ConfigureAwait(false); } } public static void CleanupResources() { _usbSerialPort?.Close(); _connection?.Close(); _usbSerialPort = null; _connection = null; _usbSerialDriver = null; } #endif }
额外配置与注意事项
- Android权限配置:在
AndroidManifest.xml中添加以下权限:<uses-permission android:name="android.permission.USB_PERMISSION" /> <uses-feature android:name="android.hardware.usb.host" android:required="false" /> - 设备兼容性:确保Android设备支持USB OTG功能,使用合格的OTG线缆
- Arduino端适配:Arduino代码需用
Serial.readStringUntil('\n')等方式读取带换行符的数据
内容的提问来源于stack exchange,提问作者Maganes
相关产品推荐
相关产品推荐

