loading-icone

Radio

Fais communiquer des micro:bit entre eux à l’aide de la radio ! 📡

Code à compléter

Introduction

Oh non ! Tu t’es perdue en pleine mer après que ton bateau ait coulé, il faut absolument que tu puisses détecter les bateaux aux alentours. En plus, tu n’as plus qu’un micro:bit et un ordinateur avec juste assez de batterie pour t’en sortir.

Info

Le but de ce TP est de créer un système de balises grâce aux cartes micro:bit. Ces balises vont permettre de se détecter entre elles, et ainsi de savoir si d’autres naufragés, ou encore des bateaux de sauvetage sont aux alentours.

Pour cela, il va falloir utiliser une balise centrale qui sera chargée de détecter les autres naufragés lorsque ceux-ci entreront dans sa zone de détection.

Ce TP va te permettre de comprendre le fonctionnement du module radio de la carte micro:bit.

Voila une petite mise en scène de la situation :

Utilisation

Suite au naufrage du navire, tu as perdu les autres membres de l’équipage. Il faut donc qu’ils puissent te détecter également.

Info

Ici, tu vas créer une balise qui va te permettre à la fois de détecter les autres naufragés ainsi que les bateaux aux alentours.

Ainsi, la carte micro:bit peut agir à la fois comme une balise réceptrice (aussi appelée serveur ou balise centrale), et comme une balise émettrice (aussi appelée client).

La balise réceptrice agit comme un phare permettant aux clients de savoir s’ils sont à côté de celle-ci.

La balise émettrice est donc une carte micro:bit qui va tenter de détecter la balise centrale.

Voici un schema simplifié des interactions entre les deux balises, que nous allons réaliser lors de ce TP.

Attention, si le récepteur est trop loin, le signal va se perdre dans l’eau !

Commençons !

Mise en place

Tout d’abord, télécharge le squelette du TP via le bouton Code à compléter, en haut de la page, puis ouvre ce fichier dans ton éditeur de texte préféré.

Explication du squelette

Avant tout, il faut importer les bons modules pour pouvoir utiliser la radio sur la carte micro:bit.

Voici le code du squelette :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import radio
from microbit import *

# L'image à afficher pour l'émetteur.
image_emetteur = Image(
    "99900:"
    "00090:"
    "99009:"
    "00909:"
    "90909:"
)

# La variable pour savoir si la carte est émettrice.
est_emetteur = True
# La puissance minimum requise pour détecter un autre naufragé.
puissance_minimum = -60

Voici le motif de image_emetteur. Il s’agit d’une image de 5x5 pixels, ce qui est la définition d’affichage d’une carte micro:bit.

Ensuite, il faut configurer le module radio et l’activer.

1
2
3
4
5
6
7
# configuration du module radio
# le paramètre channel correspond au canal sur lequel le module radio doit
# écouter et recevoir des informations.
radio.config(channel=42)

# activation du module radio
radio.on()

Info

Il existe beaucoup de paramètres pour configurer la radio que nous n’explorerons pas ici, mais n’hésite pas à regarder plus en profondeur la documentation du module si cela t’intéresse.

Finalement, pour permettre aux autres naufragés de te recevoir à n’importe quel moment, il faut ajouter une boucle qui se répète indéfiniment.

1
2
3
4
5
6
7
while True:
    # TODO : remplir le code ici

    # Cette instruction permet au code de marquer une pause de 100 millisecondes
    # entre chaque itération de la boucle pour réduire la puissance nécessaire
    # pour faire fonctionner la carte.
    sleep(100)

Info

L’instruction sleep(100) marque une pause de 100 millisecondes entre chaque tour de boucle pour éviter à la carte d’avoir à exécuter trop d’instructions à la suite.

Changer le mode de la balise

Nous voulons utiliser la balise dans plusieurs modes.

Un mode est un moyen pour une même carte d’utiliser plusieurs codes différents en fonction du contexte dans lequel elle se trouve.
Nous devons effectivement recevoir les appels des autres naufragés et être capable de leur envoyer des messages aussi.

Il faut que la balise puisse savoir si elle doit émettre ou recevoir.

À toi de jouer !

Rajoute des instructions de code dans la boucle, qui vont permettre à la balise de changer de mode lorsque le bouton A est pressé.

Pour rappel, le mode de la balise est représenté par la variable est_emetteur.

Info

Tu peux utiliser la fonction was_pressed de button_a qui t’indique si le bouton a été appuyé depuis la dernière fois que cette fonction a été appelée.

Le module radio

Le mode émetteur

Le mode émetteur permet à la balise d’émettre des messages à intervalles réguliers. Ils seront reçus par les récepteurs afin que des sauveteurs puissent te retrouver en pleine mer.

Info

La fonction send(message) du module radio permet d’envoyer un message via la radio du micro:bit.

Voici un exemple d’utilisation de la fonction radio.send :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from microbit import *
import radio

# Configuration de la radio.
radio.config(channel=42)

# Allumage de la radio.
radio.on()

# Envoie un message lorsque la carte démarre.
radio.send("Demarrage")

while True:
    # Envoie un message a chaque fois que le bouton A est appuyé.
    if button_a.was_pressed():
        radio.send("appui")

    sleep(100)

À toi de jouer !

Écris un code dans la boucle infinie, qui, lorsque la balise est en mode émetteur, envoie le message SOS à toutes les balises réceptrices.

Note: Par défaut, lorsqu’un message est envoyé, il est envoyé à toutes les cartes micro:bit qui écoutent.

Le mode récepteur

Grâce à la première partie du TP, ta balise peut maintenant envoyer des messages mais ne peut pas recevoir d’informations d’une autre balise. Il serait vraiment mieux si tu pouvais retrouver les autres naufragés.

Info

Le module radio ne permet pas de connaître la distance parcourue par un message reçu. Il va donc falloir utiliser la puissance du signal reçu pour estimer la distance.

La puissance d’un signal s’exprime en décibels (de symbole dB), comme le son ! Plus la source d’émission du signal est loin, moins la puissance en décibels sera importante.

Info

La fonction receive_full() du module radio permet, entre autres, de savoir avec quelle force le message a été reçu.

Cette fonction renvoie un tuple, ou None si aucun message n’est reçu.

Le None est une valeur propre à Python qui signifie qu’il n’y a rien. En d’autres termes cela veut dire qu’il s’agit d’une valeur vide. Dans notre cas, si la fonction receive_full renvoie None, alors aucun message n’a été reçu.

Ce tuple contient :

  • Le contenu du message
  • La puissance du signal (c’est-à-dire la force avec laquelle le message a été reçu) de -255 (le plus faible) à 0 (le plus fort).
  • Le moment auquel le message a été reçu.

Rappel : Un tuple en Python est un groupement de valeurs permettant à une fonction de renvoyer plusieurs informations.

Il est possible d’extraire les valeurs contenues dans ce tuple :

Appuie sur le bouton Run pour exécuter le code python ci-dessous
Le résultat :

        

    

Voici un exemple de l’utilisation de la méthode receive_full :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
recu = radio.receive_full()

# si aucun message n'a été reçu, il ne faut rien faire.
# dans ce cas, le message reçu serait None.
if recu is not None:
    # le premier élément du tuple est le contenu du message
    # Ici SOS par exemple.
    message = recu[0]

    # le deuxième élément est la puissance du signal.
    puissance = recu[1]

    # Affiche le message reçu par la balise client (émetteur).
    display.scroll(message)

    # Affiche la puissance du signal.
    display.scroll(str(puissance))

À toi de jouer !

Écris la partie récepteur de la balise.

Clique ici pour avoir de l'aide !

Aide : Le code peut suivre cette procédure, écrite en pseudo-code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
si la balise est réceptrice, alors
    message_reçu <- radio.receive_full()
    si message_reçu est None, alors
        On ne capte pas de balise aux alentours.
    sinon,
        message, puissance, temps <- message_reçu
        si puissance > puissance_minimum, alors
            # Une balise proche et d'autres naufragés sont détectés
            Affiche l'image Image.HAPPY
        sinon,
            # La puissance n'est pas suffisante, aucune balise n'est proche.
            Affiche l'image Image.SAD

Bravo ! Tu sais maintenant si des naufragés sont proches, et tu peux envoyer des messages aux secours.

Cette première partie du TP t’as permis de mieux comprendre l’utilisation du module radio. Dans la prochaine partie, tu vas pouvoir utiliser ce module radio pour faire évoluer ta balise.

Aller plus loin

Maintenant que tu as un système de balises fonctionel, il serait intéressant de pouvoir connaître le nombre de naufragés repérés. Pour cela, il faut que la balise centrale garde un compte de toutes les balises qui lui ont envoyé un message.

Établissement du protocole

Pour être sûr que l’émetteur et le récepteur se comprennent, il faut établir ce que l’on appelle un protocole.

Info

Un protocole est une liste de règles précises qui doivent être respectées afin de s’assurer que toutes les personnes utilisant ce protocole se comprennent.

En d’autres termes, il s’agit du langage que nous souhaitons utiliser.

Puisqu’il s’agit d’un langage, tu es libre d’utiliser ce que tu veux comme convention pour permettre aux cartes de communiquer entre elles.

Par exemple, on peut dire que lorsque la balise centrale (le récepteur) reçoit la chaîne de caractères SOS, alors un client a commencé à établir une communication avec la balise centrale. À ce moment-là, la balise centrale envoie à son tour le message MAYDAY, pour signifier aux naufragés qu’elle reçoit leur signal.

Voici un schéma explicatif de la manière dont notre protocole va fonctionner :

Pour résumer :

  1. L’émetteur envoie SOS
  2. Quand le récepteur reçoit SOS, il renvoie MAYDAY.
  3. Quand l’émetteur reçoit MAYDAY, la connexion est établie, il renvoie OK pour faire valider la connexion par le récepteur.
  4. Quand le récepteur reçoit OK, alors il sait qu’un nouvel émetteur est connecté.
  5. L’émetteur et le récepteur peuvent communiquer.
  6. Quand le récepteur ne reçoit plus de message, alors il considère que l’émetteur n’est plus dans sa zone de détection.

Une fois la connexion établie, le but va être de connaître le nombre de naufragés aux alentours.

Nous pouvons estimer qu’un client qui n’a pas envoyé de messages depuis un certain temps est déconnecté.

Côté naufragé (client/émetteur)

Désormais, il faut que le naufragé envoie le message OK lorsqu’il reçoit le message MAYDAY.

À toi de jouer !

Écris le code permettant au client d’envoyer le message OK lorsque le message MAYDAY est reçu.

Côté balise centrale (serveur/récepteur)

Maintenant que le client répond au message de la balise, il faut permettre à la balise de lire les messages de tous les clients.

Info

Lorsque le module radio de la carte micro:bit reçoit un message, le message est stocké dans la mémoire avant d’être lu. Tu peux lire les messages stockés avec la fonction receive du module radio qui renvoie une chaîne de caractères s’il y a encore des messages non lus, sinon None.

Ainsi, si le message renvoyé par receive vaut None alors, tous les messages ont été lus.

Nous savons que le récepteur peut recevoir deux messages différents :

  • SOS: Si un naufragé ignore que la balise existe et est proche.
  • OK: Lorsque le naufragé sait que la balise existe.

À toi de jouer !

Modifie le code de la balise centrale pour qu’elle tienne un compte des naufragés actuellement connectés à celle-ci.

Astuce : Le nombre de clients connectés correspond au nombre de messages reçus contenant la chaîne de caractères OK sur les dernières 5 secondes (par exemple).

Tu peux t’aider du pseudo-code ci-dessous.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# la balise est réceptrice.

tant qu'il y a des messages:
    récupérer la puissance et le contenu du message
    si la puissance est suffisante, alors:
        si le message est `SOS`, alors:
            envoyer le message `MAYDAY`
        sinon si le message est `OK`, alors:
            # un naufragé est détecté.
            Incrémenter le compteur de naufragés.

N’oublie pas de remettre le compteur de naufragés à 0 après 5 secondes !

Info

Pour calculer le temps entre deux instants, tu peux utiliser le module time, et les fonctions ticks_ms et ticks_diff

1
2
3
4
5
6
7
8
import time

compteur_1 = time.ticks_ms()

compteur_2 = time.ticks_ms()

# donne la différence en millisecondes entre compteur_1 et compteur_2
difference = time.ticks_diff(compteur_1, compteur_2)

Info

Rappel : pour envoyer un message, tu peux utiliser la fonction send du module radio qui prend une chaîne de caractères en paramètre.

Si jamais tu as besoin d’aide n’hésite pas à demander à un organisateur, ils sont là pour toi !

Toujours plus loin !

Générer un identifiant pour chaque client

Tu auras remarqué que le compte de balises n’est pas très précis. Cela est dû au fait que la balise centrale ne sait pas de qui provient chaque message.

Pour remédier à cela, il faut trouver un système fiable permettant à chaque client d’avoir un identifiant unique.

Info

Pour générer un identifiant unique par machine, le plus simple étant d’utiliser la fonction unique_id du module machine. Cette fonction va te renvoyer une chaîne de caractères particulière qui peut contenir n’importe quel caractère. Ce genre de chaînes de caractères s’appelle des byte string et représentent en fait des listes d’octets.

Note: Un octet est un groupement de 8 bits qui forment un nombre entre 0 et 255. Chaque octet représente un caractère grâce à ce que l’on appelle la table ASCII. Cette table est l’équivalent de l’alphabet où le A vaut 1 et le Z 26 par exemple ou encore l’alphabet morse ou le A vaut .- et le Z vaut --...

Pour utiliser le module machine, il faut l’importer au début de ton fichier grâce à cette ligne :

1
import machine

Tu peux ensuite créer une variable id qui contiendra machine.unique_id() avant de commencer ta boucle while, pour créer l’identifiant de ce micro:bit

À toi de jouer !

Adapte le mode émetteur de la balise pour que tous les messages envoyés par celle-ci soient de la forme <ton_identifiant_unique>:<ton_message>. Comme spécifié par le protocole.

Par exemple, si ma balise a pour identifiant unique 123456, et qu’elle doit envoyer le message SOS, le message final doit etre 123456:SOS.

Connexion !

Désormais, chaque client possède un identifiant unique. Tu peux donc savoir exactement combien de clients sont connectés à ta balise.

Comme spécifié dans le protocole, tous les messages envoyés par le client doivent être sous la forme <identifiant>:<mon_message>, et le serveur doit également répondre au client avec des messages de la même forme en spécifiant l’identifiant du client.

Maintenant que tu as un moyen de savoir qui est l’émetteur d’un message, pour connaître exactement le nombre de naufragés dans le rayon de la balise, il faut savoir combien de naufragés différents ont envoyé des messages dans les cinq dernières secondes.

À toi de jouer !

Modifie le code du serveur pour stocker l’heure du dernier message reçu pour chaque client.

Astuce : tu peux modifier la liste des identifiants et remplacer par des tuples contenant l’identifiant et l’heure du dernier message pour cet identifiant.

Tu peux maintenant savoir précisément quand chaque client a envoyé un message pour la dernière fois.

Il ne te reste plus qu’à récupérer tous les identifiants dont le dernier message remonte à moins de 5 secondes pour connaître le nombre de clients connectés en ce moment !


Conclusion

Bravo, tu es arrivée à la fin de la partie (plus ou moins) guidée de ce TP ! Tu as maintenant tous les outils nécessaires pour utiliser le module radio pour toute sorte de projets.

Améliorations

Maintenant, ton système de balise devrait être parfaitement fonctionnel. Tu peux continuer à améliorer ce projet en rajoutant encore d’autres fonctionnalités.

Voici des exemples de choses que tu peux faire :

  • Améliorer le système de protocole pour permettre aux clients de demander à la balise d’exécuter d’autres actions, comme par exemple la possibilité pour une balise émettrice d’envoyer SOS à toutes les autres balises connectées.

  • Permettre à la balise d’envoyer des informations au naufragé lorsque celui-ci se connecte, comme son nom par exemple.

Retour au Menu