feat(data) undefined: Added new HTML file for piano spectrum analyzer. fix(Config.h): Updated WiFi settings and web configuration portal details. refactor(Arduino sketch): Added WiFi and WebSocket support, enabling real-time spectrum data transmission over the

- New HTML file added to the `data` directory for a piano spectrum analyzer.
- Updated `Config.h` with WiFi settings and web configuration portal details.
- Update the Arduino sketch with WiFi and WebSocket support, enabling real-time spectrum data transmission over the web.
This commit is contained in:
2025-04-25 19:00:28 +02:00
parent c83d04eb23
commit 91b24e0da0
3 changed files with 185 additions and 3 deletions

91
data/index.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Piano Spectrum Analyzer</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #f0f0f0;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
}
#spectrum-container {
position: relative;
height: 400px;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Piano Spectrum Analyzer</h1>
<div id="spectrum-container">
<canvas id="spectrumChart"></canvas>
</div>
</div>
<script>
const ctx = document.getElementById('spectrumChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({length: 512}, (_, i) => i * (8000 / 1024)),
datasets: [{
label: 'Frequency Spectrum',
data: Array(512).fill(0),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1,
fill: false
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 0
},
scales: {
y: {
beginAtZero: true,
max: 5000
},
x: {
title: {
display: true,
text: 'Frequency (Hz)'
}
}
}
}
});
const wsUrl = `ws://${window.location.hostname}/ws`;
const ws = new WebSocket(wsUrl);
ws.onmessage = function(event) {
const spectrum = JSON.parse(event.data);
chart.data.datasets[0].data = spectrum;
chart.update();
};
ws.onclose = function() {
console.log('WebSocket connection closed');
setTimeout(() => {
window.location.reload();
}, 1000);
};
</script>
</body>
</html>

View File

@@ -6,6 +6,12 @@ namespace Config {
// Serial Configuration
constexpr int SERIAL_BAUD_RATE = 115200; // Serial communication baud rate
// WiFi Configuration
constexpr const char* WIFI_AP_NAME = "ESP32Piano"; // AP name when in configuration mode
constexpr const char* WIFI_AP_PASSWORD = "12345678"; // AP password when in configuration mode
constexpr uint32_t WIFI_CONFIG_TIMEOUT = 180; // Seconds to wait for WiFi configuration
constexpr uint32_t WIFI_CONFIG_PORT = 80; // Web configuration portal port
// I2S Pin Configuration
constexpr int I2S_MIC_SERIAL_CLOCK = 8; // SCK
constexpr int I2S_MIC_LEFT_RIGHT_CLOCK = 9; // WS/LRC

View File

@@ -1,4 +1,9 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include <SPIFFS.h>
#include "Config.h"
#include "I2SConfig.h"
#include "AudioLevelTracker.h"
@@ -9,15 +14,55 @@
static int16_t raw_samples[Config::SAMPLE_BUFFER_SIZE];
static AudioLevelTracker audioLevelTracker;
static NoteDetector noteDetector;
WiFiManager wifiManager;
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// Timing and state variables
static uint32_t lastNotePrintTime = 0;
static uint32_t lastSpectrumPrintTime = 0;
static uint32_t lastWebUpdateTime = 0;
static bool showSpectrum = false;
// Note names for display
const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
void onWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
break;
case WS_EVT_ERROR:
break;
}
}
void initWebServer() {
if (!SPIFFS.begin(true)) {
Serial.println("An error occurred while mounting SPIFFS");
return;
}
ws.onEvent(onWebSocketEvent);
server.addHandler(&ws);
// Serve static files from SPIFFS
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
// Handle not found
server.onNotFound([](AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
});
server.begin();
Serial.println("HTTP server started");
}
void handleSerialCommands() {
if (Serial.available()) {
char cmd = Serial.read();
@@ -55,19 +100,54 @@ void printNoteInfo(const DetectedNote& note) {
noteNames[noteIndex], octave, note.frequency, note.magnitude, duration);
}
void initWiFi() {
// Set configuration portal timeout
wifiManager.setConfigPortalTimeout(Config::WIFI_CONFIG_TIMEOUT);
// Set custom portal settings
wifiManager.setAPStaticIPConfig(IPAddress(192,168,4,1), IPAddress(192,168,4,1), IPAddress(255,255,255,0));
// Try to connect to saved WiFi credentials
if(!wifiManager.autoConnect(Config::WIFI_AP_NAME, Config::WIFI_AP_PASSWORD)) {
Serial.println("Failed to connect and hit timeout");
ESP.restart();
}
Serial.println("Successfully connected to WiFi");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(Config::SERIAL_BAUD_RATE);
while(!Serial) {
delay(10);
}
initWiFi();
initWebServer();
initI2S();
Serial.println("Piano Note Detection Ready (C2-C6)");
Serial.println("Press 'h' for help");
noteDetector.beginCalibration();
}
void sendSpectrumData() {
if (ws.count() > 0 && !noteDetector.isCalibrating()) {
const auto& spectrum = noteDetector.getSpectrum();
String json = "[";
for (size_t i = 0; i < Config::FFT_SIZE/2; i++) {
if (i > 0) json += ",";
json += String(spectrum[i], 2);
}
json += "]";
ws.textAll(json);
}
}
void loop() {
ws.cleanupClients();
handleSerialCommands();
size_t bytes_read = 0;
@@ -88,11 +168,16 @@ void loop() {
uint32_t currentTime = millis();
const auto& detectedNotes = noteDetector.getDetectedNotes();
// Update web clients with spectrum data
if (currentTime - lastWebUpdateTime >= 50) { // Update web clients every 50ms
sendSpectrumData();
lastWebUpdateTime = currentTime;
}
// Show spectrum if enabled
if (showSpectrum &&
!noteDetector.isCalibrating() &&
currentTime - lastSpectrumPrintTime >= Config::DEBUG_INTERVAL_MS) {
SpectrumVisualizer::visualizeSpectrum(noteDetector.getSpectrum(), Config::FFT_SIZE);
lastSpectrumPrintTime = currentTime;
}
@@ -106,6 +191,6 @@ void loop() {
}
}
// Small delay to prevent serial buffer overflow
delay(10);
// Small delay to prevent WDT reset
delay(1);
}