Affichage LED Interactif Gyroscopique avec Anneau NeoPixel et Arduino
Composants et Fournitures
| Composant | Quantité |
|---|---|
| SparkFun Arduino Pro Mini 328 - 5V/16MHz | 1 |
| Adafruit NeoPixel Ring : WS2812 5050 RGB LED (24 LEDs) | 1 |
| SparkFun IMU Breakout - MPU-9250 (utilisé comme MPU6050) | 1 |
| Plaque d'essai (générique) | 1 |
| Fils Mâle/Mâle | 1 |
| Fils Mâle/Femelle | 1 |
| Support 4 piles AA + piles | 1 |
À Propos de ce Projet
Dans ce tutoriel, nous utilisons le gyroscope MPU6050, un anneau NeoPixel et un Arduino pour construire un dispositif qui allume les LEDs correspondant à l'angle d'inclinaison.
Projet simple et ludique, assemblé sur plaque d'essai. En suivant les étapes, vous reproduirez ce que montre la vidéo. Excellent pour apprendre le gyroscope et l'anneau NeoPixel.
Je l'ai créé suite à l'intérêt suscité par un autre de mes tutoriels. J'ai remplacé de simples LEDs par un anneau NeoPixel : plus simple via la bibliothèque Adafruit et bien plus spectaculaire.
Si vous avez ces composants sous la main, c'est une excellente façon de les utiliser. Je vous guide pas à pas dans la construction et explique le fonctionnement à la fin.
Étape 1 : Matériel Requis
Pièces :
- Arduino Pro Mini 328P
- Plaque d'essai
- Gyroscope MPU6050
- Anneau NeoPixel 24 LEDs
- Support 4 piles AA + 4 piles
- Fils de liaison en U (optionnel). Esthétique sur plaque d'essai, LEDs plus visibles. Boîte de 140 sur eBay ~4 $. Sinon fils Dupont standards.
Outils :
- Adaptateur USB-série FTDI FT232RL pour programmer l'Arduino Pro Mini
- Arduino IDE
Compétences : Soudure, programmation Arduino de base.
Étape 2 : Assemblage
Schéma Fritzing joint (format .fzz) et photo pour visualisation facile des connexions.
- Soudez 3 broches mâles au dos de l'anneau NeoPixel : VCC, GND, Data In.
- Soudez deux fils Dupont mâles aux bornes du support 4 piles pour connexion plaque d'essai.
- Préparez la plaque d'essai :
- Placez anneau NeoPixel, microcontrôleur, gyroscope comme sur l'image
- Câblez tous les fils négatifs : microcontrôleur, NeoPixel, gyro
- Câblez tous les fils positifs : microcontrôleur, NeoPixel, gyro
- Câblez les fils de données : SDA/SCL microcontrôleur → gyro ; Pin D6 microcontrôleur → NeoPixel
- Vérifiez toutes connexions avant alimentation
- Optionnel : fixez le support piles au dos de la plaque avec ruban adhésif pour portabilité
Étape 3 : Code et Calibration
D'abord, installez deux bibliothèques :
- Bibliothèque Adafruit NeoPixel (contrôle NeoPixel)
- Bibliothèque MPU6050 (gyroscope)
Deux excellentes bibliothèques qui font le gros du travail ! Plus de détails sur NeoPixels ici.
Ensuite, téléchargez ma bibliothèque depuis GitHub ou copiez le code ci-dessous :
#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; // À calibrer (0-23)
MPU6050 mpu;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_GRB + 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("Programme démarré");
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); // Quadrant 1
else if (y < 0 && z < 0) lightLeds(y, z, 6, 12, 89, 0); // Quadrant 2
else if (y > 0 && z < 0) lightLeds(y, z, 13, 19, 0, 89); // Quadrant 3
else if (y > 0 && z > 0) lightLeds(y, z, 20, 24, 89, 0); // Quadrant 4
}
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; // Arctan approx
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); // LED active : vert vif
else if (i == ledNr - 1) color = strip.Color(0, 5, 0); // LED précédente : vert faible (effet fondu)
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; // 400kHz I2C
mpu.initialize();
Serial.println(mpu.testConnection() ? F("MPU6050 connexion OK") : F("MPU6050 connexion ÉCHEC"));
Serial.println(F("Initialisation DMP..."));
devStatus = mpu.dmpInitialize();
// Offsets à calibrer pour VOTRE capteur !
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
if (devStatus != 0) {
Serial.print(F("Échec init DMP (code ")); Serial.print(devStatus); Serial.println(F(")"));
return false;
}
mpu.setDMPEnabled(true);
Serial.println(F("Activation détection interruption (interruption externe Arduino 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP prêt ! Attente première interruption..."));
packetSize = mpu.dmpGetFIFOPacketSize();
return true;
}
void dmpDataReady() { mpuInterrupt = true; }
Téléversement : Via adaptateur FTDI sur l'Arduino Pro Mini.
Alimentation : Connectez le support piles.
Calibration :
Le plus important : constante LED_OFFSET (12 dans l'exemple). Ajustez de 0 à 23 pour que la LED s'allume dans la direction où vous inclinez la carte après mise sous tension.
Pour comprendre le fonctionnement détaillé, voir l'étape suivante.
Étape 4 : Fonctionnement (Optionnel)
Le Gyroscope MPU6050
MEMS (Micro-Electro-Mechanical Systems). Composant oscillant détecte l'accélération/changement de direction : un objet vibrant conserve son plan de vibration (conservation du mouvement), toute déviation révèle un changement de direction. Microcontrôleur intégré calcule roulis, tangage, lacet (roll, pitch, yaw) par calculs complexes. Données brutes bruitées et dérivantes → bibliothèque externe pour lissage et données exploitables.
Les NeoPixels
LEDs RGB adressables individuellement, chainées en bandes/anneaux. Fonctionnent en 5V, électronique intégrée : alimentez + ligne de données unique (horloge + données). Bibliothèque Adafruit propre pour anneaux NeoPixel.
Le Code
Dans loop() : bibliothèque MPU6050_6Axis_MotionApps20 appelée. Nouvelles données gyro → appel redrawLeds(x, y, z) avec lacet, tangage, roulis (degrés).
Dans redrawLeds() :
- Focus sur axes Y (tangage) et Z (roulis)
- Contraintes ±MAX_ANGLE (45°, modifiable)
- 360° divisé en 4 quadrants → appel
lightLeds()par quadrant :