pycom / pycom-micropython-sigfox

A fork of MicroPython with the ESP32 port customized to run on Pycom's IoT multi-network modules.
MIT License
198 stars 167 forks source link

Enumerating BLE characteristics breaks scanning for advertisments #148

Closed keton closed 6 years ago

keton commented 6 years ago

Hardware

Board: WiPy 2.0 Firmware: 1.17.0.b1

Test program

from network import Bluetooth
import binascii
import time

bt = Bluetooth()
bt.start_scan(-1)

while True:
    if(bt.isscanning()):
        adv=bt.get_adv()
        if not adv:
            continue
        try:
            conn = bt.connect(binascii.unhexlify('f90b69424a20')) # change to your device MAC
            print("connect")

            time.sleep(2)
            services = conn.services()
            time.sleep(2)

            for service in services:
                time.sleep(0.050)
                if type(service.uuid()) == bytes:
                    print("Srv: "+str(binascii.hexlify(service.uuid())))
                else:
                    print('Srv: = %x' % service.uuid())

                #uncomment 2 lines below to trigger bug
                #chars = service.characteristics()
                #print(chars)

            while conn.isconnected():
                pass
            print("disconnect")
        except:
            pass
        bt.start_scan(-1)

What's supposed to happen

Test program should be able to connect to BLE device, enumerate services and characteristics. When connection is dropped (i.e. peripheral device power is cut for a few seconds or device goes out of radio range) BLE scan should be restarted. This is the case as long as lines

chars = service.characteristics()
print(chars)

are commented out

What happens

When characteristic enumeration is enabled bt.start_scan(-1) at the end of while True loop throws following exception:

Traceback (most recent call last):
  File "main.py", line 43, in <module>
OSError: operation already in progress
MicroPython v1.8.6-849-d0dc708 on 2018-02-27; WiPy with ESP32
Type "help()" for more information.

I've tried to do bt.deinit() and bt.init() to reset BLE and to retry after up to 60s delay but when this exception happens only reset helps.

Please confirm whether this is a bug in micropython or maybe I'm doing something wrong.

semireg commented 6 years ago

Hi @keton. Although I'm not a PyCom employee I tested your scenario and I am unable to replicate the issue. However, the code you posted has some areas that can benefit from some minor changes to make it more stable. Note: I am testing on a WiPy 3.0, so it's possible the 2.0 behaves differently.

The Bluetooth low energy protocol is designed in such a way that the PyCom (acting as a "central") can only make a connection to a device (peripheral) when the central receives a peripheral advertisement packet. Therefore, the response from bt.get_adv() is most useful if it is immediately used to initiate a connection.

Therefore, try this:

bt.stop_scan()
bt.start_scan(-1)
my_mac = b'123456abcdef'

while True:
    if(bt.isscanning()):
        adv=bt.get_adv()
        if not adv:
            continue

        name = bt.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL)

        if adv.mac != binascii.unhexlify(my_mac):
            print('skipping {} {}'.format(binascii.hexlify(adv.mac), name))
            continue

        try:
            print("connecting to {} {}".format(my_mac, name))
            conn = bt.connect(adv.mac)
            print("connected!")

            # time.sleep(2)
            services = conn.services()
            # time.sleep(2)

            for service in services:
                # time.sleep(0.050)
                if type(service.uuid()) == bytes:
                    print("Srv: "+str(binascii.hexlify(service.uuid())))
                else:
                    print('Srv: = %x' % service.uuid())

                #uncomment 2 lines below to trigger bug
                chars = service.characteristics()
                print(chars)

            while conn.isconnected():
                pass

            print("disconnected")

        except:
            print("connection error")

        bt.start_scan(-1)

Also, the sleeps are likely not necessary.

husigeza commented 6 years ago

Hello, Thanks for reporting, the fix will be part of the next development version!