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()) } }