diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..d24ab8f --- /dev/null +++ b/doc.go @@ -0,0 +1,85 @@ +// +// Copyright 2014 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +/* +This is a cross-platform serial library for go. + +The canonical import for this library is go.bug.st/serial so the import line +is the following: + + import "go.bug.st/serial" + +It is possibile to get the list of available serial ports with the +GetPortsList function: + + ports, err := serial.GetPortsList() + if err != nil { + log.Fatal(err) + } + if len(ports) == 0 { + log.Fatal("No serial ports found!") + } + for _, port := range ports { + fmt.Printf("Found port: %v\n", port) + } + +The serial port can be opened with the OpenPort function: + + mode := &serial.Mode{ + BaudRate: 115200, + } + port, err := serial.OpenPort("/dev/ttyUSB0", mode) + if err != nil { + log.Fatal(err) + } + +The OpenPort command needs a "mode" parameter that specifies the configuration +options for the serial port. If not specified the default options are 9600_N81, +in the example above only the speed is changed so the port is opened using 115200_N81. +The following snippets shows how to declare a configuration for 57600_E71: + + mode := &serial.Mode{ + BaudRate: 57600, + Parity: serial.PARITY_EVEN, + DataBits: 7, + StopBits: serial.STOPBITS_ONE, + } + +The configuration can be changed at any time with the SetMode function: + + err := port.SetMode(mode) + if err != nil { + log.Fatal(err) + } + +The port object implements the io.ReadWriteCloser interface, so we can use +the usual Read, Write and Close functions to send and receive data from the +serial port: + + n, err := port.Write([]byte("10,20,30\n\r")) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Sent %v bytes\n", n) + + buff := make([]byte, 100) + for { + n, err := port.Read(buff) + if err != nil { + log.Fatal(err) + break + } + if n == 0 { + fmt.Println("\nEOF") + break + } + fmt.Printf("%v", string(buff[:n])) + } + +This library doesn't make use of cgo and "C" package, so it's a pure go library +that can be easily cross compiled. +*/ +package serial // import "go.bug.st/serial" diff --git a/serial.go b/serial.go index b939d0f..c2b8de2 100644 --- a/serial.go +++ b/serial.go @@ -6,39 +6,30 @@ package serial // import "go.bug.st/serial" -import "io" - -// SerialPort object -type SerialPort interface { - io.ReadWriteCloser - - // Set all parameters together - SetMode(mode *Mode) error -} - +// This structure describes a serial port configuration. type Mode struct { - BaudRate int - DataBits int - Parity Parity - StopBits StopBits + BaudRate int // The serial port bitrate (aka Baudrate) + DataBits int // Size of the character (must be 5, 6, 7 or 8) + Parity Parity // Parity (see Parity type for more info) + StopBits StopBits // Stop bits (see StopBits type for more info) } type Parity int const ( - PARITY_NONE Parity = iota - PARITY_ODD - PARITY_EVEN - PARITY_MARK - PARITY_SPACE + PARITY_NONE Parity = iota // No parity (default) + PARITY_ODD // Odd parity + PARITY_EVEN // Even parity + PARITY_MARK // Mark parity (always 1) + PARITY_SPACE // Space parity (always 0) ) type StopBits int const ( - STOPBITS_ONE StopBits = iota - STOPBITS_ONEPOINTFIVE - STOPBITS_TWO + STOPBITS_ONE StopBits = iota // 1 Stop bit + STOPBITS_ONEPOINTFIVE // 1.5 Stop bits + STOPBITS_TWO // 2 Stop bits ) // Platform independent error type for serial ports diff --git a/serial_unix.go b/serial_unix.go index edb1fac..eddefc0 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -14,25 +14,35 @@ import "strings" import "syscall" import "unsafe" -// opaque type that implements SerialPort interface for linux -type unixSerialPort struct { +// Opaque type that implements SerialPort interface for linux +type SerialPort struct { handle int } -func (port *unixSerialPort) Close() error { +// Close the serial port +func (port *SerialPort) Close() error { port.releaseExclusiveAccess() return syscall.Close(port.handle) } -func (port *unixSerialPort) Read(p []byte) (n int, err error) { +// Stores data received from the serial port into the provided byte array +// buffer. The function returns the number of bytes read. +// +// The Read function blocks until (at least) one byte is received from +// the serial port or an error occurs. +func (port *SerialPort) Read(p []byte) (n int, err error) { return syscall.Read(port.handle, p) } -func (port *unixSerialPort) Write(p []byte) (n int, err error) { +// Send the content of the data byte array to the serial port. +// Returns the number of bytes written. +func (port *SerialPort) Write(p []byte) (n int, err error) { return syscall.Write(port.handle, p) } -func (port *unixSerialPort) SetMode(mode *Mode) error { +// Set all parameters of the serial port. See the Mode structure for more +// info. +func (port *SerialPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { return err @@ -52,7 +62,8 @@ func (port *unixSerialPort) SetMode(mode *Mode) error { return port.setTermSettings(settings) } -func OpenPort(portName string, mode *Mode) (SerialPort, error) { +// Open the serial port using the specified modes +func OpenPort(portName string, mode *Mode) (*SerialPort, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { switch err { @@ -63,7 +74,7 @@ func OpenPort(portName string, mode *Mode) (SerialPort, error) { } return nil, err } - port := &unixSerialPort{ + port := &SerialPort{ handle: h, } @@ -219,20 +230,20 @@ func setRawMode(settings *syscall.Termios) { // native syscall wrapper functions -func (port *unixSerialPort) getTermSettings() (*syscall.Termios, error) { +func (port *SerialPort) getTermSettings() (*syscall.Termios, error) { settings := &syscall.Termios{} err := ioctl(port.handle, ioctl_tcgetattr, uintptr(unsafe.Pointer(settings))) return settings, err } -func (port *unixSerialPort) setTermSettings(settings *syscall.Termios) error { +func (port *SerialPort) setTermSettings(settings *syscall.Termios) error { return ioctl(port.handle, ioctl_tcsetattr, uintptr(unsafe.Pointer(settings))) } -func (port *unixSerialPort) acquireExclusiveAccess() error { +func (port *SerialPort) acquireExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCEXCL, 0) } -func (port *unixSerialPort) releaseExclusiveAccess() error { +func (port *SerialPort) releaseExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCNXCL, 0) } diff --git a/serial_windows.go b/serial_windows.go index ee14520..b19526f 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -19,7 +19,7 @@ package serial // import "go.bug.st/serial" import "syscall" // opaque type that implements SerialPort interface for Windows -type windowsSerialPort struct { +type SerialPort struct { Handle syscall.Handle } @@ -56,11 +56,11 @@ func GetPortsList() ([]string, error) { return list, nil } -func (port *windowsSerialPort) Close() error { +func (port *SerialPort) Close() error { return syscall.CloseHandle(port.Handle) } -func (port *windowsSerialPort) Read(p []byte) (int, error) { +func (port *SerialPort) Read(p []byte) (int, error) { var readed uint32 params := &DCB{} for { @@ -83,7 +83,7 @@ func (port *windowsSerialPort) Read(p []byte) (int, error) { } } -func (port *windowsSerialPort) Write(p []byte) (int, error) { +func (port *SerialPort) Write(p []byte) (int, error) { var writed uint32 err := syscall.WriteFile(port.Handle, p, &writed, nil) return int(writed), err @@ -171,7 +171,7 @@ const ( TWOSTOPBITS = 2 ) -func (port *windowsSerialPort) SetMode(mode *Mode) error { +func (port *SerialPort) SetMode(mode *Mode) error { params := DCB{} if GetCommState(port.Handle, ¶ms) != nil { port.Close() @@ -196,7 +196,7 @@ func (port *windowsSerialPort) SetMode(mode *Mode) error { return nil } -func OpenPort(portName string, mode *Mode) (SerialPort, error) { +func OpenPort(portName string, mode *Mode) (*SerialPort, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) if err != nil { @@ -219,7 +219,7 @@ func OpenPort(portName string, mode *Mode) (SerialPort, error) { return nil, err } // Create the serial port - port := &windowsSerialPort{ + port := &SerialPort{ Handle: handle, }