Skip to content

Instantly share code, notes, and snippets.

@bridger
Created February 9, 2017 01:53
Show Gist options
  • Save bridger/83fc2e9c60fd07d925b0bec80c47acab to your computer and use it in GitHub Desktop.
Save bridger/83fc2e9c60fd07d925b0bec80c47acab to your computer and use it in GitHub Desktop.
bluez scanner for broadcast listening, only once per second
# BLE scanner based on https://github.com/adamf/BLE/blob/master/ble-scanner.py
# JCS 06/07/14
# https://github.com/pauloborges/bluez/blob/master/tools/hcitool.c for lescan
# https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.6/lib/hci.h for opcodes
# https://github.com/pauloborges/bluez/blob/master/lib/hci.c#L2782 for
# functions used by lescan
import binascii
import sys
import struct
import bluetooth._bluetooth as bluez
LE_META_EVENT = 0x3e
LE_PUBLIC_ADDRESS = 0x00
LE_RANDOM_ADDRESS = 0x01
LE_SET_SCAN_PARAMETERS_CP_SIZE = 7
OGF_LE_CTL = 0x08
OCF_LE_SET_SCAN_PARAMETERS = 0x000B
OCF_LE_SET_SCAN_ENABLE = 0x000C
OCF_LE_CREATE_CONN = 0x000D
LE_ROLE_MASTER = 0x00
LE_ROLE_SLAVE = 0x01
# these are actually subevents of LE_META_EVENT
EVT_LE_CONN_COMPLETE = 0x01
EVT_LE_ADVERTISING_REPORT = 0x02
EVT_LE_CONN_UPDATE_COMPLETE = 0x03
EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE = 0x04
# Advertisment event types
ADV_IND = 0x00
ADV_DIRECT_IND = 0x01
ADV_SCAN_IND = 0x02
ADV_NONCONN_IND = 0x03
ADV_SCAN_RSP = 0x04
def returnnumberpacket(pkt):
myInteger = 0
multiple = 256
for c in pkt:
myInteger += struct.unpack("B", c)[0] * multiple
multiple = 1
return myInteger
def returnstringpacket(pkt):
myString = ""
for c in pkt:
myString += "%02x" % struct.unpack("B", c)[0]
return myString
def printpacket(pkt):
for c in pkt:
sys.stdout.write("%02x " % struct.unpack("B", c)[0])
def get_packed_bdaddr(bdaddr_string):
packable_addr = []
addr = bdaddr_string.split(':')
addr.reverse()
for b in addr:
packable_addr.append(int(b, 16))
return struct.pack("<BBBBBB", *packable_addr)
def packed_bdaddr_to_string(bdaddr_packed):
return ':'.join('%02x' % i for i in struct.unpack("<BBBBBB", bdaddr_packed[::-1]))
def hci_enable_le_scan(sock):
hci_toggle_le_scan(sock, 0x01)
def hci_disable_le_scan(sock):
hci_toggle_le_scan(sock, 0x00)
def hci_toggle_le_scan(sock, enable):
cmd_pkt = struct.pack("<BB", enable, 0x00)
bluez.hci_send_cmd(sock, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, cmd_pkt)
def start_scan(sock):
SCAN_TYPE = 0x01
INTERVAL = 0x1
WINDOW = 0x1
OWN_TYPE = 0x01
FILTER = 0x00 # all advertisements, not just whitelisted devices
# interval and window are uint_16, so we pad them with 0x0
cmd_pkt = struct.pack("<BBBBBBB", SCAN_TYPE, 0x0, INTERVAL, 0x0, WINDOW, OWN_TYPE, FILTER)
bluez.hci_send_cmd(sock, OGF_LE_CTL, OCF_LE_SET_SCAN_PARAMETERS, cmd_pkt)
hci_enable_le_scan(sock)
flt = bluez.hci_filter_new()
bluez.hci_filter_all_events(flt)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
sock.setsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, flt)
def parse_event(sock):
while True:
pkt = sock.recv(255)
ptype, event, plen = struct.unpack("BBB", pkt[:3])
if event == LE_META_EVENT:
subevent, = struct.unpack("B", pkt[3])
pkt = pkt[4:]
if subevent == EVT_LE_ADVERTISING_REPORT:
# print "advertising report"
num_reports = struct.unpack("B", pkt[0])[0]
for report in range(0, num_reports):
address = packed_bdaddr_to_string(
pkt[3:9])
if address != '04:a3:16:9a:f3:22':
continue
rssi = struct.unpack("b", pkt[-1])
rest_bin = pkt[9:-2]
rest = binascii.b2a_hex(rest_bin).decode('utf-8')
return [address, rssi, rest]
def main():
dev_id = 0
try:
sock = bluez.hci_open_dev(dev_id)
print "ble thread started"
except:
print "error accessing bluetooth device..."
sys.exit(1)
blescan.start_scan(sock)
while True:
beacon = blescan.parse_event(sock)
print beacon
print "----------"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment