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

Prusa i3 Mk3s+通过C#串口传输GCODE异常:如何避免缓冲区过载?

问题描述

我正在进行一个周末拓展项目,尝试通过Windows PC的串口,使用C#向Prusa i3 Mk3s+打印机发送并打印GCODE文件。目前已成功建立连接,但向串口流写入数据时结果不稳定,且不确定串口配置是否正确(未找到OctoPrint与该打印机的连接示例)。我推测问题在于一次性发送数据量过大,同时读取串口返回信息时,难以准确等待"ok"指令(返回信息顺序混乱)。单条指令发送无问题,但发送完整GCODE文件时,打印机在执行到文件不同位置时会出现卡顿。请问如何通过串口向3D打印机流式传输大文件,避免其内部缓冲区过载?

现有代码

private static async Task Main(string[] args)
{
    var ports = SerialPort.GetPortNames();

    if (!ports.Any())
    {
        Console.WriteLine("No ports found");
        Console.WriteLine("Press any key to continue...");
        Console.WriteLine();
        Console.ReadKey();
    }
    else
    {
        using (SerialPort mySerialPort = new SerialPort(ports.First()))
        {
            mySerialPort.BaudRate = 115200;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;

            mySerialPort.Open();

            mySerialPort.DataReceived += DataRecieved;

            using (var fileToPrint = File.OpenRead("{Path to GCODE file}"))
            using (var lineReader = new StreamReader(fileToPrint))
            {
                while (!lineReader.EndOfStream && lineReader is not null)
                {
                    var line = await lineReader.ReadLineAsync() ?? string.Empty;

                    if (!line.StartsWith(";"))
                    {
                        var cleanLine = line.Split(";").First().Trim();

                        mySerialPort.WriteLine(cleanLine);
                    }
                }
            }

            Console.WriteLine("Press any key to continue...");
            Console.WriteLine();
            Console.ReadKey();

            mySerialPort.Close();
        }
    }
}

private static void DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;

    var line = sp.ReadLine();

    Console.WriteLine("Data from printer: {0}", line);
}
解决方案

1. 确认串口配置正确性

Prusa i3 Mk3s+的标准串口配置和你当前设置完全一致,无需调整:

  • 波特率:115200
  • 奇偶校验:无
  • 停止位:1
  • 数据位:8
  • 握手:无

2. 核心改进:实现"等待ok再发下一条"的流控逻辑

打印机串口缓冲区容量有限,不能连续无限制发送指令。必须采用一问一答的方式,收到打印机返回的"ok"后再发送下一条GCODE,这是解决缓冲区过载、卡顿问题的关键。

修改后的示例代码:

private static async Task Main(string[] args)
{
    var ports = SerialPort.GetPortNames();

    if (!ports.Any())
    {
        Console.WriteLine("未找到可用串口");
        Console.WriteLine("按任意键继续...");
        Console.ReadKey();
        return;
    }

    using (SerialPort mySerialPort = new SerialPort(ports.First()))
    {
        // 保持原有正确配置
        mySerialPort.BaudRate = 115200;
        mySerialPort.Parity = Parity.None;
        mySerialPort.StopBits = StopBits.One;
        mySerialPort.DataBits = 8;
        mySerialPort.Handshake = Handshake.None;
        mySerialPort.NewLine = "\n"; // 明确指定换行符,匹配打印机通信协议

        // 用于同步指令发送与响应的信号量,初始为0,等待打印机的第一个ok
        var responseSemaphore = new SemaphoreSlim(0, 1);

        mySerialPort.DataReceived += (sender, e) =>
        {
            SerialPort sp = (SerialPort)sender;
            string line;
            // 循环读取所有可用数据,避免遗漏返回信息
            while (sp.BytesToRead > 0)
            {
                line = sp.ReadLine().Trim();
                Console.WriteLine("打印机返回: {0}", line);
                
                // 检测到ok信号后,释放信号量允许发送下一条指令
                if (line.Equals("ok", StringComparison.OrdinalIgnoreCase))
                {
                    responseSemaphore.Release();
                }
            }
        };

        mySerialPort.Open();
        Console.WriteLine("等待打印机初始化完成...");
        // 等待打印机发送第一个ok,确认连接就绪
        await responseSemaphore.WaitAsync();

        using (var fileToPrint = File.OpenRead("{GCODE文件路径}"))
        using (var lineReader = new StreamReader(fileToPrint))
        {
            string line;
            while ((line = await lineReader.ReadLineAsync()) != null)
            {
                // 过滤注释行、空行,减少无效通信
                line = line.Split(';')[0].Trim();
                if (string.IsNullOrWhiteSpace(line))
                    continue;

                // 发送指令
                Console.WriteLine("发送指令: {0}", line);
                mySerialPort.WriteLine(line);

                // 等待打印机返回ok后再继续
                await responseSemaphore.WaitAsync();
            }
        }

        Console.WriteLine("打印任务已发送完成");
        Console.WriteLine("按任意键继续...");
        Console.ReadKey();
        mySerialPort.Close();
    }
}

3. 其他优化点

  • 读取逻辑优化:在DataReceived事件中循环读取所有可用数据,避免因单次读取一行导致的返回信息遗漏或顺序混乱
  • 指令过滤:提前过滤空行和纯注释行,减少不必要的串口传输
  • 初始化等待:打印机开机后需要等待其完成初始化并返回第一个"ok",再开始发送指令,避免初始指令丢失

4. 可选增强

可以给WaitAsync()添加超时参数(如await responseSemaphore.WaitAsync(TimeSpan.FromSeconds(10))),避免因打印机无响应导致程序卡死。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 05:22:32