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

ESP32WROOM串口传大文件,C#无法完整接收单块数据求解决方案

Hey there! I totally get your frustration—serial communication can be tricky when dealing with fixed-size packets, especially when the DataReceived event spits out chunks instead of full messages. Let's walk through a reliable solution for both your ESP32 transmission code and C# reception logic to make sure those 517-byte blocks are handled correctly every time.

ESP32 Transmission Code

First, let's make sure your ESP32 is sending packets in the exact structure you defined. We'll create a helper function to assemble and send each 517-byte chunk (4-byte length + 4-byte command + 508-byte payload + 1-byte checksum):

#include <HardwareSerial.h>

// Use the appropriate serial port for your setup (e.g., Serial2 for GPIO 16/17)
HardwareSerial serialPort(2);

void setup() {
  // Initialize serial with your desired baud rate (match C# side!)
  serialPort.begin(115200);
}

// Assemble and send a complete packet per your specification
void sendFileChunk(int command, byte* payloadData, int payloadLength) {
  // Total packet length: 4 (length) + 4 (command) + payload + 1 (checksum)
  const int totalPacketLength = 4 + 4 + payloadLength + 1;
  byte packetBuffer[totalPacketLength];

  // Write message length in big-endian format (matches your example: 0x00,0x00,0x02,0x05 = 517)
  packetBuffer[0] = (totalPacketLength >> 24) & 0xFF;
  packetBuffer[1] = (totalPacketLength >> 16) & 0xFF;
  packetBuffer[2] = (totalPacketLength >> 8) & 0xFF;
  packetBuffer[3] = totalPacketLength & 0xFF;

  // Write command in big-endian format (e.g., 0xBBBBBBBB from your example)
  packetBuffer[4] = (command >> 24) & 0xFF;
  packetBuffer[5] = (command >> 16) & 0xFF;
  packetBuffer[6] = (command >> 8) & 0xFF;
  packetBuffer[7] = command & 0xFF;

  // Copy payload data into the packet
  memcpy(&packetBuffer[8], payloadData, payloadLength);

  // Calculate checksum (XOR all preceding bytes—match this logic in C#!)
  byte checksum = 0;
  for (int i = 0; i < totalPacketLength - 1; i++) {
    checksum ^= packetBuffer[i];
  }
  packetBuffer[totalPacketLength - 1] = checksum;

  // Send the entire packet at once, then wait for transmission to finish
  serialPort.write(packetBuffer, totalPacketLength);
  serialPort.flush();
}

// Example usage when sending a 508-byte file chunk
void loop() {
  // Assume you've downloaded a chunk into chunkBuffer[508]
  byte chunkBuffer[508];
  // ... fill chunkBuffer with your file data ...
  
  int downloadCommand = 0xBBBBBBBB; // Replace with your actual command value
  sendFileChunk(downloadCommand, chunkBuffer, 508);
  
  // Add delays or logic to handle the next chunk as needed
  delay(10);
}

C# Reception Logic

The main issue you're facing is that serial data arrives as a stream—there's no guarantee a single DataReceived event will give you a full 517-byte packet. The fix is to maintain a global receive buffer to accumulate bytes, then process the buffer to extract complete packets.

Here's a complete implementation:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;

class SerialPacketReceiver
{
    private SerialPort _serialPort;
    private List<byte> _receiveBuffer = new List<byte>();

    public SerialPacketReceiver()
    {
        // Initialize serial port (adjust COM port, baud rate to match ESP32)
        _serialPort = new SerialPort("COM3", 115200, Parity.None, 8, StopBits.One);
        _serialPort.DataReceived += Port_DataReceived;
        _serialPort.Open();
        Console.WriteLine("Serial port opened, waiting for data...");
    }

    private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        var port = (SerialPort)sender;
        int bytesAvailable = port.BytesToRead;
        byte[] tempBuffer = new byte[bytesAvailable];
        
        // Read incoming bytes into a temporary buffer
        port.Read(tempBuffer, 0, bytesAvailable);
        
        // Add new bytes to our global receive buffer (thread-safe)
        lock (_receiveBuffer)
        {
            _receiveBuffer.AddRange(tempBuffer);
        }
        
        // Process the buffer to extract complete packets
        ProcessReceivedPackets();
    }

    private void ProcessReceivedPackets()
    {
        lock (_receiveBuffer)
        {
            // Loop until we don't have enough data for a full packet
            while (_receiveBuffer.Count >= 4) // First 4 bytes are message length
            {
                // Extract and parse the message length (big-endian format)
                byte[] lengthBytes = _receiveBuffer.Take(4).ToArray();
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(lengthBytes); // Convert to big-endian for system compatibility
                }
                int packetLength = BitConverter.ToInt32(lengthBytes, 0);

                // Check if we have a full packet in the buffer
                if (_receiveBuffer.Count >= packetLength)
                {
                    // Extract the complete packet
                    byte[] fullPacket = _receiveBuffer.Take(packetLength).ToArray();
                    // Remove the processed bytes from the buffer
                    _receiveBuffer.RemoveRange(0, packetLength);

                    // Handle the valid packet (write to file, etc.)
                    HandleValidPacket(fullPacket);
                }
                else
                {
                    // Not enough data for a full packet yet—wait for more
                    break;
                }
            }
        }
    }

    private void HandleValidPacket(byte[] packet)
    {
        // Parse command (big-endian)
        byte[] commandBytes = packet.Skip(4).Take(4).ToArray();
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(commandBytes);
        }
        int command = BitConverter.ToInt32(commandBytes, 0);

        // Parse payload (from byte 8 to byte [packetLength-2])
        int payloadLength = packet.Length - 9; // 4+4+1 =9 bytes of header/checksum
        byte[] payload = packet.Skip(8).Take(payloadLength).ToArray();

        // Parse and verify checksum
        byte receivedChecksum = packet[packet.Length - 1];
        bool isChecksumValid = VerifyChecksum(packet, receivedChecksum);

        if (isChecksumValid)
        {
            Console.WriteLine($"Received valid packet | Command: 0x{command:X8} | Payload size: {payloadLength} bytes");
            // Append payload to your downloaded file
            File.AppendAllBytes("downloaded_file.bin", payload);
        }
        else
        {
            Console.WriteLine("Invalid checksum—discarding packet");
            // Optional: Add error handling (e.g., reset buffer, request retransmission)
        }
    }

    // Match the checksum logic from your ESP32 code (XOR in this example)
    private bool VerifyChecksum(byte[] packet, byte receivedChecksum)
    {
        byte calculatedChecksum = 0;
        // Calculate checksum for all bytes except the last one (received checksum)
        for (int i = 0; i < packet.Length - 1; i++)
        {
            calculatedChecksum ^= packet[i];
        }
        return calculatedChecksum == receivedChecksum;
    }
}

// To use:
// var receiver = new SerialPacketReceiver();
// Console.ReadLine();

Key Notes & Troubleshooting

  • Baud Rate Match: Ensure both ESP32 and C# are using the same baud rate (115200 or higher is recommended for large chunks)
  • Thread Safety: The lock around _receiveBuffer is critical because DataReceived runs on a background thread—if you need to update UI elements from HandleValidPacket, use Control.Invoke (WinForms) or Dispatcher.Invoke (WPF)
  • Checksum Consistency: Double-check that your ESP32 and C# checksum algorithms are identical (XOR, sum of bytes, etc.)
  • Buffer Reset: If you encounter corrupted data, add logic to reset the _receiveBuffer and wait for a new valid packet
  • ESP32 Serial Flush: Using serialPort.flush() ensures all bytes are sent before moving to the next chunk

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:02:31