Build Bluetooth once. Ship it everywhere.
SimpleGoBLE brings SimpleBLE to Go on Windows, macOS, and Linux. Discover and connect Bluetooth peripherals, read and write GATT values, subscribe to notifications, and host local GATT services through one API, without cgo.
- One cross-platform API: Keep your Bluetooth client and hosting logic the same on Windows, macOS, and Linux.
- Native underneath: Use WinRT, CoreBluetooth, and BlueZ through one maintained stack.
- Built for real products: Act as a central or a peripheral on every supported platform.
- Commercially supported: Ship proprietary applications with commercial licensing and direct technical support from the team that builds SimpleBLE.
- Documentation: The usage guide, API reference, and examples cover every API.
- Commercial support: Visit simpleble.org or email us about licensing and professional services.
- Community: Ask questions and share projects in GitHub Discussions.
- Dongl: Prototype and ship with the official SimpleBLE USB adapter.
- Contribute: Send changes to simpleble/simpleble; the publishing guide covers builds and releases.
Use Go 1.25 or newer:
go get github.com/simpleble/simplegobleThe module embeds the native SimpleBLE library for every supported platform and loads it without cgo, so no C compiler is needed.
Scan for five seconds and print what was found:
package main
import (
"fmt"
"log"
"time"
ble "github.com/simpleble/simplegoble"
)
func main() {
adapters, err := ble.GetAdapters()
if err != nil {
log.Fatal(err)
}
adapter := adapters[0]
adapter.SetCallbackOnScanFound(func(peripheral *ble.Peripheral) {
defer peripheral.Close()
identifier, _ := peripheral.Identifier()
address, _ := peripheral.Address()
rssi, _ := peripheral.RSSI()
fmt.Printf("%s [%s] %d dBm\n", identifier, address, rssi)
})
if err := adapter.ScanFor(5 * time.Second); err != nil {
log.Fatal(err)
}
}An adapter takes callbacks through SetCallbackOnScanStart,
SetCallbackOnScanStop, SetCallbackOnScanFound, SetCallbackOnScanUpdated,
SetCallbackOnPowerOn, and SetCallbackOnPowerOff; a peripheral takes
SetCallbackOnConnected and SetCallbackOnDisconnected. Pass nil to clear a
callback. Callbacks run one at a time on each handle's goroutine, so keep them
short. Each peripheral passed to a callback is a handle you close when finished.
Use ScanStart and ScanStop when your application controls scan timing, and
ScanGetResults for the peripherals seen by the last scan.
Read the battery level of a peripheral and follow its notifications:
const (
batteryService = "0000180f-0000-1000-8000-00805f9b34fb"
batteryLevel = "00002a19-0000-1000-8000-00805f9b34fb"
)
func followBattery(peripheral *ble.Peripheral) error {
peripheral.SetCallbackOnDisconnected(func() { fmt.Println("Disconnected") })
if err := peripheral.Connect(); err != nil {
return err
}
level, err := peripheral.Read(batteryService, batteryLevel)
if err != nil {
return err
}
fmt.Printf("Battery: %d%%\n", level[0])
return peripheral.Notify(batteryService, batteryLevel, func(value []byte) {
fmt.Printf("Battery: %d%%\n", value[0])
})
}Services lists the services, characteristics, capabilities, and descriptors
of a connected peripheral. Read, WriteRequest, and WriteCommand access
characteristic values; ReadDescriptor and WriteDescriptor access
descriptors. Notify and Indicate call your function with each value as a
byte slice, and Unsubscribe stops them. Failures are *ble.Error values with
SimpleBLE's error code in Code.
Create a local peripheral from an adapter, add services and characteristics, then start advertising:
host, err := adapter.CreateLocalPeripheral()
if err != nil {
return err
}
defer host.Close()
host.AddAdvertisedService(batteryService)
service, err := host.AddService(batteryService)
if err != nil {
return err
}
battery, err := service.AddCharacteristic(batteryLevel, ble.CapabilityRead|ble.CapabilityNotify)
if err != nil {
return err
}
battery.SetValue([]byte{100})
host.SetCallbackOnClientConnected(func(address string) { fmt.Println(address, "connected") })
return host.Start()SetValue updates the stored value and publishes it to subscribers.
Characteristics take SetCallbackOnWrite, SetCallbackOnSubscribed, and
SetCallbackOnUnsubscribed, and SetCallbackOnRead computes a value for each
read. Configure services before Start; call Stop before changing them.
A Dongl is a USB Bluetooth adapter made for SimpleBLE. Its backend is on by default; use the adapters of the Dongl backend:
dongl, err := ble.GetDonglBackend()
if err != nil {
return err
}
adapters, err := dongl.Adapters()ble.Config holds the Dongl, BlueZ, and WinRT backend settings; set them
before the first call that reaches a backend. Pairing through a Dongl happens
without the operating system: register SetCallbackOnPasskeyRequest,
SetCallbackOnPasskeyDisplay, or SetCallbackOnNumericComparison on the
peripheral before Connect.
| Platform | Architectures | Requirements |
|---|---|---|
| macOS 15.0 or newer | x64, arm64 | Bluetooth permission for the app that runs your program |
| Windows 10 or newer | x64, arm64 | |
| Linux | x64, arm64 | BlueZ and the D-Bus library |
See Bluetooth permissions for the setup each platform needs.
SimpleGoBLE follows SimpleBLE's release versions. go get picks the latest
stable release, vX.Y.Z, and development builds of the main branch are tagged
vX.Y.Z-devN:
go get github.com/simpleble/simplegoble@mainSimpleGoBLE follows the SimpleBLE license, the Business Source License 1.1 (BUSL-1.1). It is free for non-commercial use, and commercial use needs a commercial license. We offer free commercial licenses for small projects and early-stage companies: email us or leave us a message to discuss your use case. Commercial licenses carry no royalties or revenue sharing; see the licensing page for terms.
SimpleBLE is a project powered by The California Open Source Company.