Passive beacons.



  • Currently the SyPy only seems to pick up standard BT broadcasts and not broadcasts from beacons. Is there any facility, without modifying the firmware of the beacons, to pick up the 128-bit UUID of such.



  • @papasmurph Exactly. I've been trying to use a complex exponential relationship with some empirically based factors but because I have different types of device, the relative numbers between device types are awful. The simplest approach is the best so far!



  • @alidaf You can subtract TX from RSSI to get a rough distance estimation a la iBeacon's "far", "near", "immediate", but I've learned to not trust TX for real distance measurement, as most beacon manufacturers don't understand they need to relate it to the actual power. My Beacon Writer app for Wellcore's beacons makes that adjustment though.



  • @papasmurph That was my initial thought unless I could find some mutex equivalent. I'm happy with what I have working now. The key was the delay but you also allowed me to find some more useful information from the broadcast, like the tx power. I'm seeing if I can determine the distances of the beacons from the receiver. Happy days!



  • @alidaf You could maybe set a global to true when entering the callback and false when leaving, and then test on that before you affect the table outside of the callback.



  • @papasmurph That sounds awesome but way beyond my current level. Python is proving a lot more difficult than I thought it would, but anyway, many, many thanks for your help.



  • @alidaf That's some of the things my CliqTags Spotter application does (and much more: geofencing, server communication, content presentation etc), developed in Javascript for Cordova (iOS and Android), so I understand this issue. Javascript is asynchronous and (luckily) single-threaded, so I can treat each function and whatever it does as atomic, and don't get race conditions.

    Maybe this is included: https://docs.python.org/3/library/asyncio-sync.html

    I also dabble with Python :).



  • @papasmurph Yes, that makes sense.

    The reason for a mutex is that I am creating a managed list of devices that are given a time to live. Once the time to live is up, the device is removed from the list. I don't want to remove from the list if the callback is currently adding to the list, or visa-versa.

    I can live without the callback, but since it is something I have now learned, I would like to use it properly and avoid race conditions, if they are even possible in Python. I'm coming to Python from C!



  • @alidaf My advice is to not use any sleeps at all, except very short, as the advertisement buffer is very small. 1 second is way too much. Not sure about any mutex and why you'd need it.

    If I want to analyze data I copy-paste text from Termite.



  • @papasmurph I had a 1 second sleep to make the output readable. That was enough to kill capturing the beacons.

    Is there an equivalent of a mutex to know when the callback is in operation?



  • @alidaf No doubt my code works fine for me. I'll test with the latest Pycom LoPy firmware. BLE should work the same on WiPy, LoPy etc.

    I upgraded to latest LoPy firmware. Still works.



  • @papasmurph For some reason, I can only detect the beacons if using the callback function.



  • @papasmurph Yes, I made a list of everything I can pick up to determine whether it's a beacon, feather, mobile or anything else. For some reason in my own code it just wouldn't pick up beacons. I've modified yours to include specific filters and ignore anything that is not a beacon or feather and it works fine. I just need to incorporate what works back into mine!



  • @alidaf Actually, I was mistaken (bad memory). The intention of the filtering is only to check that it's an iBeacon, discriminating other types of advertisements. It's not more specific than that. Do you in practice see other values for iBeacons?



  • @papasmurph Brilliant, thank you.



  • @alidaf You are right of course. I used that filter to detect my own UUID.

    Check this out for what the data is used for: https://forum.pycom.io/topic/1911/wipy-as-an-ibeacon/3

    For more details, see https://support.kontakt.io/hc/en-gb/articles/201492492-iBeacon-advertising-packet-structure.



  • @papasmurph

    That works a treat, but only if I remove the line...

    if not data[5] == 0x4c and data[6] == 0x00 and data[7] == 0x02 and data[8] == 0x15:

    Could you please point me in the direction of where the breakdown of the 'data' field is, i.e. you have used data[5], [6], [7], [8], [25], [27] and [29].

    I also don't understand the line...

    data = [b for b in device.data]

    so would be grateful for any explanation here.

    Many thanks for the demo.



  • Sorry for the delay in replying. I had a week off.
    Many thanks for that. I'll give it a go.



  • @alidaf I figure the SyPy should be the same as the LoPy in this regards, so something like this:

    from network import Bluetooth
    import pycom
    import time
    import gc
    
    pycom.heartbeat(False)
    timeSleep = 0.01
    timeScan = 10
    
    bluetooth = Bluetooth()
    
    def new_adv_event(event):
        if event.events() == Bluetooth.NEW_ADV_EVENT:
            anydata = True
            while anydata:
                device = bluetooth.get_adv()
                if device != None:
                    rssi = device.rssi
                    data = [b for b in device.data]
                    if data[5] == 0x4c and data[6] == 0x00 and data[7] == 0x02 and data[8] == 0x15:
                        majorid = data[25] * 256 + data[26]
                        minorid = data[27] * 256 + data[28]
                        power = data[29]
                        if power > 127:
                            power -= 256
    
                        pycom.rgbled(0x000800)
                        print("iBeacon: " + str(rssi) + '/' + str(power) + ' (' + str(rssi - power) + ') ' + str(majorid) + '/' + str(minorid))            
                    else:
                        pycom.rgbled(0x000008)
    
                    time.sleep(timeSleep)
                    pycom.rgbled(0x000000)
                else:
                    anydata = False
                
    bluetooth.callback(trigger = Bluetooth.NEW_ADV_EVENT, handler = new_adv_event)
    
    while True:
        if not bluetooth.isscanning():
            print("Restarting scan")
            bluetooth.deinit()
            bluetooth.init()
            bluetooth.start_scan(timeScan)
    
        time.sleep(1)
    

 

Pycom on Twitter