1

I'm working on building a tool that can communication with 3D printers in Golang. The problem that I'm having is stumping me, it seems that I can initiate the connection to the printer. The LCD screen changes, the code starts to run, but whenever I try to read from the port I never get anything back. Specifically they're supposed to return an "ok" message so I know to continue the next message. Additionally when I send the Gcode strings it never seems to understand them, as far as I can tell they're just dropped. Here's the sample code that I'm using:

package printer

import (
    "errors"
    "fmt"
    "log"
    "strings"
    "time"
    "io/ioutil"
    "path/filepath"

    "github.com/tarm/serial"
)

//Printer is a connection to a reprap or similar printer
type Printer struct {
    s *serial.Port
}

//Connect creates the printer struct and returns it after initing it
func Connect(port string, speed int64) Printer {
    devices, err := getSerialDevices()
    if err != nil {
        log.Println(err)
    }
    for _, value := range devices {
      log.Println("Found device: ", value)
    }
    c := &serial.Config{Name: devices[0], Baud: 115200, ReadTimeout: time.Second * 5}
    s, err := serial.OpenPort(c)
    if err != nil {
        log.Println("Failed to open communication")
        log.Fatal(err)
    }

    p := Printer{}

    p.s = s

    p.readPump()
    p.readPump()

    return p
}

func (p *Printer) readPump() string {
    output := ""
    oldLength := -1
    for {
        buf := make([]byte, 128)
        /*
         This appears to be where part of the problem is,
         I never get anything back from the printer.  Per the
         tarm documentation since I'm in non-blocking mode
         I don't capture the error because it's an EOF
         */
        n, _:= p.s.Read(buf)

        /*if err != nil {
            log.Println("Failed to readPump")
            log.Fatal(err)
        }*/
        output += fmt.Sprintf("%s", buf[:n])
        if len(output) == oldLength {
            return output
        }

        oldLength = len(output)

    }
}

//SendCommand sends a single GCODE command to the printer
func (p *Printer) SendCommand(g string) error {
    g = g + "\n"
    _, err := p.s.Write([]byte(g))
    if err != nil {
        log.Println("Failed to write: ", g)
        log.Fatal(err)
    }
    if !strings.HasSuffix(p.readPump(), "ok\n") {
        log.Println(errors.New("command did not complete successfully"))
    }

    return nil
}

//Retrieve the absolute path for serial devices
func getSerialDevices() ([]string, error) {
    log.Println("getting serial devices")
    devices, err := ioutil.ReadDir("/dev/serial/by-id")
    if err != nil {
        log.Println(err)
        return nil, err
    }
    deviceList := make([]string, len(devices))
    for index, deviceLink := range devices {
        //log.Println("Found device: ", deviceLink.Name())
        abs, err := filepath.EvalSymlinks("/dev/serial/by-id/" + deviceLink.Name())
        //log.Print("Absolute Device: ")
        //log.Println(abs)
        deviceList[index] = abs
        if err != nil {
            log.Println(err)
            return nil, err
        }
    }
    return deviceList, nil
}

Here's the code I'm using to call the printer:

package main

import (
    "fmt"
    "log"

    "github.com/david-fry/go-3dprinter"
)

func main() {
    fmt.Println("Connecting to Printer...")
    p := printer.Connect("COM3", 115200)

    fmt.Println("Moving Extruder...")
    err := p.SendCommand("G28")
    err = p.SendCommand("G1 Z20")
    err = p.SendCommand("G1 Y100")
    err = p.SendCommand("G1 X100")

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Heating Extruder...")
    err = p.SendCommand("M109 S100")

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Cooling Extruder...")
    err = p.SendCommand("M109 S100")

    if err != nil {
        log.Fatal(err)
    }

}

EDIT: This is a Wanhao Duplicator 6. I can get it to work with Cura and Octoprint so I'm pretty certain the serial communication works, I'm just missing something.

EDIT2: Even more puzzling is that if I hook up the Arduino IDE and set the serial baud rate to 250000 it spits out garbage to the console. If I close that and open Cura, let it detect the printer, then close Cura and re-open the console it seems to work ok.

gochuck
  • 185
  • 2
  • 13
  • You should include the printer model or which controller(s) you're using in the question. – Peter Dec 29 '18 at 23:03
  • Updated at the bottom. I know the printer can be communicated with because I've gotten it to work just fine with Cura and Octoprint. – gochuck Dec 29 '18 at 23:11
  • Many 3d printers are Arduino based. On typical PC operating system configurations, opening the serial port will trigger a reset to a bootloader, which then takes a second or two to time out and run the main program ("sketch"). Your code tries to talk immediately. Try instead to put in several seconds delay before you try to talk. – Chris Stratton Dec 30 '18 at 00:42
  • So, I think part of the problem is that I can't set the port to 250000 baud either. I've been searching for a way to set that baud in Go but I'm coming up empty handed. The syscall package for instance does not allow that particular baud rate. – gochuck Jan 01 '19 at 21:06

1 Answers1

0

In case anyone stumbles on having trouble connecting golang to 3D printers, I finally found a solution. It turns out that the current implementation of syscalls in the go standard library does not support non-spec baud rates. Most 3D printers operate at 250K baud and this rate is not supported therefore it will fail. Using a package that implements the termios2 specification allows for unusual bit rates and fixes the problem. Here is the current implementation that I'm using and is working well.

https://github.com/distributed/sers

gochuck
  • 185
  • 2
  • 13