Pantalla LED Interactiva con Giroscopio, Anillo NeoPixel y Arduino
Componentes y Suministros
| Componente | Cantidad | Notas |
|---|---|---|
| SparkFun Arduino Pro Mini 328 - 5V/16MHz | 1 | Microcontrolador compacto |
| Adafruit NeoPixel Ring: WS2812 5050 RGB LED | 1 | Anillo 24 LEDs direccionables |
| SparkFun IMU Breakout - MPU-9250 | 1 | Giroscopio + acelerómetro + magnetómetro (código usa MPU6050) |
| Protoboard (genérica) | 1 | Montaje sin soldar |
| Cables Macho-Macho | 1 | Conexiones |
| Cables Macho-Hembra | 1 | Conexiones |
| Portapilas 4x AA + pilas | 1 | Alimentación portátil |
Sobre el Proyecto
En este tutorial usamos el giroscopio MPU6050, un anillo NeoPixel y un Arduino para construir un dispositivo que enciende LEDs correspondientes al ángulo de inclinación.
Proyecto simple y divertido, montado en protoboard. Siguiendo los pasos construirás lo que se ve en el video. Buen tutorial para aprender sobre giroscopio y anillo NeoPixel.
Lo hice por el interés visto en otro tutorial mío. Aquí reemplacé LEDs simples por anillo NeoPixel: más sencillo mediante librería Adafruit y visualmente más espectacular.
Si tienes estos componentes por ahí, es una gran forma de aprovecharlos. Te guiaré paso a paso en la construcción y explicaré el funcionamiento al final.
Paso 1: Materiales Necesarios

Piezas:
- Arduino Pro Mini 328P
- Protoboard
- Giroscopio MPU6050
- Anillo NeoPixel 24 LEDs
- Portapilas 4x AA con 4 pilas
- Cables jumper en U (opcional). Uso estos porque quedan mejor en protoboard y los LEDs son más visibles. Caja 140 en eBay ~4$. Si no tienes, sirve cable Dupont.
Herramientas:
- Adaptador USB-serie FTDI FT232RL para programar Arduino Pro Mini
- Arduino IDE
Habilidades: Soldadura básica, programación Arduino básica.
Paso 2: Montaje


Adjunto esquema Fritzing en formato .fzz e imagen para visualizar conexiones.
- Soldar 3 pines macho en parte trasera del anillo NeoPixel (ver foto): positivo, negativo, entrada datos.
- Portapilas 4x AA necesita conexión a protoboard: soldar dos cables Dupont a sus terminales.
- Preparar protoboard: colocar anillo NeoPixel, microcontrolador y giroscopio como en imagen. Conectar todos los negativos (microcontrolador, NeoPixel, giroscopio), todos los positivos, y cables de datos: SDA/SCL microcontrolador-giroscopio, pin D6 microcontrolador-NeoPixel. Revisar todas las conexiones antes de alimentar. Opcional: fijar portapilas a protoboard con cinta ducto para portabilidad.
Paso 3: Código y Calibración
Primero descargar e instalar dos librerías:
- Librería Adafruit NeoPixel (control NeoPixel)
- Librería MPU6050 (giroscopio)
Son dos excelentes librerías que hacen el trabajo pesado. Más detalles NeoPixels aquí.
Luego descargar e instalar mi librería desde GitHub o copiar abajo:
#include "I2Cdev.h"
#include <Adafruit_NeoPixel.h>
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"
#define NEOPIXED_CONTROL_PIN 6
#define NUM_LEDS 24
const int MAX_ANGLE = 45;
const int LED_OFFSET = 12;
MPU6050 mpu;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_RBG + NEO_KHZ800);
unsigned long lastPrintTime = 0;
bool initialization = false;
uint8_t mpuIntStatus, devStatus;
uint16_t packetSize, fifoCount;
uint8_t fifoBuffer[64];
Quaternion q;
VectorFloat gravity;
float ypr[3];
volatile bool mpuInterrupt = false;
void setup() {
Serial.begin(9600);
Serial.println("Programa iniciado");
initialization = initializeGyroscope();
strip.begin();
}
void loop() {
if (!initialization) return;
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
fifoCount = mpu.getFIFOCount();
if (hasFifoOverflown(mpuIntStatus, fifoCount)) { mpu.resetFIFO(); return; }
if (mpuIntStatus & 0x02) {
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
mpu.getFIFOBytes(fifoBuffer, packetSize);
fifoCount -= packetSize;
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
redrawLeds(ypr[0] * 180/M_PI, ypr[1] * 180/M_PI, ypr[2] * 180/M_PI);
}
}
boolean hasFifoOverflown(int mpuIntStatus, int fifoCount) {
return (mpuIntStatus & 0x10) || fifoCount == 1024;
}
void redrawLeds(int x, int y, int z) {
x = constrain(x, -MAX_ANGLE, MAX_ANGLE);
y = constrain(y, -MAX_ANGLE, MAX_ANGLE);
if (y < 0 && z > 0) lightLeds(y, z, 0, 5, 0, 89);
else if (y < 0 && z < 0) lightLeds(y, z, 6, 12, 89, 0);
else if (y > 0 && z < 0) lightLeds(y, z, 13, 19, 0, 89);
else if (y > 0 && z > 0) lightLeds(y, z, 20, 24, 89, 0);
}
void lightLeds(int x, int y, int fromLed, int toLed, int fromAngle, int toAngle) {
double angle = (atan((double)abs(x) / (double)abs(y)) * 4068) / 71;
int ledNr = map(angle, fromAngle, toAngle, fromLed, toLed);
printDebug(y, z, ledNr, angle);
uint32_t color;
for (int i = 0; i < NUM_LEDS; i++) {
color = strip.Color(0, 0, 0);
if (i == ledNr) color = strip.Color(0, 180, 0);
else if (i == ledNr - 1) color = strip.Color(0, 5, 0);
strip.setPixelColor(normalizeLedPosition(i), color);
}
strip.show();
}
int normalizeLedPosition(int pos) {
if (NUM_LEDS > pos + LED_OFFSET) return pos + LED_OFFSET;
return pos + LED_OFFSET - NUM_LEDS;
}
void printDebug(int y, int z, int lightLed, int angle) {
if (millis() - lastPrintTime < 500) return;
Serial.print("a="); Serial.print(angle); Serial.print("; ");
Serial.print("ll="); Serial.print(lightLed); Serial.print("; ");
Serial.print("y="); Serial.print(y); Serial.print("; ");
Serial.print("z="); Serial.print(z); Serial.println("; ");
lastPrintTime = millis();
}
bool initializeGyroscope() {
Wire.begin();
TWBR = 24; // 400kHz I2C
mpu.initialize();
Serial.println(mpu.testConnection() ? F("MPU6050 conexión OK") : F("MPU6050 conexión FALLÓ"));
Serial.println(F("Inicializando DMP..."));
devStatus = mpu.dmpInitialize();
// Offsets de calibración (ajustar para tu sensor)
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
if (devStatus != 0) {
Serial.print(F("Fallo init DMP (código ")); Serial.println(devStatus);
return false;
}
mpu.setDMPEnabled(true);
Serial.println(F("Activando detección interrupción (int. externo 0 Arduino)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP listo! Esperando primera interrupción..."));
packetSize = mpu.dmpGetFIFOPacketSize();
return true;
}
void dmpDataReady() { mpuInterrupt = true; }
Subir el código: Usar adaptador FTDI para cargar en Arduino.
Conectar alimentación (pilas).
Calibración: Lo más importante es ajustar constante LED_OFFSET. En mi ejemplo es 12. Ajustar de 0 a 23 para que al encender, el LED se encienda en la dirección que inclinas la placa.
Para más detalles de funcionamiento, ver siguiente paso.
Paso 4: Cómo Funciona (Opcional)

MPU6050: Giroscopio MEMS (Sistemas Microelectromecánicos). Tiene componente oscilante; por conservación del momento, un objeto vibrando tiende a seguir vibrando en mismo plano, y desviaciones vibracionales derivan cambio de dirección. Incluye microcontrolador propio que calcula roll, pitch, yaw con matemáticas complejas. Datos raw tienen ruido y deriva; usamos librería externa para suavizar y obtener datos limpios.
NeoPixel: LEDs RGB direccionables individualmente, encadenados en tiras/anillos. Trabajan a 5V, contienen electrónica propia: solo alimentar y comunicar por línea de datos (reloj+datos en una línea). Librería Adafruit limpia para interactuar.
Código: En loop(), librería MPU6050_6Axis_MotionApps20 es llamada. Cuando hay datos nuevos del giroscopio, llama redrawLeds(x, y, z) con yaw, pitch, roll (en radianes, convertidos a grados).
En redrawLeds():
- Enfocamos en ejes Y (pitch) y Z (roll).
- Limitamos ambos a ±MAX_ANGLE (45° definidos, modificable).
- Dividimos 360° en 4 cuadrantes y llamamos
lightLeds()para cada:- Y neg, Z pos → cuadrante 1: LEDs 0-5, ángulo 0-89°
- Y neg, Z neg → cuadrante 2: LEDs 6-12, ángulo 89-0°
- Y pos, Z neg → cuadrante 3: LEDs 13-19, ángulo 0-89°
- Y pos, Z pos → cuadrante 4: LEDs 20-24, ángulo 89-0°
- En
lightLeds():- Calculamos ángulo basado en dos ejes usando arcotangente (ver imagen adjunta).
- Mapeamos ángulo a número de LED con
map()de Arduino. - Reiniciamos tira: solo dos LEDs encendidos: el calculado y el anterior (efecto desvanecimiento).
- Usamos
normalizeLedPosition()para calibración: el anillo puede rotarse libremente y debe alinearse con giroscopio. - Imprimimos depuración: ejes, LED encendido, ángulo.
Matemáticas: Imagen adjunta muestra anillo LED y función trigonométrica para determinar ángulo.
Código
Código Arduino
Esquemáticos
sketch_gMlSrk3rCX.fzz (archivo Fritzing)
Proceso de manufactura
- Construye un Juego Giroscópico en Tiempo Real con Arduino Nano y Sensor MPU-6050
- Construye un Reloj de Pared Elegante con Anillo Neopixel 60 LEDs Adafruit y Arduino
- Sensor K30 CO₂: Monitorización en tiempo real de la calidad del aire interior
- Facilitar la comunicación sordo‑ciego usando 1Sheeld y Arduino
- Mando USB para juegos DIY con Arduino – Construye tu propio gamepad de alto rendimiento
- Alcancía Inteligente: Controla un Aceptador de Monedas con Arduino Nano
- Pixie: Reloj de pulsera NeoPixel basado en Arduino – Elegante reloj LED DIY
- Controla un LED por Bluetooth con Arduino – Guía DIY paso a paso
- Integración del sensor de huella capacitivo DFRobot (ID809) con Arduino o ESP8266
- Dominando reconocimiento y síntesis de voz en Arduino Due
- Medición Precisa de Radiación Solar con Arduino UNO y Shield Ethernet