golang构建蓝牙低能耗外设插件库gatt的使用
Golang构建蓝牙低能耗外设插件库gatt的使用
概述
gatt包提供了蓝牙低能耗(BLE) GATT(通用属性配置文件)的实现。GATT是用于编写BLE外设(服务器)和中心设备(客户端)的协议。
作为外设,您可以:
- 创建服务、特征和描述符
- 广播广告
- 接受连接
- 处理请求
作为中心设备,您可以:
- 扫描设备
- 连接设备
- 发现服务
- 发起请求
环境设置
gatt支持Linux和OS X平台。
Linux平台设置
- 首先确保您的BLE设备已关闭:
sudo hciconfig
sudo hciconfig hci0 down # 或使用您想使用的hci设备
- 如果您有BlueZ 5.14+,停止内置的蓝牙服务:
sudo service bluetooth stop
- 因为gatt程序需要管理网络设备,必须以root权限运行或授予相应能力:
sudo <executable>
# 或者
sudo setcap 'cap_net_raw,cap_net_admin=eip' <executable>
<executable>
使用示例
构建和运行示例
Go是编译型语言,需要先构建示例:
# 构建示例服务器
go build examples/server.go
# 启动示例服务器
sudo ./server
或者使用go run一步完成构建和运行:
# 构建并运行示例服务器
sudo go run examples/server.go
发现和探索设备
发现周围的外设:
sudo go run examples/discoverer.go
连接并探索外设设备:
sudo go run examples/explorer.go <peripheral ID>
交叉编译到ARM设备
# 为ARMv5目标设备构建服务器示例
GOARCH=arm GOARM=5 GOOS=linux go build examples/server.go
cp server <target device>
# 在目标设备上启动服务器
sudo ./server
示例代码
服务器示例代码
package main
import (
"log"
"time"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
"github.com/paypal/gatt/examples/service"
)
func main() {
d, err := gatt.NewDevice(option.DefaultServerOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s", err)
}
// 注册可选的处理程序
d.Handle(
gatt.CentralConnected(func(c gatt.Central) {
log.Println("Connect: ", c.ID())
}),
gatt.CentralDisconnected(func(c gatt.Central) {
log.Println("Disconnect: ", c.ID())
}),
)
// 启动设备管理
onStateChanged := func(d gatt.Device, s gatt.State) {
log.Printf("State: %s", s)
switch s {
case gatt.StatePoweredOn:
// 设置GAP和GATT服务
s1 := service.NewGapService("Gopher")
s2 := service.NewGattService()
d.AddService(s1)
d.AddService(s2)
// 添加自定义服务
s3 := service.NewBatteryService()
d.AddService(s3)
// 开始广播
adv := gatt.NewAdvertisement("gopher", []gatt.UUID{s3.UUID()})
d.Advertise(adv)
default:
}
}
d.Init(onStateChanged)
select {
case <-time.After(60 * time.Minute):
}
}
发现器示例代码
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
)
func onStateChanged(d gatt.Device, s gatt.State) {
fmt.Println("State:", s)
switch s {
case gatt.StatePoweredOn:
fmt.Println("Scanning...")
d.Scan([]gatt.UUID{}, false)
return
default:
d.StopScanning()
}
}
func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
fmt.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
fmt.Println(" Local Name =", a.LocalName)
fmt.Println(" TX Power Level =", a.TxPowerLevel)
fmt.Println(" Manufacturer Data =", a.ManufacturerData)
fmt.Println(" Service Data =", a.ServiceData)
}
func main() {
d, err := gatt.NewDevice(option.DefaultClientOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s\n", err)
return
}
// 注册处理程序
d.Handle(
gatt.PeripheralDiscovered(onPeriphDiscovered),
)
d.Init(onStateChanged)
// 等待Ctrl+C退出
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
fmt.Println("Done")
}
注意事项
-
某些BLE中心设备(特别是iOS)可能会积极缓存先前连接的结果。如果您更改了服务或特征,可能需要重启其他设备才能获取更改。
-
目前OS X版本不支持订阅指示(Indications)。
-
gatt使用HCI_CHANNEL_USER,一旦gatt打开了设备,其他程序就无法访问它。
-
对于更细粒度的设备控制,特别是Linux平台,可以参考examples/server_lnx.go中的Option用法。
更多关于golang构建蓝牙低能耗外设插件库gatt的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang构建蓝牙低能耗外设插件库gatt的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 构建蓝牙低能耗外设插件库 gatt 的使用
gatt 是一个 Go 语言实现的蓝牙低能耗 (BLE) 库,用于构建 BLE 外设(Peripheral)和中心设备(Central)。下面我将详细介绍如何使用 gatt 库构建 BLE 外设。
安装 gatt 库
首先需要安装 gatt 库:
go get github.com/paypal/gatt
注意:gatt 在 Linux 上需要 BlueZ 支持,在 macOS 上需要 Xcode 命令行工具。
基本外设示例
下面是一个简单的 BLE 外设示例,创建一个可被发现的服务和特征:
package main
import (
"fmt"
"log"
"os"
"os/signal"
"time"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
)
func main() {
// 初始化设备
d, err := gatt.NewDevice(option.DefaultServerOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s", err)
}
// 注册设备事件处理
d.Handle(
gatt.CentralConnected(func(c gatt.Central) {
fmt.Println("Central connected:", c.ID())
}),
gatt.CentralDisconnected(func(c gatt.Central) {
fmt.Println("Central disconnected:", c.ID())
}),
)
// 初始化服务
onStateChanged := func(d gatt.Device, s gatt.State) {
fmt.Printf("State: %s\n", s)
switch s {
case gatt.StatePoweredOn:
// 设置外设名称
d.SetName("MyGattDevice")
fmt.Println("Device name set to 'MyGattDevice'")
// 添加服务
s1 := gatt.NewService(gatt.MustParseUUID("09fc95c0-c111-11e3-9904-0002a5d5c51b"))
s1.AddCharacteristic(gatt.MustParseUUID("11fac9e0-c111-11e3-9246-0002a5d5c51b")).HandleReadFunc(
func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
fmt.Println("Characteristic read")
rsp.Write([]byte("Hello from Gatt!"))
},
)
d.AddService(s1)
// 开始广播
adv := &gatt.AdvPacket{}
adv.AddFlags(0x06) // LE General Discoverable | BR/EDR Not Supported
adv.AddCompleteName("MyGattDevice")
adv.AddUUIDFit(s1.UUID())
d.Advertise(adv)
default:
}
}
d.Init(onStateChanged)
// 处理中断信号
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
fmt.Println("\nStopping...")
d.StopAdvertising()
d.RemoveAllServices()
}
关键组件说明
1. 设备初始化
d, err := gatt.NewDevice(option.DefaultServerOptions...)
option.DefaultServerOptions
提供了默认的设备配置选项,包括:
- 设备角色设置为 Peripheral
- 连接参数设置
- 其他底层配置
2. 服务与特征创建
s1 := gatt.NewService(gatt.MustParseUUID("09fc95c0-c111-11e3-9904-0002a5d5c51b"))
s1.AddCharacteristic(gatt.MustParseUUID("11fac9e0-c111-11e3-9246-0002a5d5c51b"))
NewService
创建一个新的服务AddCharacteristic
为服务添加特征- UUID 可以使用标准 UUID 或自定义 UUID
3. 特征操作处理
.HandleReadFunc(
func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
rsp.Write([]byte("Hello from Gatt!"))
},
)
可以处理的特征操作包括:
HandleReadFunc
- 处理读取请求HandleWriteFunc
- 处理写入请求HandleNotifyFunc
- 处理通知/指示
4. 广播配置
adv := &gatt.AdvPacket{}
adv.AddFlags(0x06) // LE General Discoverable | BR/EDR Not Supported
adv.AddCompleteName("MyGattDevice")
adv.AddUUIDFit(s1.UUID())
d.Advertise(adv)
AddFlags
设置广播标志AddCompleteName
添加完整设备名称AddUUIDFit
添加服务 UUIDAdvertise
开始广播
完整功能示例
下面是一个更完整的示例,包含读写特征和通知:
package main
import (
"fmt"
"log"
"os"
"os/signal"
"sync"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
)
var (
notifyData = []byte{0}
notifyMu sync.Mutex
)
func main() {
d, err := gatt.NewDevice(option.DefaultServerOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s", err)
}
// 处理连接事件
d.Handle(
gatt.CentralConnected(func(c gatt.Central) {
fmt.Println("Connected:", c.ID())
}),
gatt.CentralDisconnected(func(c gatt.Central) {
fmt.Println("Disconnected:", c.ID())
}),
)
onStateChanged := func(d gatt.Device, s gatt.State) {
fmt.Printf("State: %s\n", s)
switch s {
case gatt.StatePoweredOn:
// 创建服务
s1 := gatt.NewService(gatt.MustParseUUID("180F")) // Battery Service
// 添加可读特征
c1 := s1.AddCharacteristic(gatt.MustParseUUID("2A19")) // Battery Level
c1.HandleReadFunc(func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
fmt.Println("Battery Level read")
rsp.Write([]byte{50}) // 返回50%电量
})
// 添加可写特征
c2 := s1.AddCharacteristic(gatt.MustParseUUID("0001"))
c2.HandleWriteFunc(func(r gatt.Request, data []byte) (status byte) {
fmt.Printf("Received write: %X\n", data)
return gatt.StatusSuccess
})
// 添加可通知特征
c3 := s1.AddCharacteristic(gatt.MustParseUUID("0002"))
c3.HandleNotifyFunc(
func(r gatt.Request, n gatt.Notifier) {
fmt.Println("Notification started")
for !n.Done() {
notifyMu.Lock()
n.Write(notifyData)
notifyMu.Unlock()
// 模拟数据变化
notifyData[0]++
}
fmt.Println("Notification stopped")
},
)
d.AddService(s1)
// 开始广播
adv := &gatt.AdvPacket{}
adv.AddFlags(0x06)
adv.AddCompleteName("BatteryDemo")
adv.AddUUIDFit(s1.UUID())
d.Advertise(adv)
default:
}
}
d.Init(onStateChanged)
// 等待中断信号
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
fmt.Println("\nStopping...")
d.StopAdvertising()
}
注意事项
-
权限问题:在 Linux 上运行需要蓝牙管理权限,通常需要以 root 或具有适当权限的用户运行。
-
UUID 格式:可以使用完整 UUID 或短 UUID(16-bit 或 32-bit)。
-
广播数据限制:广播数据包大小有限制(通常31字节),需合理规划广播内容。
-
跨平台支持:gatt 在 Linux 和 macOS 上支持较好,Windows 支持有限。
-
并发处理:特征操作可能并发发生,需要适当的同步机制。
通过以上示例和说明,您应该能够开始使用 gatt 库构建自己的 BLE 外设应用。根据实际需求,您可以扩展服务、特征和相应的处理逻辑。