common/serialport/serialport_windows.go (106 lines of code) (raw):

//go:build windows // +build windows package serialport import ( "fmt" "os" "syscall" "unsafe" ) const ( kernel32 = "kernel32.dll" defaultComPort = "\\\\.\\COM1" ) // Dcb structure // http://pinvoke.net/default.aspx/Structures/DCB.html type Dcb struct { DCBlength uint32 BaudRate uint32 flags [4]byte wReserved uint16 XonLim uint16 XoffLim uint16 ByteSize byte Parity byte StopBits byte XonChar byte XoffChar byte ErrorChar byte EOFChar byte EvtChar byte wReserved1 uint16 } type SerialPort struct { kernel32 *syscall.DLL dcb Dcb handle syscall.Handle fileHandle *os.File port string } // NewSerialPort creates a serial port object with predefined parameters. func newSerialPort(port string) (sp *SerialPort) { var dcb Dcb dcb.DCBlength = uint32(unsafe.Sizeof(dcb)) dcb.BaudRate = uint32(115200) dcb.ByteSize = 8 dcb.Parity = 0 dcb.StopBits = 0 kernel32Loaded := syscall.MustLoadDLL(kernel32) return &SerialPort{ kernel32: kernel32Loaded, dcb: dcb, handle: 0, fileHandle: nil, port: port, } } // OpenPort opens the serial port which MUST be done before WritePort is called. func (sp *SerialPort) OpenPort() (err error) { var comPortName *uint16 comPortName, err = syscall.UTF16PtrFromString(sp.port) if err != nil { return fmt.Errorf("Error occurred while opening serial port: %v", err.Error()) } // open COM1 port and create a handle. sp.handle, err = syscall.CreateFile( comPortName, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0) if err != nil { return fmt.Errorf("Error occurred while opening serial port: %v", err.Error()) } sp.fileHandle = os.NewFile(uintptr(sp.handle), sp.port) // set communication state with default values. var r uintptr r, _, err = sp.kernel32.MustFindProc("SetCommState").Call( uintptr(sp.handle), uintptr(unsafe.Pointer(&sp.dcb)), ) if r == 0 { return fmt.Errorf("Error occurred while opening serial port: %v", err.Error()) } return nil } // ClosePort closes the serial port, which MUST be done at the end. func (sp *SerialPort) ClosePort() error { if sp.fileHandle == nil { return fmt.Errorf("Error occurred while closing serial port: Port must be opened") } if err := sp.fileHandle.Close(); err != nil { return fmt.Errorf("Error occurred while closing serial port: %v", err.Error()) } return nil } func (sp *SerialPort) WritePort(data []byte) error { if len(data) > MaxLenOfSingleWrite { data = data[:MaxLenOfSingleWrite] } var done uint32 if err := syscall.WriteFile(sp.handle, data, &done, nil); err != nil { return fmt.Errorf("Error occurred while writing to serial port: %v", err.Error()) } syscall.WriteFile(sp.handle, []byte{'\n'}, &done, nil) return nil } func GetSerialPort() (*SerialPort, error) { sp := newSerialPort(defaultComPort) if err := sp.OpenPort(); err != nil { return nil, err } return sp, nil }