Interactieve gyroscoop LED-display met NeoPixel-ring en Arduino
Onderdelen en benodigdheden
| SparkFun Arduino Pro Mini 328 - 5V/16MHz | × | 1 |
| Adafruit NeoPixel Ring: WS2812 5050 RGB LED | × | 1 |
| SparkFun IMU Breakout - MPU-9250 | × | 1 |
| Breadboard (standaard) | × | 1 |
| Man/Man jumperdraden | × | 1 |
| Man/Vrouw jumperdraden | × | 1 |
| 4x AA batterijhouder + batterijen | × | 1 |
Over dit project
In deze tutorial gebruiken we de MPU6050 gyroscoop, een NeoPixel ring en een Arduino om een apparaat te bouwen dat LEDs aanstuurt overeenkomstig de hellingshoek.
Dit is een eenvoudig en leuk project, volledig op breadboard. Volg de stappen en je bouwt wat je in de video ziet. Goede tutorial om te leren over gyroscoop en NeoPixel-ring.
Ik bouw deze tutorial vanwege het interesse in een eerdere tutorial van mij. Hier vervang ik simpele LEDs door een NeoPixel-ring. De ring is eenvoudiger via de Adafruit-bibliotheek en ziet er spectaculairder uit.
Als je deze componenten liggen hebt, is dit een geweldige manier om ze te gebruiken. Ik neem je stap voor stap mee door de bouw en leg uit hoe het werkt in de laatste stap.
Stap 1: Benodigdheden

Onderdelen
- Arduino Pro Mini 328P
- Breadboard
- MPU6050 gyroscoop
- 24 NeoPixel LED-ring
- 4x AA batterijhouder met batterijen
- U-vormige jumperkabels (optioneel). Ik gebruik deze omdat ze er netter uitzien op het breadboard en de LEDs zichtbaarder zijn. Een doos van 140 kost ~$4 op eBay. Anders gewone Dupont-draden.
Tools:
- USB-naar-seriële FTDI-adapter FT232RL om de Arduino Pro Mini te programmeren
- Arduino IDE
Vaardigheden: Solderen, basale Arduino-programmering.
Stap 2: Montage


Fritzing-schema in .fzz formaat en foto voor visualisatie.
- Soldeer 3 mannelijke pinnen aan de achterkant van de NeoPixel-ring: - Positieve pin - Ground - Data-in pin
- De 4x batterijhouder heeft een manier nodig om aan het breadboard te koppelen. Eenvoudige oplossing: twee mannelijke Dupont-draden aan de Polen solderen.
- Breadboard voorbereiden: - NeoPixel-ring, microcontroller en gyroscoop plaatsen zoals op de afbeelding - Alle negatieve draden: naar microcontroller, NeoPixel-ring, gyro - Alle positieve draden: naar microcontroller, NeoPixel-ring, gyro - Alle datadraden: * SDA en SCL van microcontroller naar gyro * Pin D6 van microcontroller naar NeoPixel-ring - Alle verbindingen dubbelchecken voordat je spanning aanlegt - Optioneel: batterijhouder met ducttape aan de achterkant van het breadboard vastzetten voor draagbaarheid
Stap 3: Code en kalibratie
Eerst twee bibliotheken downloaden en installeren:
- Adafruit NeoPixel-bibliotheek voor NeoPixel-besturing
- MPU6050-bibliotheek voor de gyroscoop
Twee geweldige bibliotheken die het zware werk doen! Meer over NeoPixels hier.
Dan mijn bibliotheek downloaden en installeren of kopiëren uit onderstaande code:
#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;
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("Program started");
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, -1 * MAX_ANGLE, MAX_ANGLE);
y = constrain(y, -1 * 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("MPU6050 connection successful") : F("MPU6050 connection failed"));
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
if (devStatus != 0) {
Serial.print(F("DMP Initialization failed (code ")); Serial.println(devStatus);
return false;
}
mpu.setDMPEnabled(true);
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP ready! Waiting for first interrupt..."));
packetSize = mpu.dmpGetFIFOPacketSize();
return true;
}
void dmpDataReady() { mpuInterrupt = true; }
Code uploaden: Gebruik de FTDI-adapter om de code naar de Arduino te uploaden.
Voeding aansluiten (batterijen)
Kalibratie:
Het belangrijkste om te kalibreren is de constante LED_OFFSET. In mijn voorbeeld is dit 12. Pas dit aan van 0 tot 23 zodat na het aansluiten van de voeding de LED brandt in de richting waar je het bord naar kantelt.
Voor meer details over de werking, zie de volgende stap.
Stap 4: Hoe het werkt (optioneel)

Eerst wat over de MPU6050 gyroscoop. Dit is een MEMS-gyroscoop (Microelectromechanical systems). Elke type MEMS-gyroscoop heeft een trillend component waaruit versnelling en richtingsverandering afgeleid kunnen worden. Volgens de wet van behoud van impuls wil een trillend object in hetzelfde vlak blijven trillen; elke afwijking kan gebruikt worden om richtingsverandering af te leiden. De gyro bevat een eigen microcontroller om roll, pitch en yaw te berekenen met geavanceerde wiskunde.
Raw data heeft ruis en drift, daarom gebruikten we een externe bibliotheek voor schone bruikbare data.
De NeoPixels zijn individueel adresbare RGB-leds in banden en ringen. Ze werken op 5V en hebben eigen circuitry – je hoeft ze alleen te voeden en via de datalijn te communiceren (klok en data op één lijn). Adafruit levert een schone bibliotheek.
De code
In loop() roept de MPU6050_6Axis_MotionApps20-bibliotheek redrawLeds(x, y, z) aan met yaw, pitch, roll.
In redrawLeds():
- Focus op Y- en Z-as
- Beide assen constrainten van -MAX_ANGLE tot +MAX_ANGLE (45 graden, aanpasbaar)
- 360 graden in 4 kwadranten splitsen en
lightLeds()aanroepen per kwadrant:- Y negatief, Z positief → kwadrant 1: LEDs 0-5, hoek 0-89
- Y negatief, Z negatief → kwadrant 2: LEDs 6-12, hoek 89-0
- Y positief, Z negatief → kwadrant 3: LEDs 13-19, hoek 0-89
- Y positief, Z positief → kwadrant 4: LEDs 20-24, hoek 89-0
In lightLeds():
- Hoek berekenen op basis van twee assen met arctangens (zie plaatje)
- LED-nummer bepalen met Arduino
map()-functie - LED-strip resetten, behalve twee LEDs: de berekende positie en één ervoor (fade-effect)
normalizeLedPosition()voor NeoPixel-kalibratie – de ring kan gedraaid worden en moet uitgelijnd zijn met de gyroscoop- Debug-output: assen, aangebrande LED, hoek
De wiskunde
De bijgevoegde afbeelding toont de LED-ring en de trigonometrische functie voor de hoekbepaling.
Code
De Arduino-code
Schema's
Fritzing-schema (.fzz bestand)
Productieproces
- Bouw een real-time gyroscoop-spel met Arduino Nano & MPU-6050 sensor
- Een stijlvolle wandklok bouwen met Adafruit 60-LED Neopixel Ring & Arduino
- K30 CO2-sensor: Real-time binnenluchtkwaliteitmonitoring met Arduino
- Communicatie voor doofblinden vergemakkelijken met 1Sheeld en Arduino
- DIY Arduino USB Gaming Controller – Bouw Je Eigen High-Performance Gamepad
- Slimme spaarpot bouwen: Muntenacceptor besturen met Arduino Nano
- Pixie: Een Arduino-gebaseerde NeoPixel-polshorloge – Modern, DIY LED-tijdstuk
- Een LED bedienen via Bluetooth met Arduino – Eenvoudige DIY Handleiding
- Een DFRobot Capacitieve Vingerafdruksensor integreren met Arduino of ESP8266
- Spraakherkenning & Synthese Meesteren op Arduino Due
- Accurate zonnestralingsmeting met Arduino UNO en Ethernet Shield