L’application ST BLE Sensor et Blue-ST
Ce tutoriel aborde la communication BLE entre une carte NUCLEO-WB55 et un smartphone avec l’application ST BLE Sensor. Plus précisément, il explique comment :
- Mesurer une température avec la carte d’extension X-NUCLEO-IKS01A3 et l’envoyer pour affichage à ST BLE Sensor ;
- Allumer ou éteindre une LED de la NUCLEO-WB55 depuis ST BLE Sensor.
L’application ST BLE Sensor transforme votre smartphone en central BLE avec l’implémentation Blue-ST de GATT développée par STMicroelectronics. Le Software Development Kit (SDK) Blue-ST propose une définition et une gestion des caractéristiques et de leurs UUID, détaillées dans ce document.
Le schéma de principe suivant résume le cas d’usage que nous allons réaliser :
La carte NUCLEO-WB55 et l’application ST BLE Sensor, après une phase d’advertising GAP (non représentée) ont établi une connexion. C’est le protocole GATT, illustré ici, qui régit les échanges. La NUCLEO-WB55 est désormais un périphérique serveur du service ST_APP_SERVICE lequel contient deux caractéristiques, qui sont partagées avec le smartphone devenu un central client :
- La caractéristique température régulièrement modifiée par le périphérique possède l’attribut NOTIFY : cela signifie que le central sera informé des modifications de sa valeur par le périphérique (ces notifications sont réalisées par des interruptions qui surviennent dans l’application ST BLE Sensor). Le smartphone pourra ainsi afficher les variations de la température au cours du temps.
- La caractéristique interrupteur possède les attributs NOTIFY et WRITE. Elle signalera ses changements au central (par NOTIFY) et elle autorisera celui-ci à modifier sa valeur (par WRITE). C’est comme cela que l’application ST BLE Sensor pourra envoyer les commandes on/off de la LED à la NUCLEO-WB55.
Matériel requis :
- La carte NUCLEO-WB55
- En option, la carte d’extension X-NUCLEO-IKS01A3 pour le capteur HTS221 (ou tout autre module I2C avec ce capteur). Si vous n’avez pas ce matériel, vous pourrez demander au script de simuler les mesures.
- Un smartphone Android ou iOS avec l’application ST BLE Sensor installée
Attention, il est possible que vous deviez mettre à jour le firmware BLE HCI de votre carte NUCLEO-WB55, la procédure est expliquée ici.
Installation de ST BLE Sensor sur votre smartphone
Vous trouverez ST BLE Sensor sur Google Play ou IOS Store
La description complète des différents services proposés par l’application ST BLE Sensor est disponible ici : https://GitHub.com/STMicroelectronics/STBlueMS_Android
Les codes MicroPython
Vous pouvez télécharger les scripts MicroPython de ce tutoriel (entre autres) en cliquant ici.
Pour ce tutoriel, l’implémentation BLE repose sur deux scripts :
- ble_advertising.py une bibliothèque de fonctions qui seront utilisées pour construire les trames d’avertising du protocole GAP, lancé pour et avant la connexion à un central.
- ble_sensor.py, une bibliothèque qui appelle ble_advertising.py pour démarrer le protocole GAP et qui implémente aussi le protocole GATT. C’est ce fichier qui paramétrise les services, les caractéristiques et leurs UUID conformément à Blue-ST. Grâce au fichier ble_sensor.py nous allons pouvoir créer un objet BLE ayant 1 service et 2 caractéristiques.C’est ce fichier qu’il faudra modifier pour changer le profil BLE, si nécessaire.
Ces fichiers sont construits avec la classe Microbluetooth documentée ici et adaptés des scripts disponibles ici. Leur structure est assez abstraite ; nous allons cependant regarder plus en détails ble_sensor.py via le listing commenté ci-après.
Deux autres scripts seront nécessaires pour notre application :
- hts221.py, la bibliothèque qui permet de lire la température sur le capteur MEMS HTS221 de la X-NUCLEO-IKS01A3 ;
- main.py, le script qui contiendra le code du programme utilisateur et qui importera ble_advertising.py, ble_sensor.py et HTS221.py.
Comme pour tous les autres tutoriels il faudra inclure ces quatre fichiers dans le disque USB “PYBFLASH” associé à l’espace de stockage de l’USB USER de la NUCLEO-WB55.
Revue de ble_sensor.py
Voici le listing commenté de ble_sensor.py. Les commentaires les plus importants, qui vous seront utiles pour adapter ce script à d’autres cas d’usage en lui rajoutant des caractéristiques, sont ceux qui expliquent le calcul de _FEATURE_MASK. L’application ST BLE Sensor ne décodera correctement les messages émis par la NUCLEO-WB55 que si ceux-ci sont rédigés dans le respect du protocole Blue-ST. Il vous faudra notamment être attentif au bon codage de la fonction Python “pack” qui permet de construire la payload (fr : charge utile) des caractéristiques.
# Objet du script : Implémentation du protocole GATT Blue-ST pour un périphérique
# Définition d'un service _ST_APP_SERVICE avec deux caractéristiques :
# 1 - SWITCH : pour éteindre et allumer une LED du périphérique depuis un central
# 2 - TEMPERATURE : pour envoyer une mesure de température du périphérique à un central
import bluetooth # Pour gérer le BLE
from ble_advertising import adv_payload # Pour gérer l'advertising GAP
from struct import pack # Pour agréger les octets envoyés par les trames BLE
import pyb # Pour gérer les LED de la NUCLEO-WB55
# Constantes définies pour le protocole Blue-ST
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
# Pour les UUID et les codes, on se réfère à la documentation du SDK Blue-ST disponible ici :
# https://www.st.com/resource/en/user_manual/dm00550659-getting-started-with-the-bluest-protocol-and-sdk-stmicroelectronics.pdf.
# 1 - Définition du service personnalisé selon le SDK Blue-ST
# Indique que l'on va communiquer avec une appli conforme au protocole Blue-ST :
_ST_APP_UUID = bluetooth.UUID('00000000-0001-11E1-AC36-0002A5D5C51B')
# UUID d'une caractéristique de température
_TEMPERATURE_UUID = (bluetooth.UUID('00040000-0001-11E1-AC36-0002A5D5C51B'), bluetooth.FLAG_NOTIFY)
# UUID d'une caractéristique d'interrupteur
_SWITCH_UUID = (bluetooth.UUID('20000000-0001-11E1-AC36-0002A5D5C51B'), bluetooth.FLAG_WRITE|bluetooth.FLAG_NOTIFY)
# Le service contiendra ces deux caractéristiques
_ST_APP_SERVICE = (_ST_APP_UUID, (_TEMPERATURE_UUID, _SWITCH_UUID))
# 2 - Construction de la trame (contenu du message) d'avertising GAP
_PROTOCOL_VERSION = const(0x01) # Version du protocole
_DEVICE_ID = const(0x80) # Carte NUCLEO générique
_DEVICE_MAC = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC] # Adresse matérielle MAC fictive
_FEATURE_MASK = const(0x20040000) # Services sélectionnés : température (2^18) et interrupteur (2^29)
# Explication du calcul du masque déterminant les caractéristiques du service actif (_FEATURE_MASK)
# A chaque caractéristique est associé un code binaire. On doit simplement sommer les codes de toutes les caractéristiques
# que l'on souhaite exposer avec GATT :
# Caractéristique SWITCH : code = 2^29 = 100000000000000000000000000000 (en binaire) = 20000000 (en hexadécimal)
# Caractéristique TEMPERATURE : code = 2^18 = 000000000001000000000000000000 (en binaire) = 40000 (en hexadécimal)
# _FEATURE_MASK = SWITCH + TEMPERATURE = 100000000001000000000000000000 (en binaire) = 20040000 (en hexadécimal)
# Trame d'avertising : concaténation des informations avec la fonction MicroPython "pack"
# La chaîne '>BBI6B' désigne le format des arguments, voir la documention de pack ici : https://docs.python.org/3/library/struct.html
_MANUFACTURER = pack('>BBI6B', _PROTOCOL_VERSION, _DEVICE_ID, _FEATURE_MASK, *_DEVICE_MAC)
# Initialisation des LED
led_blue = pyb.LED(1)
led_red = pyb.LED(3)
class BLESensor:
# Initialisation, démarrage de GAP et publication radio des trames d'advertising
def __init__(self, ble, name='WB55-MPY'):
self._ble = ble
self._ble.active(True)
self._ble.irq(self._irq)
((self._temperature_handle,self._switch_handle),) = self._ble.gatts_register_services((_ST_APP_SERVICE, ))
self._connections = set()
self._payload = adv_payload(name=name, manufacturer=_MANUFACTURER)
self._advertise()
self._handler = None
# Gestion des évènements BLE...
def _irq(self, event, data):
# Si un central a envoyé une demande de connexion
if event == _IRQ_CENTRAL_CONNECT:
conn_handle, _, _, = data
# Se connecte au central (et arrête automatiquement l'advertising)
self._connections.add(conn_handle)
print("Connecté")
led_blue.on() # Allume la LED bleue
# Si le central a envoyé une demande de déconnexion
elif event == _IRQ_CENTRAL_DISCONNECT:
conn_handle, _, _, = data
self._connections.remove(conn_handle)
# Relance l'advertising pour permettre de nouvelles connexions
self._advertise()
print("Déconnecté")
# Si une écriture est détectée dans la caractéristique SWITCH (interrupteur) de la LED rouge
elif event == _IRQ_GATTS_WRITE:
conn_handle, value_handle, = data
if conn_handle in self._connections and value_handle == self._switch_handle:
# Lecture de la valeur de la caractéristique
data_received = self._ble.gatts_read(self._switch_handle)
self._ble.gatts_write(self._switch_handle, pack('<HB', 1000, data_received[0]))
self._ble.gatts_notify(conn_handle, self._switch_handle)
# Selon la valeur écrite, on allume ou on éteint la LED rouge
if data_received[0] == 1:
led_red.on()
else:
led_red.off()
# On écrit, dans la caractéristique "temperature", le timestamp (horodatage) et la valeur de la température
def set_data_temperature(self, timestamp, temperature, notify):
self._ble.gatts_write(self._temperature_handle, pack('<Hh', timestamp, temperature))
if notify:
for conn_handle in self._connections:
# Signale au Central (le smartphone) que la caractéristique vient d'être écrite et peut être lue
self._ble.gatts_notify(conn_handle, self._temperature_handle)
# Démarre l'advertising avec une période de 5 secondes, précise qu'un central pourra se connecter au périphérique
def _advertise(self, interval_us=500000):
self._ble.gap_advertise(interval_us, adv_data=self._payload, connectable=True)
led_blue.off() # Eteint la LED bleue
Revue de main.py
Concentrons nous à présent sur le script principal :
# Objet du script :
# Communication en BLE d'une valeur de température lue sur un capteur HTS221.
# Les valeurs de température peuvent être simulée par un générateur de nombres aléatoire
# en l'absence du capteur.
# Affichage sur l'application smartphone ST BLE Sensor.
# Possibilité de commander la LED rouge de la carte NUCLEO-WB55 depuis le smartphone.
from time import sleep_ms, time # Pour gérer l'horodatage et les temporisations
import bluetooth # Pour gérer le protocole BLE
import ble_sensor # Pour l'implémentation du protocole GATT Blue-ST
# Est-ce qu'un HTS221 est connecté à la NUCLEO-WB55 ?
SENSOR = True
# S'il n'y a pas de capteur
if not SENSOR: # Equivalent à if SENSOR == False:
print("Aucun capteur HTS221, les mesures seront simulées")
from random import randint # Pour générer des nombres entiers aléatoires
# Si le capteur est présent
else:
from machine import I2C
import hts221 # Pour gérer le capteur MEMS HTS221
# On utilise l'I2C n°1 de la carte NUCLEO-W55 pour communiquer avec les capteurs
i2c = I2C(1)
# Pause d'une seconde pour laisser à l'I2C le temps de s'initialiser
sleep_ms(1000)
# Liste des adresses I2C des périphériques présents
print("I2C addresses used : " + str(i2c.scan()))
# Instance du HTS221
sensor = hts221.HTS221(i2c)
ble = bluetooth.BLE() # Instance de la classe BLE
ble_device = ble_sensor.BLESensor(ble) # Instance de la classe Blue-ST
while True: # Boucle sans clause de sortie
# Horodatage
timestamp = time()
if not SENSOR:
# Valeur de température aléatoire entre -20 et 90°C
temp = randint(-20, 90)
else:
# Valeur de température mesurée par le capteur
temp = sensor.temperature()
stime = str(timestamp)
stemp = str(round(temp,1))
# Multiplie par 10 la température pour la coder sous forme d'entier en conservant une décimale
# (norme adoptée par Blue-ST).
ble_temp = int(temp*10)
# Affichage sur le port série de l'USB USER
print("Horodatage : " + stime + " Température : " + stemp + " °C")
# Envoi en BLE de l'horodatage et de la température en notifiant l'application
ble_device.set_data_temperature(timestamp, ble_temp, notify=1)
# Temporisation d'une seconde
sleep_ms(1000)
Connexion avec ST BLE Sensor
Une fois le script principal lancé, la NUCLEO-WB55 émet des trames BLE de type “advertising” ; ces messages permettent d’identifier l’objet Bluetooth et de signifier qu’il est prêt à être connecté. Le nom du périphérique est : “WB55-MPY”. Nous allons vérifier avec l’application smartphone que la carte est en émission Bluetooth.
Utilisation
Lancez l’application STBLESensor sur votre smartphone :
Appuyez ensuite sur l’icône loupe pour afficher les périphériques BLE environnants
Dans cet exemple, le profil BLE que nous avons construit nous permet d’afficher les valeurs de température en provenance du HTS221 de la X-NUCLEO-IKS01A3 et d’allumer ou d’éteindre la LED rouge de la NUCLEO-WB55.
Connectez-vous au STM32WB55 en appuyant sur “WB55-MPY”:
La LED bleue de la carte NUCLEO-WB55 doit s’allumer lorsqu’elle est connectée à l’application.
Nous pouvons observer, sur cet écran, l’évolution de la température.
Il est possible d’afficher la température en mode graphique.
Pour cela appuyez sur le bouton menu :
Appuyez maintenant sur :
Pour afficher le graphique, appuyez sur :
Vous pouvez utiliser le bouton pour modifier les options du graphique, comme la taille de l’axe X ou l’activation du changement automatique de l’échelle en Y.
Nous allons maintenant étudier l’envoi d’une information depuis le smartphone vers la NUCLEO-WB55. Pour cela nous utilisons l’application pour allumer ou éteindre une la LED rouge du kit de développement.
Appuyez sur le bouton menu :
Choisissez maintenant l’option :
Vous pouvez sur cet écran piloter la LED Rouge de la NUCLEO-WB55.
Pour aller plus loin
Le tutoriel Station environnementale Blue-ST montre comment ajouter encore deux caractéristiques dans ble_sensor.py et construire une petite station de mesures environnementales connectée.