Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions pkg/service/nrf_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,30 @@ import (
"github.com/librescoot/bluetooth-service/pkg/ble"
)

// InitializeNRF52 initializes communication with the nRF52
// nrfHandshakeTimeout caps how long InitializeNRF52 waits for the nRF to
// reply to the version request. The reply normally arrives in a few ms over
// USOCK; 2 seconds is generous enough to ride out worst-case scheduling
// latency and tight enough to surface a non-responsive nRF immediately
// instead of leaving the service silently degraded.
const nrfHandshakeTimeout = 2 * time.Second

// InitializeNRF52 initializes communication with the nRF52 and blocks until
// the nRF acknowledges by emitting a BLE version frame, or until
// nrfHandshakeTimeout elapses. A timeout is returned as an error so callers
// (Bootstrap.startNormally, ReconnectUSock) can surface FaultNRFInit or
// failed:reconnect as appropriate. Late-arriving versions clear the fault
// via handleBLEVersionMessage, so a slow handshake self-heals.
func (s *Service) InitializeNRF52() error {
s.log.Infof("Starting nRF52 initialization...")

// Drain any stale version signal from a previous handshake. The buffered
// channel may still hold an entry if the version handler fired between
// the previous wait and now (e.g. on rapid reconnects).
select {
case <-s.versionRxCh:
default:
}

// 1. Disable data streaming
if err := writeUARTMessage(s.usock, ble.TypeDataStream, ble.TypeDataStreamEnable, 0); err != nil {
s.log.Warnf(" failed to disable data streaming: %v", err)
Expand Down Expand Up @@ -58,8 +78,19 @@ func (s *Service) InitializeNRF52() error {
s.log.Debugf("Sent command to restart advertising without whitelist")
}

s.log.Infof("nRF52 initialization complete")
return nil
// Block until the version reply arrives or we time out. The version
// handler does a non-blocking send on versionRxCh on receipt; a reply
// that arrived during the steps above is sitting in the buffered
// channel and is consumed immediately here.
select {
case <-s.versionRxCh:
s.log.Infof("nRF52 initialization complete")
return nil
case <-time.After(nrfHandshakeTimeout):
return fmt.Errorf("handshake timeout: no version response within %v", nrfHandshakeTimeout)
case <-s.stopCh:
return fmt.Errorf("handshake aborted: service stopping")
}
}

// ShutdownNRF52 tells the nRF to stop its autonomous data stream before
Expand Down
16 changes: 12 additions & 4 deletions pkg/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ type Service struct {
// recovers.
schemaMu sync.RWMutex
schemaCache map[string]settingSchema

// Signaled by handleBLEVersionMessage whenever a BLE version frame
// arrives from the nRF. InitializeNRF52 drains this channel before
// sending the version request, then waits on it to confirm the
// handshake completed before returning. Buffered (size 1) so the
// version handler's send is non-blocking.
versionRxCh chan struct{}
}

// Fault codes for bluetooth service
Expand All @@ -82,10 +89,11 @@ const (
// New creates a new Service instance
func New(ipcClient *ipc.Client, log *logger.Logger) *Service {
return &Service{
ipc: ipcClient,
log: log,
stopCh: make(chan struct{}),
faults: ipcClient.NewFaultSet("ble:fault", "ble", "fault"),
ipc: ipcClient,
log: log,
stopCh: make(chan struct{}),
faults: ipcClient.NewFaultSet("ble:fault", "ble", "fault"),
versionRxCh: make(chan struct{}, 1),
}
}

Expand Down
9 changes: 9 additions & 0 deletions pkg/service/usock_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,15 @@ func (s *Service) handleBLEVersionMessage(msgType ble.MessageType, absSubTypeKey
if versionStr, ok := convertToString(value); ok {
s.log.Infof("Detected firmware version: %s", versionStr)

// Signal InitializeNRF52's handshake wait; clear FaultNRFInit
// so a late-arriving version after a handshake-timeout self-heals
// the fault state.
select {
case s.versionRxCh <- struct{}{}:
default:
}
s.ClearFault(FaultNRFInit)

// Write version to Redis immediately (before compatibility check)
if err := s.ipc.Hash(KeyFirmwareVersion).Set("nrf-fw-version", versionStr); err != nil {
s.log.Errorf("Failed to update BLE version in Redis: %v", err)
Expand Down