BLE Beacon Scanner#
BLE beacons send small packets of information such as a URL. The sample code listens for Eddystone TLM messages and reports the temperature send by the device.
For testing I am using a BC011 from Bluecharm Beacons.
%connect ble
%rsync
Connected to ble @ serial:///dev/ttyUSB0
Directories match
from struct import unpack
import bluetooth
import binascii
import time
class EddystoneTLM:
def __init__(self, ble, addresses=[]):
self._scanning = False
self._addresses = [ binascii.unhexlify(x) for x in addresses ]
self._ble = ble
self._ble.active(True)
self._ble.irq(self._irq)
def _irq(self, event, data):
if event == 5:
# scan result
addr_type, addr, adv_type, rssi, adv_data = data
if addr in self._addresses:
self._scan_callback(self._decode(adv_data, rssi))
# stop scanning
self._ble.gap_scan(None)
self._scanning = False
elif event == 6:
# scan done
self._scanning = False
def _decode(self, adv_data, rssi):
adv_data = bytes(adv_data) # memoryview -> bytes
eddytlm = b'\x02\x01\x06\x03\x03\xaa\xfe\x11\x16\xaa\xfe \x00'
ibeacon = b'\x02\x01\x06\x1a\xffL\x00\x02\x15'
if eddytlm in adv_data:
return {
'packet' : 'tlm',
'bat' : unpack('>H', adv_data[13:15])[0], # battery voltage [mV]
'temp' : unpack('>h', adv_data[15:17])[0] // 256, # temperature [C]
'adv' : unpack('>I', adv_data[17:21])[0], # number of packets
'sec' : unpack('>I', adv_data[21:25])[0], # time since turn on, [0.1s]
'rssi' : rssi
}
if ibeacon in adv_data:
return {
'packet' : 'iBeacon',
'name' : adv_data[9:25].decode(),
'major' : unpack('>H', adv_data[25:27])[0],
'minor' : unpack('>H', adv_data[27:29])[0],
'rssi' : adv_data[29]
}
print("unrecognized packet", binascii.hexlify(adv_data))
return None
def scan(self, callback=None):
self._scanning = True
self._scan_callback = callback
self._ble.gap_scan(20000, 30000, 30000)
@property
def scanning(self):
return self._scanning
def demo():
ble = bluetooth.BLE()
central = EddystoneTLM(ble, ['dd34020676d4'])
n = 0
def on_scan(data):
nonlocal n
print("[{:4d}] {}".format(n, data))
n += 1
# Wait for connection...
while True:
if not central.scanning:
central.scan(callback=on_scan)
time.sleep_ms(100)
if __name__ == "__main__":
demo()
[ 0] {'temp': 21, 'adv': 158327, 'sec': 10222614, 'rssi': -71, 'bat': 2968, 'packet': 'tlm'}
[ 1] {'temp': 21, 'adv': 158329, 'sec': 10222814, 'rssi': -67, 'bat': 2968, 'packet': 'tlm'}
[ 2] {'temp': 20, 'adv': 158330, 'sec': 10222914, 'rssi': -77, 'bat': 2968, 'packet': 'tlm'}
[ 3] {'temp': 20, 'adv': 158331, 'sec': 10223014, 'rssi': -67, 'bat': 2969, 'packet': 'tlm'}
[ 4] {'temp': 21, 'adv': 158332, 'sec': 10223114, 'rssi': -69, 'bat': 2969, 'packet': 'tlm'}
[ 5] {'temp': 20, 'adv': 158333, 'sec': 10223214, 'rssi': -66, 'bat': 2970, 'packet': 'tlm'}
[ 6] {'temp': 20, 'adv': 158334, 'sec': 10223314, 'rssi': -66, 'bat': 2970, 'packet': 'tlm'}
[ 7] {'temp': 20, 'adv': 158335, 'sec': 10223414, 'rssi': -76, 'bat': 2977, 'packet': 'tlm'}
[ 8] {'temp': 21, 'adv': 158336, 'sec': 10223514, 'rssi': -71, 'bat': 2974, 'packet': 'tlm'}
[ 9] {'temp': 20, 'adv': 158337, 'sec': 10223614, 'rssi': -75, 'bat': 2974, 'packet': 'tlm'}
[ 10] {'temp': 21, 'adv': 158338, 'sec': 10223714, 'rssi': -76, 'bat': 2970, 'packet': 'tlm'}
[ 11] {'temp': 20, 'adv': 158339, 'sec': 10223814, 'rssi': -75, 'bat': 2970, 'packet': 'tlm'}
[ 12] {'temp': 20, 'adv': 158340, 'sec': 10223914, 'rssi': -78, 'bat': 2971, 'packet': 'tlm'}
[ 13] {'temp': 20, 'adv': 158341, 'sec': 10224015, 'rssi': -70, 'bat': 2971, 'packet': 'tlm'}
[ 14] {'temp': 20, 'adv': 158343, 'sec': 10224215, 'rssi': -71, 'bat': 2971, 'packet': 'tlm'}
[ 15] {'temp': 21, 'adv': 158345, 'sec': 10224415, 'rssi': -69, 'bat': 2968, 'packet': 'tlm'}
[ 16] {'temp': 20, 'adv': 158346, 'sec': 10224515, 'rssi': -72, 'bat': 2968, 'packet': 'tlm'}
[ 17] {'temp': 21, 'adv': 158347, 'sec': 10224615, 'rssi': -78, 'bat': 2968, 'packet': 'tlm'}
[ 18] {'temp': 20, 'adv': 158348, 'sec': 10224715, 'rssi': -78, 'bat': 2968, 'packet': 'tlm'}
[ 19] {'temp': 20, 'adv': 158349, 'sec': 10224815, 'rssi': -79, 'bat': 2969, 'packet': 'tlm'}
[ 20] {'temp': 20, 'adv': 158350, 'sec': 10224915, 'rssi': -67, 'bat': 2969, 'packet': 'tlm'}
[ 21] {'temp': 20, 'adv': 158352, 'sec': 10225115, 'rssi': -69, 'bat': 2969, 'packet': 'tlm'}
[ 22] {'temp': 21, 'adv': 158353, 'sec': 10225215, 'rssi': -69, 'bat': 2972, 'packet': 'tlm'}
[ 23] {'temp': 21, 'adv': 158354, 'sec': 10225315, 'rssi': -82, 'bat': 2976, 'packet': 'tlm'}
[ 24] {'temp': 21, 'adv': 158356, 'sec': 10225515, 'rssi': -67, 'bat': 2972, 'packet': 'tlm'}
[ 25] {'temp': 20, 'adv': 158358, 'sec': 10225715, 'rssi': -70, 'bat': 2971, 'packet': 'tlm'}
Interrupted