Display Interativo de Giroscópio com Anel NeoPixel e Arduino
Componentes e Materiais
| Componente | Quantidade |
|---|---|
| Arduino Pro Mini 328 - 5V/16MHz (SparkFun) | 1 |
| Anel NeoPixel Adafruit: WS2812 5050 RGB LED (24 LEDs) | 1 |
| Breakout IMU SparkFun - MPU-9250 (ou MPU6050) | 1 |
| Protoboard (genérico) | 1 |
| Jumpers Macho-Macho | 1 |
| Jumpers Macho-Fêmea | 1 |
| Suporte para 4 pilhas AA + pilhas | 1 |
Sobre o Projeto
Neste tutorial, utilizamos o giroscópio MPU6050, um Anel NeoPixel e um Arduino para construir um dispositivo que acende LEDs correspondentes ao ângulo de inclinação.
É um projeto simples e divertido, montado em protoboard. Ao seguir os passos, você construirá o que viu no vídeo. É um excelente tutorial para aprender sobre giroscópio e anel NeoPixel.
Substituí LEDs simples por um Anel NeoPixel, que é mais simples de usar através da biblioteca Adafruit e visualmente mais espetacular.
Se você tem esses componentes guardados, esta é uma ótima maneira de utilizá-los. Vou guiá-lo passo a passo na construção e explicar o funcionamento no final.
Passo 1: Materiais Necessários
Peças:
- Arduino Pro Mini 328P
- Protoboard
- Giroscópio MPU6050
- Anel NeoPixel de 24 LEDs
- Suporte 4x pilhas AA com pilhas
- Jumpers em U (opcional) - ficam melhor na protoboard e deixam LEDs mais visíveis. Caixa com 140 no eBay por ~$4. Se não tiver, use jumpers Dupont.
Ferramentas:
- Adaptador USB-serial FTDI FT232RL para programar o Arduino Pro Mini
- Arduino IDE
Habilidades: Soldagem e programação básica de Arduino.
Passo 2: Montagem
O esquemático Fritzing em formato .fzz e uma imagem estão anexos para fácil visualização das conexões.
- Soldar 3 pinos macho na parte traseira do anel NeoPixel:
- Pino positivo (VCC)
- Terra (GND)
- Pino de entrada de dados (DIN)
- O suporte de 4 pilhas precisa de conexão para a protoboard: solde dois jumpers macho Dupont nos terminais.
- Prepare a protoboard:
- Posicione o anel NeoPixel, microcontrolador e giroscópio como na imagem
- Conecte todos os fios negativos: microcontrolador, anel NeoPixel, giroscópio
- Conecte todos os fios positivos: microcontrolador, anel NeoPixel, giroscópio
- Conecte fios de dados:
- SDA e SCL do microcontrolador para o giroscópio
- Pino D6 do microcontrolador para o anel NeoPixel
- Verifique todas as conexões antes de alimentar
- Opcional: use fita adesiva para fixar o suporte de pilhas na parte traseira da protoboard, tornando-o mais portátil
Passo 3: Código e Calibração
Primeiro, baixe e instale duas bibliotecas:
- Biblioteca Adafruit NeoPixel para controle do NeoPixel
- Biblioteca MPU6050 para o giroscópio
São duas ótimas bibliotecas que fazem o trabalho pesado!
Depois, baixe e instale minha biblioteca (ou copie o código abaixo):
#include "I2Cdev.h"
#include
#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;
uint8_t devStatus;
uint16_t packetSize;
uint16_t 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 fromLedPosition, int toLedPosition, int fromAngle, int toAngle) {
double angle = (atan((double)abs(x) / (double)abs(y)) * 4068) / 71;
int ledNr = map(angle, fromAngle, toAngle, fromLedPosition, toLedPosition);
printDebug(x, y, 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 position) {
if (NUM_LEDS > position + LED_OFFSET) return position + LED_OFFSET;
return position + 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;
mpu.initialize();
Serial.println(mpu.testConnection() ? F("Conexão MPU6050 bem-sucedida") : F("Falha na conexão MPU6050"));
Serial.println(F("Inicializando DMP..."));
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
if (devStatus != 0) {
Serial.print(F("Falha na inicialização DMP (código ")); Serial.println(devStatus);
return false;
}
mpu.setDMPEnabled(true);
Serial.println(F("Habilitando detecção de interrupção (interrupção externa 0 do Arduino)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP pronto! Aguardando primeira interrupção..."));
packetSize = mpu.dmpGetFIFOPacketSize();
return true;
}
void dmpDataReady() { mpuInterrupt = true; }
Carregue o código: Use o adaptador FTDI para enviar o código ao Arduino.
Conecte a alimentação (pilhas)
Calibração: O mais importante é calibrar a constante "LED_OFFSET". No meu exemplo é 12. Ajuste de 0 a 23 para que, ao ligar, o LED acenda na direção em que você inclina a placa.
Para mais detalhes sobre o funcionamento, veja o próximo passo.
Passo 4: Como Funciona (Opcional)
Primeiro, sobre o giroscópio MPU6050: é um giroscópio MEMS (Sistemas Microeletromecânicos). Cada tipo tem um componente oscilante do qual se detecta aceleração e mudança de direção. Pela lei da conservação do movimento, um objeto vibrando tende a continuar no mesmo plano, e qualquer desvio vibracional deriva mudança de direção. O giroscópio contém seu próprio microcontrolador para calcular roll, pitch e yaw através de matemática complexa. Mas os dados brutos sofrem ruído e deriva, então usamos uma biblioteca externa para suavizar e obter dados limpos.
Os NeoPixels são LEDs RGB individualmente endereçáveis e encadeados em fitas e anéis. Funcionam em 5V e contêm sua própria circuitaria - apenas alimente e comunique via linha de dados única (clock + dados). A Adafruit fornece biblioteca limpa para interagir com anéis NeoPixel.
O código: Dentro de loop(), a biblioteca MPU6050_6Axis_MotionApps20 é chamada. Quando há novos dados do giroscópio, chama redrawLeds(x, y, z) com 3 argumentos representando yaw, pitch e roll.
Dentro de redrawLeds(): focamos nos eixos y, z; limitamos ambos de -MAX_ANGLE a +MAX_ANGLE (definido como 45°); dividimos 360° em 4 quadrantes e chamamos lightLeds() para cada.
Dentro de lightLeds: calculamos ângulo baseado nos dois eixos usando arco tangente; calculamos qual LED acender via função map do Arduino; resetamos a fita exceto dois LEDs (o calculado e o anterior para efeito fade); usamos normalizeLedPosition() para calibração do NeoPixel (útil pois o anel pode ser rotacionado e deve alinhar com o giroscópio); imprimimos os dois eixos, LED aceso e ângulo.
A matemática: A imagem anexada mostra o anel de LEDs e a função trigonométrica usada para determinar o ângulo.
Código Completo
Disponível em: GitHub
Esquemático
Arquivo Fritzing: sketch_gMlSrk3rCX.fzz
Processo de manufatura
- Construa um Jogo em Tempo Real com Giroscópio: Arduino Nano + Sensor MPU-6050
- Relógio de Parede Estiloso com Anel Neopixel 60 LEDs Adafruit e Arduino
- Sensor K30 CO₂: Monitoramento de Qualidade do Ar Interno em Tempo Real
- Facilitando Comunicação Surdo-Cego com 1Sheeld e Arduino
- Controle USB para Jogos DIY com Arduino – Construa Seu Próprio Gamepad de Alto Desempenho
- Mealheiro Inteligente: Controlar Aceitador de Moedas com Arduino Nano
- Pixie: Relógio de Pulso NeoPixel Baseado em Arduino – Elegante Timepiece DIY LED
- Controlar um LED via Bluetooth com Arduino – Guia DIY Simples
- Integrando Sensor de Impressão Digital Capacitivo DFRobot com Arduino ou ESP8266
- Dominando Reconhecimento e Síntese de Fala no Arduino Due