// Import necessary modules const mqtt = require('mqtt'); // MQTT broker configuration const brokerUrl = 'mqtt://localhost'; // Broker URL (change if needed) const clientId = 'buzzer_manager'; const options = { clientId, clean: true }; // State variables let buzzerActive = false; // Indicates if buzzers are blocked let buzzerThatPressed = null; // Stores the ID of the buzzer that pressed first let tiltBuzzers = new Set(); // List of buzzer IDs currently in "tilt" mode // Connect to the MQTT broker const client = mqtt.connect(brokerUrl, options); client.on('connect', () => { console.log(`[INFO] Connected to MQTT broker ${brokerUrl} with ID ${clientId}`); // Subscribe to topics related to buzzer presses, unlocking, and tilt management client.subscribe('brainblast/buzzer/pressed/#', (err) => { if (err) console.error('[ERROR] Failed to subscribe to buzzer presses'); else console.log('[INFO] Successfully subscribed to buzzer presses'); }); client.subscribe('brainblast/buzzer/unlock', (err) => { if (err) console.error('[ERROR] Failed to subscribe to buzzer unlock'); else console.log('[INFO] Successfully subscribed to buzzer unlock'); }); client.subscribe('brainblast/buzzer/tilt', (err) => { if (err) console.error('[ERROR] Failed to subscribe to tilt management'); else console.log('[INFO] Successfully subscribed to tilt management'); }); }); // Validate buzzer payload function validateBuzzerPayload(payload) { const { buzzer_id, color } = payload; // Validate buzzer ID (should be a positive integer) if (typeof buzzer_id !== 'number' || buzzer_id <= 0) { console.error(`[ERROR] Invalid buzzer ID: ${buzzer_id}`); return false; } // Validate color (should be in the format #RRGGBB) const validColor = /^#[0-9A-F]{6}$/i.test(color); if (!validColor) { console.error(`[ERROR] Invalid color: ${color}`); return false; } return true; } // Send updated tilt status function sendTiltStatus(action, buzzerId) { const tiltList = Array.from(tiltBuzzers); // Convert Set to Array client.publish('brainblast/buzzer/status', JSON.stringify({ status: "tilt_update", tilt_buzzers: tiltList, message: `Buzzer ID ${buzzerId} ${action} to tilt mode`, timestamp: new Date().toISOString() })); console.log(`[INFO] Tilt status updated: ${tiltList.length} buzzers in tilt mode`); } // Handle incoming messages client.on('message', (topic, message) => { let payload; // Parse the incoming message try { payload = JSON.parse(message.toString()); } catch (e) { console.error(`[ERROR] Invalid JSON message received on topic ${topic}: ${message.toString()}`); return; } // Manage tilt mode for buzzers if (topic === 'brainblast/buzzer/tilt') { const { buzzer_id, status } = payload; if (typeof buzzer_id !== 'number' || !['add', 'remove'].includes(status)) { console.error('[ERROR] Invalid tilt payload, message ignored.'); return; } // Update tilt status based on the command if (status === 'add') { tiltBuzzers.add(buzzer_id); console.log(`[INFO] Buzzer ID ${buzzer_id} added to tilt mode`); } else if (status === 'remove') { tiltBuzzers.delete(buzzer_id); console.log(`[INFO] Buzzer ID ${buzzer_id} removed from tilt mode`); } // Confirm that the tilt command has been received client.publish(`brainblast/buzzer/tilt/confirmation/${buzzer_id}`, JSON.stringify({ status: "received", action: status, buzzer_id: buzzer_id, message: `Tilt command '${status}' received for buzzer ID ${buzzer_id}`, timestamp: new Date().toISOString() })); // Send the updated tilt status to all components sendTiltStatus(status === 'add' ? 'added' : 'removed', buzzer_id); return; } // Detect a buzzer press if (topic.startsWith('brainblast/buzzer/pressed/')) { // Validate buzzer payload if (!validateBuzzerPayload(payload)) { console.error('[ERROR] Invalid buzzer payload, message ignored.'); return; } const buzzerId = payload.buzzer_id; // Unique buzzer ID const color = payload.color; // Associated hex color // Always send a confirmation, even if the buzzer is in tilt mode client.publish(`brainblast/buzzer/confirmation/${buzzerId}`, JSON.stringify({ status: "received", buzzer_id: buzzerId, message: `Buzzer ID ${buzzerId} received (Color: ${color})`, timestamp: new Date().toISOString() })); // Ignore if the buzzer is in tilt mode, but notify this event if (tiltBuzzers.has(buzzerId)) { console.log(`[INFO] Buzzer ID ${buzzerId} ignored (Tilt mode active)`); // Notify that the buzzer is in tilt mode and ignored client.publish(`brainblast/buzzer/tilt/ignored/${buzzerId}`, JSON.stringify({ status: "tilt_ignored", buzzer_id: buzzerId, message: `Buzzer ID ${buzzerId} is in tilt mode and ignored.`, timestamp: new Date().toISOString() })); return; } // Notify activity even if buzzers are blocked client.publish('brainblast/buzzer/activity', JSON.stringify({ buzzer_id: buzzerId, color: color, status: buzzerActive ? "blocked" : "free", message: `Activity detected on buzzer ID ${buzzerId} (Color: ${color})`, timestamp: new Date().toISOString() })); if (!buzzerActive) { // Block further buzzers and record the first pressed ID buzzerActive = true; buzzerThatPressed = buzzerId; console.log(`[INFO] Buzzer activated by ID: ${buzzerId} (Color: ${color})`); // Notify the light manager to change to the team's color client.publish('brainblast/light/change', JSON.stringify({ color: color, effect: 'full_color' })); // Notify all components of buzzer blocking client.publish('brainblast/buzzer/status', JSON.stringify({ status: "blocked", buzzer_id: buzzerId, color: color, message: `Buzzer activated by ID ${buzzerId} (Color: ${color})`, timestamp: new Date().toISOString() })); console.log(`[INFO] Buzzers blocked and notification sent`); } } // Unlock buzzers if (topic === 'brainblast/buzzer/unlock') { console.log('[INFO] Buzzer unlock requested'); // Confirm receipt of unlock command client.publish('brainblast/buzzer/unlock/confirmation', JSON.stringify({ status: "received", message: "Buzzer unlock command received.", timestamp: new Date().toISOString() })); // Reset buzzer manager state buzzerActive = false; buzzerThatPressed = null; // Notify all components of buzzer unlock client.publish('brainblast/buzzer/status', JSON.stringify({ status: "unblocked", message: "Buzzers unblocked and ready for activation.", timestamp: new Date().toISOString() })); console.log('[INFO] Buzzers unblocked and notification sent'); } }); client.on('error', (err) => { console.error(`[ERROR] Error connecting to broker: ${err}`); }); console.log('[INFO] Buzzer manager started...');