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

#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
}