当前位置: 首页 > news >正文

.NET MAUI跨平台串口通讯方案

文章目录

      • MAUI项目架构设计
      • 平台特定实现
        • 接口定义
        • Windows平台实现
        • Android平台实现
      • MAUI主界面实现
      • 依赖注入配置
      • 相关学习资源
        • .NET MAUI开发
        • 移动端开发
        • 平台特定实现
        • 依赖注入与架构
        • 移动应用发布
        • 跨平台开发最佳实践
        • 性能优化
        • 测试与调试
        • 开源项目参考

MAUI项目架构设计

MAUI App
共享业务逻辑
串口服务接口
Windows实现
Android实现
iOS实现
macOS实现
System.IO.Ports
Android USB/Serial
External Accessory
IOKit Framework

平台特定实现

接口定义
/// <summary>
/// 跨平台串口服务接口
/// </summary>
public interface ISerialPortService
{/// <summary>/// 获取可用串口列表/// </summary>Task<string[]> GetAvailablePortsAsync();/// <summary>/// 连接到指定串口/// </summary>Task<bool> ConnectAsync(string portName, int baudRate);/// <summary>/// 断开串口连接/// </summary>Task DisconnectAsync();/// <summary>/// 发送数据/// </summary>Task SendDataAsync(byte[] data);/// <summary>/// 发送文本数据/// </summary>Task SendTextAsync(string text);/// <summary>/// 数据接收事件/// </summary>event EventHandler<SerialDataEventArgs> DataReceived;/// <summary>/// 连接状态变化事件/// </summary>event EventHandler<bool> ConnectionChanged;/// <summary>/// 是否已连接/// </summary>bool IsConnected { get; }
}/// <summary>
/// 串口数据事件参数
/// </summary>
public class SerialDataEventArgs : EventArgs
{public byte[] Data { get; set; }public string Text { get; set; }public DateTime Timestamp { get; set; }
}
Windows平台实现
#if WINDOWS
using System.IO.Ports;/// <summary>
/// Windows平台串口服务实现
/// </summary>
public class WindowsSerialPortService : ISerialPortService
{private SerialPort _serialPort;private bool _isConnected;public bool IsConnected => _isConnected;public event EventHandler<SerialDataEventArgs> DataReceived;public event EventHandler<bool> ConnectionChanged;public WindowsSerialPortService(){_serialPort = new SerialPort();_serialPort.DataReceived += OnDataReceived;}public async Task<string[]> GetAvailablePortsAsync(){return await Task.FromResult(SerialPort.GetPortNames());}public async Task<bool> ConnectAsync(string portName, int baudRate){try{if (_isConnected)await DisconnectAsync();_serialPort.PortName = portName;_serialPort.BaudRate = baudRate;_serialPort.DataBits = 8;_serialPort.Parity = Parity.None;_serialPort.StopBits = StopBits.One;_serialPort.Open();_isConnected = true;ConnectionChanged?.Invoke(this, true);return true;}catch (Exception ex){_isConnected = false;ConnectionChanged?.Invoke(this, false);return false;}}public async Task DisconnectAsync(){try{if (_serialPort?.IsOpen == true){_serialPort.Close();}_isConnected = false;ConnectionChanged?.Invoke(this, false);}catch (Exception){// 忽略关闭时的异常}}public async Task SendDataAsync(byte[] data){if (!_isConnected || !_serialPort.IsOpen)throw new InvalidOperationException("串口未连接");await Task.Run(() => _serialPort.Write(data, 0, data.Length));}public async Task SendTextAsync(string text){var data = System.Text.Encoding.UTF8.GetBytes(text);await SendDataAsync(data);}private void OnDataReceived(object sender, SerialDataReceivedEventArgs e){try{var buffer = new byte[_serialPort.BytesToRead];var bytesRead = _serialPort.Read(buffer, 0, buffer.Length);var eventArgs = new SerialDataEventArgs{Data = buffer.Take(bytesRead).ToArray(),Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),Timestamp = DateTime.Now};DataReceived?.Invoke(this, eventArgs);}catch (Exception){// 处理读取异常}}
}
#endif
Android平台实现
#if ANDROID
using Android.Hardware.Usb;
using AndroidX.Core.Content;/// <summary>
/// Android平台串口服务实现
/// 基于USB Host模式
/// </summary>
public class AndroidSerialPortService : ISerialPortService
{private UsbManager _usbManager;private UsbDevice _usbDevice;private UsbDeviceConnection _connection;private UsbInterface _interface;private UsbEndpoint _endpointIn;private UsbEndpoint _endpointOut;private bool _isConnected;private CancellationTokenSource _readCancellation;public bool IsConnected => _isConnected;public event EventHandler<SerialDataEventArgs> DataReceived;public event EventHandler<bool> ConnectionChanged;public AndroidSerialPortService(){var context = Platform.CurrentActivity ?? Android.App.Application.Context;_usbManager = (UsbManager)context.GetSystemService(Android.Content.Context.UsbService);}public async Task<string[]> GetAvailablePortsAsync(){var deviceList = _usbManager.DeviceList;var portNames = new List<string>();foreach (var device in deviceList.Values){// 检查是否为串口设备(根据VID/PID或设备类型判断)if (IsSerialDevice(device)){portNames.Add($"USB-{device.DeviceName}");}}return portNames.ToArray();}public async Task<bool> ConnectAsync(string portName, int baudRate){try{// Android USB串口连接实现// 这里需要根据具体的USB转串口芯片实现var deviceList = _usbManager.DeviceList;foreach (var device in deviceList.Values){if ($"USB-{device.DeviceName}" == portName){_usbDevice = device;break;}}if (_usbDevice == null)return false;// 请求USB权限if (!_usbManager.HasPermission(_usbDevice)){// 需要请求权限return false;}_connection = _usbManager.OpenDevice(_usbDevice);if (_connection == null)return false;// 配置USB设备_interface = _usbDevice.GetInterface(0);_connection.ClaimInterface(_interface, true);// 找到输入输出端点for (int i = 0; i < _interface.EndpointCount; i++){var endpoint = _interface.GetEndpoint(i);if (endpoint.Direction == UsbAddressing.In)_endpointIn = endpoint;else if (endpoint.Direction == UsbAddressing.Out)_endpointOut = endpoint;}_isConnected = true;ConnectionChanged?.Invoke(this, true);// 启动数据读取StartDataReading();return true;}catch (Exception){_isConnected = false;ConnectionChanged?.Invoke(this, false);return false;}}public async Task DisconnectAsync(){_isConnected = false;_readCancellation?.Cancel();_connection?.ReleaseInterface(_interface);_connection?.Close();ConnectionChanged?.Invoke(this, false);}public async Task SendDataAsync(byte[] data){if (!_isConnected || _connection == null || _endpointOut == null)throw new InvalidOperationException("设备未连接");await Task.Run(() =>{_connection.BulkTransfer(_endpointOut, data, data.Length, 1000);});}public async Task SendTextAsync(string text){var data = System.Text.Encoding.UTF8.GetBytes(text);await SendDataAsync(data);}private void StartDataReading(){_readCancellation = new CancellationTokenSource();Task.Run(async () =>{var buffer = new byte[1024];while (!_readCancellation.Token.IsCancellationRequested && _isConnected){try{var bytesRead = _connection.BulkTransfer(_endpointIn, buffer, buffer.Length, 100);if (bytesRead > 0){var eventArgs = new SerialDataEventArgs{Data = buffer.Take(bytesRead).ToArray(),Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),Timestamp = DateTime.Now};DataReceived?.Invoke(this, eventArgs);}await Task.Delay(10);}catch (Exception){// 处理读取异常}}});}private bool IsSerialDevice(UsbDevice device){// 根据VID/PID或设备类型判断是否为串口设备// 这里可以添加常见USB转串口芯片的识别逻辑return device.DeviceClass == UsbClass.CdcData || device.DeviceClass == UsbClass.Comm;}
}
#endif

MAUI主界面实现

/// <summary>
/// MAUI主页面
/// </summary>
public partial class MainPage : ContentPage
{private readonly ISerialPortService _serialService;private readonly ObservableCollection<string> _receivedMessages;public MainPage(ISerialPortService serialService){InitializeComponent();_serialService = serialService;_receivedMessages = new ObservableCollection<string>();MessagesCollectionView.ItemsSource = _receivedMessages;// 绑定事件_serialService.DataReceived += OnDataReceived;_serialService.ConnectionChanged += OnConnectionChanged;// 加载可用串口LoadAvailablePorts();}private async void LoadAvailablePorts(){try{var ports = await _serialService.GetAvailablePortsAsync();PortPicker.ItemsSource = ports;if (ports.Length > 0)PortPicker.SelectedIndex = 0;}catch (Exception ex){await DisplayAlert("错误", $"加载串口列表失败: {ex.Message}", "确定");}}private async void OnConnectClicked(object sender, EventArgs e){try{if (_serialService.IsConnected){await _serialService.DisconnectAsync();}else{if (PortPicker.SelectedItem == null){await DisplayAlert("提示", "请选择串口", "确定");return;}var portName = PortPicker.SelectedItem.ToString();var baudRate = int.Parse(BaudRatePicker.SelectedItem?.ToString() ?? "9600");var success = await _serialService.ConnectAsync(portName, baudRate);if (!success){await DisplayAlert("错误", "连接失败", "确定");}}}catch (Exception ex){await DisplayAlert("错误", $"连接操作失败: {ex.Message}", "确定");}}private async void OnSendClicked(object sender, EventArgs e){try{if (!_serialService.IsConnected){await DisplayAlert("提示", "请先连接串口", "确定");return;}var text = SendEntry.Text;if (string.IsNullOrWhiteSpace(text)){await DisplayAlert("提示", "请输入要发送的内容", "确定");return;}await _serialService.SendTextAsync(text + "\r\n");SendEntry.Text = string.Empty;// 在消息列表中显示发送的内容_receivedMessages.Add($"[发送] {DateTime.Now:HH:mm:ss} - {text}");}catch (Exception ex){await DisplayAlert("错误", $"发送失败: {ex.Message}", "确定");}}private void OnDataReceived(object sender, SerialDataEventArgs e){// 在UI线程中更新界面MainThread.BeginInvokeOnMainThread(() =>{_receivedMessages.Add($"[接收] {e.Timestamp:HH:mm:ss} - {e.Text.Trim()}");// 自动滚动到最新消息if (_receivedMessages.Count > 0){MessagesCollectionView.ScrollTo(_receivedMessages.Last());}});}private void OnConnectionChanged(object sender, bool isConnected){MainThread.BeginInvokeOnMainThread(() =>{ConnectButton.Text = isConnected ? "断开" : "连接";StatusLabel.Text = isConnected ? "已连接" : "未连接";StatusLabel.TextColor = isConnected ? Colors.Green : Colors.Red;// 控制界面元素的启用状态PortPicker.IsEnabled = !isConnected;BaudRatePicker.IsEnabled = !isConnected;SendButton.IsEnabled = isConnected;SendEntry.IsEnabled = isConnected;});}private async void OnRefreshPortsClicked(object sender, EventArgs e){await LoadAvailablePorts();}
}

依赖注入配置

/// <summary>
/// MAUI应用程序配置
/// </summary>
public static class MauiProgram
{public static MauiApp CreateMauiApp(){var builder = MauiApp.CreateBuilder();builder.UseMauiApp<App>().ConfigureFonts(fonts =>{fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");});// 注册平台特定的串口服务
#if WINDOWSbuilder.Services.AddSingleton<ISerialPortService, WindowsSerialPortService>();
#elif ANDROIDbuilder.Services.AddSingleton<ISerialPortService, AndroidSerialPortService>();
#elif IOSbuilder.Services.AddSingleton<ISerialPortService, iOSSerialPortService>();
#elif MACCATALYSTbuilder.Services.AddSingleton<ISerialPortService, MacCatalystSerialPortService>();
#endif// 注册页面builder.Services.AddTransient<MainPage>();return builder.Build();}
}

相关学习资源

.NET MAUI开发
  • .NET MAUI官方文档 - 微软官方MAUI开发指南
  • MAUI Community Toolkit - MAUI社区工具包
  • .NET MAUI Samples - 官方MAUI示例项目
  • MAUI Blazor - MAUI混合应用开发
移动端开发
  • Android开发者文档 - Google官方Android开发指南
  • iOS开发文档 - Apple官方iOS开发文档
  • Xamarin.Forms指南 - Xamarin.Forms开发文档
  • Mobile DevOps - 移动应用DevOps平台
平台特定实现
  • Android USB Host - Android USB主机模式
  • iOS External Accessory - iOS外部配件框架
  • Windows Runtime API - Windows Runtime API文档
  • macOS IOKit - macOS硬件访问框架
依赖注入与架构
  • Microsoft.Extensions.DependencyInjection - .NET依赖注入
  • MVVM Pattern - MVVM架构模式
  • CommunityToolkit.Mvvm - MVVM工具包
  • Prism Framework - 企业级MVVM框架
移动应用发布
  • Google Play Console - Android应用发布平台
  • App Store Connect - iOS应用发布平台
  • Microsoft Store - Windows应用商店
  • App Center Distribution - 应用分发服务
跨平台开发最佳实践
  • Platform Behaviors - 平台集成最佳实践
  • Conditional Compilation - 条件编译指令
性能优化
  • MAUI Performance - MAUI性能优化指南
  • Memory Management - 内存管理最佳实践
  • Battery Optimization - 电池优化策略
测试与调试
  • MAUI Unit Testing - MAUI单元测试
  • UI Testing - UI自动化测试
  • Remote Debugging - 远程调试工具
  • App Center Analytics - 应用分析服务
开源项目参考
  • .NET Podcasts App - 微软MAUI示例应用
  • Weather MAUI App - 天气应用示例

在这里插入图片描述

http://www.lryc.cn/news/576245.html

相关文章:

  • 【github】从本地更新仓库里的文件笔记
  • C++基础:动态内存分配、输入输出与命名空间详解
  • 如何构建个人AIagent
  • 命名数据网络 | 签名(Signature)
  • 视觉疲劳检测如何优化智能驾驶的险情管理
  • 杭州西湖断桥不断:3D扫描还原‘残雪‘视觉骗局
  • 从0到100:房产中介小程序开发笔记(中)
  • 为什么python处理csv文件将某个值替换成另一个值并另存后,csv文件的大小减小了一半
  • 详解HashMap底层原理
  • 三、java项目自动部署流水线搭建
  • Java--数组
  • 如何使用免费软件写论文?六个免费论文生成软件使用指南
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的目标轨迹预测与防范策略制定(325)
  • Class2基础优化算法
  • 悦己汉服体验馆小程序(协同过滤算法、WebSocket即时聊天)
  • 优化 ArcPy 脚本性能
  • 桌面小屏幕实战课程:DesktopScreen 13 HTTP SERVER
  • 电子电气架构 --- 涵盖“诊断与 ECU 平台”领域特有项目要求(上)
  • 鸿蒙 List 组件解析:从基础列表到高性能界面开发指南
  • 智能制造数字孪生集成交付生态链:智慧产线极速克隆,孪生重构生产周期
  • 什么是 A/B 测试?
  • swift 对象转Json
  • matplotlib 绘制热力图
  • 基于JavaWeb的校园失物招领系统设计与实现
  • 【图像处理入门】12. 综合项目与进阶:超分辨率、医学分割与工业检测
  • SQL学习笔记4
  • 华为云Flexus+DeepSeek征文|高可用部署 Dify 平台构建 Flux 绘画中台 Chatflow 的设计与实现
  • SYSCFG 时钟在 GD32F4 系列微控制器中的作用
  • 【策划所需编程知识】
  • 每日算法刷题Day39 6.26:leetcode前缀和2道题,用时1h20min