TP05 : Station météo BLE (observer)
Dans ce TP, nous réalisons le deuxième composant de notre station météo BLE. Pour ce scénario, le système est composé de deux appareils (cibles) :
- une cible se charge de prendre des mesures environnementales et de les diffuser (broadcast) avec le BLE;
- une deuxième cible reçoit ou observe les mesures diffusées par BLE et les affiche sur l’écran.
Objectifs du TP
Ce TP a pour but de réaliser un observer BLE.
À la fin de ce TP, les étudiants :
- auront intégré le scanning BLE des données environnementales afin d’afficher les données reçues sur un écran LCD ;
- auront réalisé des analyses statiques de code et corrigé les erreurs rapportées ;
- auront réalisé des tests de connectivité BLE relatifs à la portée;
- auront rédigé un journal de travail et déposé le PDF sur Teams.
Les livrables sont :
- un release avec le tag “tp05” doit être créé sur votre dépôt.
- la correction des issues des TP 03 et TP 04.
- un journal de travail déposé sur Teams.
Temps accordé : 4 périodes de 45 minutes en classe + travail personnel à la maison
Délai
Les dates de rendu des TPs sont indiquées sur le plan de cours.
Matériel
Pour ce TP, vous utilisez le même matériel que celui du TP 03. Vous devez faire fonctionner le programme broadcaster sur une cible et le programme observer sur une autre cible.
Réalisation
La classe BLEObserver
La classe BLEObserver
est utilisée par l’application afin de scanner les
données de capteurs qui sont émises par un broadcaster. Cette classe
représente donc un rôle d’observer au sens de la terminologie BLE.
Vous devez créer la classe BLEObserver
avec les caractéristiques suivantes
:
- La classe hérite de la classe
GAPDevice
. - La classe redéfinit la méthode abstraite privée
onInitComplete
. Vous devez ajouter le mot cléoverride
à la déclaration de la méthode. - La classe définit une méthode privée
startScanning
qui est appelée lorsque l’initialisation du dispositif a été effectuée avec succès - de manière similaire à la méthodestartAdvertising
de la classeBLEBroadcaster
- La classe redéfinit la méthode
onAdvertisingReport
de la classeGap::EventHandler
. Vous devez ajouter le mot cléoverride
à la déclaration de la méthode. Cette méthode est appelée par l’API BLE lorsque le dispositif scanne et reçoit des paquets d’advertising. L’application peut ainsi réaliser un comportement spécifique en fonction des paquets d’advertising reçus. - La classe définit un constructeur qui reçoit en paramètre une référence vers
une instance de
DataMailbox
. La classe contient également un attribut pour sauvegarder cette référence et l’utiliser dans différentes méthodes.
La méthode BLEObserver::onInitComplete
La méthode BLEObserver::onInitComplete
doit être réalisée de manière similaire
à la méthode BLEBroadcaster::onInitComplete
et en tenant compte des points
suivants :
- Si une erreur est survenue lors de l’initialisation, le champ
error
du paramètre de typeble::BLE::InitializationCompleteCallbackContext
passé à l’appel de la méthode contient l’identifiant de l’erreur. Si l’initialisation s’est déroulée sans erreur, alors le champerror
contient la valeurBLE_ERROR_NONE
. Si une erreur est survenue, il faut considérer cette erreur comme fatale et stopper le système. - Finalement, la méthode doit démarrer le scanning. Tous les appels à l’API
BLE de Mbed OS devant être effectués par un seul et même thread, vous
devez donc utiliser la méthode
GAPDevice::scheduleCall
en spécifiant la méthodeBLEObserver::startScanning
en paramètre.
La méthode BLEObserver::startScanning
La méthode BLEObserver::startScanning
doit être réalisée en tenant
compte des points suivants :
- Toute erreur survenant dans cette méthode doit être considérée comme fatale et doit donc stopper le système. Cela signifie bien sûr que le code de retour de toutes les méthodes appelées doit être vérifié.
- Les paramètres de
scanning
doivent être définis en appelant la méthode
Gap::setScanParameters
. Les paramètres de scanning comprennent l’intervalle (scan_interval
) ainsi que la fenêtre de scanning (scan_window
). Ces paramètres correspondent aux définitions du standard BLE et peuvent être réglés en fonction de l’intervalle d’advertising que votreBLEBroadcaster
utilise. - Le processus de scanning doit être démarré à l’aide de la méthode
Gap::startScan
. Dans le cas de notre application observer, vous pouvez utiliser les paramètres par défaut de la méthode.
La méthode BLEObserver::onAdvertisingReport
La méthode BLEObserver::onAdvertisingReport
est la méthode qui est appelée à
chaque réception d’un paquet d’advertising. Cette méthode est donc la méthode
qui permet de décoder les paquets d’advertising reçus du broadcaster. Vous
devez réaliser cette méthode en tenant compte des points suivants :
- La méthode reçoit en paramètre une référence sur une instance de
ble::AdvertisingReportEvent
. - Afin de faciliter le développement et le débogage, vous pouvez filtrer les
messages sur la base de l’adresse du broadcaster. Cette adresse peut être
obtenue à l’aide de la méthode
ble::AdvertisingReportEvent::getPeerAddress
. Ce mécanisme de filtrage devrait être désactivé lorsque le développement de la méthode a été validé. - Afin de faciliter l’analyse du payload d’advertising, vous pouvez utiliser
une instance de
ble::AdvertisingDataParser
. Vous pouvez construire cette instance sur le stack, en spécifiant le payload à analyser avec la méthodeble::AdvertisingReportEvent::getPayload
. - À l’aide de cette instance de
ble::AdvertisingDataParser
, vous pouvez obtenir les AD Structures successives, en interrogeantble::AdvertisingDataParser::hasNext
et en appelantble::AdvertisingDataParser::next
. La méthodenext
retourne une instance deble::AdvertisingDataParser::element_t
, qui correspond à une AD Structure. Cette structure de donnée contient donc un type dans le champtype
et des données dans le champvalue
. Dans votre code, vous devez donc tester les différents types deAD Structure
reçus, qui sontble::adv_data_type_t::COMPLETE_LOCAL_NAME
,ble::adv_data_type_t::COMPLETE_LIST_16BIT_SERVICE_IDS
etble::adv_data_type_t::SERVICE_DATA
. - Le type
ble::adv_data_type_t::COMPLETE_LOCAL_NAME
permet de connaître le nom complet du broadcaster. - Le type
ble::adv_data_type_t::COMPLETE_LIST_16BIT_SERVICE_IDS
permet de connaître la liste de services mis à disposition par le broadcaster. Dans notre cas, cette liste contient un seul service qui estGattService::UUID_ENVIRONMENTAL_SERVICE
. Vous pouvez utiliser la classeUUID
afin de vérifier l’identifiant du service. - Le type
ble::adv_data_type_t::SERVICE_DATA
permet de recevoir les données de service. Dans notre cas, les données de services devraient avoir une taille de 10 octets. Les deux premiers octets contiennet à nouveau l’UUID
du service (GattService::UUID_ENVIRONMENTAL_SERVICE
), puis la pression atmosphérique, la température et l’humidité. Les valeurs environnementales sont toutes encodées comme des entiers de 16 bits ou 32 bits, comme un nombre à virgule fixe décimal. - Dans cette méthode, lorsque que les données environnementales ont été décodées
avec succès, ces données doivent être produites dans le
DataMailbox
afin qu’un consommateur puisse en faire usage.
Ci-dessous, vous avez un extrait de la méthode que vous devez compléter :
void BLEObserver::onAdvertisingReport(const ble::AdvertisingReportEvent& event) {
ble::address_t address = event.getPeerAddress();
ble::AdvertisingDataParser adv_parser(event.getPayload());
// for debugging filter out based on mac address of broadcaster
// TO BE COMMENTED/REMOVED BEFORE PUSHING THE CODE TO GITLAB
// if (address[5] != 0xe2) {
// return;
// }
tr_debug("Got data from %02x:%02x:%02x:%02x:%02x:%02x",
address[5], address[4], address[3], address[2], address[1], address[0]);
// parse the advertising payload, looking for the environmental service data
bool serviceFound = false;
while (adv_parser.hasNext()) {
ble::AdvertisingDataParser::element_t field = adv_parser.next();
//tr_debug("Got payload of type %d", field.type.value());
switch (field.type.value()) {
case ble::adv_data_type_t::COMPLETE_LOCAL_NAME:
{
static constexpr uint8_t MAX_DEVICE_NAME_LEN = 16;
char szDeviceName[MAX_DEVICE_NAME_LEN] = {0};
strncpy(szDeviceName, (const char*) field.value.data(), field.value.size());
tr_debug("Local name of device is %s", szDeviceName);
}
break;
case ble::adv_data_type_t::COMPLETE_LIST_16BIT_SERVICE_IDS:
{
// we expect the environmental service with UUID 0x181A
for (int i = 0; i < field.value.size()/2; i +=2) {
UUID uuid(*((UUID::ShortUUIDBytes_t*) &(field.value[i])));
if (uuid == GattService::UUID_ENVIRONMENTAL_SERVICE) {
serviceFound = true;
tr_debug("Environmental service found in service list");
}
// exit the loop
break;
}
}
break;
...
}
}
}
Le programme principal
Afin de simplifier la réalisation des scénarios de broadcaster et
d’observer, vous pouvez réaliser les deux comportements dans une seule
application. Dans cette application, vous pouvez tester la présence du capteur
Weather click. Si le capteur est présent, l’application réalise le scénario du
broadcaster, avec une instance de DataMailbox
utilisée pour échanger des
données entre le SensorDataServer
et le BLEBroadcaster
. Si le capteur n’est
pas présent, l’application réalise le scénario de l’observer, avec une
instance de DataMailbox
utilisée pour échanger des données entre le
BLEObserver
et le programme affichant des données sur l’écran LCD. Vous
pouvez ensuite flasher le même programme sur deux dispositifs différents, les
deux étant équipés d’un module “X-NUCLEO-BNRG2A1” et un seul étant équipé d’un
capteur Weather click.
Tests et validations
Vous devez effectuer un test qui démontre que vos dispositifs broadcaster et observer fonctionnent. Pour ce test, vous devez rapporter les distances que vous avez testées entre les deux dispositifs et décrire à partir de quelle distance la transmission de données ne fonctionne plus.
Vous devez à nouveau configurer votre projet afin que l’analyse statique soit effectuée de manière plus stricte.
À ne pas oublier
- Choisissez de bons noms pour les classes, les méthodes et les variables.
- Implémentez les bibliothèques avec un haut niveau d’abstraction pour pouvoir réutiliser les méthodes dans d’autres projets.
- Faites des “git commit” régulièrement avec de bons commentaires.
- Utilisez des assertions dans votre code pour le documenter et le rendre plus robuste.
Journal de travail
- Rédigez un rapport (journal de travail) avec les indications suivantes :
- Une page de titre avec au minimum :
- le nom et le logo officiel de l’école
- le nom du cours : Module Acquisition et traitement de données : Internet des Objets
- le titre de votre document : Travail Pratique 5 : Station météo BLE Observer
- le numéro de votre groupe
- les noms des auteurs (vous) avec la classe dans laquelle vous êtes
- la date à laquelle vous avez terminé le rapport
- éventuellement la version du rapport
- Une introduction pour poser le contexte
- Un résumé des notions que vous avez apprises pendant ce TP en précisant si c’est
- non acquis
- acquis, mais encore à exercer
- parfaitement acquis
- Un résumé des points qui vous semblent importants et que vous devez retenir
- Les réponses aux questions.
- Le code source bien formaté et avec du “syntax highlighting” de votre code source.
- Une conclusion par laquelle vous donnez vos impressions sur le TP, ce que vous avez aimé, ce que vous avez moins aimé, et éventuellement des suggestions pour des changements. Indiquez également le nombre d’heures que vous avez passées, par personne, en dehors des heures de TP en classe.
Important
Déposez votre rapport dans le devoir Teams correspondant avec le nom report05.pdf
.