You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将串口接收的二进制字节数组转换为对象?

问题原因分析

你用BinaryFormatter解析失败是必然的——BinaryFormatter是.NET专属的序列化工具,它序列化的二进制数据包含类型元数据、额外结构信息,和Arduino直接发送的C语言结构体二进制格式完全不兼容。Arduino发送的是结构体字段按内存布局直接转的原始二进制,没有任何额外信息,必须按字段类型、顺序、字节序手动解析。

解决方案

1. 定义C#对应结构体

先在WPF项目里定义和Arduino结构体字段完全匹配的C#结构体,注意对齐方式(Arduino默认1字节对齐,C#里要显式指定):

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MainCompPacket
{
    public byte status;               // 对应uint8_t
    public byte sensorStatus;         // 对应uint8_t
    public float pressure;            // 对应float(4字节IEEE754)
    public float accX;                // 对应float
    public float accY;                // 对应float
    public float accZ;                // 对应float
    public float gyroX;               // 对应float
    public float gyroY;               // 对应float
    public float gyroZ;               // 对应float
    public double latitude;           // 对应double(8字节IEEE754)
    public double longitude;          // 对应double
}

2. 处理串口接收的完整数据包

SerialPort的DataReceived事件可能只返回部分数据,所以需要先缓存数据,直到凑够一个完整的结构体字节数(计算得:1+1+4*7+8*2=46字节):

private readonly MemoryStream _receiveCache = new MemoryStream();
private readonly int _fullPacketSize = Marshal.SizeOf(typeof(MainCompPacket)); // 结果为46

private void dataRecieved(object sender, SerialDataReceivedEventArgs e)
{
    var serialPort = (SerialPort)sender;
    var tempBuf = new byte[serialPort.BytesToRead];
    int readCount = serialPort.Read(tempBuf, 0, tempBuf.Length);
    
    // 将新数据写入缓存
    _receiveCache.Write(tempBuf, 0, readCount);
    
    // 循环处理所有完整的数据包
    while (_receiveCache.Length >= _fullPacketSize)
    {
        // 读取一个完整包
        _receiveCache.Position = 0;
        var packetBuf = new byte[_fullPacketSize];
        _receiveCache.Read(packetBuf, 0, _fullPacketSize);
        
        // 保留缓存中剩余的不完整数据
        var remainingData = new byte[_receiveCache.Length - _fullPacketSize];
        _receiveCache.Read(remainingData, 0, remainingData.Length);
        _receiveCache.SetLength(0);
        _receiveCache.Write(remainingData, 0, remainingData.Length);
        
        // 解析数据包
        var parsedPacket = ParsePacket(packetBuf);
        
        // 更新UI(必须用Dispatcher,因为串口事件在后台线程)
        Dispatcher.Invoke(() =>
        {
            // 这里写你的UI更新逻辑,比如绑定数据到界面控件
            // 示例:txtPressure.Text = parsedPacket.pressure.ToString("F2");
        });
    }
}

3. 解析二进制数据包

有两种可靠的解析方式,选一种即可:

方式一:用Marshal直接转换(高效)

private MainCompPacket ParsePacket(byte[] buffer)
{
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    try
    {
        IntPtr ptr = handle.AddrOfPinnedObject();
        return (MainCompPacket)Marshal.PtrToStructure(ptr, typeof(MainCompPacket));
    }
    finally
    {
        handle.Free();
    }
}

方式二:手动逐字段解析(更直观,无需非托管内存)

private MainCompPacket ParsePacket(byte[] buffer)
{
    var packet = new MainCompPacket();
    int offset = 0;
    
    // 解析byte类型字段
    packet.status = buffer[offset++];
    packet.sensorStatus = buffer[offset++];
    
    // 解析float类型字段(4字节,小端序,和Arduino一致)
    packet.pressure = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.accX = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.accY = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.accZ = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.gyroX = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.gyroY = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    packet.gyroZ = BitConverter.ToSingle(buffer, offset);
    offset += 4;
    
    // 解析double类型字段(8字节)
    packet.latitude = BitConverter.ToDouble(buffer, offset);
    offset += 8;
    
    packet.longitude = BitConverter.ToDouble(buffer, offset);
    
    return packet;
}

注意事项

  • 字节序:Arduino的AVR架构是小端序,x86/x64平台的WPF程序默认也是小端序,所以无需转换;如果运行在ARM大端平台,需要手动反转每个字段的字节顺序。
  • 数据校验:如果串口传输可能丢包/出错,可以在Arduino的结构体前后加校验位(比如CRC),解析前先校验数据包完整性。

内容的提问来源于stack exchange,提问作者Emin Niftiyev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:15:31