Skip to content

feat!: pass a ConnectionState to the connect handler - #518

Open
acouvreur wants to merge 1 commit into
devfrom
connection-state
Open

acouvreur wants to merge 1 commit into
devfrom
connection-state

Conversation

@acouvreur

@acouvreur acouvreur commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Replaces #444. It follows the suggestion on #443 to pass a struct instead of a new argument.

The connect handler changes from func(device Device, connected bool) to func(device Device, state ConnectionState).

type ConnectionState struct {
	Connected bool
	Err       error
}

This breaks every program that sets a connect handler. The fix in each one is to use state.Connected instead of connected.

Err says why the device disconnected. It is nil when the device connected, when this side ended the connection, or when the platform gives no reason.

Platform Err on disconnect
nRF SoftDevice DisconnectReason, the HCI code of BLE_GAP_EVT_DISCONNECTED
HCI (NINA, CYW43439, HCI UART, ESP32) DisconnectReason, from the Disconnection Complete event
macOS the CoreBluetooth error of didDisconnectPeripheral
Windows, Linux, Web Bluetooth nil, the platform gives no reason

A caller can check the code with errors.As.

var reason bluetooth.DisconnectReason
if errors.As(state.Err, &reason) && reason == bluetooth.DisconnectTimeout {
	// reconnect
}

Why this shape

  1. A struct can get new fields later without another breaking change. Go does this for hook arguments in net/http/httptrace, for example DNSDoneInfo and WroteRequestInfo. grpc passes SubConnState to its state listener, with the state and a ConnectionError that is nil unless the connection failed.
  2. The device stays its own argument, like http.Server.ConnState func(net.Conn, ConnState), Android onConnectionStateChange(gatt, status, newState) and Nordic's onDeviceDisconnected(device, reason).
  3. Err is nil when this side ended the connection, as in CoreBluetooth didDisconnectPeripheral. So state.Err != nil means the same on every platform. Nordic and HCI report reason 0x16 in that case, which becomes nil.
  4. A typed reason lets a caller tell a timeout from a remote disconnect, like Nordic's REASON_TIMEOUT and REASON_TERMINATE_PEER_USER, or Kable's State.Disconnected(status). DisconnectReason follows AttributeProtocolError, with the codes from Core Specification v5.4, Vol 1, Part F, Table 1.1.

Follow-ups

Tests

Check Result
go test and go vet on Windows pass
go vet and the changed examples for Linux and js/wasm pass
tinygo builds for pca10040-s132v6, pca10056-s140v7, microbit-v2-s113v7, microbit-s110v8, circuitplay-bluefruit, nano-rp2040, badger2040-w, circuitplay-express with hci_uart, xiao-esp32c3 pass
Windows 11 with a real peripheral the handler gets Connected: true with a nil Err

How to verify

The reason path is not tested on hardware yet.

nRF52:

  1. Flash the advertisement example, for example tinygo flash -target=pca10056-s140v7 -monitor ./examples/advertisement.
  2. Connect from a phone with nRF Connect, then disconnect from the phone. Expect reason: HCI error 0x13 (Remote User Terminated Connection).
  3. Connect again and take the phone out of range. Expect reason: HCI error 0x08 (Connection Timeout).

macOS: connect to a peripheral from a program with a connect handler, then power off the peripheral. Expect a non-nil Err. Call Disconnect instead and expect a nil Err.

The connect handler now takes a ConnectionState instead of a bool. It holds
whether the device is connected and, when it disconnected, the reason.

Nordic and HCI adapters report the HCI reason code as a DisconnectReason.
macOS passes the CoreBluetooth error. The reason is nil when this side
ended the connection, and on Windows, Linux and the web, which give none.
@acouvreur
acouvreur requested review from deadprogram and removed request for deadprogram October 17, 2026 20:13
@acouvreur
acouvreur marked this pull request as ready for review October 17, 2026 20:31
@acouvreur
acouvreur requested a review from deadprogram October 17, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant