jacobsa / go-serial Goto Github PK
View Code? Open in Web Editor NEWA Go library for dealing with serial ports.
License: Apache License 2.0
A Go library for dealing with serial ports.
License: Apache License 2.0
I'm not 100% sure yet, but it looks like when I'm waiting on a read that will not complete (in case of canceling the transaction). There appears to be no mechanism to cancel an in progress read.
So I launch a read in a go-routine, but can't cancel it, so when I go to send a write afterwards the port seems to still be blocked on the read.
Thoughts?
Currently Read()
blocks until buf
is full - or I don't understand the function correctly (honestly, let's hope so). I'm looking for a way to Read()
to buf
for say time.Second
before Read()
does return
so that I can parse the data (if any) on hand rather than waiting for an infinite amount of time for a buffer to fill that might not.
I'm trying to send data with 2 stop bits (Linux/ARMv6 = Raspberry Pi 3 B+, stretch) and I'm positive I'm only getting just one. (Actually scoped timings with my audio interface to confirm.) The go-serial code related to stop bits for Linux is as below.
switch options.StopBits {
case 1:
case 2:
t2.c_cflag |= syscall.CSTOPB
default:
return nil, errors.New("invalid setting for StopBits")
}
Not much difference between 1 and 2 bits.
Yes, I need 2 stobits.
PS. Is it anyhow possible to disable the serial protocol 111...0xxxxxxxx1(1)...111 and just throw series of 0s and 1s down the pipe through UART? I can render those bit strings in my code as long as I have some drain that swallows my bits at constant rate.
PPS. Now that I've opened my question spree, does anyone know why switching baud rate is so slow? Is it due to drivers and hardware? Could it be made faster. I takes 20ms or more on this Raspberry Pi.
if vmin == 0 && vtime < 100 {
return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize")
}
Why is the above required? I have an application where I want to read any data from the serial port and immediately return if there is nothing to read. I tried commenting this out and it seems to work fine.
Trying to get this working in Windows Subsystem for Linux. "SYS_IOCTL: inappropriate ioctl for device" when trying to open the port.
In Windows implementation, timeout value is being divided by 100:
timeoutConstant := uint32(round(float64(options.InterCharacterTimeout) / 100.0))
It is explained by the following commentary:
"The documentation states that ReadIntervalTimeout is set in MS but empirical investigation determines that it seems to interpret in units of 100ms."
But I don't think it is correct. In my tests, windows is interpreting it as MS, not 100s of MS, as stated in the documentation. Which Windows version was used for your tests?
For example, in my machine, I have to set a timeout of 200000 to achieve 2 seconds timeout. I am using Windows 10. I have some experiece with serial communication in Windows, but with other languages.
Looking at pySerial's options may be informative:
http://pyserial.sourceforge.net/pyserial_api.html
Hello!
In Linux, when we try to read from port and wait fo data, we have a io.EOF error, and can hande it.
Code (simplified):
var n int = 0
var err error
res:=make([byte, 1024])
for n==0{
n, err =port.Read(res)
if err!=nil && err!=io.EOF{
log.Printf("port.Read: %v", err)
}
log.Printf("Read: %v bytes", n)
But, when connection lost occurs, the error is also io.EOF ! How can I distinguish disconnection and waiting for data case?
Now, when I get io.EOF error, I check if /dev/tty* file phisically exists on disk, to recognize a cable disconnection. But, this method is quite ugly.
And, in addition, can you advise me, what is the best way to handle IO errors , using your package? In Windows, when cable is re-connected, I can continue work without closing and re-opening port, but in Linux, I have "bad descriptor" error. So I can not use "defer port.Close()", And, additionally,manual closing of port causes crash of the stack of all functions , which used the descriptor at the moment. Is there a way to "renew" descriptor or some another practice?
` // Set up options.
options := serial.OpenOptions{
PortName: "/dev/ttyS1",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
port, err := serial.Open(options)
if err != nil {
fmt.Printf("serial.Open: %v\n", err)
return
}
`
GOOS=linux GOARCH=mipsle
serial.Open: SYS_IOCTL: inappropriate ioctl for device
I am currently utilizing your package in an clock program that sends time over serial USB. I want to ensure that the program can "attempt" to open the device via serial USB, but ignore the case when the Arduino is not connected to my Raspberry Pi via USB.
I am declaring the serial.OpenOptions{}
with the following code.
Options = serial.OpenOptions{
PortName: nixie.FindArduino(),
BaudRate: 115200,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
Where
nixie.FindArduino()
looks in/dev
for the file "ttyACM0", and returns an empty string if it cannot find the device.
Next, I try to use the following code to open the serial USB tty connection (but still open a temporary file if no such file is found)
var Port io.ReadWriteCloser
if Options.PortName != "" {
Port, err := serial.Open(Options)
if err != nil {
foundNixie = false
} else {
foundNixie = true
}
defer Port.Close()
} else {
Port, _ := os.OpenFile("tempfile", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0600)
defer Port.Close()
}
However, this program will panic out whenever I try to send strings over serial USB.
b := []byte("TRIAL STRING")
_, err := Port.Write(b)
if err != nil {
log.Fatalf("Port.Write: %v", err)
}
/home/pi/go/src/github.com/gilgameshskytrooper/prometheus/prometheus.go:231 +0x110
github.com/robfig/cron.FuncJob.Run(0x10717cb0)
/home/pi/go/src/github.com/robfig/cron/cron.go:92 +0x1c
github.com/robfig/cron.(*Cron).runWithRecovery(0x107189f0, 0x3ec300, 0x10717cb0)
/home/pi/go/src/github.com/robfig/cron/cron.go:165 +0x54
created by github.com/robfig/cron.(*Cron).run
/home/pi/go/src/github.com/robfig/cron/cron.go:199 +0x554
On the other hand, the method I was using previously of just declaring and assigning the Port
variable works flawlessly when the USB device is connected, but immediately fails when I try to write anything if the device is not connected.
Options := serial.OpenOptions{
PortName: nixie.FindArduino(),
BaudRate: 115200,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
Port, err := serial.Open(Options)
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x12fac]
goroutine 1 [running]:
panic(0x2c6568, 0x1070c010)
/usr/lib/go-1.7/src/runtime/panic.go:500 +0x33c
main.main()
/home/pi/go/src/github.com/gilgameshskytrooper/prometheus/prometheus.go:209 +0x190
How can I write the program so that the program can continue to run even if the serial device cannot be found?
I am using this package in an open source project https://github.com/SMerrony/DasherG to access legacy hardware and it works just fine with one exception: I can't see any way to send a serial BREAK signal.
Have I overlooked something, or could this feature be added?
It seems when I open a port and execute a read there can be old data in the buffer. I have to disconnect/reconnect the USB-UART to clear the old data.
I do not see any flush function. Is flushing the buffer possible?
Hi all, I'm using this library on Linux to configure and maintain connectivity of a USB LTE modem. One of the steps results in the modem "disappearing" as it restarts. My modems TTY device (/dev/ttyACM0
in my case but this would also apply to /dev/ttyUSBx
and similar) disappears and reappears about 5 seconds later.
I put in code to handle that by checking for the existence of /dev/ttyACM0
before reading / writing to it and to execute serial.Close()
and then (after a small delay) serial.Open()
again. There are no errors re-opening the device, but the moment I try and call serial.Read()
or serial.Write()
the Go program errors out with the following;
Error reading from serial port: read /dev/ttyACM0: file already closed
or
Error writing to serial port: write /dev/ttyACM0: file already closed
How can I handle this without restarting the Go program? To me it seems like the Close() event isn't fully doing its task or some state in the serial library is persisting.
Test on windows 10, serial.Open: The parameter is incorrect.
options := serial.OpenOptions{
PortName: "COM1",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
thx
There should be a hardware
directory containing an Arduino sketch that echos serial data it receives with some extra prefix. This can be used to write a test that makes sure the library works in at least a basic way.
For extra points, the Arduino could be configured to set other options for serial transmission (speed, parity, character width).
...once the library is usable. It should give install instructions and basic use instructions.
https://github.com/jacobsa/go-serial/blob/master/README.markdown#os-support says it supports Windows but ends with saying Windows would be a lot more work. Seems contradictory.
Pretty sure this is a go-serial bug (due to TCSETS2 being hard coded ? ) :
On OpenWRT, I get
error opening port:%!(EXTRA *os.SyscallError=SYS_IOCTL: inappropriate ioctl for device)
... could be because OpenWRT uses musl (not glibc).
More details , doing "strace" on OpenWRT, shows :
ioctl(5, _IOC(_IOC_READ, 0x54, 0x2b, 0x2c), 0x9116c90) = -1 ENOTTY (Not a tty)
FYI, to cross compile for MIPS (Atheros Soc) on OpenWRT, do :
export GOARCH=mips
export GOMIPS=softfloat # requires the recently released go1.10 to get the softfloat code generator
code:
options := serial.OpenOptions{
PortName: "/dev/ttyS1",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
port, err := serial.Open(options)
if err != nil {
fmt.Printf("serial.Open: %v\n", err)
return
}
env:
GOOS=linux GOARCH=mipsle go1.8.3 cpu:mt7688
error:
serial.Open: SYS_IOCTL: inappropriate ioctl for device
thanks
I'm trying to implement this code in go
and looks like these two line are crucial:
ioctl_flags &= TIOCM_RTS;
ioctl_flags &= TIOCM_DTR;
Is there a way to set them with this library?
I was expecting Read to be interrupted by Close, but this doesn't work.
The issue is that in blocking mode, the read is not interrupted even if the file is closed until the vmin or vtime condition is satisfied, and because I use vmin > 0 and vtime > 0, Read would never return.
The solution is to put it into non-blocking mode. In this case, read returns EAGAIN, and the go epoll implementation will properly wait for data to be available: https://github.com/golang/go/blob/release-branch.go1.12/src/internal/poll/fd_unix.go#L168
Just removing this isn't enough, because Fd puts it into blocking mode: https://github.com/golang/go/blob/release-branch.go1.12/src/os/file_unix.go#L70
So I removed the first SetNonblock call and added syscall.SetNonblock(int(file.Fd()), true)
to the end of openInternal
in open_linux.go. This works fine in my testing.
Hi,
You might want to look at golang/go#22099
I am running in the same issue with go-serial.
Could you please release v1.0.0 (or any other version of your pleasing)? Otherwise I will have to reference v0.0.0-20180131005756-15cf729a72d4
in go.mod which looks ugly
This can be done by making open_linux.go
and so on, with stub implementations of openInternal
that just return "not implemented" errors.
hello:
I think this code setCommState should replaced by like this
if err = setCommState(h, options); err == nil {
if err = setupComm(h, 64, 64); err != nil {
return nil, err
}
if err = setCommTimeouts(h, options); err != nil {
return nil, err
}
if err = setCommMask(h); err != nil {
return nil, err
}
}
the vmware create the pipe serial port \\.\pipe\com_1 will can work
this idea form putty
Otherwise it can not work
On nix / mac it looks simple, just enumerate /dev/tty.usbmodem
No idea how you'd do that on Windows and what you'd expect to see.
Any thoughts?
If I open a serial port with the RTSCTSFlowControl parameter set to true, how can I check the status of CTS?
On OSX I have a working integration test that relies on IO timeouts. I want to issue a read, then after a set amount of time cancel that read.
This works on OSX, but on windows it seems the read blocks indefinitly.
When I look at the windows code I see that it seems to ignore the options struct while setting timeouts.
Just wondering if you had any background on that.
func setCommTimeouts(h syscall.Handle) error {
var timeouts structTimeouts
const MAXDWORD = 1<<32 - 1
timeouts.ReadIntervalTimeout = MAXDWORD
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1
//timeouts.ReadTotalTimeoutConstant = 1
fmt.Println("Got timeout 100")
/* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx
For blocking I/O see below:
Remarks:
If an application sets ReadIntervalTimeout and
ReadTotalTimeoutMultiplier to MAXDWORD and sets
ReadTotalTimeoutConstant to a value greater than zero and
less than MAXDWORD, one of the following occurs when the
ReadFile function is called:
If there are any bytes in the input buffer, ReadFile returns
immediately with the bytes in the buffer.
If there are no bytes in the input buffer, ReadFile waits
until a byte arrives and then returns immediately.
If no bytes arrive within the time specified by
ReadTotalTimeoutConstant, ReadFile times out.
*/
r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
if r == 0 {
return err
}
return nil
}
That doesn't seem to take the options struct at all into account.
Any thoughts?
Thanks!
When trying the sample code with Mac OS 10.10, I get the following error:
serial.Open: SYS_IOCTL: invalid argument
Some digging around showed that the call to setTermios() in open_darwin.go fails.
I'm using a Delock USB to Serial adapter with FTDI chipset.
I'm having trouble using the library on Windows. It's working great on macos.
I'm using read timeouts to allow me to detect when there are bytes to be read (part of the comm protocol for this device is that in certain circumstances I need to "drain" unwanted bytes and wait for the stream to empty before trying to send a command or wait for response, so I wait for a read to timeout). This works fine on macos, but on Windows, the stream is giving me a non-ending stream of "0" bytes (never timing out). My suspicion is that despite having set Hardware flow control on the COM1 port (via Device Manager), DTR is being ignored? Ideas?
Code looks like this:
readerChannel = make(chan serialReadResponse)
readerChannelQuit = make(chan bool)
go func () {
var arr []byte = make([]byte,1);
for {
select {
case <- readerChannelQuit:
close(readerChannelQuit)
close(readerChannel)
return
default:
var response serialReadResponse
var n int
n, response.err = stream.Read(arr);
if n == 1 {
response.data = arr[0]
}
readerChannel <- response
}
}
}()
and the application level reads look like:
select {
case response := <-readerChannel:
if response.err != nil {
return response.data,errors.Wrap(err, "failed to read byte")
}
return response.data, nil
case <-time.After(time.Millisecond * time.Duration(timeoutMS)):
// call timed out
return 0,errors.Errorf("TIMEOUT: read timed out at %d ms", timeoutMS)
}
and I'm initializing the connection thusly:
options := serial.OpenOptions{
PortName: port,
BaudRate: 9600,
ParityMode: serial.PARITY_NONE,
RTSCTSFlowControl: true,
InterCharacterTimeout: 500,
MinimumReadSize: 1,
DataBits: 8,
StopBits: 1,
}
Hello, I tried exactly as the example (example the port name and add function read for recv data). I successfully write Byte array to port but failed to read from it. it alway return an error which called "read /dev/tty.usbserial1: interrupted system call". i've tried serveral time but Disappointing results. any suggestions? looking forward to your reply.
I running it on macOS sierra 10.12.6, here is the code:
`import (
"fmt"
"github.com/jacobsa/go-serial/serial"
"log"
"testing"
)
func TestSerialData(t *testing.T) {
// Set up options.
options := serial.OpenOptions{
PortName: "/dev/tty.usbserial1",
BaudRate: 19200,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}
// Open the port.
port, err := serial.Open(options)
if err != nil {
log.Fatalf("serial.Open: %v", err)
}
// Make sure to close it later.
defer port.Close()
// Write 4 bytes to the port.
b := []byte{0x00, 0x01, 0x02, 0x03}
w, err := port.Write(b)
if err != nil {
log.Fatalf("port.Write: %v", err)
}
fmt.Println("Wrote", w, "bytes.")
buf := make([]byte, 128)
r, err := port.Read(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Read", r, "bytes.")
}`
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.