diff --git a/README.md b/README.md index 5829cd0..b6e9915 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,17 @@ func main() { NonBlocking Mode ---------------- +By default goserial reads in blocking mode. Which means Read() will +block until at least one byte is returned. If that's not what you want, +specify a ReadTimeout in milliseconds and the Read() will timeout +returning 0 bytes if no bytes are read. + +```go + c := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: 1000} -```go - c := &serial.Config{Name: "COM45", Baud: 115200, NonBlockingRead: true, ReadTimeout: 1000} - // On Windows, ReadTimeout is in milliseconds (1000 = 1sec) - // where as on Linux and POSIX systems, ReadTimeout is in deciseconds (10 = 1sec) + // In this mode, you will want to suppress error for read + // as 0 bytes returns EOF error on Linux / POSIX + n, _ = s.Read(buf) ``` Possible Future Work diff --git a/serial.go b/serial.go index d4c010e..f0c8d6b 100644 --- a/serial.go +++ b/serial.go @@ -65,12 +65,11 @@ import "io" // // For example: // -// c0 := &serial.Config{Name: "COM45", Baud: 115200, NonBlockingRead: true, ReadTimeout: 1000} +// c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: 1000} // or // c1 := new(serial.Config) // c1.Name = "/dev/tty.usbserial" // c1.Baud = 115200 -// c1.NonBlockingRead = true // c1.ReadTimeout = 1000 // type Config struct { @@ -86,13 +85,12 @@ type Config struct { // XONFlowControl bool // CRLFTranslate bool - NonBlockingRead bool - ReadTimeout uint32 + ReadTimeout uint32 } // OpenPort opens a serial port with the specified configuration func OpenPort(c *Config) (io.ReadWriteCloser, error) { - return openPort(c.Name, c.Baud, c.NonBlockingRead, c.ReadTimeout) + return openPort(c.Name, c.Baud, c.ReadTimeout) } // func Flush() diff --git a/serial_linux.go b/serial_linux.go index 0c59db3..5978157 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -9,7 +9,7 @@ import ( "unsafe" ) -func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { +func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { var bauds = map[int]uint32{ 50: syscall.B50, @@ -64,8 +64,19 @@ func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) ( fd := f.Fd() // set blocking / non-blocking read var minBytesToRead uint8 = 1 - if nonBlockingRead == true { + if readTimeout > 0 { + // EOF on zero read minBytesToRead = 0 + // capping the timeout + if readTimeout < 100 { + // min possible timeout + readTimeout = 100 + } else if readTimeout > 25500 { + // max possible timeout + readTimeout = 25500 + } + // convert milliseconds to deciseconds as expected by VTIME + readTimeout = readTimeout / 100 } t := syscall.Termios{ Iflag: syscall.IGNPAR, diff --git a/serial_posix.go b/serial_posix.go index fe3c8d5..b69e29e 100644 --- a/serial_posix.go +++ b/serial_posix.go @@ -17,7 +17,7 @@ import ( //"unsafe" ) -func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { +func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) if err != nil { return @@ -77,19 +77,30 @@ func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) ( // Select raw mode st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG) st.c_oflag &= ^C.tcflag_t(C.OPOST) - + // set blocking / non-blocking read - var minBytesToRead uint8 = 1 - if nonBlockingRead == true { + /* + * http://man7.org/linux/man-pages/man3/termios.3.html + * - Supports blocking read and read with timeout operations + */ + var minBytesToRead uint8 = 1 + if readTimeout > 0 { // EOF on zero read minBytesToRead = 0 + // capping the timeout + if readTimeout < 100 { + // min possible timeout + readTimeout = 100 + } else if readTimeout > 25500 { + // max possible timeout + readTimeout = 25500 + } + // convert milliseconds to deciseconds as expected by VTIME + readTimeout = readTimeout / 100 } st.c_cc[C.VMIN] = C.cc_t(minBytesToRead) st.c_cc[C.VTIME] = C.cc_t(readTimeout) - - st.c_oflag &= ^C.tcflag_t(C.OPOST) - st.c_oflag &= ^C.tcflag_t(C.OPOST) - + _, err = C.tcsetattr(fd, C.TCSANOW, &st) if err != nil { f.Close() diff --git a/serial_windows.go b/serial_windows.go index a32aee5..0892d76 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -37,7 +37,7 @@ type structTimeouts struct { WriteTotalTimeoutConstant uint32 } -func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { +func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) { if len(name) > 0 && name[0] != '\\' { name = "\\\\.\\" + name } @@ -65,7 +65,7 @@ func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) ( if err = setupComm(h, 64, 64); err != nil { return } - if err = setCommTimeouts(h, nonBlockingRead, readTimeout); err != nil { + if err = setCommTimeouts(h, readTimeout); err != nil { return } if err = setCommMask(h); err != nil { @@ -178,18 +178,20 @@ func setCommState(h syscall.Handle, baud int) error { return nil } -func setCommTimeouts(h syscall.Handle, nonBlockingRead bool, readTimeout uint32) error { +func setCommTimeouts(h syscall.Handle, readTimeout uint32) error { var timeouts structTimeouts const MAXDWORD = 1<<32 - 1 - if nonBlockingRead == true { + if readTimeout > 0 { + // non-blocking read timeouts.ReadIntervalTimeout = 1000 timeouts.ReadTotalTimeoutMultiplier = 0 timeouts.ReadTotalTimeoutConstant = readTimeout } else { + // blocking read timeouts.ReadIntervalTimeout = MAXDWORD timeouts.ReadTotalTimeoutMultiplier = MAXDWORD timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1 - } + } /* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx