You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
9.4 KiB
279 lines
9.4 KiB
#include <Arduino.h> |
|
#include <BLEDevice.h> |
|
#include <BLEServer.h> |
|
#include <BLEUtils.h> |
|
#include <BLE2902.h> |
|
#include <BLESecurity.h> |
|
#include <esp_gap_ble_api.h> |
|
#include <Adafruit_NeoPixel.h> |
|
|
|
// Configuration LED WS2812 |
|
#define LED_PIN 48 |
|
#define LED_COUNT 1 |
|
Adafruit_NeoPixel led(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); |
|
|
|
// Couleurs |
|
#define COLOR_OFF led.Color(0, 0, 0) |
|
#define COLOR_WHITE led.Color(255, 255, 255) |
|
#define COLOR_RED led.Color(255, 0, 0) |
|
#define COLOR_GREEN led.Color(0, 255, 0) |
|
#define COLOR_BLUE led.Color(0, 0, 255) |
|
#define COLOR_YELLOW led.Color(255, 255, 0) |
|
|
|
// UUID pour le service et la caractéristique BLE |
|
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" |
|
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" |
|
#define COMMAND_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a9" |
|
|
|
// Configuration des GPIO à surveiller |
|
// GPIO compatibles ESP32 Wrover ET ESP32-C3 : 2, 4, 5, 6 |
|
const int GPIO_PINS[] = {2, 4, 5, 6}; // GPIOs à surveiller (A, B, C, D) |
|
const int NUM_PINS = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); |
|
|
|
// GPIO pour l'identification du module (8, 9, 10) |
|
const int ID_GPIO_PINS[] = {8, 9, 10}; // Bit 0, Bit 1, Bit 2 |
|
const int NUM_ID_PINS = sizeof(ID_GPIO_PINS) / sizeof(ID_GPIO_PINS[0]); |
|
|
|
// Variables globales |
|
BLEServer* pServer = NULL; |
|
BLECharacteristic* pCharacteristic = NULL; |
|
BLECharacteristic* pCommandCharacteristic = NULL; |
|
bool deviceConnected = false; |
|
bool oldDeviceConnected = false; |
|
uint8_t gpioStates = 0; // Stocke l'état des GPIOs (bit à bit) |
|
unsigned long lastBlinkTime = 0; |
|
bool blinkState = false; |
|
bool gpioEnabled = false; // false = GPIO désactivés (résultats ou scores), true = GPIO actifs |
|
|
|
// Nom du module (sera déterminé automatiquement) |
|
String moduleName = "BleQuiz-1"; |
|
|
|
// Callback pour la caractéristique de commande |
|
class CommandCallbacks: public BLECharacteristicCallbacks { |
|
void onWrite(BLECharacteristic* pCharacteristic) { |
|
std::string value = pCharacteristic->getValue(); |
|
if (value.length() > 0) { |
|
String command = String(value.c_str()); |
|
Serial.println("Commande reçue: " + command); |
|
|
|
if (command == "RESET") { |
|
// Garder la LED dans sa couleur actuelle, désactiver les GPIO |
|
gpioEnabled = false; |
|
Serial.println("Résultats affichés - LED figée, GPIO désactivés"); |
|
} else if (command == "START") { |
|
// Passer en blanc fixe, activer les GPIO |
|
gpioEnabled = true; |
|
led.setPixelColor(0, COLOR_WHITE); |
|
led.show(); |
|
Serial.println("Question démarrée - Blanc fixe, GPIO actifs"); |
|
} else if (command == "SCORES") { |
|
// Passer en blanc fixe, désactiver les GPIO |
|
gpioEnabled = false; |
|
led.setPixelColor(0, COLOR_WHITE); |
|
led.show(); |
|
Serial.println("Scores affichés - Blanc fixe, GPIO désactivés"); |
|
} |
|
} |
|
} |
|
}; |
|
|
|
class MyServerCallbacks: public BLEServerCallbacks { |
|
void onConnect(BLEServer* pServer) { |
|
deviceConnected = true; |
|
gpioEnabled = true; |
|
led.setPixelColor(0, COLOR_WHITE); |
|
led.show(); |
|
Serial.println("Client connecté - Blanc fixe"); |
|
}; |
|
|
|
void onDisconnect(BLEServer* pServer) { |
|
deviceConnected = false; |
|
gpioEnabled = false; |
|
Serial.println("Client déconnecté - Mode clignotant"); |
|
} |
|
}; |
|
|
|
void setup() { |
|
Serial.begin(115200); |
|
Serial.println("Démarrage du module ESP32 Bluetooth..."); |
|
|
|
// Initialiser la LED WS2812 (éteinte au démarrage) |
|
led.begin(); |
|
led.setPixelColor(0, COLOR_OFF); |
|
led.show(); |
|
|
|
// Configuration des GPIO d'identification (8, 9, 10) en entrée avec pull-up |
|
for (int i = 0; i < NUM_ID_PINS; i++) { |
|
pinMode(ID_GPIO_PINS[i], INPUT_PULLUP); |
|
} |
|
|
|
// Lire l'ID du module (GPIO 8=bit0, 9=bit1, 10=bit2) |
|
uint8_t moduleId = 0; |
|
for (int i = 0; i < NUM_ID_PINS; i++) { |
|
if (digitalRead(ID_GPIO_PINS[i]) == LOW) { |
|
moduleId |= (1 << i); |
|
} |
|
} |
|
moduleId += 1; // ID de 1 à 8 (000=1, 001=2, ..., 111=8) |
|
moduleName = "BleQuiz-" + String(moduleId); |
|
|
|
Serial.print("ID du module détecté: "); |
|
Serial.println(moduleId); |
|
Serial.print("Nom du module: "); |
|
Serial.println(moduleName); |
|
|
|
// Configuration des GPIO en entrée avec pull-up |
|
for (int i = 0; i < NUM_PINS; i++) { |
|
pinMode(GPIO_PINS[i], INPUT_PULLUP); |
|
} |
|
|
|
// Initialisation BLE |
|
BLEDevice::init(moduleName.c_str()); |
|
|
|
// --- Configuration sécurité BLE pour compatibilité Android/iOS --- |
|
// Mode avec bonding pour mémoriser l'appairage |
|
BLESecurity *pSecurity = new BLESecurity(); |
|
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); // Appairage sécurisé avec bonding |
|
pSecurity->setCapability(ESP_IO_CAP_OUT); // Affichage d'un code (via Serial) |
|
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); |
|
pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); |
|
|
|
// Activer le static passkey (code fixe : 123456) |
|
uint32_t passkey = 123456; |
|
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); |
|
|
|
Serial.println("Code d'appairage : 123456"); |
|
|
|
// Créer le serveur BLE |
|
pServer = BLEDevice::createServer(); |
|
pServer->setCallbacks(new MyServerCallbacks()); |
|
|
|
// Créer le service BLE |
|
BLEService *pService = pServer->createService(SERVICE_UUID); |
|
|
|
// Créer la caractéristique BLE |
|
pCharacteristic = pService->createCharacteristic( |
|
CHARACTERISTIC_UUID, |
|
BLECharacteristic::PROPERTY_READ | |
|
BLECharacteristic::PROPERTY_WRITE | |
|
BLECharacteristic::PROPERTY_NOTIFY | |
|
BLECharacteristic::PROPERTY_INDICATE |
|
); |
|
|
|
// Créer un descripteur BLE2902 pour les notifications |
|
pCharacteristic->addDescriptor(new BLE2902()); |
|
|
|
// Créer la caractéristique de commande (pour RESET) |
|
pCommandCharacteristic = pService->createCharacteristic( |
|
COMMAND_UUID, |
|
BLECharacteristic::PROPERTY_WRITE |
|
); |
|
pCommandCharacteristic->setCallbacks(new CommandCallbacks()); |
|
|
|
// Démarrer le service |
|
pService->start(); |
|
|
|
// Démarrer l'advertising |
|
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); |
|
pAdvertising->addServiceUUID(SERVICE_UUID); |
|
pAdvertising->setScanResponse(true); // Activer scan response pour iOS |
|
pAdvertising->setMinPreferred(0x06); // iOS recommande 20ms minimum |
|
pAdvertising->setMaxPreferred(0x12); // iOS recommande 40ms maximum |
|
BLEDevice::startAdvertising(); |
|
|
|
Serial.println("Advertising BLE démarré"); |
|
|
|
// Initialiser l'état des GPIO |
|
for (int i = 0; i < NUM_PINS; i++) { |
|
int pinState = digitalRead(GPIO_PINS[i]); |
|
if (pinState == LOW) { |
|
gpioStates |= (1 << i); |
|
} |
|
} |
|
|
|
Serial.println("En attente de connexion..."); |
|
} |
|
|
|
void loop() { |
|
// Gestion du clignotement blanc uniquement si déconnecté |
|
if (!deviceConnected) { |
|
unsigned long currentTime = millis(); |
|
if (currentTime - lastBlinkTime >= 500) { |
|
lastBlinkTime = currentTime; |
|
blinkState = !blinkState; |
|
if (blinkState) { |
|
led.setPixelColor(0, COLOR_WHITE); |
|
} else { |
|
led.setPixelColor(0, COLOR_OFF); |
|
} |
|
led.show(); |
|
} |
|
} |
|
|
|
// Lire l'état de toutes les GPIO |
|
uint8_t currentStates = 0; |
|
for (int i = 0; i < NUM_PINS; i++) { |
|
int pinState = digitalRead(GPIO_PINS[i]); |
|
if (pinState == LOW) { // GPIO à l'état bas |
|
currentStates |= (1 << i); |
|
} |
|
} |
|
|
|
// Détecter les changements d'état des GPIO (passage à LOW) |
|
// Uniquement si connecté ET GPIO activés |
|
uint8_t newPresses = currentStates & ~gpioStates; |
|
if (newPresses != 0 && deviceConnected && gpioEnabled) { |
|
// Allumer la LED selon le GPIO pressé |
|
if (newPresses & (1 << 0)) { // GPIO 2 |
|
led.setPixelColor(0, COLOR_RED); |
|
led.show(); |
|
Serial.println("GPIO 2 pressé - LED ROUGE"); |
|
} else if (newPresses & (1 << 1)) { // GPIO 4 |
|
led.setPixelColor(0, COLOR_GREEN); |
|
led.show(); |
|
Serial.println("GPIO 4 pressé - LED VERTE"); |
|
} else if (newPresses & (1 << 2)) { // GPIO 5 |
|
led.setPixelColor(0, COLOR_BLUE); |
|
led.show(); |
|
Serial.println("GPIO 5 pressé - LED BLEUE"); |
|
} else if (newPresses & (1 << 3)) { // GPIO 6 |
|
led.setPixelColor(0, COLOR_YELLOW); |
|
led.show(); |
|
Serial.println("GPIO 6 pressé - LED JAUNE"); |
|
} |
|
} |
|
|
|
// Créer un message JSON avec l'état des GPIO |
|
String message = "{\"module\":\"" + moduleName + "\",\"gpios\":["; |
|
for (int i = 0; i < NUM_PINS; i++) { |
|
message += "{\"pin\":" + String(GPIO_PINS[i]) + ",\"state\":"; |
|
message += ((currentStates & (1 << i)) ? "1" : "0"); |
|
message += "}"; |
|
if (i < NUM_PINS - 1) message += ","; |
|
} |
|
message += "]}"; |
|
|
|
// Mettre à jour la valeur de la caractéristique |
|
pCharacteristic->setValue(message.c_str()); |
|
|
|
// Si l'état a changé et qu'un client est connecté, envoyer notification |
|
if (currentStates != gpioStates && deviceConnected) { |
|
gpioStates = currentStates; |
|
pCharacteristic->notify(); |
|
Serial.println("État changé: " + message); |
|
} |
|
|
|
// Gestion de la reconnexion |
|
if (!deviceConnected && oldDeviceConnected) { |
|
delay(500); |
|
pServer->startAdvertising(); |
|
Serial.println("Redémarrage de l'advertising"); |
|
oldDeviceConnected = deviceConnected; |
|
} |
|
|
|
if (deviceConnected && !oldDeviceConnected) { |
|
oldDeviceConnected = deviceConnected; |
|
} |
|
|
|
delay(100); // Délai pour éviter une lecture trop fréquente |
|
}
|
|
|