From ca20cb15ce95c655e920c332f28e9ddf5dde65be Mon Sep 17 00:00:00 2001 From: Alex Bucknall Date: Wed, 25 Feb 2026 16:34:35 +0000 Subject: [PATCH] feat: auto-detect Notecard port by default via USB VID/PID New installs no longer save a specific serial port to config. Instead, the port is left empty so note-go's built-in auto-detection (USB VID/PID 30A4:0001) finds the Notecard on each transaction. This means unplugging and reconnecting to a different USB port just works. - Default config uses empty port for auto-detect - Accept `-port auto` and `NOTE_PORT=auto` to re-enable auto-detect - Show `auto-detect` in config output and mark detected Notecard ports - Backfill resolved port in-memory after Open() for PCAP mode Co-Authored-By: Claude Opus 4.6 --- lib/config.go | 26 ++++++++++++++------------ notecard/main.go | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/config.go b/lib/config.go index 808921b..d9d58cd 100644 --- a/lib/config.go +++ b/lib/config.go @@ -163,8 +163,7 @@ func (config *ConfigSettings) Print() { configPort := config.IPort[config.Interface] if configPort.Port == "" { - fmt.Printf(" -port -\n") - fmt.Printf(" -portconfig -\n") + fmt.Printf(" -port auto-detect\n") } else { fmt.Printf(" -port %s\n", configPort.Port) fmt.Printf(" -portconfig %d\n", configPort.PortConfig) @@ -207,16 +206,13 @@ func (config *ConfigSettings) SetDefaultCredentials(token string, email string, } func defaultConfig() *ConfigSettings { - iface, port, portConfig := notecard.Defaults() + iface, _, _ := notecard.Defaults() return &ConfigSettings{ When: time.Now().UTC().Format("2006-01-02T15:04:05Z"), Hub: notehub.DefaultAPIService, HubCreds: map[string]ConfigCreds{}, IPort: map[string]ConfigPort{ - iface: { - Port: port, - PortConfig: portConfig, - }, + iface: {}, }, } } @@ -321,8 +317,9 @@ func ConfigFlagsProcess() (err error) { defaultPort := config.IPort[config.Interface] - if configFlagPort == "-" { + if configFlagPort == "-" || strings.EqualFold(configFlagPort, "auto") { defaultPort.Port = "" + defaultPort.PortConfig = 0 } else if configFlagPort != "" { defaultPort.Port = configFlagPort } @@ -345,7 +342,7 @@ func ConfigFlagsRegister(notecardFlags bool, notehubFlags bool) { // Process the commands if notecardFlags { flag.StringVar(&configFlagInterface, "interface", "", "select 'serial' or 'i2c' interface for Notecard") - flag.StringVar(&configFlagPort, "port", "", "select serial or i2c port for Notecard") + flag.StringVar(&configFlagPort, "port", "", "select serial or i2c port ('auto' for auto-detect)") flag.IntVar(&configFlagPortConfig, "portconfig", 0, "set serial device speed or i2c address for Notecard") } if notehubFlags { @@ -404,9 +401,14 @@ func FlagParse(notecardFlags bool, notehubFlags bool) (err error) { // Override via env vars if specified if port := os.Getenv("NOTE_PORT"); port != "" { temp := config.IPort[config.Interface] - temp.Port = port - if portConfig, err := strconv.Atoi(os.Getenv("NOTE_PORT_CONFIG")); err == nil { - temp.PortConfig = portConfig + if strings.EqualFold(port, "auto") || port == "-" { + temp.Port = "" + temp.PortConfig = 0 + } else { + temp.Port = port + if portConfig, err := strconv.Atoi(os.Getenv("NOTE_PORT_CONFIG")); err == nil { + temp.PortConfig = portConfig + } } config.IPort[config.Interface] = temp } diff --git a/notecard/main.go b/notecard/main.go index ce66293..d9b26f9 100644 --- a/notecard/main.go +++ b/notecard/main.go @@ -283,28 +283,40 @@ func main() { fmt.Printf(" Example: notecard -port /dev/ttyUSB0 -pcap usb -output capture.pcap\n") fmt.Printf(" Example: notecard -port /dev/ttyAMA0 -pcap aux -portconfig 115200 -output capture.pcap\n") fmt.Printf("\n") - nInterface, nPort, _ := notecard.Defaults() + nInterface, _, _ := notecard.Defaults() if config.Interface != "" { nInterface = config.Interface - nPort = config.IPort[config.Interface].Port } + nPort := config.IPort[nInterface].Port var ports []string + var notecardPorts []string if nInterface == notecard.NotecardInterfaceSerial { - ports, _, _, _ = notecard.SerialPorts() + ports, _, notecardPorts, _ = notecard.SerialPorts() } if nInterface == notecard.NotecardInterfaceI2C { - ports, _, _, _ = notecard.I2CPorts() + ports, _, notecardPorts, _ = notecard.I2CPorts() } if len(ports) != 0 { - fmt.Printf("Ports on '%s':\n", nInterface) + if nPort == "" { + fmt.Printf("Ports on '%s' (auto-detect enabled):\n", nInterface) + } else { + fmt.Printf("Ports on '%s':\n", nInterface) + } + // Build a set of notecard ports for quick lookup + ncPortSet := make(map[string]bool) + for _, p := range notecardPorts { + ncPortSet[p] = true + } for _, port := range ports { if port == nPort { - nPortConfig := config.IPort[config.Interface].PortConfig + nPortConfig := config.IPort[nInterface].PortConfig if nPortConfig == 0 { fmt.Printf(" %s ***\n", port) } else { fmt.Printf(" %s (%d) ***\n", port, nPortConfig) } + } else if nPort == "" && ncPortSet[port] { + fmt.Printf(" %s (notecard) ***\n", port) } else { fmt.Printf(" %s\n", port) } @@ -352,6 +364,18 @@ func main() { notecard.InitialTraceMode = actionTrace card, err = notecard.Open(config.Interface, config.IPort[config.Interface].Port, configVal) + // If auto-detect was used, backfill the resolved port into in-memory config + // so that PCAP mode (which reads config.IPort[].Port for raw serial.Open()) works. + if err == nil && config.IPort[config.Interface].Port == "" { + _, resolvedPort, resolvedPortConfig := card.Identify() + temp := config.IPort[config.Interface] + temp.Port = resolvedPort + if temp.PortConfig == 0 { + temp.PortConfig = resolvedPortConfig + } + config.IPort[config.Interface] = temp + } + // Process non-config commands var rsp notecard.Request