-2

I am working on an extended weekend project where I wanted to see if I could send and print a GCODE file from my windows PC via the serial port and C#.

I am getting the printer to connect but I am not getting consistent results when trying to write to the serial stream. I am not even sure that my serial port settings are correct, I couldn't find any good examples of how OctoPrint connects to my printer for example. I think my problem is that I am sending too much data all at once but I am also struggling to read from the serial connection and wait for only "ok" as they seem to come out of order.

I can connect and send a single line, no problem. Once I give it an entire file with many lines the printer gets to various parts of the file and just hangs.

How can I stream large files over the serial port to a 3D printer without overloading the printers internal buffer?

Code I am using:

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);
}
Bailey Miller
  • 1,376
  • 2
  • 20
  • 36
  • Can you clarify what your actual question is here? – stuartd May 01 '23 at 20:08
  • 1
    @stuartd I updated it, essentially I am looking to understand if my serial settings are wrong or if I need to do something special to write in chunks. – Bailey Miller May 01 '23 at 20:12
  • 1
    "*How can I stream large files over the serial port to a 3D printer without overloading the printers internal buffer?*" -- Using either hardware or software flow-control (aka handshake) are the conventional methods of avoiding/solving such overrun problems. Both sides of the serial link must support the flow-control. Note that your code specifies `Handshake.None` for its side. – sawdust May 02 '23 at 04:01

1 Answers1

0

Code that solved this for me:

Specifically:

  • Reading the response in a better way, from what I can tell I wasn't reading complete lines and wasn't catching the "ok" response all the time.
  • Using an event wait handler (AutoReset) to tell the writer when it was "ok" to send more data to the printer

Areas for improvement:

  • I am still not sure that I am connecting to the printer correctly. The way that I read data received feels wrong.
  • Capturing the busy response from the printer and delaying the next message by a few ms to not spam the serial connection.
internal class Program
{
    private static AutoResetEvent okWaiter = new AutoResetEvent(false);

    private static async Task Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Console()
            .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
            .CreateLogger();

        // Starting application
        Log.Information("Application starting...");

        try
        {
            var ports = SerialPort.GetPortNames();

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

                    printer.Open();

                    printer.DataReceived += DataReceived;

                    var sender = Task.Factory.StartNew(() =>
                    {
                        okWaiter.Set();
                        using (var fileToPrint = File.OpenRead({Path to GCODE}))
                        using (var lineReader = new StreamReader(fileToPrint))
                        {
                            while (!lineReader.EndOfStream && lineReader is not null)
                            {
                                var line = lineReader.ReadLine() ?? string.Empty;

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

                                    okWaiter.WaitOne();
                                    Log.Information("Sending: {gcode}", cleanLine);
                                    printer.WriteLine(cleanLine);

                                }
                            }
                        }

                        Log.Information("Print complete");
                    });

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

                    printer.Close();
                }
            }
        }
        catch (Exception e)
        {
            Log.Error(e, "Application error");
        }
        finally
        {
            // Closing application
            Log.Information("Application closing...");
            await Log.CloseAndFlushAsync();
        }
    }

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

        using (var textReader = new StreamReader(sp.BaseStream))
        {
            while (!textReader.EndOfStream)
            {
                var line = textReader.ReadLine() ?? "";

                Log.Information("Response: {response}", line);

                if (line.StartsWith("T:"))
                {
                    var pattern = @"\d+\.\d{1,2}";
                    var temps = line.Split(" ");

                    var nozzleTemp = Regex.Match(temps.First(), pattern);
                    var bedTemp = Regex.Match(temps.Last(), pattern);

                    Console.Title = $"Extruder {nozzleTemp} Bed {bedTemp}";
                }

                if (line == "ok")
                {
                    okWaiter.Set();
                }
            }
        }
    }
}
Bailey Miller
  • 1,376
  • 2
  • 20
  • 36