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" "github.com/tinygo-org/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 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() { initDisplay() drawStatus("Advertising...") drawCounter(0) must("enable BLE", adapter.Enable()) 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, }, }, })) adv := adapter.DefaultAdvertisement() must("configure adv", adv.Configure(bluetooth.AdvertisementOptions{ LocalName: "NiceBadge", ServiceUUIDs: []bluetooth.UUID{serviceUUID}, })) must("start adv", adv.Start()) for { counter++ drawCounter(counter) // Write sends a BLE notification to subscribed centrals. // If no device is subscribed, Write returns an error. _, err := txChar.Write([]byte(strconv.Itoa(counter) + "\n")) wasConnected := connected connected = err == nil if wasConnected != connected { if connected { drawStatus("Connected ") } else { drawStatus("Advertising...") must("restart adv", adv.Start()) } } 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()) } }