-
Notifications
You must be signed in to change notification settings - Fork 0
improvements to the USB communication and configuration handling in the backend and initial add of config maker #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,14 @@ type Route struct { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Handler http.HandlerFunc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var StartNormalMode = Route{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Path: "/startNormalMode", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Handler: func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| go usbUtility.ESP32MidiListener(0, midiOutputPipeline.MidiOutChannel) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Fprint(w, "Normal mode started") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Make listener start idempotent; prevent multiple goroutines binding the same USB port. Calling /startNormalMode repeatedly spawns overlapping ESP32MidiListener loops, likely racing the serial port despite internal mutexing. Add a minimal in-process guard and reuse it from both start routes: @@
-import (
+import (
"fmt"
+ "net/http"
+ "sync"
espConfigUtility "modularMidiGoApp/backend/espConfigUtility"
midiCCOutputer "modularMidiGoApp/backend/midiUtility"
midiOutputPipeline "modularMidiGoApp/backend/midiUtility/midiOutputPipeline"
"modularMidiGoApp/backend/usbUtility"
- "net/http"
)
@@
-var StartNormalMode = Route{
- Path: "/startNormalMode",
- Handler: func(w http.ResponseWriter, r *http.Request) {
- go usbUtility.ESP32MidiListener(0, midiOutputPipeline.MidiOutChannel)
- fmt.Fprint(w, "Normal mode started")
- },
-}
+var (
+ listenerMu sync.Mutex
+ listenerRunning bool
+)
+
+func startESP32Listener(w http.ResponseWriter) {
+ listenerMu.Lock()
+ if listenerRunning {
+ http.Error(w, "Listener already running", http.StatusConflict)
+ listenerMu.Unlock()
+ return
+ }
+ listenerRunning = true
+ listenerMu.Unlock()
+
+ go func() {
+ usbUtility.ESP32MidiListener(0, midiOutputPipeline.MidiOutChannel)
+ listenerMu.Lock()
+ listenerRunning = false
+ listenerMu.Unlock()
+ }()
+ fmt.Fprint(w, "Normal mode started")
+}
+
+var StartNormalMode = Route{
+ Path: "/startNormalMode",
+ Handler: func(w http.ResponseWriter, r *http.Request) {
+ startESP32Listener(w)
+ },
+}And update StartUSBListener to call startESP32Listener(w) as well. I can also add a /stopNormalMode route that signals usbUtility’s stopChan via an exported Stop() helper for clean shutdown. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var TestCallRoute = Route{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Path: "/testCall", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Handler: func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "log" | ||||||||||||||||||||||||||||||||||||||||||||||||
| midiOutputPipeline "modularMidiGoApp/backend/midiUtility/midiOutputPipeline" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "sync" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "time" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| "go.bug.st/serial" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -16,10 +17,12 @@ type USBPortsList struct { | |||||||||||||||||||||||||||||||||||||||||||||||
| SelectedUSBDevice string `json:"selected_usb_device"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| var usbMutex = &sync.Mutex{} | ||||||||||||||||||||||||||||||||||||||||||||||||
| var stopChan = make(chan struct{}) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func StopESP32MidiListener() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| close(stopChan) | ||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println("ESP32MidiListener stop signal sent.") | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Stop semantics are one-shot and unsafe; double-close can panic, and restart becomes impossible.
Minimal hardening: var usbMutex = &sync.Mutex{}
var stopChan = make(chan struct{})
+var stopMu sync.Mutex
func StopESP32MidiListener() {
- close(stopChan)
- fmt.Println("ESP32MidiListener stop signal sent.")
+ stopMu.Lock()
+ defer stopMu.Unlock()
+ select {
+ case <-stopChan:
+ // already closed
+ default:
+ close(stopChan)
+ fmt.Println("ESP32MidiListener stop signal sent.")
+ }
}Follow-up: expose a Start that reinitializes a fresh stop channel per run (or switch to context.Context). 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func ESP32MidiListener(channel uint8, outputChan chan<- midiOutputPipeline.MidiCCMessage) { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -36,13 +39,15 @@ func ESP32MidiListener(channel uint8, outputChan chan<- midiOutputPipeline.MidiC | |||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err := listenToESP32(channel, outputChan); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("ESP32 connection error: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
| log.Println("Retrying in 5 seconds...") | ||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Printf("ESP32 connection error: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println("Retrying in 5 seconds...") | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| select { | ||||||||||||||||||||||||||||||||||||||||||||||||
| case <-time.After(5 * time.Second): | ||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| case <-stopChan: | ||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println("ESP32MidiListener stopping...") | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -51,6 +56,8 @@ func ESP32MidiListener(channel uint8, outputChan chan<- midiOutputPipeline.MidiC | |||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func listenToESP32(channel uint8, outputChan chan<- midiOutputPipeline.MidiCCMessage) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| usbMutex.Lock() | ||||||||||||||||||||||||||||||||||||||||||||||||
| defer usbMutex.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Get the selected USB device | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Global mutex held for entire listen loop blocks configuration writes (deadlock/liveness issue).
Options (pick one):
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| deviceName, err := GetSelectedUSBDevice(FilePath) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -75,19 +82,6 @@ func listenToESP32(channel uint8, outputChan chan<- midiOutputPipeline.MidiCCMes | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Successfully connected to ESP32 on %s", deviceName) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Wait for ESP32 to be ready for mode selection | ||||||||||||||||||||||||||||||||||||||||||||||||
| modeSelectReader := bufio.NewReader(port) | ||||||||||||||||||||||||||||||||||||||||||||||||
| port.SetReadTimeout(30 * time.Second) | ||||||||||||||||||||||||||||||||||||||||||||||||
| for { | ||||||||||||||||||||||||||||||||||||||||||||||||
| line, err := modeSelectReader.ReadString('\n') | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("error reading from USB device: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if line == "SELECT_MODE\n" || line == "SELECT_MODE\r\n" { | ||||||||||||||||||||||||||||||||||||||||||||||||
| log.Println("ESP32 ready for mode selection") | ||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| //send mode selection command | ||||||||||||||||||||||||||||||||||||||||||||||||
| if _, err := port.Write([]byte("BROADCAST_MODE\n")); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to write mode selection to USB device: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -134,6 +128,7 @@ func processMidiData(data []byte, channel uint8, outputChan chan<- midiOutputPip | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Process pairs of bytes (CC number, value) | ||||||||||||||||||||||||||||||||||||||||||||||||
| for i := 0; i < len(data); i += 2 { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| ccNumber := data[i] | ||||||||||||||||||||||||||||||||||||||||||||||||
| value := data[i+1] | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -157,6 +152,8 @@ func processMidiData(data []byte, channel uint8, outputChan chan<- midiOutputPip | |||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func WriteToUSB(data interface{}) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| usbMutex.Lock() | ||||||||||||||||||||||||||||||||||||||||||||||||
| defer usbMutex.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Get the selected USB device | ||||||||||||||||||||||||||||||||||||||||||||||||
| deviceName, err := GetSelectedUSBDevice(FilePath) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -186,20 +183,6 @@ func WriteToUSB(data interface{}) error { | |||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("unsupported data type for writing to USB device") | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| //Wait for ESP32 to be ready for mode selection | ||||||||||||||||||||||||||||||||||||||||||||||||
| reader := bufio.NewReader(conn) | ||||||||||||||||||||||||||||||||||||||||||||||||
| conn.SetReadTimeout(30 * time.Second) | ||||||||||||||||||||||||||||||||||||||||||||||||
| for { | ||||||||||||||||||||||||||||||||||||||||||||||||
| line, err := reader.ReadString('\n') | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("error reading from USB device: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if line == "SELECT_MODE\n" || line == "SELECT_MODE\r\n" { | ||||||||||||||||||||||||||||||||||||||||||||||||
| log.Println("ESP32 ready for mode selection") | ||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| //send mode selection command | ||||||||||||||||||||||||||||||||||||||||||||||||
| if _, err := conn.Write([]byte("FLASH_MODE\n")); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to write mode selection to USB device: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential write hang if the listener is running.
Given
listenToESP32currently holdsusbMutexfor its entire lifetime, thisWriteToUSBcall will block while the listener runs. Either:If choosing the minimal path, reinstate a stop before writing:
func WritePinConfig() error { ... - if err := usbUtility.WriteToUSB(configString.String()); err != nil { + // Ensure exclusive USB access + usbUtility.StopESP32MidiListener() + if err := usbUtility.WriteToUSB(configString.String()); err != nil { fmt.Printf("Error writing to USB: %v\n", err) return err }📝 Committable suggestion
🤖 Prompt for AI Agents