P21 Balise Bluetooth Low Energy : Différence entre versions
(→Journal de bord) |
|||
(10 révisions intermédiaires par un autre utilisateur non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
+ | <br style="clear: both;"/> | ||
+ | <include nopre noesc src="/home/pedago/pimasc/include/video-BaliseBLE-iframe.html" /> | ||
+ | __TOC__ | ||
+ | <br style="clear: both;"/> | ||
== Objectifs du projet == | == Objectifs du projet == | ||
Notre PFE est une partie importante d'un projet de paiement sans contact. Ce nouveau moyen de paiement utilise la technologie Bluetooth Low Energy et un serveur GATT pour fonctionner. Le principe de ce nouveau moyen de paiement est simple, l'utilisateur aura son mobile sur lui muni de l'application de paiement. Un serveur GATT sera lancé à proximité des caisses et présentera des services de paiements. Le client enverra automatiquement ses données vers le serveur GATT au moment de payer. Une confirmation de paiement sera par la suite envoyé vers le serveur GATT après que les données utilisateurs aient été envoyées. | Notre PFE est une partie importante d'un projet de paiement sans contact. Ce nouveau moyen de paiement utilise la technologie Bluetooth Low Energy et un serveur GATT pour fonctionner. Le principe de ce nouveau moyen de paiement est simple, l'utilisateur aura son mobile sur lui muni de l'application de paiement. Un serveur GATT sera lancé à proximité des caisses et présentera des services de paiements. Le client enverra automatiquement ses données vers le serveur GATT au moment de payer. Une confirmation de paiement sera par la suite envoyé vers le serveur GATT après que les données utilisateurs aient été envoyées. | ||
Ligne 95 : | Ligne 99 : | ||
Plusieurs activités ont été ajouté pour pouvoir remplir les objectifs de la deuxième partie du projet à savoir : | Plusieurs activités ont été ajouté pour pouvoir remplir les objectifs de la deuxième partie du projet à savoir : | ||
− | ==== | + | ====Envoi manuel==== |
L'utilisateur peut choisir lui même sur quelle caractéristique écrire. L'envoie est déclenché par l'utilisateur par l'appui sur le bouton write de la figure suivante : | L'utilisateur peut choisir lui même sur quelle caractéristique écrire. L'envoie est déclenché par l'utilisateur par l'appui sur le bouton write de la figure suivante : | ||
Ligne 105 : | Ligne 109 : | ||
[[Fichier:Info.png|200px]] | [[Fichier:Info.png|200px]] | ||
− | ==== | + | ====Envoi automatique==== |
Si l'utilisateur clic sur le mode automatique, le service de la raspberry présentant la propriété "écriture" sera automatiquement sélectionnée et les données de l'utilisateur saisie dans l'activité de saisie des données seront automatiquement envoyées. | Si l'utilisateur clic sur le mode automatique, le service de la raspberry présentant la propriété "écriture" sera automatiquement sélectionnée et les données de l'utilisateur saisie dans l'activité de saisie des données seront automatiquement envoyées. | ||
− | ==== | + | ====Bench==== |
Dans ce mode, on peut effectuer des tests d'écriture sur la raspberry. On peut choisir la taille des paquets de données à envoyer et le nombre de fois que l'on souhaite l'envoyer. Comme le montre la vue de l'activité correspondante. | Dans ce mode, on peut effectuer des tests d'écriture sur la raspberry. On peut choisir la taille des paquets de données à envoyer et le nombre de fois que l'on souhaite l'envoyer. Comme le montre la vue de l'activité correspondante. | ||
Ligne 119 : | Ligne 123 : | ||
On voit que sur les tests effectués, la taille des paquets de données influe grandement sur la vitesse. Cet écart de vitesse est due au fait que lorsque l'on envoie les informations par petits paquets de données, on fait plus souvent appel à la fonction d'écriture. Il vaut donc mieux envoyer les données par segments de 20. | On voit que sur les tests effectués, la taille des paquets de données influe grandement sur la vitesse. Cet écart de vitesse est due au fait que lorsque l'on envoie les informations par petits paquets de données, on fait plus souvent appel à la fonction d'écriture. Il vaut donc mieux envoyer les données par segments de 20. | ||
− | ==== | + | ====Envoie d'une photo vers le serveur GATT==== |
==Journal de bord== | ==Journal de bord== | ||
Ligne 352 : | Ligne 356 : | ||
'''Après vacance/15janvier/Semaine12''' | '''Après vacance/15janvier/Semaine12''' | ||
− | < | + | <ul> |
− | <li> | + | <li>Rendez-vous avec nos tuteurs entreprises pour faire le point et discuter de la suite su projet</li> |
− | <li>Discussion sur un moyen de gérer la connexion multiple | + | <li>Discussion sur un moyen de gérer la connexion multiple</li> |
<li>Envoi d'une photo</li> | <li>Envoi d'une photo</li> | ||
<li>Service Android pour le lancement automatique de l'application</li> | <li>Service Android pour le lancement automatique de l'application</li> | ||
− | </ | + | </ul> |
+ | |||
'''Semaine 13''' | '''Semaine 13''' | ||
− | + | <ul> | |
− | + | <li>Recherche sur le choix d'une interface plus facile d'utilisation</li> | |
+ | <li>Recherche sur la génération d'uuid unique côté Raspberry</li> | ||
+ | </ul> | ||
+ | |||
'''Semaine 14''' | '''Semaine 14''' | ||
− | + | <ul> | |
− | + | <li>Changement de l'interface avec nouvelle vue où l'utilisateur peut enregistrer ses informations : nom,prénom,mail ( encore présent lors fermeture/réouverture de l'application)</li> | |
+ | <li>Recherche sur les Services Android</li> | ||
+ | </ul> | ||
+ | |||
'''Semaine 15''' | '''Semaine 15''' | ||
− | + | <ul> | |
− | + | <li>Intégration d'un bouton qui envoi les informations automatiquement vers la raspberry lors du clic puis déconnexion automatique</li> | |
+ | <li>Intégration d'un bouton qui envoi les informations manuellement vers la raspberry : sélection manuelle de la raspberry / sélection manuelle du service et de la caractéristique / clic sur bouton "Write"</li> | ||
+ | </ul> | ||
'''Semaine 16''' | '''Semaine 16''' | ||
− | + | <ul> | |
− | + | <li>Rechercher sur une manière de choisir une photo et de l'enregistrer même lorsque l'application est fermée.</li> | |
+ | <li>Intégration de deux boutons Start Service / Stop Service</li> | ||
+ | <li>Recherche sur une façon de générer des UUID unique côté Raspberry</li> | ||
+ | </ul> | ||
+ | '''Semaine 17''' | ||
− | '''Semaine | + | <ul> |
+ | <li>Nouveau bouton "Sélectionner une photo" dans la vue "Mes informations" qui permet de choisir une photo et de l'enregistrer</li> | ||
+ | <li>Recherche sur un moyen d'envoyer la photo(donnée de grande taille)</li> | ||
+ | <li>Utilisation d'un module Nodejs "node-uuid" pour générer des UUID aléatoires</li> | ||
+ | </ul> | ||
+ | |||
+ | |||
+ | '''Semaine 18''' | ||
+ | |||
+ | <ul> | ||
+ | <li>Encodage de la photo en format Base64String</li> | ||
+ | <li>Découpage du String en paquets de String de 20 octets / Envoi vers la raspberry par boucle ce ces paquets</li> | ||
+ | <li>Le bouton "Start Service" permet de faire en sorte d’exécuter l'analyse des appareils en arrière-plan et rend possible la communication autonome avec le réseau blueooth low energy environnant le mobile </li> | ||
+ | <li>Continue les recherches sur "node-uuid"</li> | ||
+ | </ul> | ||
+ | |||
+ | |||
+ | '''Semaine 19''' | ||
+ | |||
+ | <ul> | ||
+ | <li>Modification du script Nodejs pour enregistrer directement dans un fichier les informations envoyées</li> | ||
+ | <li>Récupération dans le fichier du nom/prenom/mail et du String de l'image</li> | ||
+ | <li>Décodage du String et reproduction de la photo initiale envoyée grâce au site web [http://www.askapache.com/online-tools/base64-image-converter/ Décodage Base64String <->Image]</li> | ||
+ | <li>L'envoi d'une image est possible si l'image de la taille est petite (75x75 pixels). Si cette taille est excédée, l'image est mal envoyée</li> | ||
+ | </ul> | ||
+ | |||
+ | '''Semaine 20''' | ||
+ | |||
+ | <ul> | ||
+ | <li>Préparation du rapport</li> | ||
+ | <li>Réalisation de la vidéo</li> | ||
+ | <li>Préparation de la soutenance</li> | ||
+ | </ul> | ||
== Bibliographie et liens == | == Bibliographie et liens == |
Version actuelle datée du 11 mars 2015 à 14:44
Sommaire
Objectifs du projet
Notre PFE est une partie importante d'un projet de paiement sans contact. Ce nouveau moyen de paiement utilise la technologie Bluetooth Low Energy et un serveur GATT pour fonctionner. Le principe de ce nouveau moyen de paiement est simple, l'utilisateur aura son mobile sur lui muni de l'application de paiement. Un serveur GATT sera lancé à proximité des caisses et présentera des services de paiements. Le client enverra automatiquement ses données vers le serveur GATT au moment de payer. Une confirmation de paiement sera par la suite envoyé vers le serveur GATT après que les données utilisateurs aient été envoyées.
Dans un contexte réel de paiement, le système doit pouvoir gérer plusieurs utilisateurs. Ainsi le comportement du système aura un comportement décrit par le diagramme de séquence suivant :
Le matériel et la technologie utilisée
Les beacons sont les dispositifs standards pour la localisation en intérieur. Le problème avec ce matériel est qu'il ne peut qu'émettre des données. Il ne permet pas de recevoir les informations provenant des appareils mobiles(typiquement l'UUID du téléphone). Or la réception des informations mobiles est importante pour pouvoir être collecté par l'ordinateur distant. On utilisera donc des Raspberry Pi munis d'un dongle Bluetooth BLE avec une stack bluez pour pouvoir recevoir des données venant des appareils mobiles appariés et les remonter vers l'ordinateur. Enfin, on va utiliser du Bluetooth low energy pour sa faible consommation électrique et sa légèreté par rapport au Bluetooth classique. Le tableau ci-dessous résume les différences entre le Bluetooth LE et le Bluetooth classique.
Bluetooth classique | Bluetooth LE | |
---|---|---|
Distance | 100 m | <100 m |
vitesse de transmission | 0.7 / 2.1 Mbits/s | 0.27 Mbits/s |
temps de latence (d'un état non connecté à un état connecté) | 100 ms | 6 ms |
Nombre d'esclaves | 7 | Dépend de l'implémentation |
temps pour envoyer des données | 100 ms | 3 ms |
pic de courant | <30 mA | <15 mA |
Puissance consommée | 1 W | 0.01 W à 0.5 W |
taille des données | 31 octets (20 octets par défaut ) | |
Le bluetooth LE utilise la bande de fréquence 2.4 Ghz chaque canal est séparé par 2 Mhz
Les fréquences en vert sont utilisées pour transmettre les trames d'advertising.
Une trame Bluetooth LE a le profile suivant.
On constate la charge utile dans une trame est de 66% (donnée/longeur_totale = 31/47 = 0.66)
On se servira du champs contenant la puissance du signal pour estimer la distance à laquelle l'utilisateur se trouve de la balise.
Développement de l'application côté serveur
La connexion de plusieurs mobiles sur un seul serveur GATT est impossible, car le mobile est considéré comme un "master" et le serveur GATT est considéré comme un "slave", or il ne peut y avoir qu'un seul device "master" dans une connexion. L'implémentation qui a été choisie pour contourner le problème est la suivante :
Lors de la découverte des services présentées par la raspberry, le mobile se connecte automatiquement au service de la raspberry envoie ses données. Du côté de la raspberry, un UUID aléatoire est généré. Une fois l'UUID reçu la connexion est rompue et le mobile se connecte sur un service d'écriture qui a pour UUID l'UUID aléatoire transmis par la raspberry juste avant d'avoir rompu la connexion et envoie une confirmation de paiement.
Envoie simple d'une chaîne de caractère
Comme annoncé, on utilise une Raspberry Pi munie du système d'exploitation Raspbian wheezy et de la stack bluetooth bluez. On rajoute dessus le serveur GATT bleno. Au démarrage de la raspberry, on active le dongle bluetooth à l'aide de la commande : hciconfig hi0 up ensuite, on lance le script du serveur. Ce dernier permet d'envoyer des trames d'advertising tant qu'une connexion n'est pas effectuée avec un périphérique. Lorsque l'on écrit sur le serveur, on récupère directement sur le terminal les données envoyées par le périphérique. (Voir section sur le développement côté mobile)
Ecriture sur un fichier
Lorsque les données sont envoyées vers la Raspberry, les données sont affichées sur le terminal mais aussi sauvegardées dans un fichier.
Generation des UUID aléatoires
A chaque écriture sur la raspberry, un nouvel UUID aléatoire est généré. Le changement d'UUID du service d'écriture n'est pour l'instant pas géré
Développement de l'application mobile
L'application mobile permet à l'utilisateur de découvrir les devices bluetooth aux alentours. Ils apparaissent à l'écran sous forme d'une liste déroulante. L'utilisateur peut alors cliquer sur le device de son pour pouvoir s'y connecter. Après la connexion, l'utilisateur a le choix entre plusieurs profile (correspond aux devices bluetooth). Ces profiles implémentent plusieurs services qui eux même implémentent plusieurs caractéristiques comme le montre la figure ci dessous.
Plusieurs activités ont été ajouté pour pouvoir remplir les objectifs de la deuxième partie du projet à savoir :
Envoi manuel
L'utilisateur peut choisir lui même sur quelle caractéristique écrire. L'envoie est déclenché par l'utilisateur par l'appui sur le bouton write de la figure suivante :
Les données qui sont alors envoyées sont des données que l'utilisateur aura saisies une fois dans une vue activité prévue à cet effet. Une vue de cette activité est donnée par la figure suivante :
Envoi automatique
Si l'utilisateur clic sur le mode automatique, le service de la raspberry présentant la propriété "écriture" sera automatiquement sélectionnée et les données de l'utilisateur saisie dans l'activité de saisie des données seront automatiquement envoyées.
Bench
Dans ce mode, on peut effectuer des tests d'écriture sur la raspberry. On peut choisir la taille des paquets de données à envoyer et le nombre de fois que l'on souhaite l'envoyer. Comme le montre la vue de l'activité correspondante.
On voit que sur les tests effectués, la taille des paquets de données influe grandement sur la vitesse. Cet écart de vitesse est due au fait que lorsque l'on envoie les informations par petits paquets de données, on fait plus souvent appel à la fonction d'écriture. Il vaut donc mieux envoyer les données par segments de 20.
Envoie d'une photo vers le serveur GATT
Journal de bord
25 Septembre 2014
Définition des objectifs du projet avec les Encadrants. Récupération d'une parti du matériel nécessaire
Semaine 1
Compréhension de la mise en marche de la RaspberryPi.
Installation du système d'exploitation RASPBIAN sur la carte SD.
Connection en SSH sur la Raspberry grâce au port Ethernet avec le logiciel Putty.
Semaine 2
Installation de la stack Bluetooth Bluez sur le système Raspbian.
Prise en main des commandes Bluez
Semaine 3
Mise en fonctionnement de la RaspberryPi en tant que iBeacon à l'aide des commandes Bluez et visualisation sur une application Android "nRF Master Control Panel".
Mise en fonctionnement de la RaspberryPi en tant que iBeacon à l'aide d'un programme javascript (Node.js).
Fichier adv.js :
<source lang="javascript">
var uuid = 'e2c56db5dffb48d2b060d0f5a71096e0'; var major = 0; var minor = 0; var meaquredPower = -59; bleno.startAdvertisingIBeacon(uuid, major, minor, measuredPower);
</source>
Après exécution de ce programme sur le terminal de la Raspberry, on récupère sur la même application :
Semaine 4
Recherche sur le développement de l'application mobile et recherche sur la création d'un serveur GATT côté Raspberry.
Semaine 5
Pour créer des périphériques BLE disposant de services , nous utilisons bleno, un module Nodejs. Voici le programme serveur utilisé :
var util = require('util'); var bleno = require('bleno'); var BlenoPrimaryService = bleno.PrimaryService; var BlenoCharacteristic = bleno.Characteristic; var BlenoDescriptor = bleno.Descriptor; console.log('bleno'); var StaticReadOnlyCharacteristic = function() { StaticReadOnlyCharacteristic.super_.call(this, { uuid: 'fffffffffffffffffffffffffffffff1', properties: ['read'], value: new Buffer('armagan'), descriptors: [ new BlenoDescriptor({ uuid: '2901', value: 'user description' }) ] }); }; util.inherits(StaticReadOnlyCharacteristic, BlenoCharacteristic);
var DynamicReadOnlyCharacteristic = function() { DynamicReadOnlyCharacteristic.super_.call(this, { uuid: 'fffffffffffffffffffffffffffffff2', properties: ['read'] }); }; util.inherits(DynamicReadOnlyCharacteristic, BlenoCharacteristic); DynamicReadOnlyCharacteristic.prototype.onReadRequest = function(offset, callback) { var result = this.RESULT_SUCCESS; var data = new Buffer('kevin'); if (offset > data.length) { result = this.RESULT_INVALID_OFFSET; data = null; } callback(result, data); };
var WriteOnlyCharacteristic = function() { WriteOnlyCharacteristic.super_.call(this, { uuid: 'fffffffffffffffffffffffffffffff3', properties: ['write', 'writeWithoutResponse'] }); }; util.inherits(WriteOnlyCharacteristic, BlenoCharacteristic); WriteOnlyCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) { console.log('WriteOnlyCharacteristic write request: ' + data.toString() + ' ' + offset + ' ' + withoutResponse); callback(this.RESULT_SUCCESS); }; var NotifyOnlyCharacteristic = function() { NotifyOnlyCharacteristic.super_.call(this, { uuid: 'fffffffffffffffffffffffffffffff4', properties: ['notify'] }); }; util.inherits(NotifyOnlyCharacteristic, BlenoCharacteristic); NotifyOnlyCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) { console.log('NotifyOnlyCharacteristic subscribe'); this.counter = 0; this.changeInterval = setInterval(function() { var data = new Buffer(4); data.writeUInt32LE(this.counter, 0); console.log('NotifyOnlyCharacteristic update value: ' + this.counter); updateValueCallback(data); this.counter++; }.bind(this), 5000); }; NotifyOnlyCharacteristic.prototype.onUnsubscribe = function() { console.log('NotifyOnlyCharacteristic unsubscribe'); if (this.changeInterval) { clearInterval(this.changeInterval); this.changeInterval = null; } }; NotifyOnlyCharacteristic.prototype.onNotify = function() { console.log('NotifyOnlyCharacteristic on notify'); }; function SampleService() { SampleService.super_.call(this, { uuid: 'fffffffffffffffffffffffffffffff0', characteristics: [ new StaticReadOnlyCharacteristic(), new DynamicReadOnlyCharacteristic(), new WriteOnlyCharacteristic(), new NotifyOnlyCharacteristic ] }); }
util.inherits(SampleService, BlenoPrimaryService); bleno.on('stateChange', function(state) { console.log('on -> stateChange: ' + state); if (state === 'poweredOn') { bleno.startAdvertising('RaspberryPi', ['fffffffffffffffffffffffffffffff0']); } else { bleno.stopAdvertising(); } }); // Linux only events ///////////////// bleno.on('accept', function(clientAddress) { console.log('on -> accept, client: ' + clientAddress); if (bleno.updateRssi) { bleno.updateRssi(); } }); bleno.on('disconnect', function(clientAddress) { console.log('on -> disconnect, client: ' + clientAddress); }); bleno.on('rssiUpdate', function(rssi) { console.log('on -> rssiUpdate: ' + rssi); }); ////////////////////////////////////// bleno.on('advertisingStart', function(error) { console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success')); if (!error) { bleno.setServices([ new SampleService() ]); } }); bleno.on('advertisingStop', function() { console.log('on -> advertisingStop'); }); bleno.on('servicesSet', function() { console.log('on -> servicesSet'); });
Semaine 6
Après des recherches à propos du Bluetooth Low Energy sur Android, nous nous sommes basés sur le projet opensource disponible ici : [[1]]
Ce projet-ci permet de base de détecter les appareils BLE et de se connecter sur l'un de ces appareils. Après connection, nous avons accès aux services et aux caractéristiques disponible. Seulement la fonctionnalité Read est implémentée dans l'application. Notre but étant de pouvoir envoyer des informations vers le serveur GATT de la Raspberry, c'est à dire écrire (Write) sur les caractéristiques des services proposées par le serveur.
Semaine 7
Implémentation de la fonctionnalité "Ecrire" (Write) dans l'application. Selon les propriétés des caractéristiques proposées , un bouton Write ou Read apparaît. Lors de l'appui sur le bouton Write, une fenêtre de dialoge s'ouvre où l'utilisateur peut renseigner son nom et prénom et envoyer ses informations vers la Raspberry. Lors de l'appui sur le bouton Read, la valeur de la caractéristique d'affiche.
Semaine 8
Utilisation d'une librairie iBeacon sur Android Studio afin de lancer l'application automatiquement sans intervention de l'utilisateur.
Pour cela nous utilisons les iBeacons de Gimbal disponible gratuitement ici : [[2]]
Semaine 9
Après compréhension de la librairie d'écoute des beacons, nous avons spécifié tout d'abord l'uuid de la beacon Gimbal afin qu'elle réveille notre application lorsqu'elle est à portée du portable. Nous avons par la suite trouvé plus convenient de réveiller l'application par le RaspberryPi mais il a fallu modifié le programme Nodejs: StartAdvertisingIBeacon plutôt que StartAdvertising, cette librairie faisant l'écoute d'iBeacon seulement.
Semaine 10
Impossible de connecter plusieurs appareils en même temps. premiers bench de transmission sur la raspberry réalisé.
Semaine 11
Préparation du rapport/soutenance de mi projet
Après vacance/15janvier/Semaine12
- Rendez-vous avec nos tuteurs entreprises pour faire le point et discuter de la suite su projet
- Discussion sur un moyen de gérer la connexion multiple
- Envoi d'une photo
- Service Android pour le lancement automatique de l'application
Semaine 13
- Recherche sur le choix d'une interface plus facile d'utilisation
- Recherche sur la génération d'uuid unique côté Raspberry
Semaine 14
- Changement de l'interface avec nouvelle vue où l'utilisateur peut enregistrer ses informations : nom,prénom,mail ( encore présent lors fermeture/réouverture de l'application)
- Recherche sur les Services Android
Semaine 15
- Intégration d'un bouton qui envoi les informations automatiquement vers la raspberry lors du clic puis déconnexion automatique
- Intégration d'un bouton qui envoi les informations manuellement vers la raspberry : sélection manuelle de la raspberry / sélection manuelle du service et de la caractéristique / clic sur bouton "Write"
Semaine 16
- Rechercher sur une manière de choisir une photo et de l'enregistrer même lorsque l'application est fermée.
- Intégration de deux boutons Start Service / Stop Service
- Recherche sur une façon de générer des UUID unique côté Raspberry
Semaine 17
- Nouveau bouton "Sélectionner une photo" dans la vue "Mes informations" qui permet de choisir une photo et de l'enregistrer
- Recherche sur un moyen d'envoyer la photo(donnée de grande taille)
- Utilisation d'un module Nodejs "node-uuid" pour générer des UUID aléatoires
Semaine 18
- Encodage de la photo en format Base64String
- Découpage du String en paquets de String de 20 octets / Envoi vers la raspberry par boucle ce ces paquets
- Le bouton "Start Service" permet de faire en sorte d’exécuter l'analyse des appareils en arrière-plan et rend possible la communication autonome avec le réseau blueooth low energy environnant le mobile
- Continue les recherches sur "node-uuid"
Semaine 19
- Modification du script Nodejs pour enregistrer directement dans un fichier les informations envoyées
- Récupération dans le fichier du nom/prenom/mail et du String de l'image
- Décodage du String et reproduction de la photo initiale envoyée grâce au site web Décodage Base64String <->Image
- L'envoi d'une image est possible si l'image de la taille est petite (75x75 pixels). Si cette taille est excédée, l'image est mal envoyée
Semaine 20
- Préparation du rapport
- Réalisation de la vidéo
- Préparation de la soutenance
Bibliographie et liens
- Getting Started with Bluetooth Low Energy, Tools and Techniques for Low-Power Networking By Kevin Townsend, Carles Cufí, Akiba, Robert Davidson, Publisher: O'Reilly Media [3]
- Adafruit, pibeacon [4]
Matériel
* 1 RaspberryPi * 1 Dongle Bluetooth 4.0 * 1 Samsung Galaxy Tab4 * 1 Portable Samsung GS3