Fecha: 14 de noviembre de 2025
Este documento contiene la documentación técnica para el proyecto STEAM del curso Laboratorio STEAM+ de la tecnicatura Redes y Software del Instituto Tecnológico de Informática de UTU año 2025.
Proyecto: Bandmate
1.Integrantes
- Hernán Sierra
- Raúl Vidal
- Ezequiel Valiunas
- Tabaré Maciel
2. Descripción
El Bandmate Project surge como una iniciativa interdisciplinaria que combina electrónica, programación y teoría musical con un objetivo claro: diseñar un dispositivo inteligente de afinación automática para instrumentos musicales. El proyecto fue concebido por un equipo integrado por Ezequiel Valiunas, Hernán Darío Sierra, Tabaré Maciel y Raúl Vidal, quienes aportan su conocimiento técnico y su pasión por la música al desarrollo de una herramienta innovadora.
3. Materiales






4 . Diseño Mecánico
Para lo que se refiere al diseño mecánico precisaremos una guitarra , tocar la nota seleccionada a través de la botonera , para su mejor funcionamiento se recomienda no estar a mas de 30 cmm del módulo MAX9814 para su correcto funcionamiento. Aunque la mejor manera seria insertarlo dentro de la caja de resonancia de la guitarra para que no se obstruya con el sonido ambiente (aunque es susceptible al estar demasiado cerca de la fuente de sonido)
5. Diseño Electrónico
En lo que respecta al Diseño Electrónico utilizaremos un protoboard donde se interconectaran los diferentes módulos .
Luego se conectan tanto el micrófono MAX9814 y el módulo display el I2C al Arduino
Conexión de los pines tanto al arduino como al protoboard
El módulo display el I2C al arduino va conectado de la siguiente manera
| LCD I2C | Arduino UNO |
| VCC | 5V Alimentación |
| GND | GND Tierra común |
| SDA | A4 Datos I2C |
| SLC | A5 Reloj I2C |
Para el Micrófono debe quedar de la siguiente manera
| Módulo MAX9814 | Arduino UNO |
| OUT | A0 (entrada analógica) |
| VCC | 5V |
| GND | GND |
Los Botones quedan conectados de la siguiente manera
| Botón | Arduino UNO |
| 6° Cuerda | PIN DIGITAL 1 |
| 5° Cuerda | PIN DIGITAL 2 |
| 4° Cuerda | PIN DIGITAL 3 |
| 3° Cuerda | PIN DIGITAL 4 |
| 2° Cuerda | PIN DIGITAL 5 |
| 1° Cuerda | PIN DIGITAL 6 |



En este caso del diagrama en Tinkercad en vez del sensor de movimiento infrarrojo seria el micrófono MAX 9814
6. Software de Diseño
Medición de Frecuencia
para medir la frecuencia con el módulo MAX9814 se activa con mayor intensidad dependiendo de los hz que emite cada cuerda al ser golpeada generando así el sonido. Calculando con FFT, que es un algoritmo eficiente que calcula la Transformada Discreta de Fourier
A continuación presentaremos el código para el Arduino UNO para la programación del prototipo.
Con el fin de tener un código mas limpio y organizado , decidimos tener el programa separado por funciones. Dividiéndose así en 8 capas :
Bandmate.ino
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#include "config.h"
#include "audio.h"
#include "display.h"
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Pines de botones
#define BUTTON_E6 2
#define BUTTON_A5 3
#define BUTTON_D4 4
#define BUTTON_G3 5
#define BUTTON_B2 6
#define BUTTON_E1 7
int selectedString = -1;
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("BandMate listo!");
delay(1000);
audioSetup();
pinMode(BUTTON_E6, INPUT_PULLUP);
pinMode(BUTTON_A5, INPUT_PULLUP);
pinMode(BUTTON_D4, INPUT_PULLUP);
pinMode(BUTTON_G3, INPUT_PULLUP);
pinMode(BUTTON_B2, INPUT_PULLUP);
pinMode(BUTTON_E1, INPUT_PULLUP);
}
void loop() {
checkButtons();
processAudio();
displayUpdate(selectedString);
delay(50);
}
// Función para leer botones y actualizar cuerda seleccionada
void checkButtons() {
int previous = selectedString;
if (digitalRead(BUTTON_E6) == LOW) selectedString = 0;
else if (digitalRead(BUTTON_A5) == LOW) selectedString = 1;
else if (digitalRead(BUTTON_D4) == LOW) selectedString = 2;
else if (digitalRead(BUTTON_G3) == LOW) selectedString = 3;
else if (digitalRead(BUTTON_B2) == LOW) selectedString = 4;
else if (digitalRead(BUTTON_E1) == LOW) selectedString = 5;
else selectedString = -1;
// Mostrar mensaje temporal si cambió la selección
if (selectedString != previous && selectedString != -1) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Afinar ");
lcd.print(getStringInfoByIndex(selectedString));
delay(1000); // 1 segundo de feedback
}
}
// Función auxiliar para convertir índice a texto
const char* getStringInfoByIndex(int index) {
switch(index) {
case 0: return "6ta cuerda";
case 1: return "5ta cuerda";
case 2: return "4ta cuerda";
case 3: return "3ra cuerda";
case 4: return "2da cuerda";
case 5: return "1ra cuerda";
default: return "Desconocida";
}
}
audio.cpp
#include "audio.h"
#include <arduinoFFT.h>
#include <LiquidCrystal_I2C.h>
extern LiquidCrystal_I2C lcd;
double vReal[SAMPLES];
double vImag[SAMPLES];
ArduinoFFT<double> FFT(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);
void audioInit() {
pinMode(MIC_PIN, INPUT);
Serial.println("Micrófono inicializado en pin A0");
}
void audioSetup() {
audioInit();
Serial.println("Sistema de audio listo");
}
void processAudio() {
captureFrequency(); // Solo captura frecuencia
}
double captureFrequency() {
for (int i = 0; i < SAMPLES; i++) {
vReal[i] = analogRead(MIC_PIN) - 512; // Centrar señal
vImag[i] = 0;
}
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
FFT.compute(FFTDirection::Forward);
FFT.complexToMagnitude();
double peak = 0;
int index = 0;
for (int i = 1; i < SAMPLES / 2; i++) {
if (vReal[i] > peak) {
peak = vReal[i];
index = i;
}
}
double freq = (index * ((double)SAMPLING_FREQUENCY / SAMPLES));
if (peak < 5 || freq < 60 || freq > 400) return 0;
return freq;
}
void testMicrophone() {
int reading = analogRead(MIC_PIN);
Serial.print("Current ADC: ");
Serial.print(reading);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ADC Test:");
lcd.setCursor(0, 1);
lcd.print(reading);
if (reading > 100) Serial.println(" - Signal detected!");
else Serial.println(" - Low signal");
}
double getAverageFrequency() {
// Promediar varias lecturas para estabilidad
double sum = 0;
int count = 5;
for(int i = 0; i < count; i++) sum += captureFrequency();
return sum / count;
}
audio.h
#ifndef AUDIO_H
#define AUDIO_H
#include <arduinoFFT.h>
#include "config.h"
extern double vReal[SAMPLES];
extern double vImag[SAMPLES];
void audioSetup();
void processAudio();
double captureFrequency();
void testMicrophone();
double getAverageFrequency();
#endif
config.h
#ifndef CONFIG_H
#define CONFIG_H
#include <Arduino.h>
// Pines y configuración general
#define MIC_PIN A0
#define SAMPLES 128
#define SAMPLING_FREQUENCY 6000 // Hz
#endif
display.cpp
#include "display.h"
#include "audio.h"
#include "notes.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
extern LiquidCrystal_I2C lcd;
void displayNoteInfo(const char* note, double diff, double freq) {
lcd.clear();
lcd.setCursor(0, 0);
const char* stringInfo = getStringInfo(note);
lcd.print(stringInfo);
lcd.print("-");
char noteName[4];
strncpy(noteName, note, 2);
noteName[2] = '\0';
lcd.print(noteName);
lcd.setCursor(0, 1);
if (diff > 15) lcd.print(">> MUY AGUDO");
else if (diff < -15) lcd.print(">> MUY GRAVE");
else if (diff > 5) lcd.print("> AGUDO");
else if (diff < -5) lcd.print("> GRAVE");
else lcd.print("** AFINADO **");
}
void displayWaiting() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Esperando nota...");
}
const char* getStringInfo(const char* note) {
if (strstr(note, "E2")) return "6ta cuerda";
if (strstr(note, "A2")) return "5ta cuerda";
if (strstr(note, "D3")) return "4ta cuerda";
if (strstr(note, "G3")) return "3ra cuerda";
if (strstr(note, "B3")) return "2da cuerda";
if (strstr(note, "E4")) return "1ra cuerda";
return "Desconocida";
}
int getStringIndex(const char* note) {
if (strstr(note, "E2")) return 0;
if (strstr(note, "A2")) return 1;
if (strstr(note, "D3")) return 2;
if (strstr(note, "G3")) return 3;
if (strstr(note, "B3")) return 4;
if (strstr(note, "E4")) return 5;
return -1;
}
void displayUpdate(int selectedString) {
double frequency = getAverageFrequency();
if (frequency > 0) {
NoteRef note = nearestNote(frequency);
double diff = centsDiff(frequency, note.freq);
if (selectedString != -1 && getStringIndex(note.name) != selectedString) {
displayWaiting();
return;
}
displayNoteInfo(note.name, diff, frequency);
} else {
displayWaiting();
}
}
display.h
#ifndef DISPLAY_H
#define DISPLAY_H
#include <LiquidCrystal_I2C.h>
extern LiquidCrystal_I2C lcd;
void displayUpdate(int selectedString);
void displayNoteInfo(const char* note, double diff, double freq);
void displayWaiting();
const char* getStringInfo(const char* note);
int getStringIndex(const char* note);
#endif
notes.cpp
#include "notes.h"
#include <math.h>
NoteRef notes[] = {
{"E2-6ta", 82.41}, {"A2-5ta", 110.00}, {"D3-4ta", 146.83},
{"G3-3ra", 196.00}, {"B3-2da", 246.94}, {"E4-1ra", 329.63}
};
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 (int i = 0; i < 6; i++) {
double d = fabs(centsDiff(f, notes[i].freq));
if (d < bestDiff) { bestDiff = d; best = notes[i]; }
}
return best;
}
notes.h
#ifndef NOTES_H
#define NOTES_H
struct NoteRef {
const char* name;
double freq;
};
extern NoteRef notes[6];
double centsDiff(double f, double ref);
NoteRef nearestNote(double f);
#endif
7. Referencias y recursos
- Herramienta para diseño de circuitos Tinkercad https://www.tinkercad.com/
- Herramienta para generar el código y librerías https://www.arduino.cc/en/software/
- Para consulta de dudas y optimización del código https://chatgpt.com/