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

发布于:2025-07-02 ⋅ 阅读:(24) ⋅ 点赞:(0)

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 WINDOWS
        builder.Services.AddSingleton<ISerialPortService, WindowsSerialPortService>();
#elif ANDROID
        builder.Services.AddSingleton<ISerialPortService, AndroidSerialPortService>();
#elif IOS
        builder.Services.AddSingleton<ISerialPortService, iOSSerialPortService>();
#elif MACCATALYST
        builder.Services.AddSingleton<ISerialPortService, MacCatalystSerialPortService>();
#endif

        // 注册页面
        builder.Services.AddTransient<MainPage>();

        return builder.Build();
    }
}

相关学习资源

.NET MAUI开发
移动端开发
平台特定实现
依赖注入与架构
移动应用发布
跨平台开发最佳实践
性能优化
测试与调试
开源项目参考

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到