Aller au contenu

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.

Schéma bloc

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éthode startAdvertising de la classe BLEBroadcaster
  • La classe redéfinit la méthode onAdvertisingReport de la classe Gap::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 type ble::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 champ error contient la valeur BLE_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éthode BLEObserver::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 votre BLEBroadcaster 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éthode ble::AdvertisingReportEvent::getPayload.
  • À l’aide de cette instance de ble::AdvertisingDataParser, vous pouvez obtenir les AD Structures successives, en interrogeant ble::AdvertisingDataParser::hasNext et en appelant ble::AdvertisingDataParser::next. La méthode next retourne une instance de ble::AdvertisingDataParser::element_t, qui correspond à une AD Structure. Cette structure de donnée contient donc un type dans le champ type et des données dans le champ value. Dans votre code, vous devez donc tester les différents types de AD Structure reçus, qui sont ble::adv_data_type_t::COMPLETE_LOCAL_NAME, ble::adv_data_type_t::COMPLETE_LIST_16BIT_SERVICE_IDS et ble::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 est GattService::UUID_ENVIRONMENTAL_SERVICE. Vous pouvez utiliser la classe UUID 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.