Skip to content

Commit

Permalink
SBCscanner solution restructured
Browse files Browse the repository at this point in the history
  • Loading branch information
aslanfaez committed Oct 30, 2018
1 parent 616b26f commit 558d2e5
Show file tree
Hide file tree
Showing 17 changed files with 484 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ If the scanBuffer is to much higher than postIntervals you will end up with some

After configuring your device and editing config.ini you can run it by running abcScanner.py as **root**
```
$ sudo python3 abcScanner.py
$ sudo python3 sbcScanner.py
```
For Detailed instructions on how to config your raspberry pi and run this project read [readmeDetail.md](https://github.com/ubeac/SBCGateway/blob/master/readmeDetail.md)

20 changes: 17 additions & 3 deletions BluezHelper.py → bleScanner/BleHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import bluetooth._bluetooth as bluez
import datetime

class BluezHelper:
class BleHelper:
def __init__(self,dev_id = 0):

self.LE_META_EVENT = 0x3e
Expand Down Expand Up @@ -44,7 +44,6 @@ def __init__(self,dev_id = 0):
self.hci_enable_le_scan(self.sock)



def returnnumberpacket(self, pkt):
myInteger = 0
multiple = 256
Expand Down Expand Up @@ -147,7 +146,7 @@ def parse_events(self, sock, loop_count=100):
num_reports = pkt[0]
report_pkt_offset = 0
for i in range(0, num_reports):
myFullList.append([pkt.hex(),str(datetime.datetime.utcnow())])
myFullList.append({"data": pkt.hex(), "ts": str(datetime.datetime.utcnow())})

done = True
sock.setsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, old_filter)
Expand All @@ -157,3 +156,18 @@ def parse_events(self, sock, loop_count=100):

def scan(self,numberOfScannes=1):
return self.parse_events(self.sock, numberOfScannes)

#testing the helper class and write results to ble.txt
if __name__ == '__main__':
bleScanner = BleHelper()
f = open("ble.txt","w")
f.write('Data;time stamp UTC\r\n')
f.close()

while True:
res = bleScanner.scan(1)
f = open("ble.txt", "a+")
for dev in res:
f.write(str(dev[0]) +";"+ str(dev[1])+"\r\n")
f.close()

46 changes: 46 additions & 0 deletions bleScanner/HTTPPostHelper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/python3
import json
import requests
import datetime
from uuid import getnode as get_mac

class HTTPPostHelper:
def __init__(self, Id, Name,PostAddress):
self.Id = Id
self.Name = Name
# getting mac address on fly so there would be less configurations
self.mac = "".join(c + ":" if i % 2 else c for i, c in enumerate(hex(get_mac())[2:].zfill(12)))[:-1]
self.PostAddress = PostAddress
self.sended = False
self.beaconData_ = []

def postData(self):
#generating needed json structure


data = {
'id': self.Id,
'name': self.Name,
'mac' : self.mac,
'ts' : str(datetime.datetime.utcnow()),
'data' : self.beaconData_
}
#print(json.dumps(data))
response = requests.post(self.PostAddress, json=json.dumps(data))
if response.status_code == 200 :
self.sended = True
else:
self.sended = False


def startPosting(self,BeaconData):
# check if the data is posted before flushing it
# if data is not postet it will append new data to the list
if self.sended:
# print('Posted')
self.beaconData_.clear()
self.beaconData_.extend(BeaconData)
else:
self.beaconData_.extend(BeaconData)

self.postData()
31 changes: 31 additions & 0 deletions bleScanner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
![Logo of the project](http://ui.ubeac.io/static/img/logo.svg)

# SBCGateway
This is a gateway for ubeac project.
It will Scan for **[ble advertisements](https://en.wikipedia.org/wiki/Bluetooth_advertising)** and send them to **[ubeac](http://ui.ubeac.io)** servers.

## Getting started
For running this project you will need a fresh installation of [raspbian lite](https://www.raspberrypi.org/downloads/raspbian/) on Bluetooth enable [RaspberryPi](https://www.raspberrypi.org/products/).
You need to have [python 3](https://www.python.org/), [pybluez](https://github.com/pybluez/pybluez) and [requests](http://docs.python-requests.org/en/master/).

You can get the project from our [Git Repository](https://github.com/ubeac/SBCGateway)

### Config.ini
postAddress is the link to the listener provided from [ubeac](http://ui.ubeac.io) or for debug you can use [HookServer](http://hook.ubeac.io).

Set the id and name as you want.

postInterval is the time laps between posts to the ubeac server.It's in seconds and you can set it to less than a second by providing a decimal.

scanBuffer is the amount of scans beacon scanner do before sending data to post thread minimum number is 1 and you can set it to 100 if you are posting data to server slowly.

If the scanBuffer is to much higher than postIntervals you will end up with some empty posts.

### Running The project

After configuring your device and editing config.ini you can run it by running abcScanner.py as **root**
```
$ sudo python3 sbcScanner.py
```
For Detailed instructions on how to config your raspberry pi and run this project read [readmeDetail.md](https://github.com/ubeac/SBCGateway/blob/master/readmeDetail.md)

4 changes: 2 additions & 2 deletions sbcScanner.py → bleScanner/bleScanner.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/python3
import BluezHelper
import BleHelper
import HTTPPostHelper
import datetime
import time
Expand All @@ -19,7 +19,7 @@
# shared list used to exchange Beacon scans with post thread
returnedList =[]

ScanThreat = BluezHelper.BluezHelper(0)
ScanThreat = BleHelper.BleHelper(0)
HttpPostThreat = HTTPPostHelper.HTTPPostHelper(id, name,postAddress)

def scanProcess():
Expand Down
4 changes: 2 additions & 2 deletions config.ini → bleScanner/config.ini
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[serverConfigurations]
postAddress = http://hook.ubeac.io/fcnE79WQ
postAddress = http://hook.ubeac.io/oZ9oYVYG

[GatewayConfiguration]
id = aslan
name = Rpi1
name = BleScanner1
PostInterval = 1

[scannerConfiguration]
Expand Down
File renamed without changes.
File renamed without changes.
184 changes: 184 additions & 0 deletions btScanner/InqueryHelper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/python3
# performs a simple device inquiry, followed by a remote name request of each
# discovered device

import os
import sys
import struct
import bluetooth._bluetooth as bluez
import bluetooth
import datetime

class InqueryHelper:
def __init__(self,dev_id=0):
self.dev_id = dev_id
self.sock = ''
try:
self.sock = bluez.hci_open_dev(self.dev_id)
except:
print("error accessing bluetooth device...")
sys.exit(1)

try:
mode = self.read_inquiry_mode(self.sock)
except Exception as e:
print("error reading inquiry mode. ")
print("Are you sure this a bluetooth 1.2 device?")
print(e)
sys.exit(1)
print("current inquiry mode is %d" % mode)

if mode != 1:
print("writing inquiry mode...")
try:
result = self.write_inquiry_mode(self.sock, 1)
except Exception as e:
print("error writing inquiry mode. Are you sure you're root?")
print(e)
sys.exit(1)
if result != 0:
print("error while setting inquiry mode")
print("result: %d" % result)


def printpacket(self,pkt):
for c in pkt:
sys.stdout.write("%02x " % struct.unpack("B",c)[0])
print()


def read_inquiry_mode(self,sock):
"""returns the current mode, or -1 on failure"""
# save current filter
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

# Setup socket filter to receive only events related to the
# read_inquiry_mode command
flt = bluez.hci_filter_new()
opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL,
bluez.OCF_READ_INQUIRY_MODE)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE);
bluez.hci_filter_set_opcode(flt, opcode)
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )

# first read the current inquiry mode.
bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL,
bluez.OCF_READ_INQUIRY_MODE )

pkt = sock.recv(255)

status,mode = struct.unpack("xxxxxxBB", pkt)
if status != 0: mode = -1

# restore old filter
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
return mode

def write_inquiry_mode(self,sock, mode):
"""returns 0 on success, -1 on failure"""
# save current filter
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

# Setup socket filter to receive only events related to the
# write_inquiry_mode command
flt = bluez.hci_filter_new()
opcode = bluez.cmd_opcode_pack(bluez.OGF_HOST_CTL,
bluez.OCF_WRITE_INQUIRY_MODE)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
bluez.hci_filter_set_event(flt, bluez.EVT_CMD_COMPLETE)
bluez.hci_filter_set_opcode(flt, opcode)
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt)

# send the command!
bluez.hci_send_cmd(sock, bluez.OGF_HOST_CTL,
bluez.OCF_WRITE_INQUIRY_MODE, struct.pack("B", mode))

pkt = sock.recv(255)

status = struct.unpack("xxxxxxB", pkt)[0]

# restore old filter
sock.setsockopt(bluez.SOL_HCI, bluez.HCI_FILTER, old_filter)
if status != 0: return -1
return 0

def device_inquiry_with_with_rssi(self,sock):
# save current filter
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)

# perform a device inquiry on bluetooth device #0
# The inquiry should last 8 * 1.28 = 10.24 seconds
# before the inquiry is performed, bluez should flush its cache of
# previously discovered devices
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)

duration = 4
max_responses = 255
cmd_pkt = struct.pack("BBBBB", 0x33, 0x8b, 0x9e, duration, max_responses)
bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL, bluez.OCF_INQUIRY, cmd_pkt)

results = []

done = False
while not done:
pkt = sock.recv(255)
ptype, event, plen = struct.unpack("BBB", pkt[:3])
if event == bluez.EVT_INQUIRY_RESULT_WITH_RSSI:
pkt = pkt[3:]
nrsp = bluetooth.get_byte(pkt[0])
for i in range(nrsp):
addr = bluez.ba2str( pkt[1+6*i:1+6*i+6] )
rssi = bluetooth.byte_to_signed_int(
bluetooth.get_byte(pkt[1+13*nrsp+i]))
results.append((addr, rssi, str(datetime.datetime.utcnow())))

elif event == bluez.EVT_INQUIRY_COMPLETE:
done = True
elif event == bluez.EVT_CMD_STATUS:
status, ncmd, opcode = struct.unpack("BBH", pkt[3:7])
if status != 0:
print("uh oh...")
self.printpacket(pkt[3:7])
done = True
elif event == bluez.EVT_INQUIRY_RESULT:
pkt = pkt[3:]
nrsp = bluetooth.get_byte(pkt[0])
for i in range(nrsp):
addr = bluez.ba2str(pkt[1+6*i:1+6*i+6])
results.append((addr, -1, str(datetime.datetime.utcnow())))
else:
print("unrecognized packet type 0x%02x" % ptype)


# restore old filter
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )

return results

def scan(self):
# scan for bluetooth device names
inquryResult = self.device_inquiry_with_with_rssi(self.sock)
results = []
for device in inquryResult:
name = bluetooth.lookup_name(device[0], 10)
results.append({"mac": device[0], "rssi": device[1], "name": name, "ts": device[2]})
return results


# testing the helper class and write results to btinquery.txt
if __name__ == '__main__':
inqurer = InqueryHelper()
f = open("btinquery.txt","w")
f.write('Bluetooth MAC;rssi;Name;time stamp UTC\r\n')
f.close()

while True:
res = inqurer.scan()
f = open("btinquery.txt", "a+")
for dev in res:
f.write(str(dev['mac']) +";"+ str(dev["rssi"])+';'+str(dev["name"])+';'+dev["ts"]+"\r\n")
f.close()
48 changes: 48 additions & 0 deletions btScanner/btScanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/python3
import InqueryHelper
import HTTPPostHelper
import datetime
import time
import _thread
import configparser


config = configparser.ConfigParser()
config.read('config.ini')

#needed configurations read from config file
postAddress = config['serverConfigurations']['postAddress']
id = config['GatewayConfiguration']['id']
name = config['GatewayConfiguration']['name']
interval = int(config['GatewayConfiguration']['PostInterval'])

# shared list used to exchange Beacon scans with post thread
returnedList =[]

ScanThreat = InqueryHelper.InqueryHelper(0)
HttpPostThreat = HTTPPostHelper.HTTPPostHelper(id, name,postAddress)

def scanProcess():
while True:
returnedList.extend(ScanThreat.scan())

def postProcess():
while True:
#temporary list used for exchange
templist = returnedList.copy()
returnedList.clear()
try:
HttpPostThreat.startPosting(templist)
except:
print('couldnt post data')
time.sleep(interval)


try:
_thread.start_new_thread(scanProcess, ())
_thread.start_new_thread(postProcess, ())
except:
print ("Error: unable to start thread")

while True:
pass
Loading

0 comments on commit 558d2e5

Please sign in to comment.