Continuando con la entrada anterior De Consola a Display, regresé a ocuparme de la conexión del display pero con arduino. El proyecto en la entrada mencionada estaba bien, pero como ocurre en ocaciones, al momento de validar, algunos problemas surgieron, el mas sencillo por llamados al include de la biblioteca del controlador del display, el LiquidCrystal_I2C.h, aquí la solución pasó por cargar desde el administrador de bibliotecas del IDE de Arduino la bliblioteca de Frank Brabander. Probé el siguiente código y… funcionó!

#include "LiquidCrystal_I2C.h"
#include  <Wire.h>

LiquidCrystal_I2C lcd(0x27,  16, 2);

void setup() {
  lcd.init();
  lcd.backlight();
}

void loop() {
  delay(1000);
  lcd.setCursor(0,0);
  lcd.print("Hola, Grupo!!!");
  lcd.setCursor(0,1);
  lcd.print("Vamo Arriba");
}

El display desplegaba los mensajes. Sólo tuve que ajustar el brillo y habilitar la luz de fondo de la pantalla (un cable a modo de jumper).

Ahora el paso siguiente era volver con el micro. Allí el código aplicado fué el siguiente:

#include <arduinoFFT.h>  // Official ArduinoFFT library v2.0.4

#define MIC_PIN A0
#define SAMPLES 128
#define SAMPLING_FREQUENCY 4000

double vReal[SAMPLES];
double vImag[SAMPLES];

ArduinoFFT<double> FFT(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);

// Standard guitar tuning (E2, A2, D3, G3, B3, E4)
struct NoteRef { const char* name; double freq; };
NoteRef notes[] = {
  {"E2", 82.41}, {"A2", 110.00}, {"D3", 146.83},
  {"G3", 196.00}, {"B3", 246.94}, {"E4", 329.63}
};

// Calculate cents difference
double centsDiff(double f, double ref) {
  if (f <= 0 || ref <= 0) return 0;
  return 1200.0 * log(f / ref) / log(2.0);
}

// Find nearest note
NoteRef nearestNote(double f) {
  NoteRef best = notes[0];
  double bestDiff = 1e9;
  for (auto &n : notes) {
    double d = fabs(centsDiff(f, n.freq));
    if (d < bestDiff) { bestDiff = d; best = n; }
  }
  return best;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Arduino Guitar Tuner (E A D G B E)");
}

void loop() {
  // Capture audio samples
  unsigned long microsStart = micros();
  for (int i = 0; i < SAMPLES; i++) {
    vReal[i] = analogRead(MIC_PIN);
    vImag[i] = 0;
    while (micros() - microsStart < (1000000UL * (i + 1) / SAMPLING_FREQUENCY));
  }

  // Remove DC offset
  double mean = 0;
  for (int i = 0; i < SAMPLES; i++) mean += vReal[i];
  mean /= SAMPLES;
  for (int i = 0; i < SAMPLES; i++) vReal[i] -= mean;

  // Check for minimum amplitude to avoid noise
  double maxAmplitude = 0;
  for (int i = 0; i < SAMPLES; i++) {
    if (fabs(vReal[i]) > maxAmplitude) maxAmplitude = fabs(vReal[i]);
  }

  if (maxAmplitude < 20) { // adjust threshold if needed
    Serial.println("");
    delay(100);
    return;
  }

  // FFT computation
  FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.compute(FFT_FORWARD);
  FFT.complexToMagnitude();

  // Find peak frequency
  double maxMag = 0;
  int maxIndex = 0;
  for (int i = 1; i < SAMPLES / 2; i++) {
    if (vReal[i] > maxMag) {
      maxMag = vReal[i];
      maxIndex = i;
    }
  }

  double freq = (maxIndex * 1.0 * SAMPLING_FREQUENCY) / SAMPLES;

  // Find nearest note
  NoteRef n = nearestNote(freq);
  double cents = centsDiff(freq, n.freq);

  // Print results
  Serial.print("Detected freq: ");
  Serial.print(freq, 2);
  Serial.print(" Hz | Nearest note: ");
  Serial.print(n.name);
  Serial.print(" (");
  Serial.print(n.freq, 2);
  Serial.print(" Hz)");

  if (fabs(cents) < 5) Serial.println(" | In tune ✅");
  else if (cents > 0) Serial.println(" | Sharp ↗");
  else Serial.println(" | Flat ↘");

  delay(200);
}

Claro que esto debía funcionar junto con el display así que en un primer paso, la idea era mantener las conexiones existentes, por lo que sólo me auxilié con el protoboard para compartir el conector de 5v para el micro y el display.

El MAX9814 debe conectar de la siguiente manera:

Módulo MAX9814Arduino
OUTA0 (entrada analógica)
VCC5V
GNDGND

Para el micro debe quedar:

LCD I2CArduino UNO Nota
VCC5V Alimentación
GNDGND Tierra común
SDAA4 Datos I2C
SCLA5 Reloj I2C

Ahora sólo restaba unir los códigos para que el resultado de la evaluación acústica se redirigiera al panel LCD. El siguiente, fué el código resultante.

#include <arduinoFFT.h>   // Official ArduinoFFT library v2.0.4
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// --- LCD Config (ajusta dirección si es necesario: 0x27 o 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define MIC_PIN A0
#define SAMPLES 128
#define SAMPLING_FREQUENCY 4000

double vReal[SAMPLES];
double vImag[SAMPLES];

ArduinoFFT<double> FFT(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);

// --- Standard guitar tuning (E2, A2, D3, G3, B3, E4)
struct NoteRef { const char* name; double freq; };
NoteRef notes[] = {
  {"E2", 82.41}, {"A2", 110.00}, {"D3", 146.83},
  {"G3", 196.00}, {"B3", 246.94}, {"E4", 329.63}
};

// --- Funciones auxiliares
double centsDiff(double f, double ref) {
  if (f <= 0 || ref <= 0) return 0;
  return 1200.0 * log(f / ref) / log(2.0);
}

NoteRef nearestNote(double f) {
  NoteRef best = notes[0];
  double bestDiff = 1e9;
  for (auto &n : notes) {
    double d = fabs(centsDiff(f, n.freq));
    if (d < bestDiff) { bestDiff = d; best = n; }
  }
  return best;
}

// --- SETUP
void setup() {
  Serial.begin(115200);
  Serial.println("Arduino Guitar Tuner (E A D G B E)");

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Guitar Tuner");
  lcd.setCursor(0,1);
  lcd.print("Inicializando..");
  delay(2000);
  lcd.clear();
}

// --- LOOP
void loop() {
  // Captura de muestras
  unsigned long microsStart = micros();
  for (int i = 0; i < SAMPLES; i++) {
    vReal[i] = analogRead(MIC_PIN);
    vImag[i] = 0;
    while (micros() - microsStart < (1000000UL * (i + 1) / SAMPLING_FREQUENCY));
  }

  // Eliminar offset DC
  double mean = 0;
  for (int i = 0; i < SAMPLES; i++) mean += vReal[i];
  mean /= SAMPLES;
  for (int i = 0; i < SAMPLES; i++) vReal[i] -= mean;

  // Amplitud mínima (ruido)
  double maxAmplitude = 0;
  for (int i = 0; i < SAMPLES; i++) {
    if (fabs(vReal[i]) > maxAmplitude) maxAmplitude = fabs(vReal[i]);
  }

  if (maxAmplitude < 20) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Esperando...");
    delay(100);
    return;
  }

  // FFT
  FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.compute(FFT_FORWARD);
  FFT.complexToMagnitude();

  // Pico
  double maxMag = 0;
  int maxIndex = 0;
  for (int i = 1; i < SAMPLES / 2; i++) {
    if (vReal[i] > maxMag) {
      maxMag = vReal[i];
      maxIndex = i;
    }
  }
  double freq = (maxIndex * 1.0 * SAMPLING_FREQUENCY) / SAMPLES;

  // Nota cercana
  NoteRef n = nearestNote(freq);
  double cents = centsDiff(freq, n.freq);

  // --- Serial
  Serial.print("Freq: ");
  Serial.print(freq, 2);
  Serial.print(" Hz | Note: ");
  Serial.print(n.name);

  if (fabs(cents) < 5) Serial.println(" | In tune ✅");
  else if (cents > 0) Serial.println(" | Sharp ↗");
  else Serial.println(" | Flat ↘");

  // --- LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("F:");
  lcd.print(freq, 1);
  lcd.print("Hz");

  lcd.setCursor(0,1);
  lcd.print("Nota ");
  lcd.print(n.name);

  if (fabs(cents) < 5) lcd.print(" OK");
  else if (cents > 0) lcd.print(" #");
  else lcd.print(" b");

  delay(200);
}

Finalmente, todo resultó.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *