# ==========================================================
# ESP32 BLE - Température + Humidité (pédagogique, sans POO)
# - Mise à jour locale toutes les UPDATE_PERIOD secondes
# - Notification toutes les NOTIFY_PERIOD secondes
# - Taille des données: 2 octets (temp) + 2 octets (hum)
# ==========================================================
import bluetooth
import random
import struct
import time
from micropython import const
from ble_advertising import advertising_payload
from machine import Timer
# ----------------------------------------------------------
# ÉTAPE 1) Constantes IRQ (événements BLE)
# ----------------------------------------------------------
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_INDICATE_DONE = const(20) # Indication terminée (ACK reçu)
# ----------------------------------------------------------
# ÉTAPE 2) Flags GATT (droits d'accès)
# ----------------------------------------------------------
_FLAG_READ = const(0x0002)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
# ----------------------------------------------------------
# ÉTAPE 3) UUIDs (standards BLE)
# Service Environmental Sensing: 0x181A
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
# Caractéristiques standard :
# Température: 0x2A6E => sint16, résolution 0.01°C (2 octets)
# Humidité: 0x2A6F => uint16, résolution 0.01% (2 octets)
_TEMP_CHAR = (bluetooth.UUID(0x2A6E), _FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE)
_HUM_CHAR = (bluetooth.UUID(0x2A6F), _FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE)
_ENV_SENSE_SERVICE = (_ENV_SENSE_UUID, (_TEMP_CHAR, _HUM_CHAR))
# ----------------------------------------------------------
# ÉTAPE 4) Paramètres pédagogiques (périodicité)
# ----------------------------------------------------------
UPDATE_PERIOD = 1 # toutes les 1 seconde : on calcule une nouvelle mesure
NOTIFY_PERIOD = 10 # toutes les 10 secondes : on envoie une notification
INDICATE_PERIOD = 20 # Periode pour envoyer une indication
# ----------------------------------------------------------
# ÉTAPE 5) Variables globales (sans classes)
# ----------------------------------------------------------
ble = bluetooth.BLE()
connections = set()
handle_temp = None
handle_hum = None
payload = None
# ----------------------------------------------------------
# ÉTAPE 6) Fonctions BLE utiles
# ----------------------------------------------------------
def start_advertising(interval_us=500000):
print(">> Advertising BLE (Env Sensing)...")
ble.gap_advertise(interval_us, adv_data=payload)
def irq_handler(event, data):
if event == _IRQ_CENTRAL_CONNECT:
conn_handle, _, _ = data
print("✅ Connecté :", conn_handle)
connections.add(conn_handle)
elif event == _IRQ_CENTRAL_DISCONNECT:
conn_handle, _, _ = data
print("❌ Déconnecté :", conn_handle)
if conn_handle in connections:
connections.remove(conn_handle)
start_advertising()
elif event == _IRQ_GATTS_INDICATE_DONE:
conn_handle, value_handle, status = data
# status=0 => OK en général
print("ℹ️ Indication DONE:", conn_handle, value_handle, "status=", status)
pass
# ----------------------------------------------------------
# ÉTAPE 7) Formatage BLE + tailles (TRÈS IMPORTANT)
# ----------------------------------------------------------
def pack_temperature(temp_deg_c):
"""
Température BLE (0x2A6E) :
- type : sint16 little-endian
- unité : 0.01°C
=> 2 octets
"""
return struct.pack("<h", int(temp_deg_c * 100)) # 2 bytes
def pack_humidity(hum_percent):
"""
Humidité BLE (0x2A6F) :
- type : uint16 little-endian
- unité : 0.01 %
=> 2 octets
"""
return struct.pack("<H", int(hum_percent * 100)) # 2 bytes
def sensor_temp_hum(t):
# --- Mesure simulée ---*
temp = random.uniform(20, 30) #°C
hum = random.uniform(40, 60) #%
# 1) Formater les données en BLE
temp_bytes = pack_temperature(temp)
hum_bytes = pack_humidity(hum)
print("\n🌡️ Temp =", round(temp, 2), "°C | 💧 Hum =", round(hum, 2), "%")
# Écrire dans la GATT (valeur "officielle" lisible par READ)
ble.gatts_write(handle_temp, temp_bytes)
ble.gatts_write(handle_hum, hum_bytes)
def sens_notify(t):
for ch in connections:
ble.gatts_notify(ch, handle_temp) # envoie la valeur stockée (2 octets)
ble.gatts_notify(ch, handle_hum) # envoie la valeur stockée (2 octets)
print(" 📤 Notification envoyée (temp + hum) à", len(connections), "client(s)")
def sens_indicate(t):
for ch in connections:
ble.gatts_indicate(ch, handle_temp) # envoie la valeur stockée (2 octets)
ble.gatts_indicate(ch, handle_hum) # envoie la valeur stockée (2 octets)
print(" 📤 Notification envoyée (temp + hum) à", len(connections), "client(s)")
# ----------------------------------------------------------
# ÉTAPE 9) Programme principal
# ----------------------------------------------------------
def main():
global handle_temp, handle_hum, payload
print("=== ESP32 BLE Température + Humidité (pédagogique) ===")
# 9.1 Activer BLE + IRQ
ble.active(True)
ble.irq(irq_handler)
# 9.2 Enregistrer le service -> récupérer handles
((handle_temp, handle_hum),) = ble.gatts_register_services((_ENV_SENSE_SERVICE,))
print("Handles: temp =", handle_temp, "| hum =", handle_hum)
# 9.3 Préparer advertising
payload = advertising_payload(name="ESP32-Env", services=[_ENV_SENSE_UUID])
start_advertising()
#Les timers :
tim = Timer(0)
tim1 =Timer(1)
tim2 =Timer(2)
tim.init(period=1000, mode=Timer.PERIODIC, callback=sensor_temp_hum)
tim1.init(period=10000, mode=Timer.PERIODIC, callback=sens_notify)
tim2.init(period=20000, mode=Timer.PERIODIC, callback=sens_indicate)
while True:
time.sleep(1)
if __name__ == "__main__":
main()