Skip to content

Commit

Permalink
Adds NonBlocking Mode and ReadTimeout Config
Browse files Browse the repository at this point in the history
- Read can be set to non-blocking mode
- In NonBlocking Mode, ReadTimeout can be specified
  • Loading branch information
madhurjain committed Jan 17, 2015
1 parent 6517d24 commit ff5de8d
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 13 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ func main() {
}
```

NonBlocking Mode
----------------

```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)
```

Possible Future Work
--------------------
- better tests (loopback etc)
9 changes: 6 additions & 3 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ import "io"
//
// For example:
//
// c0 := &serial.Config{Name: "COM45", Baud: 115200}
// c0 := &serial.Config{Name: "COM45", Baud: 115200, NonBlockingRead: true, ReadTimeout: 1000}
// or
// c1 := new(serial.Config)
// c1.Name = "/dev/tty.usbserial"
// c1.Baud = 115200
// c1.NonBlockingRead = true
// c1.ReadTimeout = 1000
//
type Config struct {
Name string
Expand All @@ -84,12 +86,13 @@ type Config struct {
// XONFlowControl bool

// CRLFTranslate bool
// TimeoutStuff int
NonBlockingRead 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)
return openPort(c.Name, c.Baud, c.NonBlockingRead, c.ReadTimeout)
}

// func Flush()
Expand Down
9 changes: 7 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) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, nonBlockingRead bool, readTimeout uint32) (rwc io.ReadWriteCloser, err error) {

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

fd := f.Fd()
// set blocking / non-blocking read
var minBytesToRead uint8 = 1
if nonBlockingRead == true {
minBytesToRead = 0
}
t := syscall.Termios{
Iflag: syscall.IGNPAR,
Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate,
Cc: [32]uint8{syscall.VMIN: 1},
Cc: [32]uint8{syscall.VMIN: minBytesToRead, syscall.VTIME: uint8(readTimeout)},
Ispeed: rate,
Ospeed: rate,
}
Expand Down
16 changes: 14 additions & 2 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) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, nonBlockingRead bool, 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,7 +77,19 @@ func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
// 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 {
// EOF on zero read
minBytesToRead = 0
}
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
18 changes: 12 additions & 6 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) (rwc io.ReadWriteCloser, err error) {
func openPort(name string, baud int, nonBlockingRead bool, 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) (rwc io.ReadWriteCloser, err error) {
if err = setupComm(h, 64, 64); err != nil {
return
}
if err = setCommTimeouts(h); err != nil {
if err = setCommTimeouts(h, nonBlockingRead, readTimeout); err != nil {
return
}
if err = setCommMask(h); err != nil {
Expand Down Expand Up @@ -178,12 +178,18 @@ func setCommState(h syscall.Handle, baud int) error {
return nil
}

func setCommTimeouts(h syscall.Handle) error {
func setCommTimeouts(h syscall.Handle, nonBlockingRead bool, readTimeout uint32) error {
var timeouts structTimeouts
const MAXDWORD = 1<<32 - 1
timeouts.ReadIntervalTimeout = MAXDWORD
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1
if nonBlockingRead == true {
timeouts.ReadIntervalTimeout = 1000
timeouts.ReadTotalTimeoutMultiplier = 0
timeouts.ReadTotalTimeoutConstant = readTimeout
} else {
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 ff5de8d

Please sign in to comment.