User Tools

Site Tools


start:utils:cockpit:p1:mcu:hardware

Hardware

Composants externe

Modification affichages MAX7219

Un affichage ressemble à ceci

Chaque module est câblé ainsi

Si on place les 2 modules en série, il y a un problème car on peut voir que les connexions par défaut (en rouge) sont en trop. Et il manque la liaison entre le dernier module de la ligne du bas et le premier de la ligne du haut.

Ce qui est voulu c'est ceci:

Il faut donc couper cette piste au dos du module:

Et relier souder les 2 modules ensembles

ESP32-C3 SuperMini

Aucune librairie pour commander les WS2812B depuis un Arduino Q est disponible pour l'instant. Pour pouvoir utiliser ces leds, j'ai ajouté un ESP32-C3 Supermini sur le port I2C de l'Arduino Q afin de piloter les leds “à distance”.

Le sketch de cet ESP32-C3 SuperMini est le suivant:

sketch.ino
// ─────────────────────────────────────────────────────────────────────────────
// ESP32-C3 SuperMini — I2C Slave + WS2812B Ring + DFR0299 MP3
// ─────────────────────────────────────────────────────────────────────────────
// I2C Slave : SDA=GPIO8, SCL=GPIO9, addr=0x42
// Ring      : GPIO6, 120 LEDs (ring1=32, ring2=40, ring3=48)
// DFR0299   : Serial (GPIO20=RX, GPIO21=TX)
// SD card   : dossier MP3/, fichiers nommés 0001.mp3 .. 9999.mp3
// Ring layout : ring1=32, ring2=40, ring3=48
// ─────────────────────────────────────────────────────────────────────────────
 
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <DFRobotDFPlayerMini.h>
 
// ── Pins ─────────────────────────────────────────────────────────────────────
#define PIN_SDA         8
#define PIN_SCL         9
#define I2C_ADDR        0x42
#define PIN_RING        6
#define NUM_LEDS        120
 
// ── Ring layout ───────────────────────────────────────────────────────────────
#define RING1_START     0    // 32 LEDs
#define RING2_START     32   // 40 LEDs
#define RING3_START     72   // 48 LEDs
 
// Ring milieu CO2 : LEDs 32-71, éteindre 32-35 et 68-71
#define CO2_START       36
#define CO2_LEDS        32   // LEDs actives pour CO2
 
// ── Commandes I2C ─────────────────────────────────────────────────────────────
#define CMD_CO2         0x01
#define CMD_GYRO        0x02
#define CMD_BRIGHTNESS  0x03
#define CMD_VOLUME      0x04
#define CMD_PLAY        0x05
 
// ── Numéros de fichiers MP3 (dossier MP3/, nommés XXXX.mp3) ──────────────────
#define SND_STARTUP     1004
#define SND_VERY_HIGH   1001
#define SND_HIGH        1002
#define SND_MEDIUM      1003
#define SND_LOW         1005  // à adapter selon vos fichiers
#define SND_INFO        1006
#define SND_CO2_400     1101
#define SND_CO2_600     1102
#define SND_CO2_1000    1103
#define SND_CO2_1500    1104
 
// ── Couleurs criticité ────────────────────────────────────────────────────────
// index : 0=non défini, 1=P1 RED, 2=P2 ORANGE, 3=P3 YELLOW, 4=P4 GREEN, 5=P5 BLUE
const uint8_t CRIT_R[] = {0, 255, 255, 255,   0,   0};
const uint8_t CRIT_G[] = {0,   0, 165, 255, 128,   0};
const uint8_t CRIT_B[] = {0,   0,   0,   0,   0, 255};
 
// ── Objets ────────────────────────────────────────────────────────────────────
Adafruit_NeoPixel   ring(NUM_LEDS, PIN_RING, NEO_GRB + NEO_KHZ800);
DFRobotDFPlayerMini mp3;
 
// ── État ──────────────────────────────────────────────────────────────────────
#define MODE_CO2   1
#define MODE_GYRO  2
int      currentMode = MODE_CO2;
uint8_t  currentCrit = 1;
uint16_t currentCO2  = 400;
uint8_t  brightness  = 20;
uint8_t  mp3Volume   = 25;
 
// Gyrophare
#define TRAIL_LEN   6      // longueur de la traînée
float gyroAngle = 0.0f;   // angle en degrés [0..360[
unsigned long lastGyroTime = 0;
const float GYRO_SPEED = 2.0f;  // tours/sec
 
// Standby MP3
unsigned long lastPlayTime   = 0;
bool          waitingStandby = false;
const unsigned long STANDBY_DELAY = 5000;
 
// ── Buffer I2C ────────────────────────────────────────────────────────────────
volatile uint8_t i2cBuf[4];
volatile uint8_t i2cLen   = 0;
volatile bool    i2cReady = false;
 
// ── MP3 helpers ───────────────────────────────────────────────────────────────
void mp3_setVolume(uint8_t vol) {
    mp3.start();
    delay(200);
    mp3.volume(vol);
    delay(50);
    mp3.sleep();
}
 
void mp3_play(int fileNum) {
    mp3.start();
    delay(200);
    mp3.volume(mp3Volume);
    delay(50);
    mp3.playMp3Folder(fileNum);
    lastPlayTime   = millis();
    waitingStandby = true;
}
 
// Convertit le numéro de fichier reçu via I2C en numéro MP3 réel
int toMp3File(uint8_t file) {
    switch (file) {
        case 0:   return SND_STARTUP;
        case 1:   return SND_VERY_HIGH;
        case 2:   return SND_HIGH;
        case 3:   return SND_MEDIUM;
        case 4:   return SND_LOW;
        case 5:   return SND_INFO;
        case 101: return SND_CO2_400;
        case 102: return SND_CO2_600;
        case 103: return SND_CO2_1000;
        case 104: return SND_CO2_1500;
        default:  return SND_STARTUP;
    }
}
 
// ── I2C callbacks ─────────────────────────────────────────────────────────────
void onReceive(int numBytes) {
    i2cLen = 0;
    while (Wire.available() && i2cLen < 4) {
        i2cBuf[i2cLen++] = Wire.read();
    }
    i2cReady = true;
}
 
// ── Ring : mode CO2 ───────────────────────────────────────────────────────────
void ring_showCO2(uint16_t co2) {
    ring.clear();
    if (co2 < 400)  co2 = 400;
    if (co2 > 2000) co2 = 2000;
    uint8_t ledsToLight = map(co2, 400, 2000, 0, CO2_LEDS);
    for (uint8_t i = 0; i < ledsToLight; i++) {
        float ratio = (float)i / (float)(CO2_LEDS - 1);
        uint8_t r, g;
        if (ratio < 0.5f) { r = (uint8_t)(255 * ratio * 2.0f); g = 255; }
        else               { r = 255; g = (uint8_t)(255 * (1.0f - (ratio - 0.5f) * 2.0f)); }
        ring.setPixelColor(CO2_START + i, ring.Color(r, g, 0));
    }
    ring.show();
}
 
// ── Ring : mode gyrophare ─────────────────────────────────────────────────────
 
void ring_drawSpots(int ringStart, int ringSize) {
    uint8_t r = CRIT_R[currentCrit];
    uint8_t g = CRIT_G[currentCrit];
    uint8_t b = CRIT_B[currentCrit];
 
    // 2 spots espacés de 180°
    float spotAngles[2] = { gyroAngle, fmod(gyroAngle + 180.0f, 360.0f) };
 
    for (int src = 0; src < 2; src++) {
        // Position de la tête du spot sur ce ring
        float headF = (spotAngles[src] / 360.0f) * ringSize;
        int   head  = (int)headF % ringSize;
 
        // Tête + traînée
        for (int t = 0; t < TRAIL_LEN; t++) {
            int ledIdx = (head - t + ringSize) % ringSize;
            // Intensité décroissante quadratique
            float intensity = (float)(TRAIL_LEN - t) / (float)TRAIL_LEN;
            intensity = intensity * intensity;
            uint8_t cr = (uint8_t)(r * intensity);
            uint8_t cg = (uint8_t)(g * intensity);
            uint8_t cb = (uint8_t)(b * intensity);
            // Additionner avec couleur existante (2 spots peuvent se croiser)
            uint32_t existing = ring.getPixelColor(ringStart + ledIdx);
            cr = (uint8_t)min(255, (int)cr + (int)((existing >> 16) & 0xFF));
            cg = (uint8_t)min(255, (int)cg + (int)((existing >>  8) & 0xFF));
            cb = (uint8_t)min(255, (int)cb + (int)( existing        & 0xFF));
            ring.setPixelColor(ringStart + ledIdx, ring.Color(cr, cg, cb));
        }
    }
}
 
void ring_updateGyro() {
    // Avancer l'angle selon le temps écoulé
    unsigned long now = millis();
    float elapsed = (now - lastGyroTime) / 1000.0f;
    lastGyroTime = now;
    gyroAngle += elapsed * GYRO_SPEED * 360.0f;
    if (gyroAngle >= 360.0f) gyroAngle -= 360.0f;
 
    ring.clear();
    ring_drawSpots(RING1_START, 32);
    ring_drawSpots(RING2_START, 40);
    ring_drawSpots(RING3_START, 48);
    ring.show();
}
 
// ── Traitement commandes I2C ──────────────────────────────────────────────────
void processI2C() {
    if (!i2cReady) return;
    i2cReady = false;
    uint8_t cmd = i2cBuf[0];
    switch (cmd) {
        case CMD_CO2: {
            uint16_t co2 = ((uint16_t)i2cBuf[1] << 8) | i2cBuf[2];
            currentCO2  = co2;
            currentMode = MODE_CO2;
            ring_showCO2(co2);
            break;
        }
        case CMD_GYRO: {
            uint8_t crit = i2cBuf[1];
            if (crit >= 1 && crit <= 5) currentCrit = crit;
            currentMode = MODE_GYRO;
            break;
        }
        case CMD_BRIGHTNESS: {
            brightness = i2cBuf[1];
            ring.setBrightness(brightness);
            ring.show();
            break;
        }
        case CMD_VOLUME: {
            mp3Volume = i2cBuf[1];
            if (mp3Volume > 30) mp3Volume = 30;
            mp3_setVolume(mp3Volume);
            break;
        }
        case CMD_PLAY: {
            mp3_play(toMp3File(i2cBuf[1]));
            break;
        }
    }
}
 
// ── Setup ─────────────────────────────────────────────────────────────────────
void setup() {
    // I2C slave
    Wire.begin(I2C_ADDR, PIN_SDA, PIN_SCL);
    Wire.onReceive(onReceive);
 
    // Ring — séquence de démarrage (5 couleurs de criticité)
    ring.begin();
    ring.setBrightness(brightness);
    ring.clear();
    ring.show();
    // Séquence de démarrage : gyrophare avec changement de couleur 5→1→5
    const int seq[] = {5, 4, 3, 2, 1, 1, 2, 3, 4, 5};
    lastGyroTime = millis();
    unsigned long seqTimer = millis();
    int seqIdx = 0;
    currentMode = MODE_GYRO;
    while (seqIdx < 10) {
        currentCrit = seq[seqIdx];
        ring_updateGyro();
        delay(20);  // ~50 fps
        if (millis() - seqTimer >= 400) {
            seqIdx++;
            seqTimer = millis();
        }
    }
    currentMode = MODE_CO2;
    ring.clear();
    ring.show();
 
    // MP3
    Serial.begin(9600);
    delay(2000);
    mp3.begin(Serial, true);
    delay(500);
    mp3.outputDevice(DFPLAYER_DEVICE_SD);
    delay(200);
    mp3.volume(mp3Volume);
    delay(200);
    mp3.playMp3Folder(SND_STARTUP);
    delay(11000);   // attendre fin du son de démarrage
    mp3.sleep();    // standby
}
 
// ── Loop ──────────────────────────────────────────────────────────────────────
void loop() {
    processI2C();
 
    // Repasser en standby après la fin du son
    if (waitingStandby && millis() - lastPlayTime >= STANDBY_DELAY) {
        waitingStandby = false;
        mp3.sleep();
    }
 
    if (currentMode == MODE_GYRO) {
        ring_updateGyro();
        delay(20);  // ~50 fps
    }
}

DFR0299

Le DFR0299 ou “DFPlayer - Mini MP3 Player” est un petit lecteur de MP3. Les fichiers doivent être placés sur une carte SD et sont lus à la demande.

Les fichiers utilisés sont:

Fichier Événement
Démarrage
Ticket VERY_HIGH
Ticket HIGH
Ticket MEDIUM
Ticket LOW
Ticket INFORMATION
CO₂ 400-600
CO₂ 600-1000
CO₂ 1000-1500
CO₂ 1500-2000

Branchements / PCB

Un PCB est branché sur l'Arduino Q pour permettre la connexion des autres composants

Ce PCB a été réalisé avec KiCad et produit par Aisler.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
start/utils/cockpit/p1/mcu/hardware.txt · Last modified: by admin_wiki