Harden BLE host link and expose latency tools in the dashboard.
Add BLE RTT/stream tests and live tick median in the UI, fix concurrent WebSocket writes and late BATTERY replies, and skip 2M PHY updates that assert under ESP-NOW coexistence. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+49
-10
@@ -175,7 +175,17 @@ func (b *bleLink) onNotify(buf []byte) {
|
||||
select {
|
||||
case b.respCh <- payload:
|
||||
default:
|
||||
log.Printf("BLE: response channel full, dropping frame type=0x%02x", payload[0])
|
||||
// Prefer dropping the oldest queued frame so a late/stale reply
|
||||
// does not block the matching response for the in-flight command.
|
||||
select {
|
||||
case <-b.respCh:
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case b.respCh <- payload:
|
||||
default:
|
||||
log.Printf("BLE: response channel full, dropping frame type=0x%02x", payload[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,9 +199,21 @@ func (b *bleLink) exchange(cmdID byte, cmdName string) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (b *bleLink) exchangePayload(payload []byte, cmdName string) ([]byte, error) {
|
||||
return b.exchangePayloadTimeout(payload, cmdName, readTimeout)
|
||||
}
|
||||
|
||||
func (b *bleLink) exchangePayloadTimeout(payload []byte, cmdName string, timeout time.Duration) ([]byte, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if len(payload) == 0 {
|
||||
return nil, fmt.Errorf("empty payload")
|
||||
}
|
||||
expect := payload[0]
|
||||
if timeout <= 0 {
|
||||
timeout = readTimeout
|
||||
}
|
||||
|
||||
frame, err := uartframe.EncodeFrame(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode frame: %w", err)
|
||||
@@ -212,16 +234,33 @@ sent:
|
||||
return nil, fmt.Errorf("BLE write: %w (check DIP8=BLE)", err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(readTimeout)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case resp := <-b.respCh:
|
||||
if !b.quiet {
|
||||
log.Printf("BLE response (%d bytes)", len(resp))
|
||||
deadline := time.Now().Add(timeout)
|
||||
for {
|
||||
remain := time.Until(deadline)
|
||||
if remain <= 0 {
|
||||
return nil, fmt.Errorf("BLE read timeout for %s (check DIP8=BLE / -ble)", cmdName)
|
||||
}
|
||||
timer := time.NewTimer(remain)
|
||||
select {
|
||||
case resp := <-b.respCh:
|
||||
timer.Stop()
|
||||
if len(resp) == 0 {
|
||||
continue
|
||||
}
|
||||
if resp[0] != expect {
|
||||
// Stale reply (e.g. late BATTERY_STATUS after a short timeout).
|
||||
if !b.quiet {
|
||||
log.Printf("BLE: discard type=0x%02x while waiting for 0x%02x (%s)", resp[0], expect, cmdName)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !b.quiet {
|
||||
log.Printf("BLE response (%d bytes)", len(resp))
|
||||
}
|
||||
return resp, nil
|
||||
case <-timer.C:
|
||||
return nil, fmt.Errorf("BLE read timeout for %s (check DIP8=BLE / -ble)", cmdName)
|
||||
}
|
||||
return resp, nil
|
||||
case <-timer.C:
|
||||
return nil, fmt.Errorf("BLE read timeout for %s (check DIP8=BLE / -ble)", cmdName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user