Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AngeloKiriakoulis committed Jul 9, 2023
0 parents commit e59d7bc
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/Project 3 BLE-based Indoor asset and people tracking.zip
20 changes: 20 additions & 0 deletions ARCHITECTURE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Ανάλυση Τελικής Αρχιτεκτονικής για το Project της Ομάδας 3:"BLE-based Indoor asset and people tracking"

Edge->Database->Gem->Widget

1.Edge-Level Αρχιτεκτονική: Κάναμε χρήση BLE συσκευών για τον εντοπισμό της θέσης asset ή ανθρώπων σε ένα χώρο. Συγκεκριμένα, είχαμε πρόσβαση σε SmartBond™ DA14695 Bluetooth Low Energy 5.2 Daughter-Board/Main-Board και SmartBond™ Wireless Ranging (WiRa™) από την εταιρία ®Renesas.
Αρχικά υλοποιήσαμε 2 custom BLE Gateway με τη χρήση των DA14695 Main-Boards και το protocol stack implementation του BlueZ. Χρησιμοποιήσαμε τη βιβλιοθήκη pybluez της Python, ώστε να έχουμε πρόσβαση στα BLE δεδομένα που μαζεύει το Main-board. Από εκεί έγινε η επεξεργασία των δεδομένων για 2 διαφορετικά demo.
Το πρώτο αφορά τη χρήση και των 2 BLE Gateways για proximity sensing, μέσω μιας απλής ανάγνωσης των RSSI τιμών των BLE Συσκευών. Οι συσκευές αυτές φιλτραριστηκαν, ώστε να παίρνουμε μόνο δεδομένα για 2 DA14695 Daughter-Boards και να εντοπιστεί το RSSI τους. Για συγκεκριμένα εύρη τιμών RSSI, ο αλγόριθμος στο Gateway κατατάσσει το proximity των συσκευών σε 3 τιμές (Immediate,Near,Far). Η υλοποίηση αυτή έχει στόχο να προσομοιάσει μια λύση για proximity sensing, μέσω του BLE Mesh. Kάθε Gateway λειτουργεί ως κόμβος και μπορεί να επικοινωνήσει με το ζητούμενο κόμβο (εδώ τα 2 asset με τα BLE Tags) με τρόπο peer-to-peer. Οι συσκευές που βρίσκονται εκτός εμβέλειας μεταξύ τους μπορούν ακόμα να επικοινωνούν περνώντας μηνύματα Over The Air (OTA) μέσω ενδιάμεσων κόμβων.
Το δεύτερο demo, προσεγγίζει τον εντοπισμό ανθρώπων/αντικειμένων με μεγαλύτερη ακρίβεια και κάνει χρήση, στο edge επίπεδο, του SmartBond™ Wireless Ranging (WiRa™). Τοποθετώντας στο χώρο τουλάχιστον 3 στατικά Beacons (DA14695 Daughter-Boards), που παίζουν το ρόλο των Responders, μπορούμε να βρούμε τη θέση του WiRa™, που έχει ρυθμιστεί ως Initiator. Από τον υπολογισμό, μέσω BLE Range DTE, 3 αποστάσεων, μπορούμε να εφαρμόσουμε τριγωνισμό και να εξάγουμε τη θέση του WiRa™ στο χώρο. Το WiRa έπειτα στέλνει τα δεδομένα (AltBeacon μήνυμα) στο Gateway που έχουμε υλοποιήσει. Τα δεδομένα αυτά τα κάνουμε parse, για να πάρουμε τις σωστές τιμές από τα σωστά fields του AltBeacon και έπειτα εκτελούμε τριγωνισμό και κανονικοποίηση συντεταγμένων, κάτι που θα μας χρειαστεί για το UI κομμάτι του project.

2. Cloud: PUT requests στέλνονται απο το edge προς την πλαρφόρμα της YODIWO (xρησιμοποιήσαμε την εφαρμογή postman για να μετατρέψουμε το αίτημα στην επιθυμητή γλώσσα προγραμματισμού, python). Στην βάση δεδομένων της YODIWO έχουμε ορίσει κατάλληλα τύπους assets ,ανάλογα το demo, με συγκεκριμένα extra fields (attributes των assets).Τα PUT requests ανανεώνουν σε τακτά χρονικά διαστήματα αυτά τα fields. Για το πρώτο demo χρειαζόμαστε asset με 4 extra fields(id,name,proximity_level_from_gateway1,proximity_level_from_gateway2) ενώ για το δεύτερο demo χρειαζόμαστε 2 extra fields (xcoordinate,ycoordinate). Αφου έχουμε εξασφαλίσει ότι τα δεδομένα στην βάση μας ανανεώνονται σωστά μένει να τα τροφοδοτήσουμε κατάλληλα στα widget της πλατφόρμας της YODIWO.
Τα widget αποτελούν το UI κομμάτι της υλοποίησης μας. Χρησιμοποιούμε 1 widget για κάθε demo (Asset tracker widget και Maps widget).
Το κάθε widget χρειάζεται να προγραμματιστεί μέσω ενός κατάλληλου GEM, το οποίο ορίζουμε να παίρνει τα κατάλληλα δεδομένα απο την βάση και να τα δείχνει στο widget με τον επιθυμητό τρόπο. Στην ουσία το GEM μας επιτρέπει να αυτοματοποιούμε την αναπαραγωγή δεδομένων στο widget με δεδομένα που αντλεί απο την βάση.
Το κάθε gem αποτελείται απο 5 στάδια: Data SRC,JS Preperation,Query,JS Handling, Data DST. Αναλυτικά:

Data SRC: Σε αυτό το στάδιο συγκεντρώνουμε τα κατάλληλα δεδομένα του account μας στην πλατφόρμα της Yodiwo (πχ OrgId,RestAPIBasePath κ.α.), τα οποία θα χρειαστούμε στα επόμενα στάδια.
JS Preperation: Σε αυτό το στάδιο επεξεργαζόμαστε απλά τα results απο το προηγούμενο στάδιο και τα τροφοδοτούμε με κατάλληλο τρόπο στο επόμενο στάδιο.
Query: Σε αυτό το στάδιο κάνουμε ένα POST request σε κατάλληλο endpoint της YODIWO για να αντλήσουμε τα δεδομένα που βρίσκονται στην βάση και αφορούν τα assets μας.(POST https://$(config.RestAPIBasePath)/fm/assets/search).Σε αυτό το αίτημα ως body χρησιμοποιούμε πληροφορία που έχει προέλθει απο προηγούμενα στάδια ώστε να πάρουμε μόνο τα δεδομένα που μας ενδιαφέρουν.
JS Handling: Σε αυτό το στάδιο γράφουμε τον κώδικα σε JavaScript που χρειάζεται για να τροφοδοτήσουμε το widget που θέλουμε με τα δεδομένα της βάσης.
Data DST: Στο τελευταίο αυτό στάδιο απλά αντιστοιχούμε τις return μεταβλητές του προηγούμενου βήματος σε input ports του widget.

17 changes: 17 additions & 0 deletions edge/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Οδηγίες Εγκατάστασης Edge-Level για το Project Ομάδας 3:"BLE-based Indoor asset and people tracking"

1. Εγκατάσταση λογισμικού Linux σε host ή virtual machine. Για το demo έγινε χρήση του Ubuntu OS, εγκατεστημένο στο VMWare Workstation 17 Player.
2. Εγκατάσταση Python Version 3.x, όπως περιγράφεται στο site https://opensource.com/article/20/4/install-python-linux. Εμείς έπρεπε να συμπεριλάβουμε το dependency python3-docutils.
3. Εγκατάσταση Bluez 5.x, σύμφωνα με τις οδηγίες στο site https://learn.adafruit.com/install-bluez-on-the-raspberry-pi/installation.
4. Εγκατάσταση βιβλιοθηκών bluepy, requests, json μέσω της pip3.
5. Σύνδεση του BLE Controller (SmartBond™ DA14695 Bluetooth Low Energy 5.2 Main-Board) σε ένα USB-port της συσκευής (Αν γίνεται χρήση virtual machine, επιλογή για connect στο virtual machine και όχι στον host). Έπειτα χρήση της εντολής "hciattach -s 115200 /dev/ttyUSB0 any" για το setup.

Για το ble-mesh Demo:
6. Ενεργοποίηση των asset-beacons(SmartBond™ DA14695 Bluetooth Low Energy 5.2 Daughter-Board) από το built-in διακόπτη
7. Run το ble_mesh.py αρχείο. Ίσως κάποια asset-beacons χρειάζονται πάτημα reset, για data advertisement μετά από κάποιο διάστημα.

Για το WiRa Demo:
6. Εισαγωγή του SmartBond™ Wireless Ranging (WiRa™) σε ένα USB-port. Το configuration έγινε με τη χρήση του SmartSnippetsToolBox, υπό την εποπτεία ομάδας από τη ®Renesas. Για την εμφάνιση δεδομένων σειριακής εισόδου από το WiRa™ έγινε χρήση του λογισμικού Tera Term.
7. Run το wira.py αρχείο.

Για την πρόσβαση στο UI κομμάτι της εφαρμογής απαιτείται πρόσβαση στην πλατφόρμα της YODIWO.
118 changes: 118 additions & 0 deletions edge/ble_mesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import requests
import json
from bluepy.btle import Scanner, ScanEntry, DefaultDelegate

# Authors: Aggelos Kiriakoulis, Themis Nikellis
# Date: 2023-02-14
# Description
"""This code is a Bluetooth Low Energy (BLE) scanner that scans for two specific ®Renesas BLE devices with known Bluetooth addresses. When each device is detected, the Received Signal Strength Indicator (RSSI) is checked to determine the proximity of the device. If the device is within a certain range, the code sends a PUT request to the ®Yodiwo YodiFEM Platform with JSON data that includes the proximity of the device. This is one of the 2 implemented scanners"""

json_dict={"48:23:35:ee:bb:aa":["https://fm2service-dev.yodiwo.com/fm/assets/31843", {
"OrgId": "65",
"DeploymentId": "272",
"BuildingId": "1439",
"Name": "DEMO ASSET OSCILLOSCOPE",
"Description": "",
"RefAssetId": None,
"AssetTypeId": 68,
"AssetCategoryId": None,
"MaintainerId": None,
"GeoJson": None,
"ExtraFields": [
{
"AssetExtraFieldInfoId": 105,
"ValueStr": "DEMO_ASSET_OSCILLOSCOPE_ID"
},
{
"AssetExtraFieldInfoId": 106,
"ValueStr": "OSCILLOSCOPE"
},
{
"AssetExtraFieldInfoId": 107,
"ValueStr": "Far"
}

]
}],
"48:23:35:00:00:f8":["https://fm2service-dev.yodiwo.com/fm/assets/31844", {
"OrgId": "65",
"DeploymentId": "272",
"BuildingId": "1439",
"Name": "DEMO ASSET GENERATOR",
"Description": "",
"RefAssetId": None,
"AssetTypeId": 68,
"AssetCategoryId": None,
"MaintainerId": None,
"GeoJson": None,
"ExtraFields": [
{
"AssetExtraFieldInfoId": 105,
"ValueStr": "DEMO_ASSET_GENERATOR_ID"
},
{
"AssetExtraFieldInfoId": 106,
"ValueStr": "GENERATOR"
},
{
"AssetExtraFieldInfoId": 107,
"ValueStr": "Near"
}
]
}]}


"""This function takes an integer value representing the RSSI of a detected device and returns a string indicating the proximity of the device based on pre-defined ranges. The ranges are defined as Immediate, Near, and Far. The function is used to set the value of an "ExtraField" in the JSON data to the proximity of the device."""
def check_range(num):
if -59 <= num <= -20:
return "Immediate"
elif -79 <= num <= -60:
return "Near"
elif -100 <= num <= -80:
return "Far"
else:
return "Number is not in any of the given ranges."


"""This function takes the Bluetooth address and RSSI of a detected device and constructs a JSON payload with the proximity value based on the RSSI. The payload is then sent in a PUT request to the ®YodiFEM Platform with the provided headers."""
def put_json(BD_address,RSSI):
url = json_dict[BD_address][0]
print(check_range(RSSI))
json_dict[BD_address][1]["ExtraFields"][2]["ValueStr"] = check_range(RSSI)

payload = json.dumps(json_dict[BD_address][1])

headers = {
'x-api-key': '_UxiU2rXZxqSH8e9rM8ihm6b_7kOEZTOTw5td40B0pic4-OCNb1h6PQMXczj_ldj57f2T3vOusn2FBjLfhUcgyKCPkOo0218o5KOLMrS5kAyIlC_1B9r6WqjqZAK9wF9STrmo_Cc4RUIf3l63PEf-9LuMwZNljjghjr6hMQn_UTHMSgfbkQ7QCOr28ACARCldx1wZHb6AfbbsBMDWbMvTk0OxGGWMYCLfUNut3Ckcy4P7ckHXjaxxG-JV0D8u-w',
'Content-Type': 'application/json',
'Cookie': 'NCSRF_FEMP=RandomBytes%23jMEZH%2fHzcy%2f96g%3d%3d%7cHmac%23zOb%2btoJATefbdTHG%2f22bBN5GXAoLqh39A844IaTLr7k%3d%7cCreatedDate%232023-01-19T18%3a09%3a06.2288017%2b00%3a00'
}

response = requests.request("PUT", url, headers=headers, data=payload)

print(response.text)


"""This class inherits from the DefaultDelegate class of the bluepy.btle module and overrides the handleDiscovery() method to handle
BLE device discoveries. When a new device is discovered, the method checks if it matches the specified Bluetooth addresses, and if so,
it calls the put_json() function with the device's address and RSSI."""
class ScanDelegate(DefaultDelegate):
def __init__(self):
DefaultDelegate.__init__(self)

def handleDiscovery(self, dev, isNewDev, isNewData):
if isNewData and dev.addr == "48:23:35:ee:bb:aa":
print("Received new data from", dev.addr)
print(f'Device {dev.addr} found, RSSI={dev.rssi} dB\n')
put_json(dev.addr,dev.rssi)
if isNewData and dev.addr == "48:23:35:00:00:f8":
print("Received new data from", dev.addr)
print(f'Device {dev.addr} found, RSSI={dev.rssi} dB\n')
put_json(dev.addr,dev.rssi)

scanner = Scanner(1).withDelegate(ScanDelegate())
while True:
"""This line starts an infinite loop that scans for BLE devices every 2 seconds using the Scanner object from
the bluepy.btle module with the ScanDelegate class as a delegate. This loop will continue to run until the program is manually stopped."""
scanner.scan(2.0)

137 changes: 137 additions & 0 deletions edge/wira.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from bluepy.btle import Scanner, ScanEntry, DefaultDelegate
import struct
import requests
import json

# Authors: Aggelos Kiriakoulis, Themis Nikellis
# Date: 2023-02-14
# Description
"""This is a Python code that acts as a Bluetooth Low Energy (BLE) Gateway. It gathers the information sent by the ®Renesas WiRa Device (Initiator), which is an AltBeacon Message (non connectable undirected advertising), that contains the distance between the device and 3 static Beacons (Responders).The code then uses trilateration to calculate the location of the device in a 2D space relative to three predefined points. The calculated location is then sent to the ®Yodiwo YodiFEM Platform in the form of a JSON object."""

#X_MAX = 8.42 #Distance between 2 responders (68,f1), in the x axis.
#Y_MAX = 7.35 #Distance between 2 responders (68,f2), in the y axis.
X_MAX = 1.7 #Test Distance (x axis)
Y_MAX = 4.25 #Test Distance (y axis)

"""This function takes the BLE advertisement data as an argument, and parses the AltBeacon Message, in order to retrieve the distances between the Initiator and the Responders. It converts the hexadecimal data to float numbers and returns the three distances in a list."""
def getDistance(val):
indexes=[]
distances=[]
for i in range(8,14,2):
indexes.append(val[i:i+2])
hexnums=[]
for i in range(20,44,8):
hexnums.append(val[i:i+8])
for hex_num in hexnums:
hex_inv = ""
for i in range(7,0,-2):
hex_inv += hex_num[i-1] + hex_num[i]
l = int(hex_inv, 16)
f = struct.pack('>l', l)
f = struct.unpack('>f', f)[0]
distances.append(f)
print(indexes,distances)
x_norm, y_norm = triangulate(indexes,distances)
return x_norm, y_norm


"""This function takes the list of distances and the list of indexes that identify the three reference points, and performs trilateration to estimate the location of the device. The function returns the normalized (0 to 1) x and y coordinates of the estimated location. We normalize the distance, so it can be shown in the ®Yodiwo Emerald Map Widget"""
def triangulate(ind,dist):
coordinates = {'68':[0.0,0.0],'f2':[0.0,Y_MAX],'f1':[X_MAX,0.0]}
x1 = coordinates[ind[0]][0]
y1 = coordinates[ind[0]][1]
r1 = dist[0]

x2 = coordinates[ind[1]][0]
y2 = coordinates[ind[1]][1]
r2 = dist[1]

x3 = coordinates[ind[2]][0]
y3 = coordinates[ind[2]][1]
r3 = dist[2]

x = 0.0
y = 0.0

A = x1 - x2
B = y1 - y2
D = x1 - x3
E = y1 - y3

T = (r1*r1 - x1*x1 - y1*y1)
C = (r2*r2 - x2*x2 - y2*y2) - T
F = (r3*r3 - x3*x3 - y3*y3) - T

Mx = (C*E - B*F)/2.0
My = (A*F - D*C)/2.0

M = A*E - D*B

if M != 0:
x = Mx/M
y = My/M
if x<0: x=0.0
if y<0: y=0.0
print(x,y,'\n')

x_norm=x/X_MAX
y_norm=y/Y_MAX
if x_norm>1.0: x_norm=1.0
if y_norm>1.0: y_norm=1.0
print(x_norm,y_norm,'\n')
return x_norm, y_norm


"""This function takes the x and y coordinates of the estimated location, constructs a JSON object, and sends it as an HTTP PUT request the ®YodiFEM URL."""
def put_json(x,y):
url = "https://fm2service-dev.yodiwo.com/fm/assets/31853"

payload = json.dumps({
"OrgId": "65",
"DeploymentId": "272",
"BuildingId": "1439",
"Name": "ASSET_FOR_DEMO_2",
"Description": "",
"RefAssetId": None,
"AssetTypeId": 70,
"AssetCategoryId": None,
"MaintainerId": None,
"GeoJson": None,
"ExtraFields": [
{
"AssetExtraFieldInfoId": 111,
"ValueDbl": x
},
{
"AssetExtraFieldInfoId": 112,
"ValueDbl": y
}
]
})
headers = {
'x-api-key': '_UxiU2rXZxqSH8e9rM8ihm6b_7kOEZTOTw5td40B0pic4-OCNb1h6PQMXczj_ldj57f2T3vOusn2FBjLfhUcgyKCPkOo0218o5KOLMrS5kAyIlC_1B9r6WqjqZAK9wF9STrmo_Cc4RUIf3l63PEf-9LuMwZNljjghjr6hMQn_UTHMSgfbkQ7QCOr28ACARCldx1wZHb6AfbbsBMDWbMvTk0OxGGWMYCLfUNut3Ckcy4P7ckHXjaxxG-JV0D8u-w',
'Content-Type': 'application/json',
'Cookie': 'NCSRF_FEMP=RandomBytes%234ZZcsc4Qri5uUw%3d%3d%7cHmac%23dPZ8F5aSG3lR0sUusuRWDphrM%2ftbRoeYkWrea2KQ1nk%3d%7cCreatedDate%232023-02-14T10%3a38%3a54.8379777%2b00%3a00'
}

response = requests.request("PUT", url, headers=headers, data=payload)

print(response.text)


"""This is a class that is derived from the DefaultDelegate class of the bluepy.btle module. It is used to handle BLE scan events, detected by the ®Renesas BLE Controller"""
class ScanDelegate(DefaultDelegate):
def __init__(self):
DefaultDelegate.__init__(self)
"""This is a method of the ScanDelegate class that is called when a new BLE advertisement from the initiator is detected."""
def handleDiscovery(self, dev, isNewDev, isNewData):
if isNewData and dev.addr == "48:23:35:27:11:04":
print("Received new data from", dev.addr)
print(f'Device {dev.addr} found, RSSI={dev.rssi} dB')
for (adtype, desc, value) in dev.getScanData():
x,y = getDistance(value)
put_json(x,y)

scanner = Scanner(1).withDelegate(ScanDelegate())
while True:
scanner.scan(2.0)
Loading

0 comments on commit e59d7bc

Please sign in to comment.