From 96099891022ed4441e6edbe7ccae8bfc2b971f59 Mon Sep 17 00:00:00 2001 From: scayac Date: Mon, 5 Jan 2026 10:18:56 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 + README.md | 180 +++++++++++++++++++ cert.pem | 22 +++ include/README | 37 ++++ index.html | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ key.pem | 28 +++ lib/README | 46 +++++ platformio.ini | 15 ++ server.js | 101 +++++++++++ server.py | 76 ++++++++ src/main.cpp | 134 ++++++++++++++ start.sh | 79 +++++++++ test/README | 11 ++ 13 files changed, 1203 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 cert.pem create mode 100644 include/README create mode 100644 index.html create mode 100644 key.pem create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 server.js create mode 100755 server.py create mode 100644 src/main.cpp create mode 100755 start.sh create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1669249 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio +.vscode +.git diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8678e2 --- /dev/null +++ b/README.md @@ -0,0 +1,180 @@ +# ESP32 Bluetooth HTML Monitor + +Système de surveillance Bluetooth permettant de connecter plusieurs modules ESP32 et d'afficher l'état de leurs GPIOs en temps réel sur une page web. + +## 📋 Fonctionnalités + +- ✅ Connexion de plusieurs modules ESP32 via Bluetooth (Web Bluetooth API) +- ✅ Affichage en temps réel de l'état des GPIOs +- ✅ Interface web moderne et responsive +- ✅ Notifications visuelles lors des changements d'état +- ✅ Gestion automatique de la reconnexion + +## 🔧 Configuration + +### ESP32 (src/main.cpp) + +Par défaut, le code surveille 4 GPIOs : **15, 16, 17, 18** + +Pour modifier les GPIOs surveillés, éditez cette ligne : +```cpp +const int GPIO_PINS[] = {15, 16, 17, 18}; // Modifier selon vos besoins +``` + +Pour changer le nom du module (important pour différencier plusieurs ESP32) : +```cpp +String moduleName = "ESP32-Module-1"; // Changer pour chaque ESP32 +``` + +### GPIOs avec Pull-up +Les GPIOs sont configurés en **INPUT_PULLUP**, ce qui signifie : +- **État normal (non connecté)** : HIGH (3.3V) +- **État actif (connecté à GND)** : LOW (0V) + +Le système envoie une notification à la page HTML **quand un GPIO passe à l'état BAS**. + +## 🚀 Installation et utilisation + +### 1. Compiler et téléverser sur ESP32 + +```bash +# Compiler le projet +pio run + +# Téléverser sur l'ESP32 +pio run --target upload + +# Monitorer le port série (optionnel) +pio device monitor +``` + +### 2. Démarrer le serveur HTTPS + +⚠️ **Important** : Web Bluetooth nécessite HTTPS (ou localhost en HTTP) + +**Option A - Serveur Python (recommandé)** : +```bash +python3 server.py +``` + +**Option B - Serveur Node.js** : +```bash +node server.js +``` + +Les deux serveurs : +- Génèrent automatiquement un certificat SSL auto-signé +- Démarrent sur `https://localhost:8443` +- Servent la page HTML avec support Bluetooth + +### 3. Ouvrir dans le navigateur + +Ouvrez **Chrome**, **Edge** ou **Opera** et accédez à : +``` +https://localhost:8443 +``` + +**Note** : +- Vous verrez un avertissement de sécurité (certificat auto-signé) → Cliquez sur "Avancé" puis "Continuer vers localhost" +- Firefox et Safari ne supportent pas encore Web Bluetooth + +### 3. Connecter les modules + +1. Cliquez sur **"Connecter un module"** +2. Sélectionnez votre ESP32 dans la liste +3. Les GPIOs s'affichent en temps réel +4. Répétez pour connecter plusieurs modules + +## 🌐 Pourquoi HTTPS est nécessaire ? + +Web Bluetooth API nécessite un **contexte sécurisé** pour des raisons de sécurité : +- ✅ `https://` (avec certificat SSL) +- ✅ `http://localhost` ou `http://127.0.0.1` +- ❌ `file://` (ouverture directe du fichier HTML) + +Les serveurs fournis (`server.py` et `server.js`) génèrent automatiquement un certificat SSL auto-signé. + +## 🔌 Branchement des GPIOs + +Pour tester, connectez vos GPIOs à la masse (GND) : + +``` +ESP32 Bouton/Capteur +GPIO 15 ---- [Switch] ---- GND +GPIO 16 ---- [Switch] ---- GND +GPIO 17 ---- [Switch] ---- GND +GPIO 18 ---- [Switch] ---- GND +``` + +## 📊 Format des données + +Les données sont envoyées en JSON : +```json +{ + "module": "ESP32-Module-1", + "gpios": [ + {"pin": 15, "state": "0"}, + {"pin": 16, "state": "1"}, + {"pin": 17, "state": "0"}, + {"pin": 18, "state": "0"} + ] +} +``` + +- `state: "0"` = GPIO HIGH (état normal) +- `state: "1"` = GPIO LOW (état actif) + +## 🎨 Interface + +L'interface affiche : +- Le nom de chaque module connecté +- L'état de chaque GPIO (HAUT/BAS) avec code couleur +- L'horodatage de la dernière mise à jour +- Des animations visuelles lors des changements d'état + +## ⚙️ Personnalisation + +### Changer les UUID Bluetooth + +Dans `main.cpp` et `index.html`, modifiez : +```cpp +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +``` + +### Modifier le délai de lecture + +Dans `main.cpp`, ligne finale : +```cpp +delay(100); // Modifier selon vos besoins (en millisecondes) +``` + +## 🐛 Dépannage + +**L'ESP32 n'apparaît pas dans la liste Bluetooth** +- Vérifiez que le module est alimenté +- Consultez le moniteur série pour voir les messages de démarrage +- Assurez-vous que le Bluetooth est activé sur votre PC + +**Pas de données reçues** +- Vérifiez les UUID dans le code ESP32 et HTML +- Consultez la console JavaScript (F12) pour les erreurs +- Testez en connectant un GPIO à GND + +**Déconnexions fréquentes** +- Vérifiez la distance entre l'ESP32 et le PC +- Assurez-vous que l'alimentation de l'ESP32 est stable + +## 📝 Notes + +- Plusieurs modules peuvent être connectés simultanément +- Chaque module doit avoir un nom unique +- La page HTML doit être servie en HTTPS ou localhost pour Web Bluetooth +- Les changements d'état sont détectés et envoyés automatiquement + +## 🔐 Sécurité + +Pour un usage en production, considérez : +- Ajouter une authentification Bluetooth +- Chiffrer les données transmises +- Implémenter une validation côté serveur diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000..e80bd3c --- /dev/null +++ b/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUf44ZDI8lm8uItCwfYpCF/loe3SswDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCRlIxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0y +NjAxMDUwODEwMDFaFw0yNzAxMDUwODEwMDFaMFcxCzAJBgNVBAYTAkZSMQ4wDAYD +VQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5pemF0aW9u +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC7xWCAyFV83S56WwBO2tDZSCc31Rdbp2U3hMmk7rkNFZxYKQx02eIIcM7S +6W0cDiSf8slOelBPHfCk9Z4IhBN0P+VltrBDneGyYPvgF2ndtaYLak4qof2zTUnw +i2vyiZY3Srny0BUB8Y6LrXXAo6NBZCi5iOJ4plpw9PfREzj/kubcqEr+zA8t66/Z +ZmN9fSEp1PB8kEjwsSNuI7Gi72IYLQoAoLBZo08qwI1T+nUICq4/EEXR+6BR1zXd +TeghbD+s+snH2JutwUcLAo+10NylD9Zl/81u10Q4ySgjcv1KBweqLpzLv6jTIMMK +l7lEf1Ml7G3u+99Bilo8PffGFCCxAgMBAAGjUzBRMB0GA1UdDgQWBBSts6v8ASbq +YyyXyiaaY8HvByuPMTAfBgNVHSMEGDAWgBSts6v8ASbqYyyXyiaaY8HvByuPMTAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBr8GKZF7wmymVRsu3m +QEnGcsCCqNhI4xhEIfbEZ8IcBRcCBq7/0bV71yh0UGDi5Q7cXF0F6z7dl8+QlHWS +UuLSN4Kk4z/3E8qtN4vxTIzf6vpXqdizmOp4gHO+LZfBBOuu5oZuN/vE8GV29hbU +obzPwdzU95zPcGDKGOIUp+Zo2vWbqPUHmZkitPC+8WSQHxsifJez9BkP5jy9Cy4T +rGepv2wY7WNB9eEfdb/QKKqLRRH8p0S3w3ryM37e3INRTE+KuLGyeyX0wDLTxcml +K0Am+KntsaiATbbJjZykKXPkOdF02w2WK2d5XAx/EWXk3WuzEGSNuBo+/4ms6+LX +hkzu +-----END CERTIFICATE----- diff --git a/include/README b/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/include/README @@ -0,0 +1,37 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the convention is to give header files names that end with `.h'. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..26fdd9d --- /dev/null +++ b/index.html @@ -0,0 +1,471 @@ + + + + + + ESP32 Bluetooth Monitor + + + +
+

🔵 ESP32 Bluetooth Monitor

+ +
+ +
Aucun module connecté
+
+ +
+
+
📡
+
Cliquez sur "Connecter un module" pour ajouter un ESP32
+
+
+
+ + + + diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..35ab05d --- /dev/null +++ b/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7xWCAyFV83S56 +WwBO2tDZSCc31Rdbp2U3hMmk7rkNFZxYKQx02eIIcM7S6W0cDiSf8slOelBPHfCk +9Z4IhBN0P+VltrBDneGyYPvgF2ndtaYLak4qof2zTUnwi2vyiZY3Srny0BUB8Y6L +rXXAo6NBZCi5iOJ4plpw9PfREzj/kubcqEr+zA8t66/ZZmN9fSEp1PB8kEjwsSNu +I7Gi72IYLQoAoLBZo08qwI1T+nUICq4/EEXR+6BR1zXdTeghbD+s+snH2JutwUcL +Ao+10NylD9Zl/81u10Q4ySgjcv1KBweqLpzLv6jTIMMKl7lEf1Ml7G3u+99Bilo8 +PffGFCCxAgMBAAECggEAKYfSHKHSsKueFk9u3cINi+Vm0TVHNY0seK9cxydK1C9J +tYEcwXKYiRvkTud0q84gASWaq0on3ZvmaCHwOnt17xIb25eg3E+9Be5n/0xGbNR6 +vY8da1YJbkFDv5mivlH6oM+dGay23Mdv6wpqXzmCSBoOXNz/FEbc/Ztc/YgGJRaY +MmIINmcZtFJ4Yhtdel7U93Ec0kIXijlBxjj4LkyBhxh3elJOeLbZQeo34QbdvpSw +7S3Oxrb0XznEm8EsVnomWfVEkWhtSu3ylSaxw1cpk7X8VbtMvLRz4wjGNArspQWg +dCi5xiYd+vR/a3TvI+CpqOem9IJvWntRzDI5/QPrzQKBgQDvDiMI4NLi19xVQT23 +mKYIK+57DtiQqCnNtLhgmcM4dLhG+RCZgJTYgU5Ra8dwKIaNTpDewBrEb16mEC5G +L5r3/3W8nahmbS74hYyU+34rA6enzJ9IDcwWXfE3oHqWe8SVRbcG8pCfpS1y8Vr3 +HoTm0fG1oiMufbtmSAyrY0J3PQKBgQDJFKTMjt2QE+qmmfJzaPX2iOvl5neXkdRi +nx9FKbWje+wRsxlFHWPRxkhXaN5ICMTTNiF0uKs1M6bHakv0nyj3G0sxlB5rfdeK +uKw43galsaUll7XJ5isqeRd4eSqOLodwlyRfXLPQJYWqwwzOo1JOznqmSHxzIS3I +br3VCs/GhQKBgQCsrsyWBDmyDXYmy6r2bbk52+o4UGAWFrOj2bD6bvg7Vu0qr43T +JLPgtX9Kh42Ysl2Hw8IYdjKfxUdctAGJR6gtDwcQid50ptgwQ+BqWkUoc0pvhvtk +RDsxyQiPQ0hqXduCbMqqpRvdhqcPCdPJAO3GAtAUgZviYIa3esJUJ66CjQKBgChe +SZt7jwWOygv6Wg9LYh3FbT5xX08BitblxRxYfEu+5CzFfOxAMzr5CKrrtbxWblVM +x7isHksG/JOKRodssIezZgwlBVplIDGMU57zC/iVymbapzdKSx4yw0B/asiylKRI +45d1f+/oqIYYtGiDp1GE6GCbqsheP9e+S8QKcJ4VAoGAGAL2KWgm6rHH/H4uR2Rj +A9TuoA2U1o9o0633FJaNkSsGE9qtkkGSUOH0d7i2bragJ9D7SvJPqiMrxMDUo579 +tpDT3fa4ZpI3nBaVAiB35kJakC4big4pnrXNcUpE+Cmo4FKC/jQo1f4Dlly5CXXT +nzs1pRQmgMMtJFlb7O3QyZU= +-----END PRIVATE KEY----- diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..8dd1b2d --- /dev/null +++ b/platformio.ini @@ -0,0 +1,15 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..e0fda0d --- /dev/null +++ b/server.js @@ -0,0 +1,101 @@ +const https = require('https'); +const fs = require('fs'); +const path = require('path'); +const { exec } = require('child_process'); + +const PORT = 8443; + +// Générer un certificat auto-signé si nécessaire +function generateCertificate() { + return new Promise((resolve, reject) => { + const certPath = path.join(__dirname, 'cert.pem'); + const keyPath = path.join(__dirname, 'key.pem'); + + // Vérifier si les certificats existent déjà + if (fs.existsSync(certPath) && fs.existsSync(keyPath)) { + console.log('✓ Certificats existants trouvés'); + resolve({ cert: certPath, key: keyPath }); + return; + } + + console.log('Génération d\'un certificat auto-signé...'); + const cmd = `openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 -keyout key.pem -out cert.pem -subj "/C=FR/ST=State/L=City/O=Organization/CN=localhost"`; + + exec(cmd, (error) => { + if (error) { + console.error('Erreur lors de la génération du certificat:', error); + console.log('\n⚠️ Installez OpenSSL ou créez les certificats manuellement'); + reject(error); + return; + } + console.log('✓ Certificat généré avec succès'); + resolve({ cert: certPath, key: keyPath }); + }); + }); +} + +async function startServer() { + try { + const { cert, key } = await generateCertificate(); + + const options = { + key: fs.readFileSync(key), + cert: fs.readFileSync(cert) + }; + + const server = https.createServer(options, (req, res) => { + let filePath = '.' + req.url; + if (filePath === './') { + filePath = './index.html'; + } + + const extname = String(path.extname(filePath)).toLowerCase(); + const mimeTypes = { + '.html': 'text/html', + '.js': 'text/javascript', + '.css': 'text/css', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + }; + + const contentType = mimeTypes[extname] || 'application/octet-stream'; + + fs.readFile(filePath, (error, content) => { + if (error) { + if (error.code === 'ENOENT') { + res.writeHead(404, { 'Content-Type': 'text/html' }); + res.end('

404 - File Not Found

', 'utf-8'); + } else { + res.writeHead(500); + res.end('Error: ' + error.code, 'utf-8'); + } + } else { + res.writeHead(200, { 'Content-Type': contentType }); + res.end(content, 'utf-8'); + } + }); + }); + + server.listen(PORT, () => { + console.log('\n' + '='.repeat(60)); + console.log('🚀 Serveur HTTPS démarré avec succès!'); + console.log('='.repeat(60)); + console.log(`\n📱 Ouvrez Chrome et accédez à:`); + console.log(`\n 👉 https://localhost:${PORT}\n`); + console.log('⚠️ Vous verrez un avertissement de sécurité (certificat auto-signé)'); + console.log(' Cliquez sur "Avancé" puis "Continuer vers localhost"\n'); + console.log('🔵 Web Bluetooth sera maintenant disponible!\n'); + console.log('Appuyez sur Ctrl+C pour arrêter le serveur'); + console.log('='.repeat(60) + '\n'); + }); + + } catch (error) { + console.error('Impossible de démarrer le serveur:', error); + process.exit(1); + } +} + +startServer(); diff --git a/server.py b/server.py new file mode 100755 index 0000000..f44f4b4 --- /dev/null +++ b/server.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +""" +Serveur HTTPS simple pour Web Bluetooth API +Utilise un certificat auto-signé pour localhost +""" + +import http.server +import ssl +import os +import subprocess +import sys + +PORT = 8443 + +def generate_certificate(): + """Génère un certificat auto-signé si nécessaire""" + cert_file = 'cert.pem' + key_file = 'key.pem' + + if os.path.exists(cert_file) and os.path.exists(key_file): + print('✓ Certificats existants trouvés') + return cert_file, key_file + + print('Génération d\'un certificat auto-signé...') + try: + cmd = [ + 'openssl', 'req', '-newkey', 'rsa:2048', '-new', '-nodes', + '-x509', '-days', '365', '-keyout', key_file, '-out', cert_file, + '-subj', '/C=FR/ST=State/L=City/O=Organization/CN=localhost' + ] + subprocess.run(cmd, check=True, capture_output=True) + print('✓ Certificat généré avec succès') + return cert_file, key_file + except (subprocess.CalledProcessError, FileNotFoundError): + print('\n⚠️ Erreur: OpenSSL n\'est pas installé ou a échoué') + print('Installez OpenSSL: sudo apt install openssl') + sys.exit(1) + +def main(): + try: + cert_file, key_file = generate_certificate() + + # Changer vers le répertoire du script + os.chdir(os.path.dirname(os.path.abspath(__file__))) + + # Créer le serveur HTTP + handler = http.server.SimpleHTTPRequestHandler + httpd = http.server.HTTPServer(('localhost', PORT), handler) + + # Configurer SSL + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain(cert_file, key_file) + httpd.socket = context.wrap_socket(httpd.socket, server_side=True) + + print('\n' + '=' * 60) + print('🚀 Serveur HTTPS démarré avec succès!') + print('=' * 60) + print(f'\n📱 Ouvrez Chrome et accédez à:') + print(f'\n 👉 https://localhost:{PORT}\n') + print('⚠️ Vous verrez un avertissement de sécurité (certificat auto-signé)') + print(' Cliquez sur "Avancé" puis "Continuer vers localhost"\n') + print('🔵 Web Bluetooth sera maintenant disponible!\n') + print('Appuyez sur Ctrl+C pour arrêter le serveur') + print('=' * 60 + '\n') + + httpd.serve_forever() + + except KeyboardInterrupt: + print('\n\n✓ Serveur arrêté') + sys.exit(0) + except Exception as e: + print(f'\n❌ Erreur: {e}') + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4ac4011 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include + +// 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" + +// Configuration des GPIO à surveiller +const int GPIO_PINS[] = {15, 16, 17, 18}; // GPIOs à surveiller +const int NUM_PINS = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); + +// Variables globales +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t gpioStates = 0; // Stocke l'état des GPIOs (bit à bit) + +// Nom du module (peut être modifié pour chaque ESP32) +String moduleName = "ESP32-Module-2"; + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + Serial.println("Client connecté"); + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + Serial.println("Client déconnecté"); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Démarrage du module ESP32 Bluetooth..."); + + // 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()); + + // 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()); + + // Démarrer le service + pService->start(); + + // Démarrer l'advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); + BLEDevice::startAdvertising(); + + // 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..."); + Serial.print("Nom du module: "); + Serial.println(moduleName); +} + +void loop() { + // 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); + } + } + + // 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 +} diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..0977bb2 --- /dev/null +++ b/start.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Script pour lancer Chrome avec Web Bluetooth activé sur Linux + +echo "==========================================" +echo "Lancement de Chrome avec Web Bluetooth" +echo "==========================================" +echo "" + +PORT=8443 +URL="https://localhost:$PORT" + +# Vérifier si le serveur tourne déjà +if ! curl -k -s "$URL" > /dev/null 2>&1; then + echo "📡 Démarrage du serveur HTTPS..." + python3 server.py & + SERVER_PID=$! + echo " Serveur PID: $SERVER_PID" + sleep 2 +else + echo "✓ Serveur HTTPS déjà en cours d'exécution" +fi + +echo "" +echo "🚀 Lancement de Chrome avec Web Bluetooth..." +echo "" +echo "⚠️ Sur Linux, Web Bluetooth nécessite des flags expérimentaux" +echo "" + +# Détecter le navigateur disponible +if command -v google-chrome &> /dev/null; then + CHROME="google-chrome" +elif command -v chromium &> /dev/null; then + CHROME="chromium" +elif command -v chromium-browser &> /dev/null; then + CHROME="chromium-browser" +else + echo "❌ Chrome/Chromium non trouvé!" + echo " Installez-le avec: sudo apt install chromium-browser" + exit 1 +fi + +echo "📌 Utilisation de: $CHROME" +echo "" +echo "🔵 Chrome va s'ouvrir avec les flags nécessaires pour Web Bluetooth" +echo "" +echo "Dans Chrome:" +echo " 1. Acceptez le certificat auto-signé (Avancé > Continuer)" +echo " 2. Cliquez sur 'Connecter un module'" +echo " 3. Sélectionnez votre ESP32" +echo "" +echo "Appuyez sur Ctrl+C pour tout arrêter" +echo "==========================================" +echo "" + +# Lancer Chrome avec les flags nécessaires pour Web Bluetooth sur Linux +$CHROME \ + --enable-features=WebBluetooth \ + --enable-experimental-web-platform-features \ + --enable-web-bluetooth-new-permissions-backend \ + "$URL" \ + 2>/dev/null & + +CHROME_PID=$! + +# Fonction pour nettoyer à la sortie +cleanup() { + echo "" + echo "🛑 Arrêt du serveur..." + if [ ! -z "$SERVER_PID" ]; then + kill $SERVER_PID 2>/dev/null + fi + exit 0 +} + +trap cleanup SIGINT SIGTERM + +# Attendre +wait $CHROME_PID diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html