Internet der Dinge WS2015/ThingLocator

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche

In dieser Wiki-Seite wird das Projekt ThingLocator dokumentiert.

Die Projekt Präsentation ist hier verfügbar: Datei:ThingLocator Praesentation.pdf

Die Projekt Demo als Video hier verfügbar: Datei:Demo Video Link

Einleitung

Projektbeschreibung

Das Ziel dieses Projektes ist die Entwicklung einer Android-Applikation, die mittels Bluetooth Low Energy sich mit sog. „Beacons“ verbindet und Lokalisierungsoptionen anbietet sowie eines externen Webservice, welcher Beacon- und Lokalisierungsdaten anhand von Constraints verwaltet. Die Beacons sind kleine, stromsparende Bluetooth Low Energy Geräte, welche man an Dingen des Alltags wie z.B. Schlüsselbund, Geldbeutel, Tasche o.Ä. anbringen kann. Bisherige Recherchen ergaben, dass über die Signalstärke zwischen Smartphone und Beacon, die Entfernung zu den Geräten gemessen bzw. eingeschätzt werden kann. Dadurch soll dem Anwender mitgeteilt werden, ob ein Gegenstand in der Nähe liegt. Außerdem könnten Alarme ausgelöst werden, wenn sich das Smartphone und die Geräte zu weit auseinander entfernen, um somit den Anwender zu erinnern.

Die erste Phase des Projektes wäre eine Anforderungsanalyse durchzuführen und ein Design bzw. Konzept einer Android-Applikation und dessen externen Webservices aufzustellen. Die zweite Phase wäre die Implementierung der Android-Applikation und des Webservice, welcher, von einem Smartphone oder mehreren, die Daten der Beacons übermittelt bekommt. Darunter fallen unter anderem die eindeutige UUID des Beacon, die Reichweite, Signalstärke und angegebene Daten vom Benutzer zum Beacon. Auf dem externen Webservice kann der Anwender Constraints verwalten, wie z.B. das bestimmte Beacons nicht in der Nähe sein oder nicht getrennt werden dürfen. Sollten die Constraints verletzt werden, können Benachrichtungen und Alarme den Anwender auf diese Ereignisse hinweisen.

Erwartete Ergebnisse

E1 Beschreibung der relevanten Software- und Hardware-Umgebungen, Werkzeuge und verwendete Vorarbeiten.

E2 Auswahl von geeigneten Beacons mit passenden BLE Profilen.

E3 Analyse von möglichen BLE Profilen (Proximity Profile, Find me Profile) sowie Lokalisierung über RSSI.

E4 Recherche und Einarbeitung in Bluetooth Low Energy Android API und/oder anderen notwendigen APIs.

E5 Anforderungsanalyse und Design der Android-Applikation und des externen Webservice.

E6 Implementierung der Android-Applikation und des externen Webservice.

E7 Evaluation der App- und Webservice-Funktionalitäten \zb Güte der Lokalisierung und Einhaltung der Constraints.

E8 Wiki-Seite als Projektdokumentation verwenden und regelmäßig Erkenntnisse, Fortschritte \ua eintragen.

E9 Projektvortrag inkl. Vorstellung der Applikationen.

Geplante Arbeitsschritte

A1 (Ergebnis 1) Planung des Projektes und Erfassung der notwendigen Hardware- und Softwareumgebung.

A2 (Ergebnis 8) Erstellung einer Wiki-Seite für das Projekt. Fortlaufende Dokumentation der erreichten Arbeitsergebnisse und Projektfortschritte.

A3 (Ergebnis 2) Recherche über geeignete Beacons mit notwendigen GATT Profilen.

A4 (Ergebnis 3) Untersuchung der Bluetooth LE GATT Profile auf möglichen Nutzwert und Informationsbeschaffung der Lokalisierung mit BLE.

A5 (Ergebnis 4) Einarbeitung in die Bluetooth Low Energy Android API und anderen notwendigen APIs.

A6 (Ergebnis 5) Ausformulierung der Funktionalität und Abgrenzung (Anforderungsanalyse). Erstellung eines Konzeptes der Android-Applikation und des Webservice.

A7 (Ergebnis 6) Implementierung und Test der Android-Applikation und des Webservice. Dieser bekommt vom Smartphone die Daten (UUIDs, Reichweite, Stärke, Zuordnung zum Gegenstand) der Beacons übermittelt. Mittels vom Benutzer eingestellten Constraints können die Beacons anhand der übermittelten Daten Eigenschaften erhalten wie z.B. dass bestimmte Beacons sich nicht annähern oder trennen dürfen.

A8 (Ergebnis 7) Evaluation der App- und Webservice-Funktionalitäten. Dabei wird die Güte der Lokalisierung sowie Einhaltung der Constraints bewertet.

A9 (Ergebnis 9) Erstellung von Präsentationsfolien und Vorbereitung für die Vorstellung des Projektes.

Analyse

Gesamtarchitektur im Groben

Umsetzung.png

Die Architektur ist nach einem Client/Server-Schema aufgebaut. Auf der linken Seite sind die Smartphones (oder Devices), welche als „Sensoren“ arbeiten und die Dinge bzw. Beacons abspeichern und orten können. Jedes Smartphone kann n Dinge speichern, und ein Ding kann auch von mehreren Smartphones verwaltet und lokalisiert werden.

Wenn das Device Änderungen an der Lokalisierung von den Dingen feststellt, stellt dieser einen REST-Request über das Internet an einen Apache Webserver. Der Webserver stellt REST-Schnittstellen für das Hinzufügen, Löschen und Überprüfen der Constraints bereit. Der Request läuft asynchron, d.h. das Device wartet im Hintergrund auf eine Response vom Webserver.

Ein Benutzer kann über ein Web-Interface seine Beacons sowie Constraints verwalten. Dabei können auch unbekannte Beacons eingegeben werden um auf diese Constraints abzubilden.

Der Webserver speichert die Daten in einer SQLite Datenbank.

Grundlagen

Bluetooth Low Energy

Bluetooth Low Energy (oder auch Bluetooth Smart oder abgekürzt BLE) ist eine stark stromsparende Version von Bluetooth, welche mit der Bluetooth 4.0 Spezifikation herauskam. Mit Bluetooth LE soll sich im Bereich von WPAN neue Möglichkeiten ergeben wie z.B. im Bereich Fitness, wo Wearables Informationen mit dem Smartphone auf Basis von BLE austauschen.

Bluetooth Low Energy nutzt wie Bluetooth Classic oder WLAN das 2,4 GHz ISM-Band. Hierbei gibt es 40 Kanäle welche in zwei Klassen unterteilt werden: Advertising Kanäle und Daten Kanäle. Die Advertising Kanäle sind für die Erkennung und Verbinden von Geräten sowie zum Broadcasten von Daten vorhanden. Es gibt drei feste Advertising Kanäle: 37 mit 2402 MHz, 38 mit 2426 MHz und 39 mit 2480 MHz. Die restlichen Kanäle sind für die Datenkommunikation vorhanden, nach dem ein Gerät verbunden wurde. [1]

Da Beacons, oder zumindest iBeacons, keine Verbindung zulassen sind in diesem Fall die Daten Kanäle irrelevant. Aufgabe des Beacons ist die Umgebung auch zu informieren. Nach dem eine Verbindung hergestellt wurde, wird dies nicht mehr möglich sein, da es dann nicht mehr im Advertisement Kanal sendet.

BLE soll mit einer Reichweite bis zu 10m benutzbar sein. Die Übertragungsrate entspricht 1 MBit/s und hat einen Energieverbrauch von max. 15 mA. Eine höhere Reichweite wäre an sich möglich, wobei der Energieverbrauch dann aber wieder steigt. [2]

Anwendungen, die über Bluetooth LE kommunizieren, verwenden das Generic Attribute Profile [3] Protokoll. GATT beschreibt die Kommunikation zwischen Anwendungen und den Geräten. Hierbei gibt es zwei Rollen: den Server, in der Regel das Peripherie Gerät wie z.B. das Beacon, welches GATT Befehle und Anfragen annimmt und eine Antwort zurückschickt, und den Client, in der Regel der Empfänger wie z.B. das Smartphone, welches GATT Befehle und Anfragen sendet.

In GATT sind Profile festgelegt, welche mittels mehreren Services einen Anwendungsfall abdecken. Ein Service enthält Charakteristiken, welches wiederum einen bestimmten Wert enthält. Diese Charakteristik wird zwischen Client und Server übertragen. Zum Beispiel kann eine Charakteristik die gemessene Temperatur am Gerät sein. Die Abfrage von solchen Werten oder das Setzen, erfolgt über bestimmte UUIDs, die der Charakteristik zugeordnet sind. Ein Deskriptor kann noch weitere Informationen zu der Charakteristik ergänzen. Zum Beispiel den minimalen und maximalen Wert, welcher gemessen werden kann, oder die Einheit von dem jeweiligen Wert.

Beacon

Ein Beacon ist ein relativ kleiner Sender, der über Bluetooth Low Energy arbeitet. Das Beacon sendet in einem bestimmten Intervall (z.B. alle 3 Sekunden) ein Signal, so dass es von Empfängern erkannt werden kann. Hierbei werden Informationen wie z.B. die UUID oder die Reichweite (in RSSI) gesendet. Die Idee ist stark Marketing-belastet. Dabei soll anhand der IDs vom Beacon erkannt werden um welchen Ort oder welches Produkt es sich handelt und so semantisch-relevante Informationen dem Anwender mitgeteilt werden. Angenommen der Anwender ist an einer Bushaltestelle, an welcher ein Beacon vorhanden ist. Wenn sich der Benutzer mit seinem Smartphone nähert, bekommt er die Benachrichtigung über den Busfahrplan oder wann der nächste Bus abfährt. Ein anderes Beispiel wäre die Nutzung in einem Geschäft, bei welchem das Beacon Produktinformationen mitteilt. Diese Informationen werden allerdings nicht vom Beacon selbst geschickt, sondern über die UUID, Major und Minor IDs an einem Server ausgewertet und dann an das Smartphone zugesendet.

Es gibt verschiedene Protokolle / Formate für Beacons. Eines der bekanntesten ist iBeacon [4] [5], welches von Apple in 2013 vorgestellt wurde. Das iBeacon Advertisement ist wie folgt aufgebaut [6]:

Ibeacon adv.png

Die ersten 9 Bytes des Advertisement (AD) ist der iBeacon Prefix. Diese 9 Bytes sind für iBeacon fix, bis auf die Flags in Byte 2, welche variabel sein können. Anschließend folgt die Proximity UUID mit 16 Bytes. Daraufhin folgt die Major und Minor ID mit jeweils zwei Bytes. Die Proximity UUID, Major und Minor identifizieren ein Gerät Beacon eindeutig. Der letzte Wert TxPower ist der Referenz RSSI-Wert für 1 Meter, welches vom Hersteller vorgegeben wird.

Byte Beispiel Erklärung
Byte 0 02 Anzahl der Bytes im ersten AD
Byte 1 01 Flags für Advertisement Typ
Byte 2 1A Flags für LE
Byte 3 1A Anzahl der Bytes im zweiten (und letzten) AD
Byte 4 FF Hersteller-spezifische AD Daten Typ
Byte 5-6 4C 00 (Apple) Hersteller ID Code
Byte 7 02 iBeacon AD Indikator
Byte 8 15 iBeacon AD Indikator
Byte 9-24 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 iBeacon Proximity UUID
Byte 25-26 00 00 Major
Byte 27-28 00 00 Minor
Byte 29 C5 Zweier-Komplement von TxPower

Neben iBeacon gibt es noch andere Formate wie z.B. Eddystone [7] (the open beacon format from Google) oder AltBeacon [8]. Jedes dieser Protokolle haben Ihren eigenen Aufbau der Advertisement Nachricht und sind somit nicht kompatibel unter einander.

Da für das Projekt ein iBeacon ausgeliehen wurde, beziehen wir uns lediglich auf dieses Format.

Lokalisierung

In diesem Abschnitt wird nur die Lokalisierung auf Beacons betrachtet, da nur diese in unserem Anwendungsfall relevant ist.

Die Lokalisierung von Beacons wird anhand dem Received Signal Strength Indicator (RSSI) durchgeführt. Dieser Wert gibt die Signalstärke in -dBm (Dezibel-Milliwatt) an. Je größer der Wert ist, desto besser ist das Signal zwischen Transmitter und Receiver. Zu jedem Beacon ist ein voreingestellter RSSI-Wert für die Entfernung von 1 Meter vorgegeben (vgl. iBeacon Advertisement TxPower). Die Berechnung der Distanz geht hervor aus dem ausgelesen RSSI-Wert und dem TxPower-Wert.

Allerdings gibt es einige Probleme:

- RSSI Wert kann je nach Hersteller und Gerät varrieren (verschiedene Bauteile, Transmitter usw.)

- räumliche Einflüsse oder andere Signale stören das Signal zwischen Transmitter und Receiver.

- keine allgemein Überführung von RSSI zu Meter

Bei der Recherche für eine Lösung sind wir auf die Android Beacon Library [9] gestoßen. Diese Bibliothek stellt für Android eine API bereit um mit Beacons zu interagieren. Innerhalb der Bibliothek wurde die Distanz-Berechnung so gelöst, dass eine nicht-lineare Regression (power regression) auf Basis von einer Abbildung von RSSI-zu-Distanz-Werten durchgeführt wurde [10]. Hierbei wird die Formel

Fehler beim Parsen (MathML mit SVG- oder PNG-Rückgriff (empfohlen für moderne Browser und Barrierefreiheitswerkzeuge): Ungültige Antwort („Math extension cannot connect to Restbase.“) von Server „https://en.wikipedia.org/api/rest_v1/“:): {\displaystyle d = A \times (r/t)^{B} + C}

verwendet, bei welcher d die Distanz in Meter angibt, r der gemessene RSSI-Wert, t der TxPower-Wert und A, B, C Konstanten um die nicht-lineare Funktion darzustellen. Demnach müsste man für jedes Beacon eine Kalibrierung durchführen, in dem die RSSI-Werte zu bekannten Distanz-Werten gesammelt und anhand dessen die Koeffizienten für die Funktion berechnet werden. In dem Beitrag [11] hat ein Entwickler der Android Beacon Library erläutert, welche Parameter verwendet haben. Diese haben sie durch deren Messpunkte und Berechnung einer Best-Fit Curve erhalten:

Fehler beim Parsen (MathML mit SVG- oder PNG-Rückgriff (empfohlen für moderne Browser und Barrierefreiheitswerkzeuge): Ungültige Antwort („Math extension cannot connect to Restbase.“) von Server „https://en.wikipedia.org/api/rest_v1/“:): {\displaystyle d = 0,89976\times(r/t)^{7,7095} + 0,111}

Diese Funktion wird verwendet um die Distanz zu berechnen. Ein Vergleich zu Apple CoreLocation ist nicht verfügbar, da der Quellcode für die Distanz Berechnung nicht offen liegt.

Neben der Distanz-Berechnung besteht noch die Zuordnung in Distanz-Klassen. Apple iBeacon verwendet Klassen für die Lokalisierung: immediate, near, far und unknown [12]. Den Klassen sind Distanz-Werte zugeordnet, d.h. alles was z.B. unter 50cm sich befindet ist in der Klasse immediate. Somit kann einfacher gesagt werden ob ein bestimmtes Ding in der Umgebung ist oder nicht. Da die Distanz-Bereiche unpassend zu unserem Anwendungsszenario sind, ändern wir die Standard-Werte zu den Klassen folgt ab: <= 1m entspricht immediate, > 1m && <= 3m entspricht near, > 3m && <= 7m entspricht far und > 7m entspricht unknown.

Constraints

Constraints sind spezielle prädikatenlogische Formeln, die Bedingungen oder Einschränkungen beschreiben. Wörtlich übersetzt bedeutet der Begriff „Constraint“ Zwang oder Nebenbedingung. [13]

Man kann jede mathematische Formel als ein Constraint ansehen. Jede Gleichung als auch Ungleichung, die bei beliebiger Interpretation der Variablen nicht mehr der Korrektheit entspricht ist somit eine Constraint-Verletzung.

Als Beispiel bediene ich mich hierbei aus dem Buch von Petra Hofstedt, Armin Wolf[14]. Sie haben dort als Beispiel die berühmte Gleichung aus der Physik herangezogen zur Widerstandsberechnung (U = R * I ) auch bekannt als das Ohmsche Gesetz. Belegt man nun die Variablen mit den Werten U = 3, R = 15 und I = 0,2 so ist die Gleichung erfüllt. Würde man aber für die Variablen die Werte U = 5, R = 15 und I = 0,2 einsetzen, wäre das Constraint verletzt.

Ebenso lassen sich Constraints aufstellen, welche wie in der Prädikatenlogik, Aussagen über ganze Elementenmengen (sogenannte Trägermengen) ausdrücken. Nehmen wir hierzu als Beispiel die bekannte Tatsache, dass fast alles ein Ende hat, nur eben die Wurst nicht (wobei wir ausser Acht lassen, dass die zwei Enden hat): ∀x(HatEnde(x) ∨ Wurst(x))

So lässt sich auch hier ein Objekt für die Variable X einsetzen und über den aussagelogischen Operator auf Erfüllbarkeit (Constraint eingehalten) oder Unerfüllbarkeit (Constraint verletzt) testen.

Wie man merkt, ist das Einsatzgebiet von Constraints sehr vielseitig und lässt sich daher schwer bis garnicht von einem Gebiet auf ein anderes Gebiet übertragen. Das liegt daran, dass Constraints im Allgemeinen nur in bestimmt definierten Klassen arbeiten, welche vom einem Constraint-System durch die Syntax und Semantik bestimmt wird.


Aufgrund der hohen Komplexität von „Constraint-Programming“ wurde sich für diese Projekt bei der Constraint Bildung auf eine wohldefinierte Trägermenge geeinigt. Diese wären:

  1. die Menge aller gespeicherten Beacons und Devices
  2. die Menge der von iBeacon definierten Distanzen
  3. für Angabe der maximalen bzw. minimalen Distanz die beiden Vergleichszeichen {<=, >=}
  4. sowie für die Bildung eines Constraints der die Relation zwischen zwei Beacons zum Ausdruck bringen soll, die beiden logischen Operatoren {AND, OR}

REST

Representational State Transfer (abk. REST) ist ein Programmierstil für verteilte Anwendungen insbesondere für Webservices. Der Begriff wurde im Jahr 2000 von Roy Fielding geprägt.[15] Allgemein soll damit der Übergang von einem Zustand in den Nächsten herbeigeführt werden. Der Schwerpunkt liegt bei REST auf einer Maschine-zu-Maschine-Kommunikation. Diese erfolgt durch eine Datentransfer vom Client zum Server und zurück. Der Server stellt hierbei ein Interface bereit, das auf Standard Protokollen wie HTTP und HTTPS basiert und somit Operationen zum Datenaustausch wie zB. GET und POST ermöglicht. Über die URI (Unique Resource Identifizier) wird der Ort und Name des Servers sowie die dort bereitgestellten Ressourcen/Services angegeben und eindeutig identifiziert.


Doch wie kann eine solche Ressource aussehen?

  1. Adressierbarkeit: z.B.: Ein Kunde mit der Kundennummer 123456 könnte also zum Beispiel über die URI (http://ws.mydomain.tld/customers/123456) adressiert werden.
  2. Zustandslosigkeit: Die Kommunikation der Teilnehmer untereinander ist zustandslos somit sind REST-Services sehr einfach skalierbar; da keine Sitzungen existieren können auch mehrere Anfragen eines Clients auf verschiedene Server verteilt werden.
  3. Einheitliche Schnittstelle: Wie oben schon erwähnt muss die Ressource über standardisierte HTTP-Methoden wie z. B. GET, POST oder PUT ansprechbar sein.
  4. Entkopplung von Ressourcen und Repräsentation: Ein Client kann die Response auf seine Anfrage im ebenfalls standardisierten XML- oder JSON-Format anfordern und erhalten.


Ein weiterer Vorteil einer REST Schnittstelle ist der Sicherheitsaspekt. Dieser braucht auf der Abstraktionsebene von REST nicht behandelt zu werden, da dies auf der Protokollebene von HTTPS bzw. TLS geschieht.

Auch bei der Implementierung einer solchen REST Schnittstelle, kann auf alle gängigen Programmiersprachen wie zum Beispiel JavaScript , Python, PHP umgesetzt werde. Ebenso werden in den jeweiligen Sprachen unzählige openSource Frameworks im Internet bereitgestellt.

Anforderungen

App

Registrierung des Devices / Smartphone am Server

Das Smartphone registriert sich zuerst am Server, in dem die Server-Adresse in den App-Einstellungen eingetragen wird. Dabei wird der Identifikator vom Smartphone/Device zum Server innerhalb eines HTTP REST Request übermittelt. Anhand dieses Identifikators können Beacons mit dem Device in zugeordnet werden. Je nachdem, ob die Registrierung erfolgreich war, wird eine entsprechende HTTP REST Response geschickt. In der Response ist gekennzeichnet, ob die Registrierung erfolgreich war oder ein Fehler aufgetreten ist.

Registrierung von Beacons am Server

Beim Hinzufügen von Beacons in der App wird das jeweilige Beacon auch am Server registriert. Dabei wird die UUID, Major, Minor, Name und momentane Location vom Beacon zusammen mit der ID vom Smartphone an den Server übermittelt. Am Server wird das Beacon dem jeweiligen Smartphone zugeordnet. Die Registrierung erfolgt durch den den HTTP Request/Response. Beacons können auch manuell beim Server registriert werden, durch händisches Eintragen der Daten über eine Webschnittstelle. Diese Beacons Daten werden für die Verwaltung von Constraints verwendet.

Ortung von Beacons in der App

Zu den gespeicherten Beacons/Dinge soll die Distanz bzw. die Location ausgegeben und regelmäßig aktualisiert werden. Dabei soll die Reichweite in Meter ausgegeben werden sowie die jeweilige Distanz-Klasse, in welcher das Beacon zugeordnet wurde (immediate, near, far, unknown). Die Abbildung von Reichweite zu Distanz-Klasse soll vom Anwender einstellbar sein, so dass er seine eigene Distanz-Werte verwenden kann (z.B. 3 Meter für immediate). Sämtliche Distanz-Berechnungen und Location-Angaben sollen anhand des RSSI-Wertes durchgeführt werden.

Überprüfung der Constraints

Die App soll regelmäßig prüfen ob ein Constraint verletzt wurde. Dabei soll bei einem Zustandswechsel (der Wechsel zwischen Distanz-Klassen, also z.B. von near auf far) der Server mittels HTTP REST Request benachrichtigt werden, welcher die Constraint-Überprüfung durchführt. Hierbei wird die Device ID, Beacon UUID, Major, Minor und Location des jeweiligen Beacon übermittelt werden. Der Server antwortet mittels HTTP REST Response auf die Anfrage, in welcher drin steht ob ein Constraint verletzt wurde. Die App reagiert bei verletzten Constraint, dass der Anwender über Notifications benachrichtigt wird.

Anwendungsfälle für App

Wir haben zu Beginn des Projektes mehrere Anwendungsfälle aufgeschrieben die umgesetzt werden sollen.

Anwendungsfalldiagramm
Anwendungsfall UC01
Name Auflistung der gespeicherten Dinge
Ziel Benutzer werden bereits hinzugefügte „Dinge“ (Beacons) aufgelistet.
Akteur Anwender
Auslöser Aktivität wird geöffnet
Vorbedingung Keine
Nachbedingung (Erfolg) Gespeicherte Dinge werden aufgelistet
Nachbedingung (Misserfolg) Keine Dinge gespeichert, Benutzer wird informiert um neue Dinge hinzuzufügen
Standardablauf 1. Aktivität wird geöffnet.
2. Gespeicherte Dinge werden vom System aufgelistet zusammen mit Optionen.
3. Benutzer kann Optionen auf Dingen starten.
Alternativabläufe 2. Es werden keine Dinge aufgelistet. Benutzer wird informiert um neue Dinge hinzuzufügen.
Folgbaren Anwendungsfall UC02, UC03, UC04
Anwendungsfall UC02
Name Scan nach neuen Dingen
Ziel Benutzer sieht Dinge (Beacons) in seiner Umgebung
Akteur Anwender
Auslöser Button „Scan“ aus der Aktivität UC01 wird gedrückt
Vorbedingung Keine
Nachbedingung (Erfolg) Dinge in Umgebung werden aufgelistet
Nachbedingung (Misserfolg) Es befinden sich keine Dinge in der Umgebung.
Standardablauf 1. Button „Scan“ aus der Aktivität UC01 wird gedrückt.
2. System sucht automatisch nach Beacons, die dem System noch nicht bekannt sind.
3. Benutzer bekommt eine Auflistung der gefundenen Beacons.
Alternativabläufe 2.1 Benutzer drückt Button „Rescan“
3.1 Es wurden keine Beacons gefunden.
Folgbaren Anwendungsfall UC01, UC02.1
Anwendungsfall UC02.1
Name Hinzufügen von neuem Beacon
Ziel Neues Ding / Beacon wird dem System hinzugefügt.
Akteur Anwender
Auslöser Button "Hinzufügen" aus UC02 wird gedrückt.
Vorbedingung Beacon wird in UC02 aufgelistet
Nachbedingung (Erfolg) Beacon wird dem System hinzugefügt.
Nachbedingung (Misserfolg) Beacon ist nicht klassifizierbar.
Standardablauf 1. Button "Hinzufügen" aus UC02 wird gedrückt.
2. Aktivität "Hinzufügen" wird gestartet.
3. Anwender wird aufgefordert einen Namen einzutragen.
3. Anwender gibt Name für Ding ein.
4. Anwender drückt auf "Speichern".
5. Ding bzw. Beacon wird im System gespeichert.
6. Push-Nachricht an Server, dass Ding hinzugefügt wurden ist.
7. Wechsel zum Anwendungsfall UC01.
Alternativabläufe 5.1 Name schon vergeben, erneute Namensabfrage.
4.1 Anwender drückt auf "Abbrechen".
Folgbaren Anwendungsfall UC02, UC01
Anwendungsfall UC03
Name Ding Optionen
Ziel Aktionen auf Dingen ausführen
Akteur Anwender
Auslöser Anwender drückt Buttons des zugehörigen Ding
Vorbedingung Ding wird aufgelistet
Nachbedingung (Erfolg) UC03.x wird gestartet
Nachbedingung (Misserfolg) Keine
Standardablauf Keine
Alternativabläufe Keine
Folgbaren Anwendungsfall UC03.1 - UC03.5
Anwendungsfall UC03.1
Name App-spezifische Einstellung für ein Ding anpassen
Ziel Mögliche Einstellungen werden aufgelistet
Akteur Anwender
Auslöser Button "Einstellung" des zugehörigen Ding wird gedrückt
Vorbedingung Ding muss in der Auflistung aus UC01 vorhanden sein
Nachbedingung (Erfolg) Einstellung wird geöffnet
Nachbedingung (Misserfolg) Keine
Standardablauf 1. Anwender drückt auf Button "Einstellung" des zugehörigen Ding
2. Aktivität für Einstellung wird geöffnet
Alternativabläufe
Folgbaren Anwendungsfall UC03.1.1
Anwendungsfall UC03.1.1
Name Kalibrierung
Ziel Zur genaueren Distanzangaben wird der RSSI Wert für definierte Entfernung gespeichert
Akteur Anwender
Auslöser Button "Kalibrierung" im Menü "Einstellung" aus UC03.1 wird gedrückt
Vorbedingung UC03.1 & Beacon in Reichweite
Nachbedingung (Erfolg) Kalibrierungsdaten zum jeweiligen Beacon wird im System gespeichert
Nachbedingung (Misserfolg) Daten konnten nicht übernommen werden.
Standardablauf 1. Benutzer drückt Button "Kalibrierung" im Menü.
2. Benutzer wird aufgefordert Smartphone und Ding in "unmittelbarer" Nähe zusammen zu bringen.
3. RSSI-Wert wird gespeichert.
4. Benutzer wird aufgefordert Smartphone und Ding in "naher" Nähe zusammen zu bringen.
5. RSSI-Wert wird gespeichert.
6. Benutzer wird aufgefordert Smartphone und Ding in "ferner" Nähe zusammen zu bringen.
7. RSSI-Wert wird gespeichert.
8. Benutzer drückt "Speichern".
9. Kalibrierungswerte werden im System zum jeweiligen Ding gespeichert.
Alternativabläufe 1.1 Benutzer drückt "Abbrechen"
3.1, 5.1, 7.1 Werte konnten nicht ermittelt werden. Vorgang wird übersprungen.
Folgbaren Anwendungsfall UC03.1
Anwendungsfall UC03.2
Name Löschen eines Dinges
Ziel Ein Ding wird aus dem System entfernt
Akteur Anwender
Auslöser Button "Löschen" wird zum zugehörigen Ding gedrückt.
Vorbedingung Ding ist in Liste aus UC01 aufgelistet.
Nachbedingung (Erfolg) Ding ist aus System entfernt.
Nachbedingung (Misserfolg) Ding ist noch im System vorhanden.
Standardablauf 1. Anwender drückt Button "Löschen" des jeweiligen Dinges.
2. Anwender muss Löschvorgang bestätigen.
3. Ding wird aus System gelöscht.
4. Push-Nachricht an Server, dass Ding gelöscht wurde.
Alternativabläufe 2.1 Anwender verneint den Löschvorgang.
Folgbaren Anwendungsfall UC01
Anwendungsfall UC03.3
Name Orten eines Dinges
Ziel Ein Ding wird lokalisiert.
Akteur Anwender
Auslöser Button "Orten" wird gedrückt.
Vorbedingung Ding ist in Liste aus UC01 aufgelistet.
Nachbedingung (Erfolg) Benutzer erhält Entfernung zum Beacon
Nachbedingung (Misserfolg) Benutzer hat keine Auskunft über Entfernung, da entweder Beacon außerhalb der Reichweite oder Beacon nicht aktiv.
Standardablauf 1. Anwender drückt Button "Orten" des jeweiligen Dinges.
2. Neue Aktivität wird gestartet.
3. System versucht Verbindung zum Beacon herzustellen.
4. System klassifiziert empfangenen RSSI Wert und teilt Entfernung dem Benutzer mit.
5. Anwender beendet die Aktivität.
Alternativabläufe 5.1 Anwender kann jederzeit die Aktivität wechseln.
Folgbaren Anwendungsfall UC01
Anwendungsfall UC03.4 (Weggefallen)
Name Alarm (de)aktivieren
Ziel Automatische Benachrichtigung über Verbindungsabbruch aktivieren bzw. deaktivieren
Akteur Anwender
Auslöser Betätigung des Schalters des jeweiligen Dinges.
Vorbedingung Ding ist in Liste aus UC01 aufgelistet.
Nachbedingung (Erfolg) Keine weiteren Benachrichtigung bei Verbindungsabbruch oder Benachrichtigung eingeschaltet
Nachbedingung (Misserfolg) keine Veränderung
Standardablauf 1. Anwender betätigt Schalter des zugehörigen Dinges.
2. Ding wird aus System für Benachrichtigung hinzugefügt bzw. entnommen.
Alternativabläufe Keine
Folgbaren Anwendungsfall UC01
Anwendungsfall UC03.5
Name Daten zum Server pushen
Ziel Daten der Beacons werden zum Server gepusht.
Akteur Anwender & System
Auslöser Button "Push to Server" wird gedrückt / automatisch bei Ereignissen
Vorbedingung Server-Adresse ist unter Einstellungen in UC04.1 eingetragen und ist Erreichbar
Nachbedingung (Erfolg) Daten des Beacons ist beim Server angekommen.
Nachbedingung (Misserfolg) Server nicht erreichbar oder Übertragungsfehler.
Standardablauf 1. Anwender drückt auf Button "Push to Server".
2. Daten des Beacons (ID, Name, Device Name für das Smartphone, Signalstärke, letzte Signalstärke) wird zum Server gesendet.
3. Server antwortet mit OK.
Alternativabläufe 3.1 Server antwortet mit Fehlercode. Fehlercode wird behandelt.
1.1 Automatische Durchführung bei Ereignissen wie Verbinden, Trennen von Beacons
Folgbaren Anwendungsfall UC01
Anwendungsfall UC04
Name App-Einstellungen anpassen
Ziel App-spezifische Einstellungen werden vorgenommen.
Akteur Anwender
Auslöser Button "Einstellung" in Übersicht wird gedrückt.
Vorbedingung Keine
Nachbedingung (Erfolg) Aktivität für Einstellung wird geöffnet.
Nachbedingung (Misserfolg) Keine
Standardablauf 1. Anwender drückt Button "Einstellung" in App-Übersicht.
2. Neue Aktivität für Einstellungen wird gestartet.
3. Benutzer kann Server-Adresse und Geräte-Name eintragen / ändern.
4. Benutzer drückt auf Button "Speichern".
5. System überprüft ob Server-Adresse erreichbar.
7. System überprüft ob am Server Geräte-Name schon verwendet wurde.
Alternativabläufe 4.1 Benutzer drückt auf Button "Abbrechen" und schließt die Aktivität.
5.1 Server nicht erreichbar. Mitteilung an Benutzer.
7.1 Geräte-Name ist schon auf Server vergeben. Mitteilung an Benutzer.
Folgbaren Anwendungsfall UC01

Server

Eine der wichtigsten Anforderungen an die Infrastruktur war die ständige Erreichbarkeit der Webschnittstelle. Nur so wurde eine zeitlich und räumlich unabhängige Entwicklung sowohl an der mobilen Applikation als auch der Server Schnittstelle gewährleistet. Somit entfiel die Arbeit an lokalen Maschinen. Die Nutzung eines kommerziellen Providers befand man hier für nicht geeignet da sowohl für die Installation des Frameworks also auch der Datenbank administrative Rechte benötigt werden. Dies wird jedoch für gängige Webnutzung von den Providern nur für eine hohe monatliche Gebühr zu Verfügung gestellt. Daher wurden die Server der Hochschule RheinMain verwendet, da diese für Bildungszwecke für Studenten kostenfrei gestellt werden. Seitens der Laboringenieure musste noch die Installation einer sqlite3 Datenbank durchgeführt werden sowie einige Zugriffsrechte auf Verzeichnisse und Konfigurationsdateien wie zum Beispiel „.htaccess“ angepasst werden.

REST-Schnittstelle

Der Server soll eine REST-Schnittstelle mit vordefinierten Routen ins Internet bereitstellen, über welche er, auf Anfragen von der App, ein Service ausführt und mit dem Ergebnis antwortet.

Definition der Services der REST-Schnittstelle

Wird ein Beacon von der App gefunden und in der App gespeichert, so soll dieses Beacon auch persistent auf dem Server gespeichert werden. Das bedeutet der Server muss eine Schnittstelle bereitstellen, über welche die Daten des Beacons von der App zum Server übermittelt werden können. Die Schnittstelle gibt als Antwort auf die Anfrage der App eine Rückmeldung ob das Sichern in der Datenbank erfolgreich war. Im Fehlerfall wir eine Fehlermeldung übermittelt. Der Server soll auch äquivalente Funktionen zum Löschen und Ändern von Beacons umsetzen.

Eine weitere Schnittstelle soll der App ermöglichen Constraints Abfrage für Beacons durchzuführen. Dabei soll vom Server anhand der empfangenen Beacon-Daten überprüft werden, ob ein Constraint zum Beacon in der Datenbank existiert und ob dieser verletzt wurde. In beiden Fällen soll der Server auf die Constraint Anfrage eine Antwort senden, in welcher der Status der Constraint-Überprüfung enthalten ist. Falls Constraints verletzt wurden werden diese in der Antwort übermittelt. Ebenso ist es auch hier vorstellbar, dass eine Distanz Änderung eines Beacons, gleich mehrere Constraints verletzt. In einem solchen Fall werden alle verletzten Constraints Benachrichtigung zurück an die App geschickt.


Beacons

Neuer Beacon -Screenshot

Es soll über das Webinterface möglich sein manuell Beacons anzulegen und zu löschen sowie eine gesamte Übersicht aller gespeicherten Beacons zu liefern. Dem Benutzer soll ermöglicht werden über eine Eingabemaske, unabhängig von der App, Beacons mit ihrem Namen, UUID sowie Major und der Minor einzutragen und dauerhaft zu speichern. Sollte für den Löschfall die Bedingung eintreten, dass für dieses Beacon noch ein Constraint formuliert ist, so muss der Benutzer darüber benachrichtigt werden. Das Fortfahren oder der Abbruch des Löschvorgangs soll der Benutzer in jedem Fall individuell entscheiden können.

Constraints

Neuer Constraint -Screenshot

Der Benutzer soll die Möglichkeit haben über eine Eingabemaske Constraints einzugeben. Über diese Constraints wird die minimale bzw. die maximale Distanz des Beacons zum Device/Smartphone beschrieben. Wird diese unterschritten bzw. überschritten, soll der Benutzer in der App eine Benachrichtigung erhalten. Der Text der Benachrichtigung wird vom Benutzer bei der Eingabe eines Constraints eigens formuliert. Die Eingabemaske soll dem Benutzer ermöglichen ein Constraint für nur ein und zwei Beacons zu formulieren.

Hier ein Beispiel für ein Constraint mit einem Beacon:

  1. eins Ein Beacon mit dem Namen Schlüssel soll sich vom Smartphone nicht weiter als 2m entfernen. Ansonsten soll eine Nachricht geschickt werden, welche den Text trägt: „Achtung, Sie haben Ihren Schlüssel verloren!“
  2. zwei Ein Beacon mit dem Namen Ehefrau soll sich zum Smartphone nicht näher als 5m nähern. Ansonsten soll eine Nachricht geschickt werden, welche den Text trägt: „Achtung, Ihre Ehefrau kommt!“

und nun ein Beispiel für Constraints mit zwei Beacon:

  1. eins Beacon Eins mit dem Namen „Schlüssel“ soll sich von Smartphone nicht weiter als 2m entfernen und das Beacon Zwei mit dem Namen „Dieb“ soll sich zum Smartphone nicht näher als 10m Nähern. Ansonsten soll eine Nachricht geschickt werden, welche den Text trägt: „Achtung, passen Sie auf Ihre Wertsachen auf!“

Dem Benutzer soll jederzeit es ermöglicht werden alle erstellen Constraints einzusehen und diese bei Bedarf zu ändern oder gar zu Löschen.

Verwendete Frameworks / APIs

Android BLE API

Für die Android Applikation wurde zuerst auf API Level 18 (Android 4.3) gearbeitet, da mit Android 4.3 der BLE Support für Android und eine entsprechende API eingeführt wurde. Hierbei kann das Android Smartphone als "central role" agieren und sich mit BLE Geräten ("peripherie") verbinden und über GATT Daten austauschen. Eine Einführung zu BLE in Android findet man unter [16]. Die Dokumentation für BLE unter Android ist sehr einsteiger freundlich gemacht und man kann mit relativ wenig Mühe eine Bluetooth Verbindung in Android implementieren.

Die Implementierung vom Scan wurde über den BluetoothAdapter und über die Funktionen startLeScan (BluetoothAdapter.LeScanCallback callback) und stopLeScan(BluetoothAdapter.LeScanCallback callback) durchgeführt. Hierbei wird den Funktionen eine anonyme Klasse oder eine Instanz der BluetoothAdapter.LeScanCallback-Klasse übergeben, welche die Methode onLeScan(BluetoothDevice device, int rssi, byte scanRecord) enthält, die bei einem gefunden Gerät aufgerufen werden. In dieser Methode wird als Parameter das BluetoothDevice, der RSSI-Wert und das Advertisement in Bytes übergeben. Aus diesen Daten kann die Location berechnet werden sowie überprüft werden ob es sich um ein iBeacon handelt.

Ab Android 5.0 mit API Level 21 wurde BLE erweitert. Es ist nun auch möglich als "peripheral device" zu agieren und Advertisement zu schicken. Die API wurde weiterhin angepasst und dabei die vorher verwendeten Funktionen als deprecated markiert. Da unsere Test-Smartphone und Test-Tablet eh mit Android 5.0 und Android 6.0 ausgestattet waren, wurde die Implementierung hierbei umgeändert.

Es wird weiterhin der BluetoothAdapter verwendet, welcher allerdings über getBluetoothLeScanner() eine Instanz der BluetoothLeScanner-Klasse liefert. Diese Klasse ist extra für BLE Scanning ausgelegt und hat nur wenige Methoden, die für das Starten und Stoppen vom Scannen zuständig sind. Hierbei ist es auch möglich über ScanFilter die Suche zu filtern und nur bei bestimmten BLE Geräten den Callback auszulösen. Das Starten des Scans wird über startScan(ScanCallback callback) und das Stoppen über stopScan(ScanCallback callback) durchgeführt. Hierbei wird wieder eine Instanz der ScanCallback-Klasse übergeben um den Callback zu behandeln.

Asynchronous Http Client for Android

Für die REST Request und Responses in der Android App wurde der Asynchronous Http Client for Android verwendet. Der Client bietet die Möglichkeit asynchrone HTTP Aufrufe zu starten, die über einen Callback die Response behandeln. Dabei wird alles in einem separaten Thread, losgelöst vom UI (User Interface) Thread, durchgeführt. Dies ist sehr wichtig, da ansonsten die App bei jeder Kommunikation still stehen würde, bis die Response eintrifft.

Ein großer Vorteil bei diesem HTTP Client ist, dass es einen HTTP Response Handler extra für JSON gibt. Da wir uns während der Architektur Planung für die Antwort vom Server auf das JSON Format geeinigt haben kam uns das sehr entgegen. Dabei wird den Callback-Methoden ein JSON-Objekt bzw. JSON-Array übergeben, welches die jeweiligen Key-Value-Paare beinhaltet, die der Server schickt.

Umsetzung

App

Architektur

App architektur.png

Die grobe Architektur der App wird in der obigen Abbildung dargestellt. Hierbei sind die wichtigsten Klassen enthalten.

Der Startpunkt der App ist die Klasse ThingOverviewActivity. Hierbei wird die Klasse BeaconDataApplication als Application initialisiert, welche sozusagen, den Zustand der App darstellt. Die BeaconDataApplication Klasse beinhaltet die gespeicherten und zwar gefundene, aber unbekannte BeaconThing-Objekte sowie die BluetoothDevice-Objekte. Jede Aktivität oder Klasse, die auf gespeicherte Beacons oder BluetoothDevices zugreifen soll, verwendet BeaconDataApplication.

Der ScanBackgroundService wird ebenfalls in ThingOverviewActivity als Service gestartet. Mittels diesem Service, läuft ein Task im Hintergrund, welcher unabhängig von dem Zustand des LifeCycles einer Aktivität ist. Das heißt Task wird ausgeführt obwohl die App nicht im Vordergrund ist oder eine Aktivität geschlossen wurde. Der ScanBackgroundService holt den BluetoothAdapter sowie den BluetoothLeScanner, wie in Android BLE API beschrieben. Im Service wird ein Task gestartet um einen wiederholenden BLE Scan durchzuführen. Dies wird in Bluetooth LE Scan beschrieben.

Das Paket activities enthält alle Activities der App. Die Activities sind teilweise fest verbunden mit den Klassen aus den Paketen adapters. Wenn Auflistungen von Daten in Activities dargestellt werden, wird ein ListAdapter verwendet, welche die Liste füllt und bei Veränderungen aktualisiert. Die Adapter enthalten jeweils einen ViewHolder aus dem Paket viewholders um die GUI-Elemente zu bestimmen. Im Paket models sind Klassen zugeordnet, welche ein "Objekt" abbilden sollen. Im Ausschnitt z.B. BeaconThing, welches das gespeicherte Beacon mit seinen "Thing"-Informationen abbildet.

Lokalisierung

Die Lokalisierung von Beacons wird über den RSSI-Wert durchgeführt. Der RSSI-Wert wird bei jedem BLE Scan für gefundene Beacons aktualisiert. Zusätzlich wird beim ersten Hinzufügen des Beacons der TxPower-Wert gespeichert. Aus diesen beiden Werten wird die Location berechnet.

Ingesamt sind drei Optionen zur Distanz-Berechnung implementiert:

  • Berechnung der Distanz in Meter über RSSI und TxPower-Wert und anschließende Einordnung in Distanz-Klasse (Standard-Option)
  • Ermitteln der Distanz-Klasse anhand des RSSI-Wertes
  • eigene Reichweiten für Distanz-Klassen und Verwendung der ersten Option

Die Berechnung der Distanz in Meter wird über die Methode BeaconThing.calculateAccuracy() durchgeführt [17]:

public double calculateAccuracy() {
    if (rssi == 0) {
        return -1.0;
    }

    double ratio = rssi * 1.0 / txPower;
    if (ratio < 1.0) {
        return Math.pow(ratio, 10);
    } else {
        double accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
        return accuracy;
    }
}

Um den Distanz-Wert in die Distanz-Klasse einzuordnen (immediate, near, far, unknown) werden zwei HashMap mit der Location und dem maximalen Distanz-Wert verwendet:

private HashMap<LocationEnum, Double> individualRanges = new HashMap<LocationEnum, Double>();
private HashMap<LocationEnum, Double> rssiToLocation = new HashMap<LocationEnum, Double>();

Diese zwei HashMaps enthalten die jeweiligen Werte für die Klassen, die der Anwender in der App einstellen kann. Dazu wird für IMMEDIATE, NEAR und FAR ein Eintrag erstellt der auf den jeweiligen RSSI oder maximalen Distanz-Wert zeigt. Standardmäßig ist in indivudalRanges

  • IMMEDIATE -> 1.0
  • NEAR -> 3.0
  • FAR -> 7.0

enthalten. Alles was über 7m heraus geht, wird in die Klasse UNKNOWN zugeordnet.


Um die Location zu ermitteln kann nun die Methode BeaconThing.calculateLocation() aufgerufen werden:

public void calculateLocation() {

    this.oldLocation = this.newLocation;

    double immediate;
    double near;
    double far;

    /* If useRssiInsteadDistance is true, then we calculate the location by the rssi-to-class values */
    if (useRssiInsteadDistance) {
        immediate = rssiToLocation.get(LocationEnum.IMMEDIATE);
        near = rssiToLocation.get(LocationEnum.NEAR);
        far = rssiToLocation.get(LocationEnum.FAR);

        if (rssi >= immediate) {
            /* wenn rssi wert Größer als immediate ist (je größer desto näher an 0, desto besser) */
            this.newLocation = LocationEnum.IMMEDIATE;
        } else if (rssi < immediate && rssi >= near) {
            this.newLocation = LocationEnum.NEAR;
        } else if (rssi < near && rssi >= far) {
            this.newLocation = LocationEnum.FAR;
        } else {
            this.newLocation = LocationEnum.UNKNOWN;
        }

    } else {
        double range = calculateAccuracy();

        /* default values are in the hashmap already included */
        immediate = this.individualRanges.get(LocationEnum.IMMEDIATE);
        near = this.individualRanges.get(LocationEnum.NEAR);
        far = this.individualRanges.get(LocationEnum.FAR);

        if (range >= 0 && range <= immediate) {
            this.newLocation = LocationEnum.IMMEDIATE;
        } else if (range > immediate && range <= near) {
            this.newLocation = LocationEnum.NEAR;
        } else if (range > near && range <= far) {
            this.newLocation = LocationEnum.FAR;
        } else {
            this.newLocation = LocationEnum.UNKNOWN;
        }
    }

}

Dabei wird die alte Location gespeichert um zu erkennen, ob es einen Wechsel gab. Es wird anschließend überprüft ob die Ermittlung der Distanz nur über die RSSI-Werte durchgeführt werden soll. Wenn dem so ist, werden die Werte aus der rssiToLocation-HashMap entnommen und mit dem RSSI Wert verglichen. Anschließend wird die neue Location gesetzt. Wenn die Ermittlung der Distanz über den Meter-Wert durchgeführt werden soll, wird zuerst der Wert berechnet über den Aufruf von calculateAccuracy(). Auch hier werden die Werte für die Bereiche aus der indivudalRanges-HashMap entnommen und dann mit dem range-Wert verglichen.

Der Vergleich der Location wird über eine Methode überprüft, welche oldLocation und newLocation vergleicht.

Bluetooth LE Scan

Der Bluetooth LE Scan wird innerhalb der Klasse ScanBackgroundService über das Runnable-Objekt gestartet:

private Runnable scanRunnable = new Runnable() {
    @Override
    public void run() {
        scanAndUpdate();
        handler.postDelayed(scanRunnable, Integer.parseInt(sp.getString("update_frequency", "5000")));
    }
};

Diese anonyme Klasse sorgt dafür, dass die Methode scanAndUpdate() alle x Sekunden aufgerufen wird (je nach Einstellung).

In der scanAndUpdate() Methode wird dem BluetoothLeScanner das Starten und Stoppen mitgeteilt:

private void scanAndUpdate() {
    List<ScanFilter> scanFilterList = new ArrayList<ScanFilter>();
    bleScanner.stopScan(scanCallback);

    ScanSettings scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build();
    bleScanner.startScan(scanFilterList, scanSettings, scanCallback);
}

Dabei wird der jetzige Scan gestoppt und ein neuer gestartet. Der Methode startScan wird als dritter Parameter ein Callback-Objekt übergeben, welches die Instanz einer anonyme Klasse ist:

private ScanCallback scanCallback = new ScanCallback() {
	@Override
	public void onScanResult(int callbackType, ScanResult result) {

		BluetoothDevice device = result.getDevice();

		/* Loop through each saved thing and look if found beacon matches with saved beacon */
		for (BeaconThing b : appState.getThings()) {
			if (b.getDeviceAddress().equals(result.getDevice().getAddress())) {
				b.setRssi(result.getRssi());
				if(appState.getThingOverviewListAdapter() != null) {
					appState.getThingOverviewListAdapter().notifyDataSetChanged();
				}

				if(b.hasLocationChanged()) {
					notifyServer(b, STATE_CHANGE);
				}

				Intent intent = new Intent(ScanBackgroundService.STATE_CHANGE);
				broadcaster.sendBroadcast(intent);
				return;
			}
		}

Die Methode onScanResult erhält als zweiten Parameter ein Objekt vom Typ ScanResult welches die Informationen zu unserem Beacon enthält. Hierbei wird zunächst überprüft ob das gefundene Beacon schon als "Ding" gespeichert wurde. Wenn dem so ist, wird der RSSI-Wert neu gesetzt. Anschließend wird über hasLocationChanged() die Location neu berechnet und zurückgegeben ob sich was verändert hat. Wenn sich die Location geändert hat, wird die Methode notifyServer() aufgerufen um einen REST-Request an den Server zu stellen (vgl. Cosntraint Request & Response Handling).

		for (BeaconThing b : appState.getUnknownThings()) {
			if (b.getDeviceAddress().equals(result.getDevice().getAddress())) {
				b.setRssi(result.getRssi());
				if(appState.getBeaconScanListAdapter() != null) {
					appState.getBeaconScanListAdapter().notifyDataSetChanged();
				}
				if(b.hasLocationChanged()) {
					notifyServer(b, STATE_CHANGE);
				}
				Intent intent = new Intent(ScanBackgroundService.STATE_CHANGE);
				broadcaster.sendBroadcast(intent);
				return;
			}
		}

Die selbe Vorgehensweise wird für unbekannte Beacons durchgeführt. Die appState (BeaconDataApplication) enthält eine Liste aller "unbekannten" Beacons. Jedes Beacon was gefunden wird, wird als unbekanntes gespeichert, damit zum einen das Advertisement nicht wiederholend extrahiert wird und zum anderen, dass der Anwender eine Liste aller verfügbaren Beacons hat.

		try {
			/* Beacon unknown, maybe iBeacon, then we have to notify server */
			/* get data from apple ibeacons only */
			byte[] data = result.getScanRecord().getManufacturerSpecificData(76);

			if(data.length != 23) return; /* its no ibeacon */


			BeaconThing thing = new BeaconThing();
			thing.setDeviceName(device.getName());
			thing.setDeviceAddress(device.getAddress());
			thing.setRegisteredOnServer(false);
			thing.setRssi(result.getRssi());

			StringBuilder sb = new StringBuilder();

			for(int i = 2; i < 18; i++) {
				sb.append(String.format("%02X", data[i]));
			}

			int major = ((data[18] & 0xFF) << 8) | (data[19] & 0xFF);
			int minor = ((data[20] & 0xFF) << 8) | (data[21] & 0xFF);

			int txPower = data[22];

			thing.setTxPower(txPower);
			thing.setMinor(minor + "");
			thing.setMajor(major + "");
			thing.setUuid(sb.toString());
			thing.calculateLocation();

			appState.getUnknownThings().add(thing);
			appState.getUnknownDevices().add(device);

			if(appState.getBeaconScanListAdapter() != null) {
				appState.getBeaconScanListAdapter().notifyDataSetChanged();
			}

			notifyServer(thing, NEW_BEACON_FOUND);
		} catch(Exception e) {
			Toast.makeText(getApplicationContext(), "Beacon gefunden, Parsen der Daten fehlgeschlagen", Toast.LENGTH_SHORT);
		}

	}
};

Der letzte Teil der Methode extrahiert die Informationen aus dem Advertisement und speichert es als neues Beacon ab. Zuerst werden die Hersteller-spezifischen Daten aus dem Advertisement geladen. Mit getManufacturerSpecificData(76) werden nur die Daten von Apple aus dem ScanRecord geladen. 76 ist der Company Identifier für Apple (in Hex 0x004C). Die Daten, die zurück gegeben werden, ist das iBeacon Advertisement, mit insgesamt 23 Bytes. Falls es nicht 23 Bytes sind, wird der Vorgang abgebrochen, da dann nicht alle Informationen gesammelt werden können. Anschließend wird ein neues BeaconThing erstellt um die Informationen zu speichern. Die 16 Bytes ab data[2], also ab dem dritten Byte, entspricht der UUID. Diese wird als Hexadezimal String konkateniert. Anschließend wird aus die Major und Minor aus Byte 18+19 und 20+21 entnommen. Das letzte Byte-Feld data[22] enthält den TxPower Wert. Da ein neues Beacon gefunden wurde, wird der Server informiert, ob Constraints verletzt wurden.

Constraint Request & Response-Handling

Im vorherigen Abschnitt wurde gesagt, dass bei einem Zustandswechsel der Location die Methode notifyServer() aufgerufen wird. Diese Funktionalität wird hier erläutert.

Für die Kommunikation mit dem HTTP-Server der die REST-Schnittstellen bereit stellt, wird die Bibliothek Asynchronous Http Client for Android verwendet. Diese ermöglicht asynchrone HTTP-Anfragen an einen Server zu stellen und die Response mittels eines Callback zu behandeln.

Für die Anfragen werden die Daten per POST übermittelt. Dazu können die Parameter für die Anfrage über die Klasse RequestParams gespeichert werden:

RequestParams params = new RequestParams();
params.add("device_id", deviceId);
params.add("beacon_uuid", thing.getUuid());
params.add("beacon_major", thing.getMajor());
params.add("beacon_minor", thing.getMinor());
params.add("beacon_rssi", thing.getRssi()+"");
params.add("beacon_location", thing.getLocation().name());
params.add("timestamp", new Date().getTime()+"");

String absoluteUrl = serverAddress + "/beacon-state-change";

Hierbei werden die Daten vom gespeicherten BeaconThing-Objekt entnommen und zum Objekt hinzugefügt. Aus den globalen App-Einstellungen wird die Server-Adresse entnommen und die Route zur REST-Schnittstelle angehängt.

Als nächstes erfolgt der einfache Aufruf mittels des AsyncHttpClient-Objekt über die post()-Methode. Dieser wird die URL, die Parameter und ein Response-Handler übermittelt. Praktischerweise gibt es einen bereits implementierten JSON-Response-Handler, welche die Response als JSONObject oder JSONArray bereitstellt.

Für die Antwort wird ein JSON Objekt in folgender Form erwartet. Diese Struktur wird geparst und auf verletzte Constraints untersucht.

{
  "constraint_violated": true oder false,
  "violated_constraints":[
    {
      "constraint_id": constraint_id,
      "message": message
    },
    {
      "constraint_id": constraint_id,
      "message": message
    }
  ]
}

Innerhalb der onSuccess-Methode wird die Response ausgewertet. onSuccess wird aufgerufen, wenn der Statuscode 200 ist. Das JSONObject muss das ein Feld constraint_violated enthalten. Der Wert dafür ist entweder auf true, bei verletzten Constraints, oder auf false gesetzt. Wenn es true ist, enthält das nächste Feld violated_constraints die einzelnen Constraints, die verletzt wurden mit ID und Nachricht. Dazu wird das Array durchlaufen und für jedes verletzte Constraint, ein LogEntry gesetzt um in der App nachzuvollziehen, um welche Beacons es sich handelt. Zum Schluss wird eine Notification erstellt, welches auf das letzte, verarbeitete, Constraint hinweist.

AsyncHttpClient client = new AsyncHttpClient();

client.post(absoluteUrl, params, new JsonHttpResponseHandler() {
    @Override
    public void onSuccess(int statusCode, Header[] headers, JSONObject object) {
        if (object != null) {
            try {
                if(object.has("constraint_violated")) {
                    boolean status = object.getBoolean("constraint_violated");
                    LogEntry entry;

                    if (status) {
                        /* If constraint was violated, then we have to look for each violation */
                        if(!object.has("violated_constraints")) {
                            Log.e("ERROR", "Response von Server enthält nicht alle Daten!");
                            return;
                        }
                        String constraintString = "";
                        JSONArray violatedConstraints = object.getJSONArray("violated_constraints");
                        for (int i = 0; i < violatedConstraints.length(); i++) {

                            JSONObject constraint = (JSONObject) violatedConstraints.get(i);

                            String constId = "";

                            if(constraint.has("constraint_id")) {
                                constId = constraint.getString("constraint_id");
                            }

                            String title = "Constraint " + constId +  " verletzt für ";

                            if(thing.getThingName() == null) {
                                title = title + thing.getDeviceName();
                            } else {
                                title = title + thing.getThingName();
                            }

                            entry = new LogEntry(title, "Ein Constraint wurde verletzt. Der Server hat folgende Meldung mitgeteilt: " + constraint.get("message").toString());
                            appState.getLogList().add(entry);
                            constraintString = constraint.get("message").toString();
                        }

                        if (type.equals(ScanBackgroundService.STATE_CHANGE)) {
                            createNotification("Constraint verletzt!", constraintString, ScanBackgroundService.STATE_CHANGE_NOTIFICATION_ID);
                        } else if (type.equals(ScanBackgroundService.NEW_BEACON_FOUND)) {
                            createNotification("Constraint verletzt!", constraintString, ScanBackgroundService.NEW_BEACON_NOTIFICATION_ID);
                        }
                    }
                }
            } catch(JSONException e) {
                e.printStackTrace();
            }
        }
    }

Wenn der Statuscode über 300 liegt, dann wird eine der entsprechenden Funktionen onFailure aufgerufen. Hierbei wird lediglich ein Log-Eintrag gesetzt, dass die Antwort vom Server nicht ausgewertet werden konnte.

    @Override
    public void onFailure(int statusCode, Header[] headers, String str, Throwable throwable) {
        /* Bei Fehlercode über 300 */
        LogEntry entry = new LogEntry("Fehler bei Server-Antwort", "Server-Antwort hat Status " + statusCode + " zurückgegeben.");
        appState.getLogList().add(entry);
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
        LogEntry entry = new LogEntry("Fehler bei Server-Antwort", "Server-Antwort hat Status " + statusCode + " zurückgegeben.");
        appState.getLogList().add(entry);
    }
});
}

Server

Architektur

Datei Struktur

Für die Umsetzung der REST-Schnittstelle wurde das slim-Framwork verwendet. Aufgrund dessen einfachen Installation, musste lediglich der Ordner „Vendor“ in das Hauptverzeichnis gelegt werden. Des Weiteren war eine Installation des Composers Tools notwendig. Diese hat drei Dateien erzeugt mit dem Namen „composer.json“, „composer.phar“ und „composer.lock“. Für das Webinterface wurden zwei Ordner erzeugt, mit dem Namen „Beacon“ und „Constraints“. In diesen wurden alle notwendigen Interface PHP-Dateien abgelegt:

  • showBeacon
  • newBeacon
  • updateBeacon
  • deleteBeacon

Für ein strukturiertes Webinterface wurde diese in drei Bereiche eingeteilt: „Header“, „Content“ und „Footer“. Diese Struktur wurde im Ordner „Templates“ mit zwei HTML Dateien realisiert. Alle weiteren Layouts wurden in der „style.css“ definiert. Im Ordner „Datenbanken“ wurde die SQLite Datenbank Datei abgelegt und das verwendete NotORM Framework. Weitere Konfiguration des Frameworks war nicht notwendig.

Die gesamte Logik des Servers, welche das Einlesen der Daten, dessen Verarbeitung und Ausgabe wurde in PHP geschrieben. Auch hier wurde getrennt zwischen Beacon und Constraints und jeweils im Ordner „class“ eine Datei angelegt.


REST / Routen

Die REST-Schnittstelle ist (vollständig) in der „Index.php“ umgesetzt. Dort werden sämtliche Instanzen bzw. das Datenbank, Temples und Klassen (Beacon und Constraints) generiert und diese dann an die Routen für die Funktionsaufrufe weitergegeben/durchgereicht. Anschließend werden in den Funktionen, wie in der Schnittstellendefinition festgelegt, die Rückgabewerte im JSON Format codiert und an die App zurück geschickt.

<?php
	/**
		Anforderungen
	*/
	require 'vendor/autoload.php';
	require "datenbank/notorm_framework/NotORM.php";
	require "class/Beacon.php";
	require "class/Constraint.php";

	session_start();
	\Slim\Slim::registerAutoloader();
  

	/**
		Laden von SLIM
	*/  
	$app = new \Slim\Slim([
		'templates.path' => 'templates'
	]);

	/**
		Laden von PHP-Klassen
	*/  
	$beaconObj = new Beacon;
	$constraintObj = new Constraint;

	/**
		Datenbank Verbindung
	*/
	$pdo = new PDO('sqlite:datenbank/idddb_new.sl3');
	$db = new NotORM($pdo);
	...
?>


Die REST-Schnittstelle wird sowohl für die Administration über das Webinterface verwendet als auch für die Kommunikation zwischen dem Smartphone und dem Server. Hierbei ist jedoch ein Konflikt bei der Übergabe der Rückgabewerte im JSON-Formt aufgetreten.

Im Falle, dass der Aufruf vom Webinterface kommt, muss der Rückgabewert als HTTP-Webseite interpretiert werden können. Da sonst diese nicht im Browser dargestellt werden kann.

/**
	Funktion führt eine Aktualisierung eines Beacons durch.
*/
$app->post("/beacon-update_id", function () use ($app, $db, $beaconObj) {
	$beaconID = $app->request()->post();
	echo $beaconObj->updateBeacon($app, $db, $beaconID);
})->name('beacon-update_id');

Wird die Route von der App aufgerufen, findet keine Darstellung der Werte im Browser statt. Daher muss der Rückgabewert ausschließlich im JSON-Format sein und darf keine HTML Quellcode beinhalten. Um dies zu gewährleisten wurde für die Aufrufe von App eine eigene Index.php erstellt. Diese Routen beinhalten alle im Quellcode die Code Zeile:

$app->response()->header("Content-Type", "application/json");

Diese wird vom Slim-Framework bereitgestellt und gewährleistet eine korrekte Codierung.

Constraint-Überprüfung

Erhält der Server von der App eine Constraint-Anfrage wird darauf hin in der Datenbank nach einem Constraint-Eintrag für diesen Beacon gesucht.

Im Falle, dass ein Constraint-Eintrag in der Datenbank für dieses Beacon existiert, wird dessen übermittelte Distanz zum Smartphone mit der maximalen bzw. minimalen Distanz Angabe im Constraint verglichen. Wird diese Über- oder Unterschritten antwortet der Server mit der in der Datenbank gespeicherten Fehlermeldung an die App zurück.

Da ein Constraint Eintrag in der Datenbank immer aus zwei Beacons besteht, welche in einer AND oder OR Verknüpfung zueinander stehen. Besteht jedoch der Anwendungsfall, dass nur ein Constraint gebildet werden muss, welcher nur ein Beacon beinhaltet, so wird das Feld mit der UUID für das zweite Beacon mit dem Wert NULL gefüllt.

Somit muss bei der Constraint-Abfrage aus der Datenbank als erstes unterschieden werden ob die gesendete Beacon-UUID im Constraint als „first-Beacon-ID“ oder als „second-Beacon-ID“ eingetragen wurde.

if($row['b_first_uuid'] == $constraint_table['beacon_uuid']) {
	$const2 = $pdo->query("SELECT * FROM 'constraint' WHERE id = ".$row['b_first_const_id'].";");
} else {
	$const2 = $pdo->query("SELECT * FROM 'constraint' WHERE id = ".$row['b_second_const_id'].";");
}

Bevor der Constraint mittels der arithmetischen Vergleichszeichen {>=, <=} auf Einhaltung geprüft werden kann, muss die Distanz, welche an dieser Stelle noch in der von iBeacon definierten Klasse gespeichert ist, in ein numerisches Zeichen umgewendet werden.

function getLocationIdByName($location) {
	$location_number = 0;
		switch ($location) {
			case "IMMEDIATE":
				$location_number = 1;
				break;
			case "NEAR":
				$location_number = 2;
				break;
			case "FAR":
				$location_number = 3;
				break;
			case "UNKNOWN":
				$location_number = 4;
				break;
		}
	return $location_number;
}

Anschliessend wird die im Constraint gespeicherte Distanz und das Vergleichszeichen mit der übermittelten Distanz Verglichen. Sollte diese nicht übereinstimmen bzw. Verletzt werden, wird die boolesche Konstante "false" zurück gegeben.

function checkConstraint($new_location, $const_type, $const_location) {
	if($const_type == "less_equal") {
		return $new_location <= $const_location;
	} else if($const_type == "greater_equal") {
		return $new_location >= $const_location;
	} else {
		return $true;
	}
}


Im Falle, dass es sich um die first-Beacon-ID handelt wird anschliessend geprüft ob der gespeicherte Constraint eingehalten wird oder nicht. Ebenso sollte es sich um die second-Beacon-ID handeln.


/* Überprüfe die Relation, also das zweite Beacon */
if($row['b_second_uuid'] != NULL) {
	try {
	if($row['b_first_uuid'] == $const['beacon_uuid']) {
		/* First beacon ist das gemeldete Beacon, das heißt das zweite Beacon ist unser anderes */
		$stmt = $pdo->query("SELECT * FROM 'constraint' WHERE id = ".$row['b_second_const_id'].";");
		if($stmt == false) throw new Exception("SELECT FROM constraint ist fehlgeschlagen");
		$const_relation = $stmt->fetch(PDO::FETCH_ASSOC);
		
		$stmt = $pdo->query("SELECT * FROM beacon WHERE beacon.beacon_uuid = '".$row['b_second_uuid']."';");
		if($stmt == false) throw new Exception("SELECT FROM beacon ist fehlgeschlagen");
		$beacon = $stmt->fetch(PDO::FETCH_ASSOC);
	} else {
		
		/* First beacon ist nicht das gemeldete, also ist das, dass andere beacon. */
		$stmt = $pdo->query("SELECT * FROM 'constraint' WHERE id = ".$row['b_first_const_id'].";");
		if($stmt == false) throw new Exception("SELECT FROM constraint ist fehlgeschlagen");
		$const_relation = $stmt->fetch(PDO::FETCH_ASSOC);
		
		$stmt = $pdo->query("SELECT * FROM beacon WHERE beacon.beacon_uuid = '".$row['b_first_uuid']."';");
		if($stmt == false) throw new Exception("SELECT FROM beacon ist fehlgeschlagen");
		$beacon = $stmt->fetch(PDO::FETCH_ASSOC);
	}

Zum Schluss werden die abgefragten Constraints noch auf Einhaltung der Relation untereinander geprüft.

if($row['relation_type'] == "AND") {
	$result = $first_const && $second_const;
} else if($row['relation_type'] == "OR") {
	$result = $first_const || $second_const;
} else {
	$result = $first_const;
}

Ist einer der geprüften Constraints fehlgeschlagen, so wird die Fehlermeldung aus der Datenbank im JSON-encode gesichert und an die App zurück geschickt.

if(!$result) {
	array_push($violated_constraints, array(
		"constraint_id" => $row['id'],
		"message" => $row['message']
	));
}

Datenbank

ER-Diagram von Constraints

Evaluation

Die Ermittlung der Location innerhalb von Räumen erwies sich aber stark schwankend. Eine kleine Veränderung der Richtung vom Smartphone oder Positionsänderungen hat für kurze Zeit die Distanz teilweise auf UNKNOWN gesetzt.

Für eine genauere Evaluation haben wir die berechnete Distanz zu den RSSI-Werten zugeordnet und mit den realen Werten verglichen. Dazu haben wir ein Diagramm aufgestellt, in welche beide Messwerte dargestellt sind.

Vergleich der Distanz-Werte

Es wurden die RSSI-Werte zu folgenden Distanzen ermittelt: 0.25, 0.5, 1m, 2m, 3m, 4m, 5m, 6m, 7m, 8m, 9m, 10m, 12m, 14m, 16m, 18m, 20m, 22m, 24m, 26m, 28m, 30m. Dazu wurde zu jedem richtigen Wert, auch der berechnete Wert dazu gestellt. Die Messung wurde in freier Umgebung durchgeführt bei regnerischem Wetter.

In der Abbildung sind zwei Kurven dargestellt. Die blaue Kurve stellt den Verlauf des RSSI-Wertes bei zunehmender Entfernung zum Beacon dar. Die Signalstärke bricht schon in den ersten zehn Metern deutlich ab, verläuft aber für die nachfolgenden 20 Metern ohne starke Schwankungen. Zwischen 15 und 20m kann man aber erkennen, dass die Signalstärke trotz größerer Entfernung, wieder besser wird.

Die orange Kurve stellt die gemessene Distanz zum RSSI-Wert dar. In den ersten 10 Metern wurde eine höhere Distanz gemessen. Dadurch ist die Kurve leicht nach rechts verschoben. Ab 15 Metern sind die gemessene Distanz-Werte deutlich abweichend von den tatsächlichen Distanz-Werten.

Bei dem Bluetooth LE Scan ist uns aufgefallen, dass ein minimales Intervall von 3 Sekunden notwendig ist. Wenn das Intervall kleiner ist, wird ein laufender Scan abgebrochen und neugestartet. Dazu kommt der Scan und die Anfrage am Server zu keinem Ergebnis.

Fazit & Ausblick

Unser Projektziel war die Entwicklung einer Android-App und eines externen Webservices, um einerseits Bluetooth LE Beacons zu orten und Constraints auf die Location der Beacons zu setzen.

Obwohl keine Erfahrung in der Entwicklung von Android-Applikationen vorhanden war, konnte ein schneller Einstieg in die Implementierung einer Android-App erreicht werden. Dies war durch die umfangreiche Dokumentation von Android und vor allem für Bluetooth LE möglich. Es hat sich herausgestellt, dass die Lokalisierung über BLE nicht trivial ist und es per RSSI keine direkte Überführung in die Distanz gibt. Um genaue Distanz-Angaben zu erhalten, müsste man das Beacon und das Handy auf bestimmte Entfernungen kalibrieren, um so eine gute Funktionskurve, anhand der die Berechnung durchgeführt wird, zu erhalten. Allerdings, löst dies nicht das Problem der Schwankungen in der Signalstärke. Durch externe Einflüsse wie Räumlichkeit oder andere Signale kann der RSSI-Wert sehr leicht abweichen. Wie am Ende auch erkannt wurde, variiert der RSSI-Wert sogar bei leichten Veränderungen der Position oder Haltung des Smartphones. Ein weiterer Ansatz für die App könnte die Unterstützung von weiteren Beacon-Typen sein. Es werden nur iBeacons unterstützt, aber Eddystone oder Altbeacons könnten für den Anwendungsfall ebenfalls genutzt werden.

Da auch keine vorab Kenntnisse in der Umsetzung einer REST-Schnittstelle existierten, wurde das stark abstrahierte Slim-Framework verwendet. Dies stellt zwar eine sehr detaillierte API bereit jedoch keine Tutorials zur Anwendung. Dies hat sehr viel Zeit und Mühe gefordert eine stabile Platform auf den Servern der Hochschule umzusetzen.

Während des Projektes wurde erkannt, dass die Definition und Implementierung von Constraints und deren Prüfung relativ umfangreich sein kann. Es wurden Constraints sowohl für ein einzelnes als auch für Paare von Beacons implementiert. Des Weiteren kann derzeit nur über die Operationen AND und OR Beacons verknüpft werden. Eine mögliche Erweiterung von Constraints könnte das Einstellen von n Beacons sein sowie Abhängigkeiten in der Form "Wenn Constraint A UND B verletzt sind, führe Operation X aus". Ebenso könnte man das System so erweitern, dass es anhand von zwei vom Benutzer definierten Constraints A und B selbstständig auf eine drittes schlissen kann. Ebenso wäre vorstellbar Constraints für das Bilden von manuellen Constraints einzuführen um so sowohl Redundante als auch Widersprüchliche Aussagen vorab zu prüfen. Eine weitere Option wäre die Implementierung von einfachen Constraints auf der App-Seite um keinen externen Webservice für die Constraint Überprüfung zu nutzen.

Aufgrund von Zeitmangel wurde bei der Implementierung der Server-Seite auf eine Benutzer Anmeldung verzichtet. Jedoch auch hier wäre vorstellbar dem Nutzer eine Administration seiner Beacons und Smartphone zu ermöglichen. Somit würde mit Hilfe von z.B. Constraint-Vorlagen die Benutzerfreundlichkeit zur Constraint-Erstellung steigen.

Referenzen

  1. https://www.bluetooth.com/specifications/bluetooth-core-specification/technical-considerations Aufgerufen am 07.02.2016
  2. https://en.wikipedia.org/wiki/Bluetooth_low_energy Aufgerufen am 07.02.2016
  3. https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx Aufgerufen am 07.02.2016
  4. https://en.wikipedia.org/wiki/IBeacon Aufgerufen am 07.02.2016
  5. https://developer.apple.com/ibeacon/ Aufgerufen am 07.02.2016
  6. http://stackoverflow.com/questions/18906988/what-is-the-ibeacon-bluetooth-profile Aufgerufen am 11.02.2016
  7. https://developers.google.com/beacons/ Aufgerufen am 07.02.2016
  8. http://altbeacon.org/ Aufgerufen am 07.02.2016
  9. https://github.com/AltBeacon Aufgerufen am 11.02.2016]
  10. http://altbeacon.github.io/android-beacon-library/distance-calculations.html Aufgerufen am 07.02.2016
  11. http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing/20434019#20434019 Aufgerufen am 07.02.2016
  12. https://developer.apple.com/ibeacon/Getting-Started-with-iBeacon.pdf Aufgerufen am 07.02.2016
  13. http://www.springer.com/us/book/9783540231844 Einführung in die Constraint-Programmierung
  14. http://www.springer.com/us/book/9783540231844 Einführung in die Constraint-Programmierung
  15. http://jpkc.fudan.edu.cn/picture/article/216/35/4b/22598d594e3d93239700ce79bce1/7ed3ec2a-03c2-49cb-8bf8-5a90ea42f523.pdf Aufgerufen am 10.02.2016
  16. http://developer.android.com/guide/topics/connectivity/bluetooth-le.html Aufgerufen am 08.02.2016
  17. http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing/20434019#20434019 Distance Calculation