Skip to content

Commit

Permalink
Improves NonBlocking mode config
Browse files Browse the repository at this point in the history
- Removed the need for NonBlockingRead boolean
- Setting ReadTimeout to positive value sets non blocking mode
- ReadTimeout is specified in milliseconds irrespective of platform
- Caps ReadTimeout on Linux / POSIX systems
- Updated README
  • Loading branch information
madhurjain committed Jan 18, 2015
1 parent ff5de8d commit 79b7c9d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 24 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 3 additions & 5 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down
15 changes: 13 additions & 2 deletions serial_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
27 changes: 19 additions & 8 deletions serial_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 7 additions & 5 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 79b7c9d

Please sign in to comment.