tinygo-org / bluetooth

Cross-platform Bluetooth API for Go and TinyGo. Supports Linux, macOS, Windows, and bare metal using Nordic SoftDevice or HCI
https://tinygo.org
Other
742 stars 136 forks source link

How to disconnect device or close adapter #247

Open hgqcode opened 7 months ago

hgqcode commented 7 months ago

It doesn't work when i use disconnect device. so What am I supposed to do

var adapter = bluetooth.DefaultAdapter

func main() {
    wait()

    println("enabling")

    // Enable BLE interface.
    must("enable BLE stack", adapter.Enable())

    ch := make(chan bluetooth.ScanResult, 1)

    // Start scanning.
    println("scanning...")
    err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {
        println("found device:", result.Address.String(), result.RSSI, result.LocalName())
        if result.Address.String() == "10:52:1C:69:DA:EE" {
            adapter.StopScan()
            ch <- result
        }
    })

    var device *bluetooth.Device
    select {
    case result := <-ch:
        device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
        if err != nil {
            println(err.Error())
            return
        }

        println("connected to ", result.Address.String())
    }

    // get services
    println("discovering services/characteristics")
    srvcs, err := device.DiscoverServices(nil)
    must("discover services", err)

    // buffer to retrieve characteristic data
    buf := make([]byte, 255)

    for _, srvc := range srvcs {
        println("- service", srvc.UUID().String())

        chars, err := srvc.DiscoverCharacteristics(nil)
        if err != nil {
            println(err)
        }
        for _, char := range chars {
            println("-- characteristic", char.UUID().String())
            n, err := char.Read(buf)
            if err != nil {
                println("    ", err.Error())
            } else {
                println("    data bytes", strconv.Itoa(n))
                println("    value =", string(buf[:n]))
            }
        }
    }

    err = device.Disconnect()
    if err != nil {
        println(err)
    }

    done()
}

func must(action string, err error) {
    if err != nil {
        panic("failed to " + action + ": " + err.Error())
    }
}

func wait() {
    time.Sleep(3 * time.Second)
}

// done just blocks forever, allows USB CDC reset for flashing new software.
func done() {
    println("Done.")

    time.Sleep(1 * time.Hour)
}
roffe commented 7 months ago

I have the same issue. Disconnect on Windows doesn't actually seem to disconnect the device and i have to restart the software before i can connect to the same BT device again

aykevl commented 7 months ago

It doesn't work when i use disconnect device

What is the error? What system are you on? How can I reproduce this issue? It's impossible to help or fix the bug without more information.

roffe commented 7 months ago

No errors are raised, the software next time around starts scanning fine and i can see other devices. but my OBDX Pro doesn't start announcing itself again until i close the test software. it's like even after i called Disconnect the device is not actually disconnected until i close the program.

Im on Windows 11 23H2 22631.3155, Go 1.22 and using tdm-gcc as C compiler if that helps. Code is compiled as 64 bit as 32 bit panics and i've raised a issue here about it https://github.com/saltosystems/winrt-go/issues/83

Here is some uggly code to trigger the problem:

package main

import (
    "log"
    "time"

    "github.com/roffe/gocan/dvi"
    "tinygo.org/x/bluetooth"
)

var (
    uartService = bluetooth.NewUUID([16]byte{0x6e, 0x40, 0x00, 0x01, 0xb5, 0xa3, 0xf3, 0x93, 0xe0, 0xa9, 0xe5, 0x0e, 0x24, 0xdc, 0xca, 0x9e})

    gatUUID = map[string]string{
        "00001800-0000-1000-8000-00805f9b34fb": "Generic Access",
        "00001801-0000-1000-8000-00805f9b34fb": "Generic Attribute",
        "6e400001-b5a3-f393-e0a9-e50e24dcca9e": "UART service",
    }

    characteristicUUID = map[string]string{
        "6e400003-b5a3-f393-e0a9-e50e24dcca9e": "TX",
        "6e400002-b5a3-f393-e0a9-e50e24dcca9e": "RX",
    }
)

var adapter = bluetooth.DefaultAdapter

func init() {
    log.SetFlags(log.LstdFlags | log.Lshortfile)
}

func main() {

    must("enable BLE stack", adapter.Enable())
    for i := 0; i < 3; i++ {
        main2()
        time.Sleep(5 * time.Second)
    }
}

func main2() {
    // Enable BLE interface.

    ch := make(chan bluetooth.ScanResult, 1)

    // Start scanning.
    println("scanning...")
    err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
        //println("found device:", device.Address.String(), device.RSSI, device.LocalName())
        log.Printf("found device: %s, %d, %s", device.Address.String(), device.RSSI, device.LocalName())
        if device.LocalName() == "OBDX Pro GT" {
            adapter.StopScan()
            ch <- device
        }
    })
    must("start scan", err)

    var device bluetooth.Device
    select {
    case d := <-ch:
        device, err = adapter.Connect(d.Address, bluetooth.ConnectionParams{})
        if err != nil {
            log.Fatal("failed to connect:", err)
        }
        log.Printf("connected to %s", d.Address.String())
    case <-time.After(10 * time.Second):
        log.Fatal("Did not find any OBDX Pro GT device")
    }

    svcs, err := device.DiscoverServices(nil)
    if err != nil {
        log.Fatal("failed to discover services:", err)
    }

    log.Println("services found:", len(svcs))
    for _, svc := range svcs {
        if name, ok := gatUUID[svc.UUID().String()]; ok {
            log.Printf("service: %s (%s)", svc.String(), name)
        } else {
            log.Printf("service: %s", svc.String())
        }
    }

    srvc := svcs[2]

    chars, err := srvc.DiscoverCharacteristics(nil)
    if err != nil {
        log.Fatal("failed to discover characteristics:", err)
    }

    for _, char := range chars {

        if name, ok := characteristicUUID[char.UUID().String()]; ok {
            log.Printf("characteristic: %s (%s)", char.String(), name)
        } else {
            log.Printf("characteristic: %s", char.String())
        }
    }

    rx := chars[0]
    tx := chars[1]

    err = rx.EnableNotifications(func(buf []byte) {
        log.Printf("received: %X %q", buf, buf)
    })

    if err != nil {
        log.Fatal("failed to enable notifications:", err)
    }

    for i := 0; i < 3; i++ {
        tx.Write([]byte("\r\n"))
        time.Sleep(50 * time.Millisecond)
    }

    //tx.Write([]byte("DXDP1\r"))
    //time.Sleep(150 * time.Millisecond)
    //tx.Write([]byte("AT@1\r"))

    payload := dvi.New(0x22, []byte{0x03}).Bytes()
    log.Printf("sending: %X", payload)

    for i := 0; i < 3; i++ {
        tx.Write(payload)
        time.Sleep(2 * time.Second)
    }

    if err := device.Disconnect(); err != nil {
        log.Fatal("failed to disconnect:", err)
    }
}

func must(action string, err error) {
    if err != nil {
        log.Fatal("failed to " + action + ": " + err.Error())
    }
}

I can see during the scans in the 2 and 3rd itteration that the EnableNotifications handler still is getting data from the device so it's indeed the "disconnect" that doesn't free the adapter properly


PS: 31 2024-03-12 14:06:31 bttest >go run .
scanning...
2024/03/12 14:06:33 main.go:50: found device: 40:A8:20:13:16:0A, -58, 
2024/03/12 14:06:33 main.go:50: found device: 40:A8:20:13:16:0A, -58, 
2024/03/12 14:06:33 main.go:50: found device: 4D:4D:4D:40:A4:8E, -68, 
2024/03/12 14:06:33 main.go:50: found device: 64:B7:08:5F:F6:9A, -92, 
2024/03/12 14:06:33 main.go:50: found device: E1:A1:83:A9:09:2A, -94, 
2024/03/12 14:06:33 main.go:50: found device: E1:A1:83:A9:09:2A, -94, S36 B64E LE
2024/03/12 14:06:33 main.go:50: found device: 49:92:32:66:14:17, -68, 
2024/03/12 14:06:33 main.go:50: found device: 49:92:32:66:14:17, -68, 
2024/03/12 14:06:33 main.go:50: found device: 4E:29:DB:25:BB:71, -88, 
2024/03/12 14:06:33 main.go:50: found device: 4E:29:DB:25:BB:71, -90, 
2024/03/12 14:06:33 main.go:50: found device: 4E:29:DB:25:BB:71, -88, S39 1D5C LE
2024/03/12 14:06:33 main.go:50: found device: 64:B7:08:5F:F6:9A, -86, 
2024/03/12 14:06:33 main.go:50: found device: 64:B7:08:5F:F6:9A, -86, OBDX Pro GT
2024/03/12 14:06:33 main.go:65: connected to 64:B7:08:5F:F6:9A
2024/03/12 14:06:35 main.go:75: services found: 3
2024/03/12 14:06:35 main.go:78: service: 00001801-0000-1000-8000-00805f9b34fb (Generic Attribute)
2024/03/12 14:06:35 main.go:78: service: 00001800-0000-1000-8000-00805f9b34fb (Generic Access)
2024/03/12 14:06:35 main.go:78: service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e (UART service)
2024/03/12 14:06:36 main.go:94: characteristic: 6e400003-b5a3-f393-e0a9-e50e24dcca9e (TX)
2024/03/12 14:06:36 main.go:94: characteristic: 6e400002-b5a3-f393-e0a9-e50e24dcca9e (RX)
2024/03/12 14:06:36 main.go:104: received: 0D "\r"
2024/03/12 14:06:36 main.go:104: received: 0A "\n"
2024/03/12 14:06:36 main.go:104: received: 0D "\r"
2024/03/12 14:06:36 main.go:104: received: 0A "\n"
2024/03/12 14:06:36 main.go:104: received: 0D "\r"
2024/03/12 14:06:36 main.go:104: received: 0A "\n"
2024/03/12 14:06:36 main.go:121: sending: 220103D9
2024/03/12 14:06:36 main.go:104: received: 22 "\""
2024/03/12 14:06:36 main.go:104: received: 0103D9 "\x01\x03\xd9"
2024/03/12 14:06:38 main.go:104: received: 22 "\""
2024/03/12 14:06:38 main.go:104: received: 0103D9 "\x01\x03\xd9"
2024/03/12 14:06:40 main.go:104: received: 22 "\""
2024/03/12 14:06:40 main.go:104: received: 0103D9 "\x01\x03\xd9"
scanning...
2024/03/12 14:06:47 main.go:50: found device: 5F:55:B0:D7:0E:1B, -64, 
2024/03/12 14:06:47 main.go:50: found device: D0:03:DF:AB:78:6F, -76, 
2024/03/12 14:06:47 main.go:50: found device: 7A:64:4B:F5:60:35, -88,
2024/03/12 14:06:47 main.go:50: found device: 7A:64:4B:F5:60:35, -88,
2024/03/12 14:06:47 main.go:50: found device: 54:2F:57:62:A0:E9, -94, 
2024/03/12 14:06:48 main.go:50: found device: 4D:4D:4D:40:A4:8E, -70, 
2024/03/12 14:06:48 main.go:50: found device: 4D:4D:4D:40:A4:8E, -70,
2024/03/12 14:06:48 main.go:50: found device: 49:92:32:66:14:17, -68, 
2024/03/12 14:06:48 main.go:50: found device: 49:92:32:66:14:17, -70,
2024/03/12 14:06:48 main.go:50: found device: E1:A1:83:A9:09:2A, -92, 
2024/03/12 14:06:48 main.go:50: found device: E1:A1:83:A9:09:2A, -92, S36 B64E LE
2024/03/12 14:06:48 main.go:50: found device: 46:46:46:EC:DC:B7, -88,
2024/03/12 14:06:48 main.go:50: found device: 46:46:46:EC:DC:B7, -88,
2024/03/12 14:06:48 main.go:50: found device: D0:03:DF:AB:78:6F, -76, 
2024/03/12 14:06:48 main.go:50: found device: 7C:0A:3F:51:1B:09, -86, 
2024/03/12 14:06:48 main.go:50: found device: 5F:55:B0:D7:0E:1B, -72,
2024/03/12 14:06:49 main.go:50: found device: D0:03:DF:AB:78:6F, -72, 
2024/03/12 14:06:49 main.go:50: found device: 49:92:32:66:14:17, -72,
2024/03/12 14:06:49 main.go:50: found device: 49:92:32:66:14:17, -72,
2024/03/12 14:06:49 main.go:50: found device: 65:03:49:F6:3A:9C, -64, 
2024/03/12 14:06:49 main.go:50: found device: 65:03:49:F6:3A:9C, -64,
2024/03/12 14:06:49 main.go:50: found device: 79:C7:BA:28:33:33, -66, 
2024/03/12 14:06:49 main.go:50: found device: 79:C7:BA:28:33:33, -66,
2024/03/12 14:06:49 main.go:50: found device: 5F:55:B0:D7:0E:1B, -68, 
2024/03/12 14:06:50 main.go:50: found device: 49:92:32:66:14:17, -64, 
2024/03/12 14:06:50 main.go:50: found device: 49:92:32:66:14:17, -62,
2024/03/12 14:06:50 main.go:50: found device: 65:03:49:F6:3A:9C, -68, 
2024/03/12 14:06:50 main.go:50: found device: 65:03:49:F6:3A:9C, -68,
2024/03/12 14:06:50 main.go:50: found device: 7C:0A:3F:51:1B:09, -84, 
2024/03/12 14:06:50 main.go:50: found device: 46:46:46:EC:DC:B7, -90, 
2024/03/12 14:06:50 main.go:50: found device: 46:46:46:EC:DC:B7, -88,
2024/03/12 14:06:50 main.go:50: found device: 79:C7:BA:28:33:33, -56, 
2024/03/12 14:06:50 main.go:50: found device: 79:C7:BA:28:33:33, -56,
2024/03/12 14:06:51 main.go:50: found device: 79:C7:BA:28:33:33, -58, 
2024/03/12 14:06:51 main.go:50: found device: 79:C7:BA:28:33:33, -58,
2024/03/12 14:06:51 main.go:50: found device: D0:03:DF:AB:78:6F, -76,
2024/03/12 14:06:51 main.go:50: found device: 4E:29:DB:25:BB:71, -90, 
2024/03/12 14:06:51 main.go:50: found device: 49:92:32:66:14:17, -72, 
2024/03/12 14:06:51 main.go:50: found device: 40:A8:20:13:16:0A, -58, 
2024/03/12 14:06:51 main.go:50: found device: 65:03:49:F6:3A:9C, -64, 
2024/03/12 14:06:51 main.go:50: found device: 65:03:49:F6:3A:9C, -64,
2024/03/12 14:06:52 main.go:50: found device: E1:A1:83:A9:09:2A, -92, 
2024/03/12 14:06:52 main.go:50: found device: 79:C7:BA:28:33:33, -68, 
2024/03/12 14:06:52 main.go:50: found device: 79:C7:BA:28:33:33, -68,
2024/03/12 14:06:52 main.go:50: found device: 4D:4D:4D:40:A4:8E, -68, 
2024/03/12 14:06:52 main.go:50: found device: 4E:29:DB:25:BB:71, -90, 
2024/03/12 14:06:52 main.go:50: found device: 4E:29:DB:25:BB:71, -90, S39 1D5C LE
2024/03/12 14:06:52 main.go:50: found device: 4E:29:DB:25:BB:71, -90,
2024/03/12 14:06:52 main.go:50: found device: 5F:55:B0:D7:0E:1B, -72,
2024/03/12 14:06:52 main.go:50: found device: 40:A8:20:13:16:0A, -60, 
2024/03/12 14:06:52 main.go:50: found device: 40:A8:20:13:16:0A, -60,
2024/03/12 14:06:52 main.go:50: found device: 65:03:49:F6:3A:9C, -68, 
2024/03/12 14:06:52 main.go:50: found device: 65:03:49:F6:3A:9C, -70,
2024/03/12 14:06:52 main.go:50: found device: 79:C7:BA:28:33:33, -56, 
2024/03/12 14:06:52 main.go:50: found device: 79:C7:BA:28:33:33, -56,
2024/03/12 14:06:53 main.go:50: found device: 46:73:E9:39:29:76, -94, 
2024/03/12 14:06:53 main.go:50: found device: 5F:55:B0:D7:0E:1B, -68, 
2024/03/12 14:06:53 main.go:50: found device: 49:92:32:66:14:17, -70,
2024/03/12 14:06:53 main.go:50: found device: 49:92:32:66:14:17, -70,
2024/03/12 14:06:53 main.go:50: found device: 65:03:49:F6:3A:9C, -72, 
2024/03/12 14:06:53 main.go:50: found device: 65:03:49:F6:3A:9C, -72,
2024/03/12 14:06:53 main.go:50: found device: 4E:29:DB:25:BB:71, -90,
2024/03/12 14:06:53 main.go:50: found device: 4D:4D:4D:40:A4:8E, -68, 
2024/03/12 14:06:53 main.go:50: found device: 4D:4D:4D:40:A4:8E, -68,
2024/03/12 14:06:53 main.go:50: found device: 79:C7:BA:28:33:33, -58,
2024/03/12 14:06:53 main.go:50: found device: 79:C7:BA:28:33:33, -58,
2024/03/12 14:06:54 main.go:50: found device: 49:92:32:66:14:17, -70, 
2024/03/12 14:06:54 main.go:50: found device: 49:92:32:66:14:17, -72,
2024/03/12 14:06:54 main.go:50: found device: D0:03:DF:AB:78:6F, -70,
2024/03/12 14:06:54 main.go:50: found device: 65:03:49:F6:3A:9C, -64, 
2024/03/12 14:06:54 main.go:50: found device: 65:03:49:F6:3A:9C, -64,
2024/03/12 14:06:54 main.go:50: found device: 46:46:46:EC:DC:B7, -88, 
2024/03/12 14:06:54 main.go:50: found device: 46:46:46:EC:DC:B7, -88,
2024/03/12 14:06:54 main.go:50: found device: 7A:64:4B:F5:60:35, -88,
2024/03/12 14:06:54 main.go:50: found device: 7A:64:4B:F5:60:35, -88,
2024/03/12 14:06:54 main.go:50: found device: D0:03:DF:AB:78:6F, -84, 
2024/03/12 14:06:54 main.go:50: found device: 5F:55:B0:D7:0E:1B, -64,
2024/03/12 14:06:55 main.go:50: found device: D0:03:DF:AB:78:6F, -76, 
2024/03/12 14:06:55 main.go:50: found device: E1:A1:83:A9:09:2A, -92,
2024/03/12 14:06:55 main.go:50: found device: 65:03:49:F6:3A:9C, -68,
2024/03/12 14:06:55 main.go:50: found device: 65:03:49:F6:3A:9C, -68,
2024/03/12 14:06:55 main.go:50: found device: 79:C7:BA:28:33:33, -56, 
2024/03/12 14:06:55 main.go:50: found device: 79:C7:BA:28:33:33, -56,
2024/03/12 14:06:55 main.go:50: found device: E1:A1:83:A9:09:2A, -92, 
2024/03/12 14:06:55 main.go:50: found device: E1:A1:83:A9:09:2A, -92, S36 B64E LE
2024/03/12 14:06:55 main.go:50: found device: 49:92:32:66:14:17, -68,
2024/03/12 14:06:55 main.go:50: found device: 49:92:32:66:14:17, -70,
2024/03/12 14:06:56 main.go:50: found device: 5F:55:B0:D7:0E:1B, -70, 
2024/03/12 14:06:56 main.go:50: found device: 46:46:46:EC:DC:B7, -90,
2024/03/12 14:06:56 main.go:50: found device: 46:46:46:EC:DC:B7, -88,
2024/03/12 14:06:56 main.go:50: found device: 65:03:49:F6:3A:9C, -72,
2024/03/12 14:06:56 main.go:50: found device: 65:03:49:F6:3A:9C, -70,
2024/03/12 14:06:56 main.go:50: found device: 4E:A9:45:DC:4E:3A, -92, 
2024/03/12 14:06:56 main.go:50: found device: 4E:A9:45:DC:4E:3A, -92,
2024/03/12 14:06:56 main.go:50: found device: 79:C7:BA:28:33:33, -58, 
2024/03/12 14:06:56 main.go:50: found device: 79:C7:BA:28:33:33, -58,
2024/03/12 14:06:56 main.go:50: found device: 49:92:32:66:14:17, -70, 
2024/03/12 14:06:56 main.go:50: found device: 49:92:32:66:14:17, -70,
2024/03/12 14:06:56 main.go:50: found device: D0:03:DF:AB:78:6F, -70,
2024/03/12 14:06:56 main.go:50: found device: 65:03:49:F6:3A:9C, -64, 
2024/03/12 14:06:56 main.go:50: found device: 65:03:49:F6:3A:9C, -64,
2024/03/12 14:06:56 main.go:50: found device: 4D:4D:4D:40:A4:8E, -64,
2024/03/12 14:06:56 main.go:50: found device: 4D:4D:4D:40:A4:8E, -64,
2024/03/12 14:06:57 main.go:50: found device: 4E:A9:45:DC:4E:3A, -90, 
2024/03/12 14:06:57 main.go:50: found device: 4E:A9:45:DC:4E:3A, -90,
2024/03/12 14:06:57 main.go:50: found device: 79:C7:BA:28:33:33, -66, 
2024/03/12 14:06:57 main.go:50: found device: 79:C7:BA:28:33:33, -66,
2024/03/12 14:06:57 main.go:50: found device: 5F:55:B0:D7:0E:1B, -70, 
2024/03/12 14:06:57 main.go:50: found device: 49:92:32:66:14:17, -64, 
2024/03/12 14:06:57 main.go:50: found device: 49:92:32:66:14:17, -64,
2024/03/12 14:06:57 main.go:50: found device: 46:46:46:EC:DC:B7, -78,
2024/03/12 14:06:57 main.go:50: found device: 46:46:46:EC:DC:B7, -78,
2024/03/12 14:06:57 main.go:50: found device: 65:03:49:F6:3A:9C, -70, 
2024/03/12 14:06:57 main.go:50: found device: 65:03:49:F6:3A:9C, -68,
2024/03/12 14:06:57 main.go:50: found device: 4E:A9:45:DC:4E:3A, -90, 
2024/03/12 14:06:57 main.go:50: found device: 4E:A9:45:DC:4E:3A, -92,
2024/03/12 14:06:58 main.go:50: found device: D0:03:DF:AB:78:6F, -86, 
2024/03/12 14:06:58 main.go:50: found device: 49:92:32:66:14:17, -68, 
2024/03/12 14:06:58 main.go:50: found device: 49:92:32:66:14:17, -68,
2024/03/12 14:06:58 main.go:50: found device: 4E:29:DB:25:BB:71, -90, 
2024/03/12 14:06:58 main.go:50: found device: 4E:29:DB:25:BB:71, -90, S39 1D5C LE
2024/03/12 14:06:58 main.go:50: found device: 4E:29:DB:25:BB:71, -88,
2024/03/12 14:06:58 main.go:50: found device: 7C:0A:3F:51:1B:09, -84, 
2024/03/12 14:06:58 main.go:50: found device: 79:C7:BA:28:33:33, -58, 
2024/03/12 14:06:58 main.go:50: found device: E1:A1:83:A9:09:2A, -92,
2024/03/12 14:06:58 main.go:50: found device: E1:A1:83:A9:09:2A, -92, S36 B64E LE
2024/03/12 14:06:59 main.go:50: found device: D0:03:DF:AB:78:6F, -70, 
2024/03/12 14:06:59 main.go:50: found device: 49:92:32:66:14:17, -72,
2024/03/12 14:06:59 main.go:50: found device: 49:92:32:66:14:17, -72,
2024/03/12 14:06:59 main.go:50: found device: 79:C7:BA:28:33:33, -66, 
2024/03/12 14:06:59 main.go:50: found device: 5F:55:B0:D7:0E:1B, -70, 
2024/03/12 14:06:59 main.go:50: found device: 4D:4D:4D:40:A4:8E, -64, 
2024/03/12 14:06:59 main.go:50: found device: 40:A8:20:13:16:0A, -54, 
2024/03/12 14:06:59 main.go:50: found device: 40:A8:20:13:16:0A, -54,
2024/03/12 14:06:59 main.go:50: found device: 46:73:E9:39:29:76, -92,
2024/03/12 14:06:59 main.go:50: found device: 7C:0A:3F:51:1B:09, -84, 
2024/03/12 14:07:00 main.go:50: found device: 49:92:32:66:14:17, -64, 
2024/03/12 14:07:00 main.go:50: found device: 46:46:46:EC:DC:B7, -88, 
2024/03/12 14:07:00 main.go:50: found device: 7A:64:4B:F5:60:35, -90, 
2024/03/12 14:07:00 main.go:50: found device: 7A:64:4B:F5:60:35, -90,
2024/03/12 14:07:00 main.go:50: found device: 4A:76:EA:55:DF:68, -90, 
2024/03/12 14:07:00 main.go:50: found device: 4E:A9:45:DC:4E:3A, -90,
2024/03/12 14:07:00 main.go:50: found device: 79:C7:BA:28:33:33, -56, 
2024/03/12 14:07:00 main.go:50: found device: 79:C7:BA:28:33:33, -56,
2024/03/12 14:07:00 main.go:50: found device: 5F:55:B0:D7:0E:1B, -70, 
2024/03/12 14:07:00 main.go:104: received: 3F "?"
2024/03/12 14:07:00 main.go:104: received: 0D0A3E4E4F20444154410D0A3E "\r\n>NO DATA\r\n>" <-- this should not happend. the device.Disconnect was called 20 seconds ago
2024/03/12 14:07:00 main.go:50: found device: 40:A8:20:13:16:0A, -64, 
2024/03/12 14:07:00 main.go:50: found device: D0:03:DF:AB:78:6F, -86, 
2024/03/12 14:07:01 main.go:50: found device: 4D:4D:4D:40:A4:8E, -72, 
2024/03/12 14:07:01 main.go:50: found device: 4D:4D:4D:40:A4:8E, -72,
2024/03/12 14:07:01 main.go:50: found device: 7C:0A:3F:51:1B:09, -84,
2024/03/12 14:07:01 main.go:50: found device: D0:03:DF:AB:78:6F, -76, 
aykevl commented 7 months ago

Unfortunately the only Windows system I have available is a Windows ARM VM, where I can't get Bluetooth to work well (not even with an external Bluetooth USB stick). So I can't debug this myself.

I looked at the code and we do correctly call Close, which says:

Closes this Bluetooth LE device. This may close the connection to the device if this is the only app with a connection.

If the device doesn't actually get disconnected, my guess would be that we hold on a reference somewhere. Can you try removing/commenting the code that scans for services/characteristics/etc? So that the code only connects, and then attempts to disconnect. Does the issue still occur in that case?

roffe commented 7 months ago

it seems that in the second time around in the loop it can connect again to the BT device, but now when i DiscoverCharacteristics on the UART service it says it has 0 characteristics so i can't get a rx or tx characteristic to communicate with

package main

import (
    "log"
    "time"

    "github.com/roffe/gocan/dvi"
    "tinygo.org/x/bluetooth"
)

var (
    uartService = bluetooth.NewUUID([16]byte{0x6e, 0x40, 0x00, 0x01, 0xb5, 0xa3, 0xf3, 0x93, 0xe0, 0xa9, 0xe5, 0x0e, 0x24, 0xdc, 0xca, 0x9e})

    gatUUID = map[string]string{
        "00001800-0000-1000-8000-00805f9b34fb": "Generic Access",
        "00001801-0000-1000-8000-00805f9b34fb": "Generic Attribute",
        "6e400001-b5a3-f393-e0a9-e50e24dcca9e": "UART service",
    }

    characteristicUUID = map[string]string{
        "6e400003-b5a3-f393-e0a9-e50e24dcca9e": "TX",
        "6e400002-b5a3-f393-e0a9-e50e24dcca9e": "RX",
    }
)

var adapter = bluetooth.DefaultAdapter

func init() {
    log.SetFlags(log.LstdFlags | log.Lshortfile)
}

func main() {
    // Enable BLE interface.
    must("enable BLE stack", adapter.Enable())
    for i := 0; i < 3; i++ {
        main2()
        time.Sleep(5 * time.Second)
    }
}

func main2() {
    ch := make(chan bluetooth.ScanResult, 1)

    addr := bluetooth.Address{}
    addr.Set("64:B7:08:5F:F6:9A")

    var err error
    ch <- bluetooth.ScanResult{Address: addr}

    var device bluetooth.Device
    select {
    case d := <-ch:
        device, err = adapter.Connect(d.Address, bluetooth.ConnectionParams{})
        if err != nil {
            log.Fatal("failed to connect:", err)
        }
        log.Printf("connected to %s", d.Address.String())
    case <-time.After(10 * time.Second):
        log.Fatal("Did not find any OBDX Pro GT device")
    }

    svcs, err := device.DiscoverServices(nil)
    if err != nil {
        log.Fatal("failed to discover services:", err)
    }

    log.Println("services found:", len(svcs))
    for _, svc := range svcs {
        if name, ok := gatUUID[svc.UUID().String()]; ok {
            log.Printf("service: %s (%s)", svc.String(), name)
        } else {
            log.Printf("service: %s", svc.String())
        }
    }

    srvc := svcs[2]

    chars, err := srvc.DiscoverCharacteristics(nil)
    if err != nil {
        log.Fatal("failed to discover characteristics:", err)
    }

    for _, char := range chars {
        if name, ok := characteristicUUID[char.UUID().String()]; ok {
            log.Printf("characteristic: %s (%s)", char.String(), name)
        } else {
            log.Printf("characteristic: %s", char.String())
        }
    }

    if len(chars) < 2 {
        log.Fatal("expected at least 2 characteristics got ", len(chars))
    }

    rx := chars[0]
    tx := chars[1]

    err = rx.EnableNotifications(func(buf []byte) {
        log.Printf("received: %X %q", buf, buf)
    })

    if err != nil {
        log.Fatal("failed to enable notifications:", err)
    }

    for i := 0; i < 3; i++ {
        tx.Write([]byte("\r\n"))
        time.Sleep(50 * time.Millisecond)
    }

    payload := dvi.New(0x22, []byte{0x03}).Bytes()
    log.Printf("sending: %X", payload)

    for i := 0; i < 3; i++ {
        tx.Write(payload)
        time.Sleep(2 * time.Second)
    }

    if err := device.Disconnect(); err != nil {
        log.Fatal("failed to disconnect:", err)
    }
}

func must(action string, err error) {
    if err != nil {
        log.Fatal("failed to " + action + ": " + err.Error())
    }
}
PS: 35 2024-03-12 18:10:53 bttest >go run .
2024/03/12 18:10:54 main.go:57: connected to 64:B7:08:5F:F6:9A
2024/03/12 18:10:56 main.go:67: services found: 3
2024/03/12 18:10:56 main.go:70: service: 00001801-0000-1000-8000-00805f9b34fb (Generic Attribute)
2024/03/12 18:10:56 main.go:70: service: 00001800-0000-1000-8000-00805f9b34fb (Generic Access)
2024/03/12 18:10:56 main.go:70: service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e (UART service)
2024/03/12 18:10:56 main.go:85: characteristic: 6e400003-b5a3-f393-e0a9-e50e24dcca9e (TX)
2024/03/12 18:10:56 main.go:85: characteristic: 6e400002-b5a3-f393-e0a9-e50e24dcca9e (RX)
2024/03/12 18:10:56 main.go:99: received: 0D "\r"
2024/03/12 18:10:56 main.go:99: received: 0A "\n"
2024/03/12 18:10:57 main.go:99: received: 0D "\r"
2024/03/12 18:10:57 main.go:99: received: 0A "\n"
2024/03/12 18:10:57 main.go:99: received: 0D "\r"
2024/03/12 18:10:57 main.go:99: received: 0A "\n"
2024/03/12 18:10:57 main.go:112: sending: 220103D9
2024/03/12 18:10:57 main.go:99: received: 22 "\""
2024/03/12 18:10:57 main.go:99: received: 0103D9 "\x01\x03\xd9"
2024/03/12 18:10:59 main.go:99: received: 22 "\""
2024/03/12 18:10:59 main.go:99: received: 0103D9 "\x01\x03\xd9"
2024/03/12 18:11:01 main.go:99: received: 22 "\""
2024/03/12 18:11:01 main.go:99: received: 0103D9 "\x01\x03\xd9"
2024/03/12 18:11:08 main.go:57: connected to 64:B7:08:5F:F6:9A
2024/03/12 18:11:08 main.go:67: services found: 3
2024/03/12 18:11:08 main.go:70: service: 00001801-0000-1000-8000-00805f9b34fb (Generic Attribute)
2024/03/12 18:11:08 main.go:70: service: 00001800-0000-1000-8000-00805f9b34fb (Generic Access)
2024/03/12 18:11:08 main.go:70: service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e (UART service)
2024/03/12 18:11:08 main.go:92: expected at least 2 characteristics got0
exit status 1
hgqcode commented 7 months ago

It doesn't work when i use disconnect device

What is the error? What system are you on? How can I reproduce this issue? It's impossible to help or fix the bug without more information.

I'm the same with Roffe.

hgqcode commented 7 months ago

Unfortunately the only Windows system I have available is a Windows ARM VM, where I can't get Bluetooth to work well (not even with an external Bluetooth USB stick). So I can't debug this myself.

I looked at the code and we do correctly call Close, which says:

Closes this Bluetooth LE device. This may close the connection to the device if this is the only app with a connection.

If the device doesn't actually get disconnected, my guess would be that we hold on a reference somewhere. Can you try removing/commenting the code that scans for services/characteristics/etc? So that the code only connects, and then attempts to disconnect. Does the issue still occur in that case?

I tried. Disconnect successfully when i removing the code that scans for characteristics. So I think you can check out this code. chars, err := srvc.DiscoverCharacteristics(nil)

roffe commented 7 months ago

I'm wildly speculating now as i don't know anything about bluetooth development but it feels like the characteristics we obtain after the scan is what's not being released, maybe there should be a .Close method on them to free them once you are done with a characteristic?

roffe commented 7 months ago

oh well, it's one of those projects, you close issues that is not solved :D.

Good thing i discovered this is not usable in my project before getting to invovled

hgqcode commented 7 months ago

oh well, it's one of those projects, you close issues that is not solved :D.

Good thing i discovered this is not usable in my project before getting to invovled

oh sorry, It was an operational error.I don't want to close issues.

aykevl commented 7 months ago

maybe there should be a .Close method on them to free them once you are done with a characteristic?

If that's the case, this is something the bluetooth library should do internally on disconnect. Not something the API user should have to worry about.

(Again, I'd like to help but don't have the right system available).

hgqcode commented 7 months ago

maybe there should be a .Close method on them to free them once you are done with a characteristic?

If that's the case, this is something the bluetooth library should do internally on disconnect. Not something the API user should have to worry about.

(Again, I'd like to help but don't have the right system available).

I think that could be one way. So what can i do if I want the function to work properly now.Can you give me some advice? Very anxious.Thank you.

ansoni-san commented 7 months ago

Maybe just patch the code. It's fairly simple code underneath. You probably just need to free / uninitialise something properly.

Disconnection is working great for me on Windows, however it does not always truly disconnect the device immediately if it is being used for something.

hgqcode commented 7 months ago

Maybe just patch the code. It's fairly simple code underneath. You probably just need to free / uninitialise something properly.

Disconnection is working great for me on Windows, however it does not always truly disconnect the device immediately if it is being used for something.

Thany you! Can you show me what to change?As far as I know,the code has not free / uninitialise method.

hellosqi commented 3 months ago

I also encountered the same problem

ansoni-san commented 3 months ago

@hgqcode @hellosqi You could narrow it down by disabling parts of DiscoverCharacteristics until it stops happening, and then dig into that.

I don't have time to help at the moment (and also we internally forked this project and haven't updated in a while), but if no-one has looked at it in the next week or so I can try to see if there's some way we can convince Windows to let go of the device sooner.

It may be possible or it may not, as with the WinRT Bluetooth API you don't own the device, you just own a session. It provides no guarantees that it will disconnect the physical device behind your session. But there probably is something that is making it hang around longer than it should.

I have a feeling that we see a variety of side-effects from this, but we have worked around it in our higher level code.