Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
aykevl committed Nov 8, 2019
0 parents commit 0cabe28
Show file tree
Hide file tree
Showing 34 changed files with 18,722 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
s132_nrf52_6.1.1/* linguist-vendored
27 changes: 27 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2019 Ayke van Laethem. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Go Bluetooth

Bluetooth API for embedded devices.

This package attempts to build a cross-system Bluetooth API written in Go. It
specifically targets embedded devices that are supported by
[TinyGo](https://tinygo.org/).

At the moment, there is only support for the
[S132](https://www.nordicsemi.com/Software-and-Tools/Software/S132)
SoftDevice (binary driver) on Nordic Semiconductors devices.

## Flashing the SoftDevice

Flashing the SoftDevice can be tricky. If you have
[nrfjprog](https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools)
installed, you can erase the flash and flash the new BLE firmware using the
following commands.

nrfjprog -f nrf52 --eraseall
nrfjprog -f nrf52 --program s132_nrf52_6.1.1/s132_nrf52_6.1.1_softdevice.hex

After that, don't reset the board but instead flash a new program to it. For
example, you can flash the Heart Rate Sensor example using `tinygo`:

tinygo flash -target=pca10040-s132v6 ./examples/heartrate
10 changes: 10 additions & 0 deletions adapter_sd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// +build softdevice,s132v6

// This file is necessary to define SVCall wrappers, because TinyGo does not yet
// support static functions in the preamble.

// Discard all 'static' attributes to define functions normally.
#define static

#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/nrf_sdm.h"
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble.h"
119 changes: 119 additions & 0 deletions adapter_sd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// +build softdevice,s132v6

package bluetooth

/*
// Define SoftDevice functions as regular function declarations (not inline
// static functions).
#define SVCALL_AS_NORMAL_FUNCTION
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/nrf_sdm.h"
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble.h"
#include "s132_nrf52_6.1.1/s132_nrf52_6.1.1_API/include/ble_gap.h"
void assertHandler(void);
*/
import "C"

import (
"device/arm"
"device/nrf"
"unsafe"
)

//export assertHandler
func assertHandler() {
println("SoftDevice assert")
}

var clockConfig C.nrf_clock_lf_cfg_t = C.nrf_clock_lf_cfg_t{
source: C.NRF_CLOCK_LF_SRC_SYNTH,
rc_ctiv: 0,
rc_temp_ctiv: 0,
accuracy: 0,
}

var (
secModeOpen C.ble_gap_conn_sec_mode_t
defaultDeviceName = [6]byte{'T', 'i', 'n', 'y', 'G', 'o'}
)

// Globally allocated buffer for incoming SoftDevice events.
var eventBuf struct {
C.ble_evt_t
buf [23]byte
}

func init() {
secModeOpen.set_bitfield_sm(1)
secModeOpen.set_bitfield_lv(1)
}

// Adapter is a dummy adapter: it represents the connection to the (only)
// SoftDevice on the chip.
type Adapter struct {
}

// DefaultAdapter is an adapter to the default Bluetooth stack on a given
// target.
var DefaultAdapter = &Adapter{}

// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
// Enable the IRQ that handles all events.
arm.EnableIRQ(nrf.IRQ_SWI2)
arm.SetPriority(nrf.IRQ_SWI2, 192)

errCode := C.sd_softdevice_enable(&clockConfig, C.nrf_fault_handler_t(C.assertHandler))
if errCode != 0 {
return Error(errCode)
}

appRAMBase := uint32(0x200039c0)
errCode = C.sd_ble_enable(&appRAMBase)
if errCode != 0 {
return Error(errCode)
}

errCode = C.sd_ble_gap_device_name_set(&secModeOpen, &defaultDeviceName[0], uint16(len(defaultDeviceName)))
if errCode != 0 {
return Error(errCode)
}

var gapConnParams C.ble_gap_conn_params_t
gapConnParams.min_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MIN
gapConnParams.max_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MAX
gapConnParams.slave_latency = 0
gapConnParams.conn_sup_timeout = C.BLE_GAP_CP_CONN_SUP_TIMEOUT_NONE

errCode = C.sd_ble_gap_ppcp_set(&gapConnParams)
if errCode != 0 {
return Error(errCode)
}

return nil
}

func handleEvent() {
// TODO: do something with the events.
}

//go:export SWI2_EGU2_IRQHandler
func handleInterrupt() {
for {
eventBufLen := uint16(unsafe.Sizeof(eventBuf))
errCode := C.sd_ble_evt_get((*uint8)(unsafe.Pointer(&eventBuf)), &eventBufLen)
if errCode != 0 {
// Possible error conditions:
// * NRF_ERROR_NOT_FOUND: no events left, break
// * NRF_ERROR_DATA_SIZE: retry with a bigger data buffer
// (currently not handled, TODO)
// * NRF_ERROR_INVALID_ADDR: pointer is not aligned, should
// not happen.
// In all cases, it's best to simply stop now.
break
}
handleEvent()
}
}
87 changes: 87 additions & 0 deletions error_sd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// +build softdevice,s132v6

package bluetooth

// Error is an error from within the SoftDevice.
type Error uint32

func (e Error) Error() string {
switch {
case e < 0x1000:
// Global errors.
switch e {
case 0:
return "no error"
case 1:
return "SVC handler is missing"
case 2:
return "SoftDevice has not been enabled"
case 3:
return "internal error"
case 4:
return "no memory for operation"
case 5:
return "not found"
case 6:
return "not supported"
case 7:
return "invalid parameter"
case 8:
return "invalid state, operation disallowed in this state"
case 9:
return "invalid Length"
case 10:
return "invalid flags"
case 11:
return "invalid data"
case 12:
return "invalid data size"
case 13:
return "operation timed out"
case 14:
return "null pointer"
case 15:
return "forbidden operation"
case 16:
return "bad memory address"
case 17:
return "busy"
case 18:
return "maximum connection count exceeded"
case 19:
return "not enough resources for operation"
default:
return "other global error"
}
case e < 0x2000:
// SDM errors.
switch e {
case 0x1000:
return "unknown LFCLK source"
case 0x1001:
return "incorrect interrupt configuration"
case 0x1002:
return "incorrect CLENR0"
default:
return "other SDM error"
}
case e < 0x3000:
// SoC errors.
return "other SoC error"
case e < 0x4000:
// STK errors.
return "other STK error"
default:
// Other errors.
return "other error"
}
}

// makeError returns an error (using the Error type) if the error code is
// non-zero, otherwise it returns nil. It is used with internal API calls.
func makeError(code uint32) error {
if code != 0 {
return Error(code)
}
return nil
}
33 changes: 33 additions & 0 deletions examples/advertisement/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"time"

"github.com/aykevl/go-bluetooth"
)

// flags + local name
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")

func main() {
adapter := bluetooth.DefaultAdapter
must("enable SD", adapter.Enable())
adv := adapter.NewAdvertisement()
options := &bluetooth.AdvertiseOptions{
Interval: bluetooth.NewAdvertiseInterval(100),
}
must("config adv", adv.Configure(advPayload, nil, options))
must("start adv", adv.Start())

println("advertising...")
for {
// Sleep forever.
time.Sleep(time.Hour)
}
}

func must(action string, err error) {
if err != nil {
panic("failed to " + action + ": " + err.Error())
}
}
44 changes: 44 additions & 0 deletions examples/heartrate/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"time"

"github.com/aykevl/go-bluetooth"
)

// flags + local name
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")

func main() {
println("starting")
adapter := bluetooth.DefaultAdapter
must("enable SD", adapter.Enable())
adv := adapter.NewAdvertisement()
options := &bluetooth.AdvertiseOptions{
Interval: bluetooth.NewAdvertiseInterval(100),
}
must("config adv", adv.Configure(advPayload, nil, options))
must("start adv", adv.Start())

must("add service", adapter.AddService(&bluetooth.Service{
UUID: bluetooth.New16BitUUID(0x180D), // Heart Rate
Characteristics: []bluetooth.Characteristic{
bluetooth.Characteristic{
UUID: bluetooth.New16BitUUID(0x2A37), // Heart Rate Measurement
Value: []byte{0, 75}, // 75bpm
},
},
}))

println("sleeping")
for {
// Sleep forever.
time.Sleep(time.Hour)
}
}

func must(action string, err error) {
if err != nil {
panic("failed to " + action + ": " + err.Error())
}
}
21 changes: 21 additions & 0 deletions gap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package bluetooth

// Advertisement encapsulates a single advertisement instance.
type Advertisement struct {
handle uint8
}

// AdvertiseOptions configures everything related to BLE advertisements.
type AdvertiseOptions struct {
Interval AdvertiseInterval
}

// AdvertiseInterval is the advertisement interval in 0.625µs units.
type AdvertiseInterval uint32

// NewAdvertiseInterval returns a new advertisement interval, based on an
// interval in milliseconds.
func NewAdvertiseInterval(intervalMillis uint32) AdvertiseInterval {
// Convert an interval to units of
return AdvertiseInterval(intervalMillis * 8 / 5)
}
Loading

0 comments on commit 0cabe28

Please sign in to comment.