Skip to content

Commit

Permalink
Uses time.Duration for ReadTimeout now
Browse files Browse the repository at this point in the history
- Replaced uint32 with time.Duration for consistency
- Updated README describing ReadTimeout is Total Timeout
- Merged duplicated logic to a common function posixTimeoutValues
- Set ReadIntervalTimeout on Windows to 0
  • Loading branch information
madhurjain committed Jan 19, 2015
1 parent 79b7c9d commit e7e575f
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 52 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,18 @@ func main() {

NonBlocking Mode
----------------
By default goserial reads in blocking mode. Which means Read() will
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.
specify a positive ReadTimeout and the Read() will timeout returning 0
bytes if no bytes are read.
Please note that this is the total timeout the read operation will wait
and not the interval timeout between two bytes.

```go
c := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: 1000}
c := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Second * 5}

// In this mode, you will want to suppress error for read
// as 0 bytes returns EOF error on Linux / POSIX
// as 0 bytes return EOF error on Linux / POSIX
n, _ = s.Read(buf)
```

Expand Down
40 changes: 34 additions & 6 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ Example usage:
*/
package serial

import "io"
import (
"io"
"time"
)

// Config contains the information needed to open a serial port.
//
Expand All @@ -65,16 +68,17 @@ import "io"
//
// For example:
//
// c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: 1000}
// c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Millisecond * 500}
// or
// c1 := new(serial.Config)
// c1.Name = "/dev/tty.usbserial"
// c1.Baud = 115200
// c1.ReadTimeout = 1000
// c1.ReadTimeout = time.Millisecond * 500
//
type Config struct {
Name string
Baud int
Name string
Baud int
ReadTimeout time.Duration // Total timeout

// Size int // 0 get translated to 8
// Parity SomeNewTypeToGetCorrectDefaultOf_None
Expand All @@ -85,14 +89,38 @@ type Config struct {
// XONFlowControl bool

// CRLFTranslate bool
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.ReadTimeout)
}

// Converts the timeout values for Linux / POSIX systems
func posixTimeoutValues(readTimeout time.Duration) (vmin uint8, vtime uint8) {
const MAXUINT8 = 1<<8 - 1 // 255
// set blocking / non-blocking read
var minBytesToRead uint8 = 1
var readTimeoutInDeci uint8 = 0
if readTimeout > 0 {
// EOF on zero read
minBytesToRead = 0
timeoutMs := uint32(readTimeout.Nanoseconds() / 1e6)
// capping the timeout
if timeoutMs < 100 {
// min possible timeout 1 Deciseconds (0.1s)
readTimeoutInDeci = 1
} else if timeoutMs > (MAXUINT8 * 100) {
// max possible timeout is 255 Deciseconds (25.5s)
readTimeoutInDeci = MAXUINT8
} else {
// convert milliseconds to deciseconds as expected by VTIME
readTimeoutInDeci = uint8(timeoutMs / 100)
}
}
return minBytesToRead, readTimeoutInDeci
}

// func Flush()

// func SendBreak()
Expand Down
22 changes: 4 additions & 18 deletions serial_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
"io"
"os"
"syscall"
"time"
"unsafe"
)

func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, readTimeout time.Duration) (rwc io.ReadWriteCloser, err error) {

var bauds = map[int]uint32{
50: syscall.B50,
Expand Down Expand Up @@ -62,26 +63,11 @@ func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser
}()

fd := f.Fd()
// set blocking / non-blocking read
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
}
vmin, vtime := posixTimeoutValues(readTimeout)
t := syscall.Termios{
Iflag: syscall.IGNPAR,
Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate,
Cc: [32]uint8{syscall.VMIN: minBytesToRead, syscall.VTIME: uint8(readTimeout)},
Cc: [32]uint8{syscall.VMIN: vmin, syscall.VTIME: vtime},
Ispeed: rate,
Ospeed: rate,
}
Expand Down
23 changes: 5 additions & 18 deletions serial_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import (
"io"
"os"
"syscall"
"time"
//"unsafe"
)

func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, readTimeout time.Duration) (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 @@ -83,23 +84,9 @@ func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser
* 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)
vmin, vtime := posixTimeoutValues(readTimeout)
st.c_cc[C.VMIN] = C.cc_t(vmin)
st.c_cc[C.VTIME] = C.cc_t(vtime)

_, err = C.tcsetattr(fd, C.TCSANOW, &st)
if err != nil {
Expand Down
13 changes: 8 additions & 5 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"sync"
"syscall"
"time"
"unsafe"
)

Expand Down Expand Up @@ -37,7 +38,7 @@ type structTimeouts struct {
WriteTotalTimeoutConstant uint32
}

func openPort(name string, baud int, readTimeout uint32) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, readTimeout time.Duration) (rwc io.ReadWriteCloser, err error) {
if len(name) > 0 && name[0] != '\\' {
name = "\\\\.\\" + name
}
Expand Down Expand Up @@ -178,14 +179,16 @@ func setCommState(h syscall.Handle, baud int) error {
return nil
}

func setCommTimeouts(h syscall.Handle, readTimeout uint32) error {
func setCommTimeouts(h syscall.Handle, readTimeout time.Duration) error {
var timeouts structTimeouts
const MAXDWORD = 1<<32 - 1
if readTimeout > 0 {
timeoutMs := uint32(readTimeout.Nanoseconds() / 1e6)

if timeoutMs > 0 {
// non-blocking read
timeouts.ReadIntervalTimeout = 1000
timeouts.ReadIntervalTimeout = 0
timeouts.ReadTotalTimeoutMultiplier = 0
timeouts.ReadTotalTimeoutConstant = readTimeout
timeouts.ReadTotalTimeoutConstant = timeoutMs
} else {
// blocking read
timeouts.ReadIntervalTimeout = MAXDWORD
Expand Down

0 comments on commit e7e575f

Please sign in to comment.