Created
June 26, 2025 13:15
-
-
Save aka-mj/72feacb1fb9c1eaa8186e12c01fa74a2 to your computer and use it in GitHub Desktop.
Serial in Go, no external dependencies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ❯ socat -d -d pty,raw,echo=0 pty,raw,echo=0 | |
| 2020/10/24 10:44:43 socat[786] N PTY is /dev/pts/16 | |
| 2020/10/24 10:44:43 socat[786] N PTY is /dev/pts/17 | |
| 2020/10/24 10:44:43 socat[786] N starting data transfer loop with FDs [5,5] and [7,7] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ❯ ./serial /dev/pts/16 /dev/pts/17 | |
| 2020/10/24 11:08:38 Sent: Test message to send!! | |
| 2020/10/24 11:08:38 Received: Test message to send!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "log" | |
| "os" | |
| "syscall" | |
| "time" | |
| "unsafe" | |
| ) | |
| const ( | |
| recvTimeout uint8 = 2 // Timeout in deciseconds | |
| baud = syscall.B115200 | |
| ) | |
| type serial struct { | |
| f *os.File | |
| } | |
| func openPort(serialDevice string) *serial { | |
| var err error | |
| s := new(serial) | |
| // Open serial port to the device | |
| s.f, err = os.OpenFile(serialDevice, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) | |
| if err != nil { | |
| fmt.Printf("Failed to open %s: %v\n", serialDevice, err) | |
| os.Exit(1) | |
| } | |
| // Termios settings | |
| t := syscall.Termios{ | |
| Iflag: syscall.IGNPAR, | |
| Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | baud, | |
| Cc: [32]uint8{syscall.VMIN: 0, syscall.VTIME: recvTimeout}, | |
| Ispeed: baud, | |
| Ospeed: baud, | |
| } | |
| // syscall to set our termios settings for our file handle | |
| syscall.Syscall6(syscall.SYS_IOCTL, | |
| uintptr(s.f.Fd()), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&t)), | |
| 0, 0, 0, | |
| ) | |
| // Set nonblocking to false | |
| if err := syscall.SetNonblock(int(s.f.Fd()), false); err != nil { | |
| fmt.Printf("Failed to set nonblocking mode: %v\n", err) | |
| os.Exit(1) | |
| } | |
| return s | |
| } | |
| // Close serial port. | |
| func (s *serial) Close() { | |
| s.f.Close() | |
| } | |
| // Write data to the serial port. | |
| // Appends a carriage return to the end of str. | |
| func (s *serial) Write(str string) error { | |
| nbytes, err := s.f.WriteString(str + "\r") | |
| if err != nil { | |
| return err | |
| } | |
| if nbytes != len(str)+1 { | |
| return errors.New("Looks like we failed to send the full message") | |
| } | |
| return nil | |
| } | |
| // Read reply from the serial port. | |
| func (s *serial) Read() (string, error) { | |
| buf := make([]byte, 128) | |
| ret := new(bytes.Buffer) | |
| for { | |
| _, err := s.f.Read(buf) | |
| if err != nil && err != io.EOF { | |
| return "", err | |
| } | |
| if err == io.EOF { | |
| break | |
| } | |
| ret.Write(buf) | |
| } | |
| // remove null bytes | |
| ret2 := new(bytes.Buffer) | |
| for _, i := range ret.Bytes() { | |
| if i != 0x00 { | |
| ret2.WriteByte(i) | |
| } | |
| } | |
| return ret2.String(), nil | |
| } | |
| func main() { | |
| s1 := openPort(os.Args[1]) | |
| defer s1.Close() | |
| s2 := openPort(os.Args[2]) | |
| defer s2.Close() | |
| sbuf := bytes.NewBufferString("Test message to send!!") | |
| go func() { | |
| // Send the data | |
| err := s1.Write(sbuf.String()) | |
| if err != nil { | |
| log.Fatal(err) | |
| os.Exit(2) | |
| } | |
| log.Printf("Sent: %v\n", sbuf.String()) | |
| }() | |
| // Receive the data | |
| rbuf := new(bytes.Buffer) | |
| count := 0 | |
| // Loop till we should have received everything | |
| for count < len(sbuf.Bytes()) { | |
| str, err := s2.Read() | |
| if err != nil { | |
| log.Fatal(err) | |
| os.Exit(3) | |
| } | |
| rbuf.WriteString(str) //write read bytes to buffer | |
| count = count + len(str) | |
| } | |
| log.Printf("Received: %v\n", rbuf.String()) | |
| time.Sleep(time.Second) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment