Skip to content

Commit b3e3cf8

Browse files
committed
Merge remote-tracking branch 'origin/ninafw-peripheral' into ninafw-custom-config
2 parents 3fccf08 + 46c28d8 commit b3e3cf8

32 files changed

+1345
-194
lines changed

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ smoketest-tinygo:
1111
@md5sum test.hex
1212
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/circuitplay
1313
@md5sum test.hex
14+
$(TINYGO) build -o test.hex -size=short -target=circuitplay-bluefruit ./examples/connparams
15+
@md5sum test.hex
1416
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/discover
1517
@md5sum test.hex
1618
$(TINYGO) build -o test.hex -size=short -target=pca10040-s132v6 ./examples/heartrate
@@ -32,14 +34,19 @@ smoketest-tinygo:
3234
@md5sum test.hex
3335
$(TINYGO) build -o test.hex -size=short -target=microbit-v2-s113v7 ./examples/nusserver
3436
@md5sum test.hex
35-
$(TINYGO) build -o test.uf2 -size=short -target=nano-rp2040 ./examples/scanner
36-
@md5sum test.hex
3737
$(TINYGO) build -o test.uf2 -size=short -target=nano-rp2040 ./examples/discover
3838
@md5sum test.hex
39+
$(TINYGO) build -o test.uf2 -size=short -target=arduino-nano33 ./examples/discover
40+
@md5sum test.hex
41+
$(TINYGO) build -o test.uf2 -size=short -target=pyportal ./examples/discover
42+
@md5sum test.hex
43+
$(TINYGO) build -o test.uf2 -size=short -target=nano-rp2040 ./examples/advertisement
44+
@md5sum test.hex
3945

4046
smoketest-linux:
4147
# Test on Linux.
4248
GOOS=linux go build -o /tmp/go-build-discard ./examples/advertisement
49+
GOOS=linux go build -o /tmp/go-build-discard ./examples/connparams
4350
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate
4451
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate-monitor
4552
GOOS=linux go build -o /tmp/go-build-discard ./examples/nusserver

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ func must(action string, err error) {
9999
| Connect to peripheral | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
100100
| Write peripheral characteristics | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
101101
| Receive notifications | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
102-
| Advertisement | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
103-
| Local services | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
104-
| Local characteristics | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
105-
| Send notifications | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
102+
| Advertisement | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |
103+
| Local services | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |
104+
| Local characteristics | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |
105+
| Send notifications | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |
106106

107107
## Linux
108108

@@ -268,11 +268,23 @@ Flashing will normally reset the board.
268268

269269
Go Bluetooth has bare metal support for boards that include a separate ESP32 Bluetooth Low Energy radio co-processor. The ESP32 must be running the Arduino or Adafruit `nina_fw` firmware.
270270

271-
See https://github.com/arduino/nina-fw for more information.
271+
Several boards created by Adafruit and Arduino already have the `nina-fw` firmware pre-loaded. This means you can use TinyGo and the Go Bluetooth package without any additional steps required.
272272

273-
The only currently supported board is the Arduino Nano RP2040 Connect.
273+
Currently supported boards include:
274274

275-
More info soon...
275+
* [Adafruit Metro M4 AirLift](https://www.adafruit.com/product/4000)
276+
* [Adafruit PyBadge](https://www.adafruit.com/product/4200) with [AirLift WiFi FeatherWing](https://www.adafruit.com/product/4264)
277+
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
278+
* [Arduino Nano 33 IoT](https://docs.arduino.cc/hardware/nano-33-iot)
279+
* [Arduino Nano RP2040 Connect](https://docs.arduino.cc/hardware/nano-rp2040-connect)
280+
281+
After you have installed TinyGo and the Go Bluetooth package, you should be able to compile/run code for your device.
282+
283+
For example, this command can be used to compile and flash an Arduino Nano RP2040 Connect board with the example we provide that turns it into a BLE peripheral to act like a heart rate monitor:
284+
285+
tinygo flash -target nano-rp2040 ./examples/heartrate
286+
287+
If you want more information about the `nina-fw` firmware, or want to add support for other ESP32-equipped boards, please see https://github.com/arduino/nina-fw
276288

277289
## API stability
278290

adapter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ const debug = false
66
// SetConnectHandler sets a handler function to be called whenever the adaptor connects
77
// or disconnects. You must call this before you call adaptor.Connect() for centrals
88
// or adaptor.Start() for peripherals in order for it to work.
9-
func (a *Adapter) SetConnectHandler(c func(device Address, connected bool)) {
9+
func (a *Adapter) SetConnectHandler(c func(device Device, connected bool)) {
1010
a.connectHandler = c
1111
}

adapter_darwin.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Adapter struct {
2424
// used to allow multiple callers to call Connect concurrently.
2525
connectMap sync.Map
2626

27-
connectHandler func(device Address, connected bool)
27+
connectHandler func(device Device, connected bool)
2828
}
2929

3030
// DefaultAdapter is the default adapter on the system.
@@ -35,7 +35,7 @@ var DefaultAdapter = &Adapter{
3535
pm: cbgo.NewPeripheralManager(nil),
3636
connectMap: sync.Map{},
3737

38-
connectHandler: func(device Address, connected bool) {
38+
connectHandler: func(device Device, connected bool) {
3939
return
4040
},
4141
}
@@ -106,7 +106,7 @@ func (cmd *centralManagerDelegate) DidDisconnectPeripheral(cmgr cbgo.CentralMana
106106
addr := Address{}
107107
uuid, _ := ParseUUID(id)
108108
addr.UUID = uuid
109-
cmd.a.connectHandler(addr, false)
109+
cmd.a.connectHandler(Device{Address: addr}, false)
110110

111111
// like with DidConnectPeripheral, check if we have a chan allocated for this and send through the peripheral
112112
// this will only be true if the receiving side is still waiting for a connection to complete

adapter_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type Adapter struct {
2323
address string
2424
defaultAdvertisement *Advertisement
2525

26-
connectHandler func(device Address, connected bool)
26+
connectHandler func(device Device, connected bool)
2727
}
2828

2929
// DefaultAdapter is the default adapter on the system. On Linux, it is the
@@ -32,7 +32,7 @@ type Adapter struct {
3232
// Make sure to call Enable() before using it to initialize the adapter.
3333
var DefaultAdapter = &Adapter{
3434
id: defaultAdapter,
35-
connectHandler: func(device Address, connected bool) {
35+
connectHandler: func(device Device, connected bool) {
3636
},
3737
}
3838

adapter_ninafw.go

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ type Adapter struct {
3838
isDefault bool
3939
scanning bool
4040

41-
connectHandler func(device Address, connected bool)
41+
connectHandler func(device Device, connected bool)
4242

43-
connectedDevices []*Device
43+
connectedDevices []Device
4444
notificationsStarted bool
4545
}
4646

@@ -49,19 +49,18 @@ type Adapter struct {
4949
// Make sure to call Enable() before using it to initialize the adapter.
5050
var DefaultAdapter = &Adapter{
5151
isDefault: true,
52-
connectHandler: func(device Address, connected bool) {
52+
connectHandler: func(device Device, connected bool) {
5353
return
5454
},
55-
connectedDevices: make([]*Device, 0, maxConnections),
55+
connectedDevices: make([]Device, 0, maxConnections),
5656
}
5757

5858
// Enable configures the BLE stack. It must be called before any
5959
// Bluetooth-related calls (unless otherwise indicated).
6060
func (a *Adapter) Enable() error {
6161
// reset the NINA in BLE mode
62-
NINA_CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
63-
NINA_RESETN.Configure(machine.PinConfig{Mode: machine.PinOutput})
64-
NINA_CS.Low()
62+
machine.NINA_CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
63+
machine.NINA_CS.Low()
6564

6665
if _debug {
6766
println("tx:", NINA_TX, "rx:", NINA_RX, "baudrate:", NINA_BAUDRATE, "cts:", NINA_CTS, "rts:", NINA_RTS)
@@ -86,7 +85,29 @@ func (a *Adapter) Enable() error {
8685
resetNINA()
8786
}
8887

88+
// serial port for nina chip
89+
uart := machine.UART_NINA
90+
cfg := machine.UARTConfig{
91+
TX: machine.NINA_TX,
92+
RX: machine.NINA_RX,
93+
BaudRate: machine.NINA_BAUDRATE,
94+
}
95+
if !machine.NINA_SOFT_FLOWCONTROL {
96+
cfg.CTS = machine.NINA_CTS
97+
cfg.RTS = machine.NINA_RTS
98+
}
99+
100+
uart.Configure(cfg)
101+
89102
a.hci, a.att = newBLEStack(uart)
103+
if machine.NINA_SOFT_FLOWCONTROL {
104+
a.hci.softRTS = machine.NINA_RTS
105+
a.hci.softRTS.Configure(machine.PinConfig{Mode: machine.PinOutput})
106+
a.hci.softRTS.High()
107+
108+
a.hci.softCTS = machine.NINA_CTS
109+
machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput})
110+
}
90111

91112
if _debug {
92113
println("starting hci")
@@ -158,17 +179,18 @@ func makeNINAAddress(mac MAC) [6]uint8 {
158179
}
159180

160181
func resetNINA() {
161-
NINA_RESETN.High()
182+
machine.NINA_RESETN.Configure(machine.PinConfig{Mode: machine.PinOutput})
183+
184+
machine.NINA_RESETN.High()
162185
time.Sleep(100 * time.Millisecond)
163186
NINA_RESETN.Low()
164187
time.Sleep(1000 * time.Millisecond)
165188
}
166189

167190
func resetNINAInverted() {
168-
if _debug {
169-
println("resetNINAInverted")
170-
}
171-
NINA_RESETN.Low()
191+
machine.NINA_RESETN.Configure(machine.PinConfig{Mode: machine.PinOutput})
192+
193+
machine.NINA_RESETN.Low()
172194
time.Sleep(100 * time.Millisecond)
173195
NINA_RESETN.High()
174196
time.Sleep(1000 * time.Millisecond)
@@ -209,7 +231,7 @@ func (a *Adapter) startNotifications() {
209231
}
210232

211233
d := a.findDevice(not.connectionHandle)
212-
if d == nil {
234+
if d.deviceInternal == nil {
213235
if _debug {
214236
println("no device found for handle", not.connectionHandle)
215237
}
@@ -236,7 +258,7 @@ func (a *Adapter) startNotifications() {
236258
}()
237259
}
238260

239-
func (a *Adapter) findDevice(handle uint16) *Device {
261+
func (a *Adapter) findDevice(handle uint16) Device {
240262
for _, d := range a.connectedDevices {
241263
if d.handle == handle {
242264
if _debug {
@@ -247,5 +269,5 @@ func (a *Adapter) findDevice(handle uint16) *Device {
247269
}
248270
}
249271

250-
return nil
272+
return Device{}
251273
}

adapter_nrf51.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ func handleEvent() {
4343
switch id {
4444
case C.BLE_GAP_EVT_CONNECTED:
4545
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
46-
DefaultAdapter.connectHandler(Address{}, true)
46+
connectEvent := gapEvent.params.unionfield_connected()
47+
device := Device{
48+
Address: Address{makeMACAddress(connectEvent.peer_addr)},
49+
connectionHandle: gapEvent.conn_handle,
50+
}
51+
DefaultAdapter.connectHandler(device, true)
4752
case C.BLE_GAP_EVT_DISCONNECTED:
4853
if defaultAdvertisement.isAdvertising.Get() != 0 {
4954
// The advertisement was running but was automatically stopped
@@ -55,7 +60,10 @@ func handleEvent() {
5560
defaultAdvertisement.start()
5661
}
5762
currentConnection.handle.Reg = C.BLE_CONN_HANDLE_INVALID
58-
DefaultAdapter.connectHandler(Address{}, false)
63+
device := Device{
64+
connectionHandle: gapEvent.conn_handle,
65+
}
66+
DefaultAdapter.connectHandler(device, false)
5967
case C.BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
6068
// Respond with the default PPCP connection parameters by passing
6169
// nil:
@@ -111,3 +119,11 @@ func (a *Adapter) Address() (MACAddress, error) {
111119
}
112120
return MACAddress{MAC: makeAddress(addr.addr)}, nil
113121
}
122+
123+
// Convert a C.ble_gap_addr_t to a MACAddress struct.
124+
func makeMACAddress(addr C.ble_gap_addr_t) MACAddress {
125+
return MACAddress{
126+
MAC: makeAddress(addr.addr),
127+
isRandom: addr.addr_type != 0,
128+
}
129+
}

adapter_nrf528xx-full.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,24 @@ func handleEvent() {
2525
switch id {
2626
case C.BLE_GAP_EVT_CONNECTED:
2727
connectEvent := gapEvent.params.unionfield_connected()
28+
device := Device{
29+
Address: Address{makeMACAddress(connectEvent.peer_addr)},
30+
connectionHandle: gapEvent.conn_handle,
31+
}
2832
switch connectEvent.role {
2933
case C.BLE_GAP_ROLE_PERIPH:
3034
if debug {
3135
println("evt: connected in peripheral role")
3236
}
3337
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
34-
DefaultAdapter.connectHandler(Address{}, true)
38+
DefaultAdapter.connectHandler(device, true)
3539
case C.BLE_GAP_ROLE_CENTRAL:
3640
if debug {
3741
println("evt: connected in central role")
3842
}
3943
connectionAttempt.connectionHandle = gapEvent.conn_handle
4044
connectionAttempt.state.Set(2) // connection was successful
41-
DefaultAdapter.connectHandler(Address{}, true)
45+
DefaultAdapter.connectHandler(device, true)
4246
}
4347
case C.BLE_GAP_EVT_DISCONNECTED:
4448
if debug {
@@ -61,7 +65,18 @@ func handleEvent() {
6165
// necessary.
6266
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
6367
}
64-
DefaultAdapter.connectHandler(Address{}, false)
68+
device := Device{
69+
connectionHandle: gapEvent.conn_handle,
70+
}
71+
DefaultAdapter.connectHandler(device, false)
72+
case C.BLE_GAP_EVT_CONN_PARAM_UPDATE:
73+
if debug {
74+
// Print connection parameters for easy debugging.
75+
params := gapEvent.params.unionfield_conn_param_update().conn_params
76+
interval_ms := params.min_conn_interval * 125 / 100 // min and max are the same here
77+
print("conn param update interval=", interval_ms, "ms latency=", params.slave_latency, " timeout=", params.conn_sup_timeout*10, "ms")
78+
println()
79+
}
6580
case C.BLE_GAP_EVT_ADV_REPORT:
6681
advReport := gapEvent.params.unionfield_adv_report()
6782
if debug && &scanReportBuffer.data[0] != (*byte)(unsafe.Pointer(advReport.data.p_data)) {
@@ -73,8 +88,7 @@ func handleEvent() {
7388
scanReportBuffer.len = byte(advReport.data.len)
7489
globalScanResult.RSSI = int16(advReport.rssi)
7590
globalScanResult.Address = Address{
76-
MACAddress{MAC: makeAddress(advReport.peer_addr.addr),
77-
isRandom: advReport.peer_addr.bitfield_addr_type() != 0},
91+
makeMACAddress(advReport.peer_addr),
7892
}
7993
globalScanResult.AdvertisementPayload = &scanReportBuffer
8094
// Signal to the main thread that there was a scan report.

adapter_nrf528xx-peripheral.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ func handleEvent() {
2828
println("evt: connected in peripheral role")
2929
}
3030
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
31-
DefaultAdapter.connectHandler(Address{}, true)
31+
connectEvent := gapEvent.params.unionfield_connected()
32+
device := Device{
33+
Address: Address{makeMACAddress(connectEvent.peer_addr)},
34+
connectionHandle: gapEvent.conn_handle,
35+
}
36+
DefaultAdapter.connectHandler(device, true)
3237
case C.BLE_GAP_EVT_DISCONNECTED:
3338
if debug {
3439
println("evt: disconnected")
@@ -44,7 +49,10 @@ func handleEvent() {
4449
// necessary.
4550
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
4651
}
47-
DefaultAdapter.connectHandler(Address{}, false)
52+
device := Device{
53+
connectionHandle: gapEvent.conn_handle,
54+
}
55+
DefaultAdapter.connectHandler(device, false)
4856
case C.BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
4957
// We need to respond with sd_ble_gap_data_length_update. Setting
5058
// both parameters to nil will make sure we send the default values.

adapter_nrf528xx.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,11 @@ func (a *Adapter) Address() (MACAddress, error) {
5959
}
6060
return MACAddress{MAC: makeAddress(addr.addr)}, nil
6161
}
62+
63+
// Convert a C.ble_gap_addr_t to a MACAddress struct.
64+
func makeMACAddress(addr C.ble_gap_addr_t) MACAddress {
65+
return MACAddress{
66+
MAC: makeAddress(addr.addr),
67+
isRandom: addr.bitfield_addr_type() != 0,
68+
}
69+
}

0 commit comments

Comments
 (0)