154 lines
3.8 KiB
Go
154 lines
3.8 KiB
Go
package main
|
|
|
|
// BLE counter example using the Nordic UART Service (NUS).
|
|
// Compatible apps: nRF Toolbox, Serial Bluetooth Terminal (Android/iOS).
|
|
// - Subscribe to TX notifications to receive the counter value every second.
|
|
// - Send "reset" to the RX characteristic to reset the counter to zero.
|
|
|
|
import (
|
|
"image/color"
|
|
"machine"
|
|
"strconv"
|
|
"time"
|
|
|
|
"tinygo.org/x/bluetooth"
|
|
"tinygo.org/x/drivers/st7789"
|
|
"tinygo.org/x/tinyfont"
|
|
"tinygo.org/x/tinyfont/freesans"
|
|
)
|
|
|
|
// Nordic UART Service UUIDs (6E400001-B5A3-F393-E0A9-E50E24DCCA9E)
|
|
var (
|
|
adapter = bluetooth.DefaultAdapter
|
|
|
|
serviceUUID = bluetooth.NewUUID([16]byte{
|
|
0x6E, 0x40, 0x00, 0x01, 0xB5, 0xA3, 0xF3, 0x93,
|
|
0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E,
|
|
})
|
|
rxUUID = bluetooth.NewUUID([16]byte{
|
|
0x6E, 0x40, 0x00, 0x02, 0xB5, 0xA3, 0xF3, 0x93,
|
|
0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E,
|
|
})
|
|
txUUID = bluetooth.NewUUID([16]byte{
|
|
0x6E, 0x40, 0x00, 0x03, 0xB5, 0xA3, 0xF3, 0x93,
|
|
0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E,
|
|
})
|
|
)
|
|
|
|
var (
|
|
display st7789.Device
|
|
connected bool
|
|
connChanged bool
|
|
counter int
|
|
)
|
|
|
|
var (
|
|
black = color.RGBA{0, 0, 0, 255}
|
|
cyan = color.RGBA{0, 200, 255, 255}
|
|
yellow = color.RGBA{255, 220, 0, 255}
|
|
gray = color.RGBA{150, 150, 150, 255}
|
|
)
|
|
|
|
func main() {
|
|
// sometimes, a little wait is needed to initialize it at hardware level
|
|
time.Sleep(2 * time.Second)
|
|
initDisplay()
|
|
drawStatus("Advertising...")
|
|
drawCounter(0)
|
|
|
|
must("enable BLE", adapter.Enable())
|
|
|
|
adv := adapter.DefaultAdvertisement()
|
|
|
|
must("configure adv", adv.Configure(bluetooth.AdvertisementOptions{
|
|
LocalName: "NiceBadge",
|
|
}))
|
|
|
|
// AddService must be called before adv.Start() on nRF52840 SoftDevice.
|
|
var txChar bluetooth.Characteristic
|
|
|
|
must("add service", adapter.AddService(&bluetooth.Service{
|
|
UUID: serviceUUID,
|
|
Characteristics: []bluetooth.CharacteristicConfig{
|
|
{
|
|
UUID: rxUUID,
|
|
Flags: bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission,
|
|
WriteEvent: func(client bluetooth.Connection, offset int, value []byte) {
|
|
cmd := string(value)
|
|
if cmd == "reset" || cmd == "reset\n" {
|
|
counter = 0
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Handle: &txChar,
|
|
UUID: txUUID,
|
|
Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission,
|
|
},
|
|
},
|
|
}))
|
|
|
|
// Only set flags here — calling adv.Start() or display ops from within
|
|
// the SoftDevice event callback causes a deadlock on nRF52840.
|
|
adapter.SetConnectHandler(func(device bluetooth.Device, c bool) {
|
|
connected = c
|
|
connChanged = true
|
|
})
|
|
|
|
must("start adv", adv.Start())
|
|
|
|
for {
|
|
if connChanged {
|
|
connChanged = false
|
|
if connected {
|
|
drawStatus("Connected ")
|
|
} else {
|
|
drawStatus("Advertising...")
|
|
adv.Start()
|
|
}
|
|
}
|
|
|
|
counter++
|
|
drawCounter(counter)
|
|
if connected {
|
|
txChar.Write([]byte(strconv.Itoa(counter) + "\n"))
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
}
|
|
|
|
func initDisplay() {
|
|
machine.SPI0.Configure(machine.SPIConfig{
|
|
SCK: machine.P1_01,
|
|
SDO: machine.P1_02,
|
|
Frequency: 8000000,
|
|
Mode: 0,
|
|
})
|
|
display = st7789.New(machine.SPI0,
|
|
machine.P1_15, machine.P1_13, machine.P0_10, machine.P0_09)
|
|
display.Configure(st7789.Config{
|
|
Rotation: st7789.ROTATION_90,
|
|
Width: 135,
|
|
Height: 240,
|
|
RowOffset: 40,
|
|
ColumnOffset: 53,
|
|
})
|
|
display.FillScreen(black)
|
|
}
|
|
|
|
func drawStatus(status string) {
|
|
display.FillRectangle(0, 0, 240, 32, black)
|
|
tinyfont.WriteLine(&display, &freesans.Regular9pt7b, 10, 22, "BLE: "+status, cyan)
|
|
}
|
|
|
|
func drawCounter(n int) {
|
|
display.FillRectangle(0, 38, 240, 97, black)
|
|
tinyfont.WriteLine(&display, &freesans.Bold12pt7b, 10, 68, "Counter", gray)
|
|
tinyfont.WriteLine(&display, &freesans.Bold12pt7b, 10, 110, strconv.Itoa(n), yellow)
|
|
}
|
|
|
|
func must(action string, err error) {
|
|
if err != nil {
|
|
panic("failed to " + action + ": " + err.Error())
|
|
}
|
|
}
|