Skip to content

Commit 0cabe28

Browse files
committed
Initial commit
0 parents  commit 0cabe28

34 files changed

+18722
-0
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
s132_nrf52_6.1.1/* linguist-vendored

LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2019 Ayke van Laethem. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of the copyright holder nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Go Bluetooth
2+
3+
Bluetooth API for embedded devices.
4+
5+
This package attempts to build a cross-system Bluetooth API written in Go. It
6+
specifically targets embedded devices that are supported by
7+
[TinyGo](https://tinygo.org/).
8+
9+
At the moment, there is only support for the
10+
[S132](https://www.nordicsemi.com/Software-and-Tools/Software/S132)
11+
SoftDevice (binary driver) on Nordic Semiconductors devices.
12+
13+
## Flashing the SoftDevice
14+
15+
Flashing the SoftDevice can be tricky. If you have
16+
[nrfjprog](https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools)
17+
installed, you can erase the flash and flash the new BLE firmware using the
18+
following commands.
19+
20+
nrfjprog -f nrf52 --eraseall
21+
nrfjprog -f nrf52 --program s132_nrf52_6.1.1/s132_nrf52_6.1.1_softdevice.hex
22+
23+
After that, don't reset the board but instead flash a new program to it. For
24+
example, you can flash the Heart Rate Sensor example using `tinygo`:
25+
26+
tinygo flash -target=pca10040-s132v6 ./examples/heartrate

adapter_sd.c

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// +build softdevice,s132v6
2+
3+
// This file is necessary to define SVCall wrappers, because TinyGo does not yet
4+
// support static functions in the preamble.
5+
6+
// Discard all 'static' attributes to define functions normally.
7+
#define static
8+
9+
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/nrf_sdm.h"
10+
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble.h"

adapter_sd.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// +build softdevice,s132v6
2+
3+
package bluetooth
4+
5+
/*
6+
// Define SoftDevice functions as regular function declarations (not inline
7+
// static functions).
8+
#define SVCALL_AS_NORMAL_FUNCTION
9+
10+
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/nrf_sdm.h"
11+
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble.h"
12+
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble_gap.h"
13+
14+
void assertHandler(void);
15+
*/
16+
import "C"
17+
18+
import (
19+
"device/arm"
20+
"device/nrf"
21+
"unsafe"
22+
)
23+
24+
//export assertHandler
25+
func assertHandler() {
26+
println("SoftDevice assert")
27+
}
28+
29+
var clockConfig C.nrf_clock_lf_cfg_t = C.nrf_clock_lf_cfg_t{
30+
source: C.NRF_CLOCK_LF_SRC_SYNTH,
31+
rc_ctiv: 0,
32+
rc_temp_ctiv: 0,
33+
accuracy: 0,
34+
}
35+
36+
var (
37+
secModeOpen C.ble_gap_conn_sec_mode_t
38+
defaultDeviceName = [6]byte{'T', 'i', 'n', 'y', 'G', 'o'}
39+
)
40+
41+
// Globally allocated buffer for incoming SoftDevice events.
42+
var eventBuf struct {
43+
C.ble_evt_t
44+
buf [23]byte
45+
}
46+
47+
func init() {
48+
secModeOpen.set_bitfield_sm(1)
49+
secModeOpen.set_bitfield_lv(1)
50+
}
51+
52+
// Adapter is a dummy adapter: it represents the connection to the (only)
53+
// SoftDevice on the chip.
54+
type Adapter struct {
55+
}
56+
57+
// DefaultAdapter is an adapter to the default Bluetooth stack on a given
58+
// target.
59+
var DefaultAdapter = &Adapter{}
60+
61+
// Enable configures the BLE stack. It must be called before any
62+
// Bluetooth-related calls (unless otherwise indicated).
63+
func (a *Adapter) Enable() error {
64+
// Enable the IRQ that handles all events.
65+
arm.EnableIRQ(nrf.IRQ_SWI2)
66+
arm.SetPriority(nrf.IRQ_SWI2, 192)
67+
68+
errCode := C.sd_softdevice_enable(&clockConfig, C.nrf_fault_handler_t(C.assertHandler))
69+
if errCode != 0 {
70+
return Error(errCode)
71+
}
72+
73+
appRAMBase := uint32(0x200039c0)
74+
errCode = C.sd_ble_enable(&appRAMBase)
75+
if errCode != 0 {
76+
return Error(errCode)
77+
}
78+
79+
errCode = C.sd_ble_gap_device_name_set(&secModeOpen, &defaultDeviceName[0], uint16(len(defaultDeviceName)))
80+
if errCode != 0 {
81+
return Error(errCode)
82+
}
83+
84+
var gapConnParams C.ble_gap_conn_params_t
85+
gapConnParams.min_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MIN
86+
gapConnParams.max_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MAX
87+
gapConnParams.slave_latency = 0
88+
gapConnParams.conn_sup_timeout = C.BLE_GAP_CP_CONN_SUP_TIMEOUT_NONE
89+
90+
errCode = C.sd_ble_gap_ppcp_set(&gapConnParams)
91+
if errCode != 0 {
92+
return Error(errCode)
93+
}
94+
95+
return nil
96+
}
97+
98+
func handleEvent() {
99+
// TODO: do something with the events.
100+
}
101+
102+
//go:export SWI2_EGU2_IRQHandler
103+
func handleInterrupt() {
104+
for {
105+
eventBufLen := uint16(unsafe.Sizeof(eventBuf))
106+
errCode := C.sd_ble_evt_get((*uint8)(unsafe.Pointer(&eventBuf)), &eventBufLen)
107+
if errCode != 0 {
108+
// Possible error conditions:
109+
// * NRF_ERROR_NOT_FOUND: no events left, break
110+
// * NRF_ERROR_DATA_SIZE: retry with a bigger data buffer
111+
// (currently not handled, TODO)
112+
// * NRF_ERROR_INVALID_ADDR: pointer is not aligned, should
113+
// not happen.
114+
// In all cases, it's best to simply stop now.
115+
break
116+
}
117+
handleEvent()
118+
}
119+
}

error_sd.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// +build softdevice,s132v6
2+
3+
package bluetooth
4+
5+
// Error is an error from within the SoftDevice.
6+
type Error uint32
7+
8+
func (e Error) Error() string {
9+
switch {
10+
case e < 0x1000:
11+
// Global errors.
12+
switch e {
13+
case 0:
14+
return "no error"
15+
case 1:
16+
return "SVC handler is missing"
17+
case 2:
18+
return "SoftDevice has not been enabled"
19+
case 3:
20+
return "internal error"
21+
case 4:
22+
return "no memory for operation"
23+
case 5:
24+
return "not found"
25+
case 6:
26+
return "not supported"
27+
case 7:
28+
return "invalid parameter"
29+
case 8:
30+
return "invalid state, operation disallowed in this state"
31+
case 9:
32+
return "invalid Length"
33+
case 10:
34+
return "invalid flags"
35+
case 11:
36+
return "invalid data"
37+
case 12:
38+
return "invalid data size"
39+
case 13:
40+
return "operation timed out"
41+
case 14:
42+
return "null pointer"
43+
case 15:
44+
return "forbidden operation"
45+
case 16:
46+
return "bad memory address"
47+
case 17:
48+
return "busy"
49+
case 18:
50+
return "maximum connection count exceeded"
51+
case 19:
52+
return "not enough resources for operation"
53+
default:
54+
return "other global error"
55+
}
56+
case e < 0x2000:
57+
// SDM errors.
58+
switch e {
59+
case 0x1000:
60+
return "unknown LFCLK source"
61+
case 0x1001:
62+
return "incorrect interrupt configuration"
63+
case 0x1002:
64+
return "incorrect CLENR0"
65+
default:
66+
return "other SDM error"
67+
}
68+
case e < 0x3000:
69+
// SoC errors.
70+
return "other SoC error"
71+
case e < 0x4000:
72+
// STK errors.
73+
return "other STK error"
74+
default:
75+
// Other errors.
76+
return "other error"
77+
}
78+
}
79+
80+
// makeError returns an error (using the Error type) if the error code is
81+
// non-zero, otherwise it returns nil. It is used with internal API calls.
82+
func makeError(code uint32) error {
83+
if code != 0 {
84+
return Error(code)
85+
}
86+
return nil
87+
}

examples/advertisement/main.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"time"
5+
6+
"github.com/aykevl/go-bluetooth"
7+
)
8+
9+
// flags + local name
10+
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
11+
12+
func main() {
13+
adapter := bluetooth.DefaultAdapter
14+
must("enable SD", adapter.Enable())
15+
adv := adapter.NewAdvertisement()
16+
options := &bluetooth.AdvertiseOptions{
17+
Interval: bluetooth.NewAdvertiseInterval(100),
18+
}
19+
must("config adv", adv.Configure(advPayload, nil, options))
20+
must("start adv", adv.Start())
21+
22+
println("advertising...")
23+
for {
24+
// Sleep forever.
25+
time.Sleep(time.Hour)
26+
}
27+
}
28+
29+
func must(action string, err error) {
30+
if err != nil {
31+
panic("failed to " + action + ": " + err.Error())
32+
}
33+
}

examples/heartrate/main.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"time"
5+
6+
"github.com/aykevl/go-bluetooth"
7+
)
8+
9+
// flags + local name
10+
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
11+
12+
func main() {
13+
println("starting")
14+
adapter := bluetooth.DefaultAdapter
15+
must("enable SD", adapter.Enable())
16+
adv := adapter.NewAdvertisement()
17+
options := &bluetooth.AdvertiseOptions{
18+
Interval: bluetooth.NewAdvertiseInterval(100),
19+
}
20+
must("config adv", adv.Configure(advPayload, nil, options))
21+
must("start adv", adv.Start())
22+
23+
must("add service", adapter.AddService(&bluetooth.Service{
24+
UUID: bluetooth.New16BitUUID(0x180D), // Heart Rate
25+
Characteristics: []bluetooth.Characteristic{
26+
bluetooth.Characteristic{
27+
UUID: bluetooth.New16BitUUID(0x2A37), // Heart Rate Measurement
28+
Value: []byte{0, 75}, // 75bpm
29+
},
30+
},
31+
}))
32+
33+
println("sleeping")
34+
for {
35+
// Sleep forever.
36+
time.Sleep(time.Hour)
37+
}
38+
}
39+
40+
func must(action string, err error) {
41+
if err != nil {
42+
panic("failed to " + action + ": " + err.Error())
43+
}
44+
}

gap.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package bluetooth
2+
3+
// Advertisement encapsulates a single advertisement instance.
4+
type Advertisement struct {
5+
handle uint8
6+
}
7+
8+
// AdvertiseOptions configures everything related to BLE advertisements.
9+
type AdvertiseOptions struct {
10+
Interval AdvertiseInterval
11+
}
12+
13+
// AdvertiseInterval is the advertisement interval in 0.625µs units.
14+
type AdvertiseInterval uint32
15+
16+
// NewAdvertiseInterval returns a new advertisement interval, based on an
17+
// interval in milliseconds.
18+
func NewAdvertiseInterval(intervalMillis uint32) AdvertiseInterval {
19+
// Convert an interval to units of
20+
return AdvertiseInterval(intervalMillis * 8 / 5)
21+
}

0 commit comments

Comments
 (0)