Browse Source

Ajout support for ESP32-S3 board

Verification parsing fichier quiz
master
scayac 3 months ago
parent
commit
dd1f360371
  1. 238
      index.html
  2. 24
      platformio.ini
  3. 2
      src/main.cpp

238
index.html

@ -373,11 +373,99 @@ @@ -373,11 +373,99 @@
grid-template-columns: 1fr;
}
}
/* Système de notifications */
.notification {
position: fixed;
top: 20px;
right: 20px;
min-width: 300px;
max-width: 500px;
padding: 20px 25px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
color: white;
font-weight: 500;
font-size: 15px;
z-index: 10000;
animation: slideIn 0.3s ease-out;
display: flex;
align-items: flex-start;
gap: 12px;
line-height: 1.5;
}
.notification.success {
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
}
.notification.error {
background: linear-gradient(135deg, #f44336 0%, #da190b 100%);
}
.notification.info {
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
}
.notification-icon {
font-size: 24px;
flex-shrink: 0;
}
.notification-content {
flex: 1;
white-space: pre-line;
}
.notification-close {
background: rgba(255,255,255,0.2);
border: none;
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
line-height: 1;
padding: 0;
flex-shrink: 0;
transition: background 0.2s;
}
.notification-close:hover {
background: rgba(255,255,255,0.3);
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
.notification.hiding {
animation: slideOut 0.3s ease-in forwards;
}
</style>
</head>
<body>
<div class="container">
<h1>📝 Quiz Interactif ESP32</h1>
<h1>📝 Quiz Interactif</h1>
<div class="header-controls">
<div class="file-input-container">
@ -392,7 +480,7 @@ @@ -392,7 +480,7 @@
<span class="scoring-label"></span>
<input type="number" id="incorrectScore" class="scoring-input" value="0" min="-10" max="10">
</div>
<button id="connectBtn" class="btn-connect">🔵 Connecter ESP32</button>
<button id="connectBtn" class="btn-connect">🔵 Connecter module</button>
<div class="modules-status" id="modulesStatus">Aucun module</div>
</div>
@ -452,9 +540,36 @@ @@ -452,9 +540,36 @@
const correctScoreInput = document.getElementById('correctScore');
const incorrectScoreInput = document.getElementById('incorrectScore');
// Fonction pour afficher une notification
function showNotification(message, type = 'info', duration = 5000) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
const icons = {
success: '✅',
error: '❌',
info: 'ℹ'
};
notification.innerHTML = `
<div class="notification-icon">${icons[type]}</div>
<div class="notification-content">${message}</div>
<button class="notification-close" onclick="this.parentElement.remove()">×</button>
`;
document.body.appendChild(notification);
if (duration > 0) {
setTimeout(() => {
notification.classList.add('hiding');
setTimeout(() => notification.remove(), 300);
}, duration);
}
}
// Vérifier si Web Bluetooth est disponible
if (!navigator.bluetooth) {
alert('Web Bluetooth non supporté. Utilisez Chrome, Edge ou Opera.');
showNotification('Web Bluetooth non supporté. Utilisez Chrome, Edge ou Opera.', 'error', 0);
connectBtn.disabled = true;
}
@ -481,13 +596,13 @@ @@ -481,13 +596,13 @@
async function connectModule() {
try {
console.log('Recherche ESP32...');
console.log('Recherche module...');
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [SERVICE_UUID] }]
});
console.log('ESP32 trouvé:', device.name);
console.log('Module trouvé:', device.name);
const server = await device.gatt.connect();
const service = await server.getPrimaryService(SERVICE_UUID);
@ -539,7 +654,7 @@ @@ -539,7 +654,7 @@
} catch (error) {
console.error('Erreur connexion:', error);
alert(`Erreur: ${error.message}`);
showNotification(`Erreur de connexion: ${error.message}`, 'error');
}
}
@ -676,9 +791,16 @@ @@ -676,9 +791,16 @@
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
parseQuizFile(content);
if (questions.length > 0) {
startQuiz();
try {
parseQuizFile(content);
if (questions.length > 0) {
showNotification(`Quiz chargé avec succès!\n\n${questions.length} question(s) trouvée(s).`, 'success', 3000);
startQuiz();
}
} catch (error) {
console.error('Erreur lors du chargement du quiz:', error);
questions = [];
// L'erreur a déjà été affichée dans parseQuizFile
}
};
reader.readAsText(file);
@ -689,37 +811,117 @@ @@ -689,37 +811,117 @@
questions = [];
const lines = content.trim().split('\n');
let currentQuestion = null;
let lineNumber = 0;
const errors = [];
for (let line of lines) {
lineNumber++;
const originalLine = line;
line = line.trim();
if (!line) continue;
if (line.startsWith('* ')) {
// Valider la question précédente avant d'en commencer une nouvelle
if (currentQuestion) {
questions.push(currentQuestion);
const questionErrors = validateQuestion(currentQuestion, lineNumber - 1);
if (questionErrors.length > 0) {
errors.push(...questionErrors);
} else {
questions.push(currentQuestion);
}
}
const questionText = line.substring(2).trim();
if (!questionText) {
errors.push(`Ligne ${lineNumber}: Question vide après '*'`);
}
currentQuestion = {
question: line.substring(2).trim(),
question: questionText,
answers: [],
correctAnswer: -1
correctAnswer: -1,
lineNumber: lineNumber
};
} else if (line.startsWith('+ ')) {
if (currentQuestion) {
currentQuestion.correctAnswer = currentQuestion.answers.length;
currentQuestion.answers.push(line.substring(2).trim());
if (!currentQuestion) {
errors.push(`Ligne ${lineNumber}: Réponse correcte '+' trouvée sans question précédente`);
continue;
}
const answerText = line.substring(2).trim();
if (!answerText) {
errors.push(`Ligne ${lineNumber}: Réponse correcte vide après '+'`);
}
if (currentQuestion.correctAnswer !== -1) {
errors.push(`Ligne ${lineNumber}: Question à la ligne ${currentQuestion.lineNumber} a déjà une réponse correcte (ligne avec '+')`);
}
currentQuestion.correctAnswer = currentQuestion.answers.length;
currentQuestion.answers.push(answerText);
} else if (line.startsWith('- ')) {
if (currentQuestion) {
currentQuestion.answers.push(line.substring(2).trim());
if (!currentQuestion) {
errors.push(`Ligne ${lineNumber}: Réponse '-' trouvée sans question précédente`);
continue;
}
const answerText = line.substring(2).trim();
if (!answerText) {
errors.push(`Ligne ${lineNumber}: Réponse vide après '-'`);
}
currentQuestion.answers.push(answerText);
} else {
// Ligne avec un format non reconnu
errors.push(`Ligne ${lineNumber}: Format non reconnu. Utilisez '* ' pour une question, '+ ' pour la bonne réponse, '- ' pour les mauvaises réponses`);
}
}
// Valider la dernière question
if (currentQuestion) {
questions.push(currentQuestion);
const questionErrors = validateQuestion(currentQuestion, lineNumber);
if (questionErrors.length > 0) {
errors.push(...questionErrors);
} else {
questions.push(currentQuestion);
}
}
// Vérifier s'il y a des erreurs
if (errors.length > 0) {
const errorMessage = `Erreurs détectées dans le fichier de quiz:\n\n${errors.join('\n')}`;
showNotification(errorMessage, 'error', 10000);
throw new Error('Format de quiz invalide');
}
// Vérifier qu'il y a au moins une question
if (questions.length === 0) {
showNotification('Erreur: Aucune question valide trouvée dans le fichier.\n\nFormat attendu:\n* Question ?\n+ Bonne réponse\n- Mauvaise réponse\n- Mauvaise réponse', 'error', 8000);
throw new Error('Aucune question trouvée');
}
}
function validateQuestion(question, lineNumber) {
const errors = [];
// Vérifier qu'il y a au moins 2 réponses
if (question.answers.length < 2) {
errors.push(`Ligne ${question.lineNumber}: Question "${question.question}" n'a que ${question.answers.length} réponse(s). Minimum requis: 2`);
}
// Vérifier qu'il y a maximum 4 réponses (A, B, C, D)
if (question.answers.length > 4) {
errors.push(`Ligne ${question.lineNumber}: Question "${question.question}" a ${question.answers.length} réponses. Maximum autorisé: 4 (A, B, C, D)`);
}
// Vérifier qu'il y a une réponse correcte
if (question.correctAnswer === -1) {
errors.push(`Ligne ${question.lineNumber}: Question "${question.question}" n'a pas de réponse correcte (utilisez '+' devant la bonne réponse)`);
}
return errors;
}
function startQuiz() {
quizContainer.classList.remove('hidden');
currentQuestionIndex = 0;

24
platformio.ini

@ -1,15 +1,19 @@ @@ -1,15 +1,19 @@
; 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
[env:esp32-s3-supermini]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
board_build.arduino.memory_type = qio_qspi
board_build.flash_mode = qio
board_build.psram_type = qio
board_upload.flash_size = 4MB
board_upload.maximum_size = 4194304
board_build.partitions = default.csv
board_build.extra_flags =
-DBOARD_HAS_PSRAM

2
src/main.cpp

@ -22,7 +22,7 @@ bool oldDeviceConnected = false; @@ -22,7 +22,7 @@ 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";
String moduleName = "ESP32S3-Module-1";
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {

Loading…
Cancel
Save