Aller au contenu

TP03 : Station météo BLE (broadcaster)

Avec ce travail pratique, nous introduisons une nouvelle dimension à notre station météo avec un module de communication BLE. La communication BLE sera réalisée dans deux travaux pratiques successifs :

  • Dans ce TP, nous réaliserons uniquement le broadcaster, qui diffuse les données environnementales dans des paquets d’advertising BLE.
  • Dans le TP 5, nous réaliserons l’observer, qui aura pour mission de scanner les paquets d’advertising pour en extraire les données et les afficher.

Le système final (après le TP 05) sera donc 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 (la cible ½ est réalisé dans le TP 03/05)

Objectifs du TP

Ce TP a pour but de réaliser un broadcaster BLE.

À la fin de ce TP, les étudiants :

  • auront intégré les composants BLE à leur solution ;
  • auront intégré le broadcasting des données environnementales par BLE;
  • auront réalisé des analyses statiques de code et corrigé les erreurs rapportées ;
  • auront rédigé un journal de travail et déposé le PDF sur Teams.

Les livrables sont :

  • un release avec le tag “tp03” doit être créé sur votre dépôt.
  • la correction des issues du TP 02 qui ont été créés lors de la correction du TP 02.
  • un journal de travail déposé sur Teams.

Temps accordé : 8 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 avez besoin du module X-NUCLEO-BNRG2A1. Assurez-vous de configurer la carte correctement en mettant le jumper J14 sur la gauche (position 1) :

X-NUCLEO-BNRG2A1

Le module BLE est placé entre le “STM32F412-DISCO” et la carte “Arduino Click Shield” :

Hardware pour le TP3

Configuration de Mbed OS et de PlatformIO

Pour utiliser le module BLE, vous devez configurer votre projet de la manière suivante :

Fichier platformio.ini

Dans le fichier platformio.ini, ajoutez la librairie BLE aux dépendances de votre projet (ligne 3) :

1
2
3
4
lib_deps =
    ...
    https://github.com/ARMmbed/mbed-os-ble-utils.git
    ...

Dossier mbedignore

Le dossier mbedignore permet de supprimer des composants de Mbed OS pour accélérer la compilation et l’édition de liens (linker) du programme. Jusqu’ici, nous avions ignoré la partie BLE et nous devons la ré-activer pour ce TP :

Editez le fichier mbedignore/connectivity/.mbedignore et supprimez les lignes suivantes :

drivers/ble/*

et

FEATURE_BLE/*

Fichier mbed_app.json

Les bibliothèques pour Mbed OS telles que la bibliothèque BLE sont souvent accompagnées de paramètres qui influencent le comportement de la bibliothèque et de sa compilation. Pour la bibliothèque BLE que nous utilisons, les paramètres sont décrits dans ce fichier mbed_lib.json

Pour modifier les valeurs par défaut, éditez le fichier mbed_app.json de votre projet et ajoutez les lignes suivantes :

{
    "target_overrides": {
        "*": {
            ...
            "platform.minimal-printf-set-floating-point-max-decimals": 4,
            "ble.ble-role-observer": true,
            "ble.ble-role-broadcaster": true,
            "ble.ble-role-central": false,
            "ble.ble-role-peripheral": false,
            "ble.ble-feature-gatt-client": false,
            "ble.ble-feature-gatt-server": false,
            "ble.ble-feature-security": false,
            "ble.ble-feature-secure-connections": false,
            "ble.ble-feature-signing": false,
            "ble.ble-feature-whitelist": false,
            "ble.ble-feature-privacy": false,
            "ble.ble-feature-phy-management": false,
            "ble.ble-feature-extended-advertising": false,
            "ble.ble-feature-periodic-advertising": false,
            "ble.ble-security-database-filesystem": false,
            "ble.ble-security-database-kvstore": false,
            "ble.ble-security-database-max-entries": 5,
            "ble.ble-gap-max-advertising-sets": 15,
            "ble.ble-gap-host-based-private-address-resolution": false,
            "ble.ble-gap-max-advertising-reports-pending-address-resolution": 16,
            "ble.ble-gap-host-privacy-resolved-cache-size": 16,
            "ble.ble-passkey-display-reversed-digits-deprecation": true
        },
        ...

Vous devez également modifier le fichier mbed_app.json pour l’utilisation de la bibliothèque BLE et pour la configuration des pins du module BLE en ajoutant les définitions suivantes :

    "DISCO_HEIAFR": {
            ...
            "target.components_add":   ["BlueNRG_2"],
            "target.features_add":     ["BLE"],
            "target.extra_labels_add": ["CORDIO"],
            "bluenrg_2.SPI_MOSI":      "D11",
            "bluenrg_2.SPI_MISO":      "D12",
            "bluenrg_2.SPI_nCS":       "A1",
            "bluenrg_2.SPI_RESET":     "D7",
            "bluenrg_2.SPI_IRQ":       "A0",
            "bluenrg_2.SPI_SCK":       "D13"
        }

Réalisation

Classe permettant d’encapsuler la production et la consommation de données

Le point de départ pour ce TP est votre réalisation du TP 02. Dans le TP03 et le TP 05, le mécanisme de production et de consommation doit être réalisé par les composants de deux applications, le broadcaster et l’observer.

Le broadcaster développé dans ce TP fonctionne sur le dispositif muni du capteur et d’un module BLE. Pour cette application, le processus de production et de consommation est réalisé de la manière suivante :

  • Comme dans le TP02, la classe SensorDataServer est un producteur de données. Cette classe a pour mission de lire les données provenant des capteurs et de les mettre à disposition d’un consommateur.
  • Dans le TP02, le consommateur des données produites par l’instance de SensorDataServer est le composant qui affiche les données sur l’écran LCD. Dans ce TP, le consommateur de ces données est le broadcaster BLE, qui diffuse ces données pour qu’un observer BLE puisse les recevoir.

L’observer qui sera développée dans le TP 05 fonctionne sur le dispositif muni d’un module BLE et d’un écran LCD. Pour cette application, le processus de production et de consommation sera réalisé de la manière suivante :

  • L’observer BLE est un producteur de données. Les données produites sont celles reçues dans les paquets d’advertising. Les données reçues sont mises à disposition d’un consommateur.
  • Le consommateur des données produites par l’instance d’observer est le composant qui affiche les données sur l’écran LCD.

Afin de réaliser le comportement de production et de consommation de manière uniforme, vous devez encapsuler ces comportements dans une classe DataMailbox. Cette classe définit le type de données représentant une mesure, possède une instance de Mail et fournit deux méthodes :

class DataMailbox 
{
public:
  struct message_t 
  {
    double temperature;
    double pressure;
    double humidity;
    time_t time;
  };
  ...
  bool produceData(const message_t& message);
  bool consumeData(message_t& message);
  ...

Après avoir réalisé cette classe, vous pouvez l’intégrer dans votre réalisation du TP 02 et tester à nouveau le bon comportement de votre application. Le comportement doit être le même que votre application du TP02.

L’API BLE fournie par Mbed OS

Comme pour la plupart des API fournies par Mbed OS, l’API BLE est disponible sous forme de classes C++. Cela permet de programmer une application BLE fonctionnant avec tous les modules BLE compatibles, en réduisant la complexité. Pour notre application, vous devez utiliser les classes suivantes :

  • la classe BLE qui permet d’obtenir un singleton représentant le device et qui permet d’initialiser ce dernier. Le concept de singleton est expliqué par exemple sous singleton pattern.
  • la classe Gap qui permet d’accéder aux fonctionnalités de découverte des dispositifs et de gestion de l’advertising et du scanning.

Avant de détailler l’utilisation de ces classes, il est important de mentionner les points suivants concernant l’API BLE de Mbed OS :

  • De nombreux appels à l’API sont asynchrones et le résultat de ces appels est donc fourni via des fonctions ou méthodes de callback.
  • L’API n’est pas thread safe. Cela signifie que tous les appels à la librairie et que le traitement asynchrone des messages doivent être effectués depuis le même thread.

La classe GAPDevice

Afin de faciliter votre travail d’intégration de la librairie BLE dans votre application, le squelette d’une classe GapDevice est donné ci-dessous :

gap_device.hpp
#ifndef GAP_DEVICE_HPP_
#define GAP_DEVICE_HPP_

#include <mbed.h>

#include "ble/BLE.h"
#include "ble/Gap.h"

class GAPDevice : private mbed::NonCopyable<GAPDevice>,
                  public ble::Gap::EventHandler {
   public:
    GAPDevice();
    ~GAPDevice();
    void start();

   protected:
    // protected methods (may be called by inheriting classes)
    template <typename T, typename R>
    void scheduleCall(T* obj, R (T::*method)())
    {
        eventQueue_.call(obj, method);
    }
    // This is called by inheriting classes when BLE interface is initialised
    virtual void onInitComplete(
        BLE::InitializationCompleteCallbackContext* context) = 0;

    // helper methods
    static void uint16_encode(uint16_t value, uint8_t* p_encoded_data);
    static void int16_encode(int16_t value, uint8_t* p_encoded_data);
    static void uint32_encode(uint32_t value, uint8_t* p_encoded_data);

    static uint16_t uint16_decode(uint8_t* p_encoded_data);
    static int16_t int16_decode(uint8_t* p_encoded_data);
    static uint32_t uint32_decode(uint8_t* p_encoded_data);

   private:
    void scheduleBleEvents(BLE::OnEventsToProcessCallbackContext* context);

    events::EventQueue eventQueue_;
    Thread bleThread_;
};

#endif /* GAP_DEVICE_HPP_ */
gap_device.cpp
#include "gap_device.hpp"

#include "mbed_trace.h"
#if defined(MBED_CONF_MBED_TRACE_ENABLE)
#define TRACE_GROUP "GAPDevice"
#endif  // MBED_CONF_MBED_TRACE_ENABLE

// constructor
GAPDevice::GAPDevice()
    : // TODO : Initialize attributs
{
}

GAPDevice::~GAPDevice()
{
    // TODO : Shutdown BLE instance if initialized
}

void GAPDevice::start()
{
    // errors in this method are considered fatal (stop the system)

    // check whether the BLE instance is already initialized
    // (it should not be)
    if (BLE::Instance().hasInitialized()) {
        MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_APPLICATION,
                                   MBED_ERROR_CODE_INITIALIZATION_FAILED),
                   "Ble instance already initialised");
    }

    // this will inform us off all events so we can schedule their handling
    // using our event queue
    BLE::Instance().onEventsToProcess(
        makeFunctionPointer(this, &GAPDevice::scheduleBleEvents));

    // set this instance as the event handler for all gap events
    BLE::Instance().gap().setEventHandler(this);

    // initialize the ble component
    ble_error_t error = BLE::Instance().init(this, &GAPDevice::onInitComplete);
    if (error != BLE_ERROR_NONE) {
        MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_APPLICATION, error),
                   "Error returned by BLE::init");
    }

    osStatus status =
        bleThread_.start(callback(&eventQueue_, &EventQueue::dispatch_forever));
    if (status != osOK) {
        MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_APPLICATION, status),
                   "Error when starting ble thread");
    }
}

// Schedule processing of events from the BLE API in our event queue (served
// by blethread_)
void GAPDevice::scheduleBleEvents(
    BLE::OnEventsToProcessCallbackContext* context)
{
    eventQueue_.call(Callback<void()>(&context->ble, &BLE::processEvents));
}

void GAPDevice::uint16_encode(uint16_t value, uint8_t* p_encoded_data)
{
    // TODO
}

void GAPDevice::int16_encode(int16_t value, uint8_t* p_encoded_data)
{
    // TODO
}

void GAPDevice::uint32_encode(uint32_t value, uint8_t* p_encoded_data)
{
    // TODO
}

uint16_t GAPDevice::uint16_decode(uint8_t* p_encoded_data)
{
    // TODO
}

int16_t GAPDevice::int16_decode(uint8_t* p_encoded_data)
{
    // TODO
}

uint32_t GAPDevice::uint32_decode(uint8_t* p_encoded_data)
{
    // TODO
}

Cette classe servira de base aux composants que vous devez développer pour votre application. Vous devrez utiliser cette classe par héritage pour les composants BLEBroadcaster (TP 03) et BLEObserver (TP 05) que vous devrez développer.

Diagramme de classes

Les principes mis en œuvre dans la classe GAPDevice sont les suivants :

  • L’API BLE de Mbed OS requiert que tous les appels aux méthodes de l’API soient exécutés par un seul et même thread. Ce thread doit être créé et démarré par l’application. L’API fournit donc une méthode ble::BLE::onEventsToProcess qui permet de signaler à l’application quand des événements de l’API doivent être traités et l’API fournit également une méthode ble::BLE::processEvents pour traiter ces événements. Ce mécanisme est réalisé pour votre application dans la classe GAPDevice (voir les méthodes GAPDevice::start() et GAPDevice::scheduleBleEvents()).

  • D’autres opérations dans les classes BLE et Gap sont effectuées de manière asynchrone. Afin de recevoir les résultats de ces opérations asynchrones, il est nécessaire d’enregistrer des callbacks pour traiter les résultats. Pour la classe ble::Gap, cet enregistrement se fait avec la méthode ble::Gap::setEventHandler(). La classe GAPDevice hérite de la classe ble::Gap::EventHandler et dans la méthode GAPDevice::start(), nous pouvons enregistrer l’instance de GAPDevice courante comme traitant les événements générés par la classe ble::Gap. Afin de traiter un événement généré par la classe ble::Gap, il suffit de redéfinir une des méthodes de la classe ble::Gap::EventHandler dans la classe GAPDevice - veuillez noter qu’aucune de ces méthodes n’est redéfinie à ce stade.

  • Un mécanisme similaire de callback est utilisé pour l’appel à la méthode ble::BLE::init() pour laquelle le callback est la méthode GAPDevice::onInitComplete(). Cette méthode est une méthode abstraite qui devra être redéfinie par les classes BLEBroadcaster et BLEObserver héritant de GAPDevice. Plus de détails sont donnés ci-dessous concernant les méthodes virtuelles et abstraites.

  • Afin d’obtenir une référence sur l’instance ble::BLE singleton, les méthodes de la classe GAPDevice font appel à la méthode ble::BLE::Instance().

  • Afin d’obtenir une référence sur l’instance ble::Gap singleton, les méthodes de la classe GAPDevice font appel à la méthode ble::BLE::Instance().Gap()

Les méthodes virtuelles

Pour rappel, les méthodes virtuelles d’une classe sont des méthodes qui peuvent être redéfinies par une classe dérivant de cette classe. Lorsqu’une classe redéfinit une méthode virtuelle, c’est bien la méthode redéfinie dans cette classe qui sera appelée lorsque cette méthode sera invoquée. Lorsqu’une classe redéfinit une méthode virtuelle, le mot clé override devrait être ajouté à la déclaration de la méthode selon la notation void f() override. Une méthode virtuelle peut être définie comme pure virtual dans une classe de base avec la notation virtual ... = 0. Dans ce cas, le type de la classe de base devient abstrait et la méthode doit être définie dans une classe dérivante afin que cette classe soit instanciable.

Le principe des méthodes virtuelles et pure virtuelles est également présenté dans le cours EduTools, sous le chapitre “From Java to C++ -> Classes and inheritance”. Veuillez vous y référer pour plus de détails et pour les exemples.

En rapport à l’utilisation d’un singleton, il est intéressant de mettre en évidence les points suivants dans la classe BLE :

class BLE 
{
public:
    ...
    // Prevent copy construction and copy assignment of BLE.
    BLE(const BLE &) = delete;
    BLE &operator=(const BLE &) = delete;
...
};

Comme commenté dans le code, les notations = delete suivant la déclaration du copy constructor et de l’opérateur d’affectation permettent d’empêcher l’utilisation de ces mécanismes. En d’autres termes, il n’est pas possible pour un programme de copier une instance de la classe BLE, ce qui est nécessaire lorsque l’on utilise des singletons. Ce mécanisme est expliqué de manière détaillée sous deleted functions.

Questions

Expliquez pourquoi le mécanisme de singleton doit déclarer le copy constructor et l’opérateur d’affectation avec un = delete. Démontrez avec un exemple comment il serait possible de dupliquer un singleton.

La classe BLEBroadcaster

La classe BLEBroadcaster est utilisée par l’application afin de diffuser les données de capteurs en mode advertising. Cette classe représente donc un rôle de broadcaster au sens de la terminologie BLE.

Vous devez créer la classe BLEBroadcaster avec les caractéristiques suivantes :

  • La classe hérite de la classe GAPDevice.
  • La classe redéfinit la méthode abstraite privée onInitComplete.
  • La classe définit une méthode privée startAdvertising qui est appelée lorsque l’initialisation du dispositif a été effectuée avec succès.
  • La classe définit une méthode publique setAdvertisementPayload qui sera appelée par l’application pour publier les données environnementales dans les paquets d’advertising.
  • La classe définit une méthode privée setServiceData qui est utilisée pour construire les données d’advertising.
  • La classe définit un attribut de type ble::AdvertisingDataBuilder. Cet attribut sera utilisé dans différentes méthodes afin d’initialiser les données d’advertising et afin de les mettre à jour lorsque de nouvelles données de capteurs sont disponibles. Cet attribut doit être initialisé à l’aide d’un buffer de type uint8_t de la taille maximale des données d’advertising (définie par ble::LEGACY_ADVERTISING_MAX_SIZE).

La méthode BLEBroadcaster::onInitComplete

La méthode BLEBroadcaster::onInitComplete doit être réalisée 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.
  • En utilisant la fonction print_mac_address() de la bibliothèque mbed-os-ble-utils, vous pouvez afficher l’adresse MAC du dispositif sur la console.
  • Finalement, la méthode doit démarrer l’advertising. Comme déjà énoncé, tous les appels à l’API BLE de Mbed OS doivent être effectués par un seul et même thread, dans notre cas le thread nommé bleThread_ défini dans la classe GAPdevice. Afin de démarrer l’advertising, vous devez donc utiliser la méthode GAPDevice::scheduleCall en spécifiant la méthode BLEBroadcaster::startAdvertising en paramètre.

La méthode BLEBroadcaster::startAdvertising

La méthode BLEBroadcaster::startAdvertising 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 d’advertising doivent être définis en appelant la méthode Gap::setAdvertisingParameters. Pour notre broadcaster, le type d’advertising est advertising_type_t::NON_CONNECTABLE_UNDIRECTED et l’intervalle d’advertising est un paramètre de votre application.
  • Le champ Flags (AD Type 0x01) doit être initialisé en appelant la méthode ble::AdvertisingDataBuilder::setFlags. Les flags à enclencher dans notre application sont ble::adv_data_flags_t::LE_GENERAL_DISCOVERABLE et ble::adv_data_flags_t::BREDR_NOT_SUPPORTED.
  • Le champ Name (AD Type 0x09) doit être initialisé en appelant la méthode ble::AdvertisingDataBuilder::setName. Choisissez un nom spécifique, d’une taille permettant de respecter la limite totale de 31 octets pour les données d’advertising.
  • Les données d’advertising du dispositif GAP - initialisées dans la classe BLEBroadcaster à l’aide de l’attribut ble::AdvertisingDataBuilder - doivent être mises à jour à l’aide de la méthode Gap::setAdvertisingPayload.
  • Finalement, le processus d’advertising doit être démarré à l’aide de la méthode Gap::startAdvertising. Dans le cas de notre application broadcaster, vous pouvez utiliser les paramètres par défaut de la méthode.

Après avoir réalisé la méthode BLEBroadcaster::startAdvertising, votre dispositif devrait envoyer des paquets d’advertising à l’intervalle fixé par l’application. Pour tester ce comportement, vous devez instancier un BLEBroadcaster dans votre application et appeler la méthode BLEBroadcaster::start. Votre dispositif devrait apparaître si vous effectuez un scan à l’aide d’une application comme Nordic Semiconductor nRF Connect for Mobile ou LightBlue.

Avant de poursuivre la réalisation, vous devez vous assurer que l’advertising fonctionne correctement. La suite de la réalisation permettra de mettre à jour les données d’advertising afin que celles-ci contiennent les données de capteurs.

La méthode BLEBroadcaster::setServiceData

La méthode BLEBroadcaster::setServiceData est mise à disposition ci-dessous :

BLEBroadcaster::setServiceData
ble_error_t BLEBroadcaster::setServiceData(UUID serviceUUID,
                                           const uint8_t* pServiceData,
                                           uint16_t serviceDataLength)
{
    // set the local service list
    ble_error_t error = advDataBuilder_.setLocalServiceList(
        mbed::make_Span(&serviceUUID, 1), true);
    if (error != BLE_ERROR_NONE) {
        tr_error("Error in setServiceData: %d", error);
        return error;
    }

    // set the service data
    error = advDataBuilder_.setServiceData(
        serviceUUID, mbed::make_Span(pServiceData, serviceDataLength));
    if (error != BLE_ERROR_NONE) {
        tr_error("Error in setServiceData: %d", error);
        return error;
    }

    // set the advertising payload
    error = BLE::Instance().gap().setAdvertisingPayload(
        ble::LEGACY_ADVERTISING_HANDLE, advDataBuilder_.getAdvertisingData());
    if (error != BLE_ERROR_NONE) {
        MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_APPLICATION, error),
                   "Error in gap().setAdvertisingPayload");
    }

    return BLE_ERROR_NONE;
}

Cette méthode peut être utilisée par la méthode BLEBroadcaster::setAdvertisementPayload à développer. Elle prend en paramètre l’UUID du service et un buffer contenant les données de services. Ces données correspondent au champ Service Data du payload d’advertising (AD type 0x16). La méthode utilise l’attribut ble::AdvertisingDataBuilder et doit donc être adaptée au nom de l’attribut que vous aurez choisi pour votre classe BLEBroadcaster.

Remarque importante

La méthode BLEBroadcaster::setServiceData pourrait être appelée alors que l’initialisation du dispositif BLE n’est pas terminée - c’est-à-dire avant que la méthode onInitComplete() ait été appelée. Comme l’API BLE n’est pas thread safe, il est possible qu’un tel appel produise un comportement inattendu et difficile à détecter. Vous devez éviter un tel scénario en retournant immédiatement de la méthode si l’initialisation n’est pas terminée, comme illustré ci-dessous

// return silently when init is not complete
if (! isInitComplete_) {
  return BLE_ERROR_NONE;
}
Le flag isInitComplete_ doit être initialisé à false et mis à true lorsque l’initialisation du dispositif BLE est terminé.

La méthode BLEBroadcaster::setAdvertisementPayload

La méthode BLEBroadcaster::setAdvertisementPayload doit être réalisée en tenant compte des points suivants :

  • La méthode prend en paramètres les données de capteurs.
  • Un buffer de 8 octets doit être créé sur le stack pour sauvegarder les données de service.
  • Les données de capteurs doivent être encodées dans le buffer à l’aide des méthodes statiques uint16_encode, int16_encode et uint32_encode définies dans la classe GAPDevice. Si vous le préférez, vous pouvez également utiliser les macros définies dans le fichier ‘bstream.h’ qui fait partie de la librairie BLE de Mbed OS.
  • Après avoir encodé les données de capteurs dans le buffer, la méthode setServiceData doit être appelée. L’UUID du service à utiliser est GattService::UUID_ENVIRONMENTAL_SERVICE.

À ce stade, votre BLEBroadcaster devrait être entièrement fonctionnel. Vous devez modifier votre programme principal et appeler la méthode BLEBroadcaster::setAdvertisementPayload lorsque des données de capteurs sont disponibles. Vous devez suivre le même modèle de programmation que celui utilisé pour l’affichage des données sur l’écran LCD. Vous pouvez vérifier le comportement de votre application broadcaster à l’aide de l’application Nordic Semiconductor nRF Connect for Mobile ou de LightBlue. L’application ne permet pas d’interpréter les données de service, mais vous devriez constater que ces données sont modifiées lorsque les valeurs des mesures de capteurs changent.

Scan avec nRF-Connect-for-mobile

Détail du payload

Explication des valeurs:

LEN. TYPE VALUE
2 0x01 0x06
7 0x09 0x544845524D4F
3 0x03 0x1A18
11 0x16 0x1A180C250000A00A6BOE
  • 0x544845524D4F = “THERMO”
  • 0C250000 = 9516 = 951.6 mbar
  • A00A = 2720 = 27.2°
  • 6BOE = 3691 = 37.2%

Le programme principal

Afin de simplifier la réalisation des scénarios de broadcaster et d’observer, vous pouvez réaliser les deux comportements (TP 03 et 05) 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.

Dans le TP 05, si le capteur n’est pas présent, l’application réalisera 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 pourrez ainsi 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 votre broadcaster fonctionne. Dans le cadre du TP 03, il suffit de démontrer que les données broadcastées par votre broadcaster sont reçus par un smartphone en utilisant l’application Nordic Semiconductor nRF Connect for Mobile. Pour cela, vous devez ajouter une copie d’écran du téléphone avec un nom de broadcaster “XX_YY”, “XX” et “YY” étant les initiales de vos noms/prénoms.

Aucun test automatique n’est exigé. Par contre, vous devez configurer l’analyse statique de code de manière plus stricte. Dans le fichier .gitlab-ci.yml, configurez l’analyse statique pour alerter lors de problèmes de niveau low. Ajoutez pour cela l’option --fail-on-defect=low à la commande pio check:

1
2
3
4
5
6
check-job:
  stage: check
  script:
    - pio run -e DISCO_F
    - pio check -e DISCO_F --skip-packages --fail-on-defect=low
    - pio test --without-uploading --without-testing

À 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 3 : Station météo BLE broadcaster
    • 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 report03.pdf.