From 9c65193dd26c59c00fbdd886c77d3d7d2327e03e Mon Sep 17 00:00:00 2001 From: wlkh <32092934+wlkh@users.noreply.github.com> Date: Wed, 12 Feb 2020 23:38:49 +0100 Subject: [PATCH] Allow same serial port to be used as input and output --- Makefile | 2 +- in.go | 38 ++------------------ main.go | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- out.go | 43 +--------------------- 4 files changed, 108 insertions(+), 83 deletions(-) diff --git a/Makefile b/Makefile index 4c6c36d..a4f80cf 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ BUILD_FOLDER=build BINARY=${BUILD_FOLDER}/nmea_ugps # Pass variables for version number, sha id and build number -VERSION=1.1.0 +VERSION=1.1.1 SHA=$(shell git rev-parse --short HEAD) # Set fallback build num if not set by environment variable BUILDNUM?=local diff --git a/in.go b/in.go index e960d39..5ce7870 100644 --- a/in.go +++ b/in.go @@ -5,7 +5,6 @@ import ( "fmt" "log" "net" - "os" "strconv" "strings" "time" @@ -115,18 +114,7 @@ func inputUDPLoop(listen string, msg chan externalMaster, inStatsCh chan inputSt } } -func inputSerialLoop(port string, baudrate int, msg chan externalMaster, inStatsCh chan inputStats) { - if baudrate == 0 { - baudrate = 112500 - } - - c := &serial.Config{Name: port, Baud: baudrate} - s, err := serial.OpenPort(c) - if err != nil { - fmt.Printf("Error opening serial port: %v\n", err) - os.Exit(1) - } - defer s.Close() +func inputSerialLoop(s *serial.Port, msg chan externalMaster, inStatsCh chan inputStats) { scanner := bufio.NewReader(s) for { @@ -153,29 +141,7 @@ func inputSerialLoop(port string, baudrate int, msg chan externalMaster, inStats } } -func inputLoop(listen string, inputStatusCh chan inputStats) { - masterCh := make(chan externalMaster, 1) - - // Use ":" to decide if this is UDP address or serial device - if len(strings.Split(listen, ":")) > 1 { - // Start listening on UDP - go inputUDPLoop(listen, masterCh, inputStatusCh) - } else { - baudrate := 0 - // Is the baudrate specified? - parts := strings.Split(listen, "@") - if len(parts) > 1 { - b, err := strconv.Atoi(parts[1]) - if err != nil { - fmt.Printf("Unable to parse baudrate: %s as numeric value\n", parts[1]) - os.Exit(1) - } - baudrate = b - listen = parts[0] - } - // Start listening on serial port - go inputSerialLoop(listen, baudrate, masterCh, inputStatusCh) - } +func inputLoop(masterCh chan externalMaster, inputStatusCh chan inputStats) { for { select { diff --git a/main.go b/main.go index 1ab793d..8554755 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,17 @@ package main import ( "flag" "fmt" + "io" "log" + "net" + "os" + "strconv" + "strings" "time" ui "github.com/gizak/termui/v3" "github.com/gizak/termui/v3/widgets" + "github.com/tarm/serial" ) var ( @@ -26,21 +32,109 @@ func debugPrintf(arguments string, a ...interface{}) { } } +// deviceIsUDP uses ":" to decide if this is UDP address or serial device +func deviceIsUDP(device string) bool { + return len(strings.Split(listen, ":")) > 1 +} + +func baudAndPortFromDevice(device string) (string, int) { + baudrate := 115200 + port := device + // Is the baudrate specified? + parts := strings.Split(device, "@") + if len(parts) > 1 { + b, err := strconv.Atoi(parts[1]) + if err != nil { + fmt.Printf("Unable to parse baudrate: %s as numeric value\n", parts[1]) + os.Exit(1) + } + baudrate = b + port = parts[0] + } + return port, baudrate +} + func main() { fmt.Printf("Water Linked NMEA UGPS bridge (v%s %s.%s)\n", Version, BuildNum, SHA) flag.StringVar(&listen, "i", "0.0.0.0:7777", "UDP device and port (host:port) OR serial device (COM7 /dev/ttyUSB1@4800) to listen for NMEA input. ") - flag.StringVar(&output, "o", "127.0.0.1:2947", "UDP device and port (host:port) OR serial device (COM7 /dev/ttyUSB1) to send NMEA output. ") + flag.StringVar(&output, "o", "", "UDP device and port (host:port) OR serial device (COM7 /dev/ttyUSB1) to send NMEA output. ") flag.StringVar(&baseURL, "url", "http://192.168.2.94", "URL of Underwater GPS") //flag.BoolVar(&verbose, "v", false, "verbose") flag.Parse() + // Same serial port for input and output? + sameInOut := (listen == output) && !deviceIsUDP(listen) + if sameInOut { + fmt.Println("Same port for input and output", listen) + } + + // Channels inStatusCh := make(chan inputStats, 1) - go inputLoop(listen, inStatusCh) + masterCh := make(chan externalMaster, 1) outStatusCh := make(chan outStats, 1) - if output != "" { - go outputLoop(output, outStatusCh) + + // Output + var writer io.Writer = nil + + // Setup input + if deviceIsUDP(listen) { + // Input from UDP + go inputUDPLoop(listen, masterCh, inStatusCh) + } else { + // Input from serial port + port, baudrate := baudAndPortFromDevice(listen) + + c := &serial.Config{Name: port, Baud: baudrate} + s, err := serial.OpenPort(c) + if err != nil { + fmt.Printf("Error opening serial port: %v\n", err) + os.Exit(1) + } + defer s.Close() + + go inputSerialLoop(s, masterCh, inStatusCh) + if sameInOut { + // Output is to same serial port as input + writer = s + } + } + go inputLoop(masterCh, inStatusCh) + + // Setup output + if output == "" { + // Output disabled + } else if deviceIsUDP(output) { + // Output to UDP + conn, err := net.Dial("udp", output) + if err != nil { + fmt.Printf("Error connecting to UDP: %v\n", err) + os.Exit(1) + } + defer conn.Close() + writer = conn + + } else if !sameInOut { + // Output to different serial port + port, baudrate := baudAndPortFromDevice(listen) + + c := &serial.Config{Name: port, Baud: baudrate} + s, err := serial.OpenPort(c) + if err != nil { + fmt.Printf("Error opening serial port: %v\n", err) + os.Exit(1) + } + defer s.Close() + writer = s + } + + if writer != nil { + go outputLoop(writer, outStatusCh) } + RunUI(inStatusCh, outStatusCh) +} + +func RunUI(inStatusCh chan inputStats, outStatusCh chan outStats) { // Let the goroutines initialize before starting GUI time.Sleep(50 * time.Millisecond) if err := ui.Init(); err != nil { @@ -71,6 +165,9 @@ func main() { outStatus := widgets.NewParagraph() outStatus.Title = "Output status" outStatus.Text = "Waiting for data" + if output == "" { + outStatus.Text = "Output not enabled" + } height = 10 outStatus.SetRect(0, y, 80, y+height) y += height @@ -81,6 +178,9 @@ func main() { ui.Render(p, inpStatus, outStatus) } + // Intial draw before any events have occured + draw() + uiEvents := ui.PollEvents() for { diff --git a/out.go b/out.go index 459f8d6..d2c001a 100644 --- a/out.go +++ b/out.go @@ -4,14 +4,9 @@ import ( "fmt" "io" "math" - "net" - "os" - "strconv" - "strings" "time" "github.com/pilebones/go-nmea" - "github.com/tarm/serial" ) type outStats struct { @@ -24,48 +19,12 @@ type outStats struct { const updateSeconds = 10 -func outputLoop(output string, outStatusCh chan outStats) { +func outputLoop(writer io.Writer, outStatusCh chan outStats) { var prevLat float64 var prevLon float64 var stats outStats - var writer io.Writer - - // Use ":" to decide if this is UDP address or serial device - if len(strings.Split(output, ":")) > 1 { - conn, err := net.Dial("udp", output) - if err != nil { - fmt.Printf("Error connecting to UDP: %v\n", err) - os.Exit(1) - } - defer conn.Close() - writer = conn - - } else { - baudrate := 115200 - port := output - // Is the baudrate specified? - parts := strings.Split(output, "@") - if len(parts) > 1 { - b, err := strconv.Atoi(parts[1]) - if err != nil { - fmt.Printf("Unable to parse baudrate: %s as numeric value\n", parts[1]) - os.Exit(1) - } - baudrate = b - port = parts[0] - } - c := &serial.Config{Name: port, Baud: baudrate} - s, err := serial.OpenPort(c) - if err != nil { - fmt.Printf("Error opening serial port: %v\n", err) - os.Exit(1) - } - defer s.Close() - writer = s - } - for { time.Sleep(100 * time.Millisecond) pos, err := getGlobalPosition()