Projet PLS : Programmable Lighting System

Discussion dans 'Discussion Technique' créé par sawblade, 9 Février 2026.

  1. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Tu tentes un copié /collé ? :D

    Je commence aussi à réfléchir a une version pour crawler (beaucoup plus compliqué à cause du drag brake automatique !)
     
    dawii apprécie ceci.
  2. dawii

    dawii membre

    Inscrit:
    16 Octobre 2020
    Messages:
    8 861
    J'aime reçus:
    21 670
    non pas du tout, juste impressionné par les lignes de code, 'suis vite largué en electro bien que ça soit très interressant:p
    Bonne continuation(y)
     
    fb11 apprécie ceci.
  3. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Ben justement, tout l'intérêt du truc, c'est qu'il n'y a rien à faire
     
    dawii apprécie ceci.
  4. dawii

    dawii membre

    Inscrit:
    16 Octobre 2020
    Messages:
    8 861
    J'aime reçus:
    21 670
    oui ou si peu:p
    Mais quand on est une buse en electro, bref:hihi:
    Rien à voir...
    Après quelques épisodes Initial D, cet été en train de refaire la saga des Fast and furious (sur netflix), ça fait drôle de revoir 20 ans après, une autre époque, tout à fond! il y a quand même de superbes prises de vue:passur:
    ..Ta voiture est clairement dans cet état d'esprit:cool:
     
  5. Nicolas

    Nicolas membre

    Inscrit:
    9 Mars 2009
    Messages:
    7 359
    J'aime reçus:
    7 967
    Des phares sur un crawler ?
    Je vois ne vois pas trop pourquoi.
     
    sawblade apprécie ceci.
  6. benjiv

    benjiv membre

    Inscrit:
    4 Janvier 2015
    Messages:
    5 678
    J'aime reçus:
    8 469
    Je pensais plutôt à la gestion du frein
     
    Nicolas et sawblade aiment ça.
  7. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Oui, je parles du mode crawler du vario, bien évidemment
     
    Nicolas apprécie ceci.
  8. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Encore une modif : rajout de la distinction frein / marche arrière :)

     
    dawii apprécie ceci.
  9. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    J'ai aussi essayé vite fait de faire une évocation du flame spiting au relâché des gaz ou lors de l'antilag. J'ai pas passé trop de temps à affiner, ce ne sera pas prioritaire. J'ai quand même fait en sorte que ce ne soit pas trop régulier lors de l'antilag

    L'essai est fait avec la led de clignotant, l'idée serait d'insérer une toute petite led dans des sorties d'échappement en tube métallique :
    - Allumage lors du bang ( relâché d'accélérateur plein gaz)
    - Allumage lors de l'activation de l'antilag (gaz maintenu a fond)


    Je trouve ça très rigolo meme si c'est pas parfait (y)
     
    Scal68, fb11 et dawii aiment ça.
  10. fb11

    fb11 membre

    Inscrit:
    18 Octobre 2018
    Messages:
    16 799
    J'aime reçus:
    38 421
    Rlaarlo l’a fait sur la Mitsu EVO 6 au 1/7 qui vient de sortir :). J’aurais pas la patience de faire ce que tu fais, ni les compétences (y)
     
    #50 fb11, 21 Août 2026
    Dernière édition: 21 Août 2026
    dawii, sawblade et Scal68 aiment ça.
  11. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Je ferai un recap a la fin, vous verrez la facilité avec laquelle on peut arriver a ce résultat

    Le gros morceau à venir, ca va etre l'agencement propre de l'ensemble du câblage. La place étant comptée, je pense me passer de toute connectique
     
    fb11 et dawii aiment ça.
  12. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    En attendant, je colle ici la dernière version du code. J'ai rajouté la distinction Feu Stop / Feu de Recul sur le deuxième appui en arrière de la gâchette.
    J'ai rajouté aussi un brouillon d'une fonction "surprise", qui fonctionne mais ne donne pas encore le rendu espéré...
    Les plus curieux la trouveront peut-etre dans le code. N'hésitez pas a chercher, les annotations au milieu des lignes de code donne un aspect plus lisible.

    C'est parti:

    #include <Servo.h>

    //=====================================================
    // ENTREES
    //=====================================================

    const byte ch2Pin = 2;
    const byte ch1Pin = 3;

    //=====================================================
    // SORTIES
    //=====================================================

    const byte outputD4 = 4; // Feux stop
    const byte outputD6 = 6;
    const byte outputD7 = 7;
    const byte servoPin = 8; // Servo
    const byte outputD9 = 9; // Feux de recul
    const byte outputD10 = 10; // Fonction D10

    Servo servo;

    //=====================================================
    // CH2
    //=====================================================

    // Marche arrière / frein
    const int SEUIL_BAS = 1300;

    // Zone neutre
    const int NEUTRE_MIN = 1300;
    const int NEUTRE_MAX = 1600;

    // Plein gaz
    const int PLEIN_GAZ = 1960;

    //=====================================================
    // D6 / D7
    //=====================================================

    const unsigned long TEMPS_TOGGLE = 3000;
    const unsigned long TEMPS_CLIGNOT = 500;

    //=====================================================
    // FREIN / MARCHE ARRIERE
    //=====================================================

    const unsigned long TEMPS_NEUTRE = 100;

    //=====================================================
    // D10
    //=====================================================

    // Impulsion lors de :
    // PLEIN GAZ -> NEUTRE
    const unsigned long D10_IMPULSION = 100;

    // Temps de plein gaz avant clignotement
    const unsigned long D10_DELAI = 2000;

    // Clignotement irrégulier rapide
    const unsigned long D10_ON_MIN = 50;
    const unsigned long D10_ON_MAX = 100;

    const unsigned long D10_OFF_MIN = 120;
    const unsigned long D10_OFF_MAX = 280;

    //=====================================================
    // SERVO
    //=====================================================

    const int SERVO_POSITION_0 = 2000;
    const int SERVO_POSITION_100 = 1000;

    //=====================================================
    // FREIN / MARCHE ARRIERE
    //=====================================================

    enum EtatGachette
    {
    AVANT,
    FREIN,
    NEUTRE_APRES_FREIN,
    MARCHE_ARRIERE
    };

    EtatGachette etatGachette = AVANT;

    unsigned long neutreTimer = 0;

    //=====================================================
    // D6
    //=====================================================

    bool d6Enable = false;
    bool d6State = false;

    unsigned long d6Timer = 0;
    unsigned long d6Blink = 0;

    bool d6WaitingRelease = false;

    //=====================================================
    // D7 / SERVO
    //=====================================================

    bool d78Enable = false;

    unsigned long d78Timer = 0;

    bool d78WaitingRelease = false;

    //=====================================================
    // D10
    //=====================================================

    // A-t-on déjà vu le plein gaz ?
    bool d10PleinGazVu = false;

    // Timer du plein gaz
    unsigned long d10GazTimer = 0;

    // Clignotement actif
    bool d10Repeat = false;

    // Etat actuel de D10
    bool d10State = false;

    // Timer de l'état actuel
    unsigned long d10Timer = 0;

    // Durée actuelle ON ou OFF
    unsigned long d10Duree = 0;

    //=====================================================
    // SETUP
    //=====================================================

    void setup()
    {
    pinMode(ch1Pin, INPUT);
    pinMode(ch2Pin, INPUT);

    pinMode(outputD4, OUTPUT);
    pinMode(outputD6, OUTPUT);
    pinMode(outputD7, OUTPUT);
    pinMode(outputD9, OUTPUT);
    pinMode(outputD10, OUTPUT);

    digitalWrite(outputD4, LOW);
    digitalWrite(outputD6, LOW);
    digitalWrite(outputD7, LOW);
    digitalWrite(outputD9, LOW);
    digitalWrite(outputD10, LOW);

    //-------------------------------------------------
    // Servo
    //-------------------------------------------------

    servo.attach(servoPin);
    servo.writeMicroseconds(SERVO_POSITION_0);

    //-------------------------------------------------
    // Aléatoire
    //-------------------------------------------------

    randomSeed(analogRead(A0));

    //-------------------------------------------------
    // Série
    //-------------------------------------------------

    Serial.begin(115200);

    Serial.println("===== DEMARRAGE =====");
    }

    //=====================================================
    // LOOP
    //=====================================================

    void loop()
    {
    unsigned long now = millis();

    //=================================================
    // LECTURE RADIO
    //=================================================

    unsigned long ch2 = pulseIn(ch2Pin, HIGH, 25000);
    unsigned long ch1 = pulseIn(ch1Pin, HIGH, 25000);

    //=================================================
    // POSITION CH2
    //=================================================

    bool gazArriere =
    (ch2 > 0 && ch2 < SEUIL_BAS);

    bool gazNeutre =
    (ch2 >= NEUTRE_MIN &&
    ch2 <= NEUTRE_MAX);

    bool gazAvant =
    (ch2 > NEUTRE_MAX);

    bool pleinGaz =
    (ch2 >= PLEIN_GAZ);

    //=================================================
    // FREIN / MARCHE ARRIERE
    //=================================================

    //-------------------------------------------------
    // AVANT
    //-------------------------------------------------

    if (etatGachette == AVANT)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    if (gazArriere)
    {
    etatGachette = FREIN;
    }
    }

    //-------------------------------------------------
    // FREIN
    //-------------------------------------------------

    else if (etatGachette == FREIN)
    {
    digitalWrite(outputD4, HIGH);
    digitalWrite(outputD9, LOW);

    if (!gazArriere)
    {
    neutreTimer = now;
    etatGachette = NEUTRE_APRES_FREIN;
    }
    }

    //-------------------------------------------------
    // NEUTRE APRES FREIN
    //-------------------------------------------------

    else if (etatGachette == NEUTRE_APRES_FREIN)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    if (gazAvant)
    {
    etatGachette = AVANT;
    }

    else if (gazNeutre)
    {
    // Attente
    }

    else if (gazArriere)
    {
    if (now - neutreTimer >= TEMPS_NEUTRE)
    {
    etatGachette = MARCHE_ARRIERE;
    }
    }
    }

    //-------------------------------------------------
    // MARCHE ARRIERE
    //-------------------------------------------------

    else if (etatGachette == MARCHE_ARRIERE)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, HIGH);

    if (gazNeutre)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    neutreTimer = now;

    etatGachette = NEUTRE_APRES_FREIN;
    }

    else if (gazAvant)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    etatGachette = AVANT;
    }
    }

    //=================================================
    // D6
    //=================================================

    if (ch2 > 0 &&
    ch1 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 < SEUIL_BAS)
    {
    if (d6Timer == 0)
    d6Timer = now;

    if (!d6WaitingRelease &&
    now - d6Timer >= TEMPS_TOGGLE)
    {
    d6Enable = !d6Enable;

    d6WaitingRelease = true;
    }
    }
    else
    {
    d6Timer = 0;
    }

    //-------------------------------------------------
    // Réarmement D6
    //-------------------------------------------------

    if (ch2 > SEUIL_BAS &&
    ch1 > SEUIL_BAS)
    {
    d6WaitingRelease = false;
    }

    //=================================================
    // CLIGNOTEMENT D6
    //=================================================

    if (d6Enable)
    {
    if (now - d6Blink >= TEMPS_CLIGNOT)
    {
    d6Blink = now;

    d6State = !d6State;

    digitalWrite(outputD6, d6State);
    }
    }
    else
    {
    d6State = false;

    digitalWrite(outputD6, LOW);
    }

    //=================================================
    // D7 + SERVO
    //=================================================

    if (ch2 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 == 0)
    {
    if (d78Timer == 0)
    d78Timer = now;

    if (!d78WaitingRelease &&
    now - d78Timer >= TEMPS_TOGGLE)
    {
    d78Enable = !d78Enable;

    d78WaitingRelease = true;
    }
    }
    else
    {
    d78Timer = 0;
    }

    //-------------------------------------------------
    // Réarmement
    //-------------------------------------------------

    if (!(ch2 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 == 0))
    {
    d78WaitingRelease = false;
    }

    //=================================================
    // SERVO + D7
    //=================================================

    if (d78Enable)
    {
    digitalWrite(outputD7, HIGH);

    servo.writeMicroseconds(SERVO_POSITION_100);
    }
    else
    {
    digitalWrite(outputD7, LOW);

    servo.writeMicroseconds(SERVO_POSITION_0);
    }

    //=================================================
    // D10
    //=================================================

    //-------------------------------------------------
    // 1. DETECTION DU PLEIN GAZ
    //-------------------------------------------------

    if (pleinGaz)
    {
    //-------------------------------------------------
    // Premier passage à plein gaz
    //-------------------------------------------------

    if (!d10PleinGazVu)
    {
    d10PleinGazVu = true;

    d10GazTimer = now;

    d10Repeat = false;
    }

    //-------------------------------------------------
    // Plein gaz maintenu > 1 seconde
    //-------------------------------------------------

    if (!d10Repeat &&
    now - d10GazTimer >= D10_DELAI)
    {
    d10Repeat = true;

    d10State = true;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    d10Duree =
    random(D10_ON_MIN,
    D10_ON_MAX + 1);
    }
    }

    //=================================================
    // 2. RETOUR DU PLEIN GAZ VERS LE NEUTRE
    //=================================================

    if (d10PleinGazVu &&
    gazNeutre)
    {
    //-------------------------------------------------
    // Impulsion unique de 200 ms
    //-------------------------------------------------

    d10State = true;

    d10Repeat = false;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    d10Duree = D10_IMPULSION;

    //-------------------------------------------------
    // On désarme
    //-------------------------------------------------

    d10PleinGazVu = false;
    }

    //=================================================
    // 3. SORTIE DU PLEIN GAZ
    //=================================================

    /*
    Si on quitte le plein gaz sans passer
    immédiatement par le neutre, on arrête
    le clignotement répétitif.

    MAIS on conserve d10PleinGazVu afin que
    le passage suivant dans la zone neutre
    puisse déclencher l'impulsion.
    */

    if (!pleinGaz)
    {
    d10Repeat = false;
    }

    //=================================================
    // 4. GESTION D10
    //=================================================

    if (d10State)
    {
    //-------------------------------------------------
    // D10 allumé
    //-------------------------------------------------

    if (now - d10Timer >= d10Duree)
    {
    d10State = false;

    digitalWrite(outputD10, LOW);

    d10Timer = now;

    //-------------------------------------------------
    // Si répétition active :
    // choisir durée OFF
    //-------------------------------------------------

    if (d10Repeat)
    {
    d10Duree =
    random(D10_OFF_MIN,
    D10_OFF_MAX + 1);
    }
    }
    }

    else if (d10Repeat)
    {
    //-------------------------------------------------
    // D10 éteint
    //-------------------------------------------------

    if (now - d10Timer >= d10Duree)
    {
    d10State = true;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    //-------------------------------------------------
    // Nouvelle durée ON
    //-------------------------------------------------

    d10Duree =
    random(D10_ON_MIN,
    D10_ON_MAX + 1);
    }
    }

    //=================================================
    // SECURITE D10
    //=================================================

    /*
    Si aucune fonction D10 n'est active,
    D10 doit être éteint.

    Cette partie évite qu'une ancienne impulsion
    puisse rester active.
    */

    if (!d10State && !d10Repeat)
    {
    digitalWrite(outputD10, LOW);
    }

    //=================================================
    // MONITEUR SERIE
    //=================================================

    static unsigned long lastPrint = 0;

    if (now - lastPrint >= 250)
    {
    lastPrint = now;

    Serial.print("D2=");
    Serial.print(ch2);

    Serial.print(" D3=");
    Serial.print(ch1);

    Serial.print(" D4=");
    Serial.print(digitalRead(outputD4));

    Serial.print(" D6=");
    Serial.print(digitalRead(outputD6));

    Serial.print(" D7=");
    Serial.print(digitalRead(outputD7));

    Serial.print(" D9=");
    Serial.print(digitalRead(outputD9));

    Serial.print(" D10=");
    Serial.print(digitalRead(outputD10));

    Serial.print(" Servo=");
    Serial.print(d78Enable ? "100%" : "0%");

    Serial.print(" D10PG=");
    Serial.print(d10PleinGazVu);

    Serial.print(" D10REP=");
    Serial.print(d10Repeat);

    Serial.print(" Etat=");

    switch (etatGachette)
    {
    case AVANT:
    Serial.print("AVANT");
    break;

    case FREIN:
    Serial.print("FREIN");
    break;

    case NEUTRE_APRES_FREIN:
    Serial.print("NEUTRE_APRES_FREIN");
    break;

    case MARCHE_ARRIERE:
    Serial.print("MARCHE_ARRIERE");
    break;
    }

    Serial.print(" PleinGaz=");
    Serial.println(pleinGaz);
    }
    }
     
  13. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Modification du jour: ajout d'une fonction feux arrières double fonction (position et stop) sur la borne D5 (la borne D4 reste en feu STOP classique, pour cabler des feux stop arrieres séparés des feux de position, ou pour un éventuel 3eme feu stop )


    Ci joint le nouveau code (j'ai aussi modifé quelques commentaires, pour que ce soit plus explicite):
    #include <Servo.h>

    //=====================================================
    // ENTREES
    //=====================================================

    const byte ch2Pin = 2;
    const byte ch1Pin = 3;

    //=====================================================
    // SORTIES
    //=====================================================

    const byte outputD4 = 4; // Feux stop
    const byte outputD5 = 5; // Feu arrière double intensité PWM
    const byte outputD6 = 6; // Warning
    const byte outputD7 = 7; // Phares
    const byte servoPin = 8; // Servo
    const byte outputD9 = 9; // Feux de recul
    const byte outputD10 = 10; // Echappement

    Servo servo;

    //=====================================================
    // CH2
    //=====================================================

    // Marche arrière / frein
    const int SEUIL_BAS = 1300;

    // ZONE NEUTRE
    const int NEUTRE_MIN = 1300;
    const int NEUTRE_MAX = 1600;

    // PLEIN GAZ
    const int PLEIN_GAZ = 1960;

    //=====================================================
    // CLIGNOTANTS / PHARES
    //=====================================================

    const unsigned long TEMPS_TOGGLE = 3000;
    const unsigned long TEMPS_CLIGNOT = 500;

    //=====================================================
    // FREIN / MARCHE ARRIERE
    //=====================================================

    const unsigned long TEMPS_NEUTRE = 100;

    //=====================================================
    // FEU ARRIERE D5
    //=====================================================

    // Intensité feu arrière avec phares allumés
    const byte D5_INTENSITE_VEILLEUSE = 70;

    // Intensité feu stop
    const byte D5_INTENSITE_STOP = 255;

    // Valeur PWM actuellement envoyée à D5
    byte d5PWM = 0;

    //=====================================================
    // ECHAPPEMENT
    //=====================================================

    // Impulsion lors de :
    // PLEIN GAZ -> NEUTRE
    const unsigned long D10_IMPULSION = 100;

    // Temps de plein gaz avant clignotement
    const unsigned long D10_DELAI = 2000;

    // Clignotement irrégulier rapide
    const unsigned long D10_ON_MIN = 50;
    const unsigned long D10_ON_MAX = 100;

    const unsigned long D10_OFF_MIN = 120;
    const unsigned long D10_OFF_MAX = 280;

    //=====================================================
    // SERVO
    //=====================================================

    const int SERVO_POSITION_0 = 2000;
    const int SERVO_POSITION_100 = 1000;

    //=====================================================
    // FREIN / MARCHE ARRIERE
    //=====================================================

    enum EtatGachette
    {
    AVANT,
    FREIN,
    NEUTRE_APRES_FREIN,
    MARCHE_ARRIERE
    };

    EtatGachette etatGachette = AVANT;

    unsigned long neutreTimer = 0;

    //=====================================================
    // WARNING
    //=====================================================

    bool d6Enable = false;
    bool d6State = false;

    unsigned long d6Timer = 0;
    unsigned long d6Blink = 0;

    bool d6WaitingRelease = false;

    //=====================================================
    // PHARES / SERVO
    //=====================================================

    bool d78Enable = false;

    unsigned long d78Timer = 0;

    bool d78WaitingRelease = false;

    //=====================================================
    // ECHAPPEMENT
    //=====================================================

    // A-t-on déjà vu le plein gaz ?
    bool d10PleinGazVu = false;

    // Timer du plein gaz
    unsigned long d10GazTimer = 0;

    // Clignotement actif
    bool d10Repeat = false;

    // Etat actuel de D10
    bool d10State = false;

    // Timer de l'état actuel
    unsigned long d10Timer = 0;

    // Durée actuelle ON ou OFF
    unsigned long d10Duree = 0;

    //=====================================================
    // SETUP
    //=====================================================

    void setup()
    {
    pinMode(ch1Pin, INPUT);
    pinMode(ch2Pin, INPUT);

    pinMode(outputD4, OUTPUT);
    pinMode(outputD5, OUTPUT);
    pinMode(outputD6, OUTPUT);
    pinMode(outputD7, OUTPUT);
    pinMode(outputD9, OUTPUT);
    pinMode(outputD10, OUTPUT);

    digitalWrite(outputD4, LOW);
    analogWrite(outputD5, 0);
    digitalWrite(outputD6, LOW);
    digitalWrite(outputD7, LOW);
    digitalWrite(outputD9, LOW);
    digitalWrite(outputD10, LOW);

    //-------------------------------------------------
    // Servo
    //-------------------------------------------------

    servo.attach(servoPin);
    servo.writeMicroseconds(SERVO_POSITION_0);

    //-------------------------------------------------
    // Aléatoire
    //-------------------------------------------------

    randomSeed(analogRead(A0));

    //-------------------------------------------------
    // Série
    //-------------------------------------------------

    Serial.begin(115200);

    Serial.println("===== DEMARRAGE =====");
    }

    //=====================================================
    // LOOP
    //=====================================================

    void loop()
    {
    unsigned long now = millis();

    //=================================================
    // LECTURE RADIO
    //=================================================

    unsigned long ch2 = pulseIn(ch2Pin, HIGH, 25000);
    unsigned long ch1 = pulseIn(ch1Pin, HIGH, 25000);

    //=================================================
    // POSITION CH2
    //=================================================

    bool gazArriere =
    (ch2 > 0 && ch2 < SEUIL_BAS);

    bool gazNeutre =
    (ch2 >= NEUTRE_MIN &&
    ch2 <= NEUTRE_MAX);

    bool gazAvant =
    (ch2 > NEUTRE_MAX);

    bool pleinGaz =
    (ch2 >= PLEIN_GAZ);

    //=================================================
    // FREIN / MARCHE ARRIERE
    //=================================================

    //-------------------------------------------------
    // AVANT
    //-------------------------------------------------

    if (etatGachette == AVANT)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    if (gazArriere)
    {
    etatGachette = FREIN;
    }
    }

    //-------------------------------------------------
    // FREIN
    //-------------------------------------------------

    else if (etatGachette == FREIN)
    {
    digitalWrite(outputD4, HIGH);
    digitalWrite(outputD9, LOW);

    if (!gazArriere)
    {
    neutreTimer = now;
    etatGachette = NEUTRE_APRES_FREIN;
    }
    }

    //-------------------------------------------------
    // NEUTRE APRES FREIN
    //-------------------------------------------------

    else if (etatGachette == NEUTRE_APRES_FREIN)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    if (gazAvant)
    {
    etatGachette = AVANT;
    }

    else if (gazNeutre)
    {
    // Attente
    }

    else if (gazArriere)
    {
    if (now - neutreTimer >= TEMPS_NEUTRE)
    {
    etatGachette = MARCHE_ARRIERE;
    }
    }
    }

    //-------------------------------------------------
    // MARCHE ARRIERE
    //-------------------------------------------------

    else if (etatGachette == MARCHE_ARRIERE)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, HIGH);

    if (gazNeutre)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    neutreTimer = now;

    etatGachette = NEUTRE_APRES_FREIN;
    }

    else if (gazAvant)
    {
    digitalWrite(outputD4, LOW);
    digitalWrite(outputD9, LOW);

    etatGachette = AVANT;
    }
    }

    //=================================================
    // WARNING
    //=================================================

    if (ch2 > 0 &&
    ch1 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 < SEUIL_BAS)
    {
    if (d6Timer == 0)
    d6Timer = now;

    if (!d6WaitingRelease &&
    now - d6Timer >= TEMPS_TOGGLE)
    {
    d6Enable = !d6Enable;

    d6WaitingRelease = true;
    }
    }
    else
    {
    d6Timer = 0;
    }

    //-------------------------------------------------
    // Réarmement WARNING
    //-------------------------------------------------

    if (ch2 > SEUIL_BAS &&
    ch1 > SEUIL_BAS)
    {
    d6WaitingRelease = false;
    }

    //=================================================
    // CLIGNOTEMENT WARNING
    //=================================================

    if (d6Enable)
    {
    if (now - d6Blink >= TEMPS_CLIGNOT)
    {
    d6Blink = now;

    d6State = !d6State;

    digitalWrite(outputD6, d6State);
    }
    }
    else
    {
    d6State = false;

    digitalWrite(outputD6, LOW);
    }

    //=================================================
    // Déclenchement PHARES + SERVO
    //=================================================

    if (ch2 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 == 0)
    {
    if (d78Timer == 0)
    d78Timer = now;

    if (!d78WaitingRelease &&
    now - d78Timer >= TEMPS_TOGGLE)
    {
    d78Enable = !d78Enable;

    d78WaitingRelease = true;
    }
    }
    else
    {
    d78Timer = 0;
    }

    //-------------------------------------------------
    // Réarmement
    //-------------------------------------------------

    if (!(ch2 > 0 &&
    ch2 < SEUIL_BAS &&
    ch1 == 0))
    {
    d78WaitingRelease = false;
    }

    //=================================================
    // PHARES D7 + SERVO
    //=================================================

    if (d78Enable)
    {
    digitalWrite(outputD7, HIGH);

    servo.writeMicroseconds(SERVO_POSITION_100);
    }
    else
    {
    digitalWrite(outputD7, LOW);

    servo.writeMicroseconds(SERVO_POSITION_0);
    }

    //=================================================
    // FEU ARRIERE DOUBLE FONCTION D5
    //=================================================

    /*
    LOGIQUE D5 :

    D7 OFF + FREIN OFF = 0 %
    D7 OFF + FREIN ON = 100 %
    D7 ON + FREIN OFF = 50 %
    D7 ON + FREIN ON = 100 %

    Le frein est prioritaire sur les phares.
    */

    if (etatGachette == FREIN)
    {
    // FREIN : pleine intensité
    d5PWM = D5_INTENSITE_STOP;
    }
    else if (d78Enable)
    {
    // PHARES : intensité réduite
    d5PWM = D5_INTENSITE_VEILLEUSE;
    }
    else
    {
    // PHARES OFF + pas de frein
    d5PWM = 0;
    }

    // Envoi du PWM sur D5
    analogWrite(outputD5, d5PWM);

    //=================================================
    // ECHAPPEMENT
    //=================================================

    //-------------------------------------------------
    // 1. DETECTION DU PLEIN GAZ
    //-------------------------------------------------

    if (pleinGaz)
    {
    //-------------------------------------------------
    // Premier passage à plein gaz
    //-------------------------------------------------

    if (!d10PleinGazVu)
    {
    d10PleinGazVu = true;

    d10GazTimer = now;

    d10Repeat = false;
    }

    //-------------------------------------------------
    // PLEIN GAZ maintenu > D10_DELAI
    //-------------------------------------------------

    if (!d10Repeat &&
    now - d10GazTimer >= D10_DELAI)
    {
    d10Repeat = true;

    d10State = true;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    d10Duree =
    random(D10_ON_MIN,
    D10_ON_MAX + 1);
    }
    }

    //=================================================
    // 2. RETOUR DU PLEIN GAZ VERS LE NEUTRE
    //=================================================

    if (d10PleinGazVu &&
    gazNeutre)
    {
    //-------------------------------------------------
    // Impulsion unique
    //-------------------------------------------------

    d10State = true;

    d10Repeat = false;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    d10Duree = D10_IMPULSION;

    //-------------------------------------------------
    // On désarme
    //-------------------------------------------------

    d10PleinGazVu = false;
    }

    //=================================================
    // 3. SORTIE DU PLEIN GAZ
    //=================================================

    /*
    Si on quitte le plein gaz sans passer
    immédiatement par le neutre, on arrête
    le clignotement répétitif.

    MAIS on conserve d10PleinGazVu afin que
    le passage suivant dans la zone neutre
    puisse déclencher l'impulsion.
    */

    if (!pleinGaz)
    {
    d10Repeat = false;
    }

    //=================================================
    // 4. ANTILAG sur ECHAPPEMENT
    //=================================================

    if (d10State)
    {
    //-------------------------------------------------
    // D10 allumé
    //-------------------------------------------------

    if (now - d10Timer >= d10Duree)
    {
    d10State = false;

    digitalWrite(outputD10, LOW);

    d10Timer = now;

    //-------------------------------------------------
    // Si répétition active :
    // choisir durée OFF
    //-------------------------------------------------

    if (d10Repeat)
    {
    d10Duree =
    random(D10_OFF_MIN,
    D10_OFF_MAX + 1);
    }
    }
    }

    else if (d10Repeat)
    {
    //-------------------------------------------------
    // D10 éteint
    //-------------------------------------------------

    if (now - d10Timer >= d10Duree)
    {
    d10State = true;

    digitalWrite(outputD10, HIGH);

    d10Timer = now;

    //-------------------------------------------------
    // Nouvelle durée ON
    //-------------------------------------------------

    d10Duree =
    random(D10_ON_MIN,
    D10_ON_MAX + 1);
    }
    }

    //=================================================
    // SECURITE D10
    //=================================================

    /*
    Si aucune fonction D10 n'est active,
    D10 doit être éteint.
    */

    if (!d10State && !d10Repeat)
    {
    digitalWrite(outputD10, LOW);
    }

    //=================================================
    // MONITEUR SERIE
    //=================================================

    static unsigned long lastPrint = 0;

    if (now - lastPrint >= 250)
    {
    lastPrint = now;

    Serial.print("D2=");
    Serial.print(ch2);

    Serial.print(" D3=");
    Serial.print(ch1);

    Serial.print(" D4=");
    Serial.print(digitalRead(outputD4));

    // IMPORTANT :
    // Affiche maintenant le PWM réellement envoyé à D5
    Serial.print(" D5_PWM=");
    Serial.print(d5PWM);

    Serial.print(" D6=");
    Serial.print(digitalRead(outputD6));

    Serial.print(" D7=");
    Serial.print(digitalRead(outputD7));

    Serial.print(" D9=");
    Serial.print(digitalRead(outputD9));

    Serial.print(" D10=");
    Serial.print(digitalRead(outputD10));

    Serial.print(" Servo=");
    Serial.print(d78Enable ? "100%" : "0%");

    Serial.print(" D10PG=");
    Serial.print(d10PleinGazVu);

    Serial.print(" D10REP=");
    Serial.print(d10Repeat);

    Serial.print(" Etat=");

    switch (etatGachette)
    {
    case AVANT:
    Serial.print("AVANT");
    break;

    case FREIN:
    Serial.print("FREIN");
    break;

    case NEUTRE_APRES_FREIN:
    Serial.print("NEUTRE_APRES_FREIN");
    break;

    case MARCHE_ARRIERE:
    Serial.print("MARCHE_ARRIERE");
    break;
    }

    Serial.print(" PleinGaz=");
    Serial.println(pleinGaz);
    }
    }
     
    fb11 apprécie ceci.
  14. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    J'ai vite fait un schema de mon système. Comme vous pouvez le constater, niveau électronique, il n'y a pas grand chose ! Même pas une résistance : pour gérer les 2 niveaux d'intensité lumineuse des Feux STOP / POSITION , car j'utilise un signal PWM (comme si c'était un signal moteur émis par un variateur, en gros) à 2 fréquences distinctes !
    En gros, on soude les fils d'éclairage du véhicule, et c'est tout...

    C'était aussi le but du projet, que ce soit le plus épuré possible. Et aussi que ce soit le plus abordable possible, même pour quelqu'un qui n'a aucune notion d'électronique.

    [​IMG]

    Les fins observateurs remarqueront peut-être qu'il reste un couple de broches de libre sur l'ULN2803A (1B et 1C). Si vous pensez à une fonction, je suis preneur d'idée :D
    Attention toutefois au fait que la contrainte est de n'utiliser que les canaux CH1 et CH2 du récepteur (ou des combinaisons) , car sur le CH3 j'ai mon gyroscope...
     
    #54 sawblade, 30 Août 2026
    Dernière édition: 30 Août 2026
    fb11 et Nicolas aiment ça.
  15. Moa Etctout

    Moa Etctout membre

    Inscrit:
    22 Septembre 2021
    Messages:
    1 603
    J'aime reçus:
    3 028
    @sawblade , j'ai pensé à un truc pour la gestion de ton allumage de feux stop avec le drag brake: plutôt que de chercher une solution pour l'allumer, si tu voyais plutôt une solution pour l'éteindre: je m'explique, par défaut, les avoir allumés et lorsque tu appuies sur la gâchette ( dans le sens avant ou arrière) ça l'éteint. Ça marcherait pas comme ça ?
     
    fb11 et sawblade aiment ça.
  16. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    En principe de base de fonctionnement oui, mais je voudrais essayer de reproduire un truc plus réaliste, comme :
    - rajouter un delai d'allumage a l'arrêt (au bout d'un moment, les feux stop s'éteignent)
    - creer un allumage sur drag brake violent sans que le véhicule s'arrête
    Histoire d'obtenir un truc plus vivant.

    Mais tu as raison, la logique de départ du système, c'est bien "feux stop allumés au neutre"
     
    dawii, Moa Etctout et fb11 aiment ça.
  17. cactuszebest

    cactuszebest membre

    Inscrit:
    9 Décembre 2009
    Messages:
    504
    J'aime reçus:
    566
    Sur le système Lane Boy RC, tu as une variable qui gère la valeur de la commande des gaz autour du neutre pour laquelle les feux stop vont se couper.
    Elle est aléatoire mais comprise entre 2 valeurs données.
    Il y a une seconde variable qui donne le temps où les feux stop restent allumé à l’arrêt avant de se couper (quand le véhicule reste à l'arrêt).
    Celle ci aussi est aléatoire mais comprise entre 2 valeurs prédéfinies.
    Ca rend le fonctionnement très réaliste parce qu'à chaque fois où les feux stop se coupent, se sera légèrement différent de la fois d'avant.

    Il y a plein de variable de ce genre sur ce système. Sur le feux de recul par exemple. Il y en a une qui donne le temps où il reste allumé après une marche arrière puis un retour au neutre.
    Ca rend le fonctionnement plus "humain".
     
    sawblade et fb11 aiment ça.
  18. Moa Etctout

    Moa Etctout membre

    Inscrit:
    22 Septembre 2021
    Messages:
    1 603
    J'aime reçus:
    3 028
    Gemini me dit ça (a adapter avec tes valeurs et impossible a vérifier pour moi qui suis une quiche en programmation):
    // --- PINOUT ---
    const int PIN_GAZ = 2; // Entrée signal RC Gaz (PWM voie 2)
    const int PIN_STOP = 9; // Sortie LED Feux Stop

    // --- PARAMÈTRES RÉGLABLES ---
    const int NEUTRE_CENTRE = 1500; // Signal neutre en microsecondes (µs)
    const int ZONE_MORTE = 40; // Plage neutre (+/- 40µs)
    const unsigned long TIMEOUT_STOP = 3000; // Extinction auto des stop après 3 sec au neutre (ms)
    const int SEUIL_DRAG_BRAKE_SEVERE = 120; // Variation brutale minimale des gaz pour déclencher le stop

    // --- VARIABLES INTERNES ---
    int gazActuel = NEUTRE_CENTRE;
    int gazPrecedent = NEUTRE_CENTRE;
    unsigned long tempsDebutNeutre = 0;
    bool auNeutre = false;
    bool feuxStopActifs = false;

    void setup() {
    pinMode(PIN_GAZ, INPUT);
    pinMode(PIN_STOP, OUTPUT);
    }

    void loop() {
    unsigned long tempsActuel = millis();

    // 1. Lecture de la voie des gaz
    gazActuel = pulseIn(PIN_GAZ, HIGH, 25000);
    if (gazActuel == 0) gazActuel = NEUTRE_CENTRE; // Sécurité si perte de signal

    // 2. Vérification de la zone morte (Véhicule au neutre)
    bool maintenantAuNeutre = (gazActuel >= (NEUTRE_CENTRE - ZONE_MORTE)) &&
    (gazActuel <= (NEUTRE_CENTRE + ZONE_MORTE));

    // 3. Calcul de la décélération rapide (Drag Brake violent en mouvement)
    // Delta positif lors d'un lâcher de gaz en marche avant
    int deltaGaz = gazPrecedent - gazActuel;

    // --- LOGIQUE DES FEUX STOP ---

    // CAS A : Déclenchement sur Drag Brake violent (même en mouvement)
    if (gazPrecedent > (NEUTRE_CENTRE + ZONE_MORTE) && deltaGaz > SEUIL_DRAG_BRAKE_SEVERE) {
    feuxStopActifs = true;
    }

    // CAS B : Gestion de l'arrêt au neutre avec temporisation
    if (maintenantAuNeutre) {
    if (!auNeutre) {
    // Transition vers le neutre : activation initiale et démarrage du timer
    auNeutre = true;
    feuxStopActifs = true;
    tempsDebutNeutre = tempsActuel;
    } else {
    // Si on reste au neutre, vérifier le délai d'extinction
    if (tempsActuel - tempsDebutNeutre >= TIMEOUT_STOP) {
    feuxStopActifs = false; // Extinction après timeout
    }
    }
    } else {
    // Si on accélère à nouveau (marche avant ou arrière volontaire)
    auNeutre = false;

    // Si l'accélération est stabilisée ou en hausse, éteindre le stop
    if (deltaGaz <= 0) {
    feuxStopActifs = false;
    }
    }

    // 4. Application de l'état sur la LED
    digitalWrite(PIN_STOP, feuxStopActifs ? HIGH : LOW);

    // Mémorisation de la position pour le prochain cycle
    gazPrecedent = gazActuel;

    delay(20);
    // Petite pause pour stabiliser les mesures (~50 Hz)
    }
     
  19. Moa Etctout

    Moa Etctout membre

    Inscrit:
    22 Septembre 2021
    Messages:
    1 603
    J'aime reçus:
    3 028
    Et avec un temps aléatoire ( ici entre 3 et 10 secondes, ça donnerai ça :

    // --- PINOUT ---
    const int PIN_GAZ = 2; // Entrée signal RC Gaz (PWM voie 2)
    const int PIN_STOP = 9; // Sortie LED Feux Stop

    // --- PARAMÈTRES RÉGLABLES ---
    const int NEUTRE_CENTRE = 1500; // Signal neutre en microsecondes (µs)
    const int ZONE_MORTE = 40; // Plage neutre (+/- 40µs)
    const int SEUIL_DRAG_BRAKE_SEVERE = 120; // Variation brutale minimale des gaz pour déclencher le stop

    // Plage du délai aléatoire à l'arrêt (en millisecondes)
    const unsigned long DELAI_STOP_MIN = 3000; // 3 secondes
    const unsigned long DELAI_STOP_MAX = 10000; // 10 secondes

    // --- VARIABLES INTERNES ---
    int gazActuel = NEUTRE_CENTRE;
    int gazPrecedent = NEUTRE_CENTRE;
    unsigned long tempsDebutNeutre = 0;
    unsigned long delaiExtinctionStop = 3000; // Stockera le délai tiré au sort
    bool auNeutre = false;
    bool feuxStopActifs = false;

    void setup() {
    pinMode(PIN_GAZ, INPUT);
    pinMode(PIN_STOP, OUTPUT);

    // Initialise le générateur aléatoire à partir du bruit électrique d'une broche analogique libre
    randomSeed(analogRead(A0));
    }

    void loop() {
    unsigned long tempsActuel = millis();

    // 1. Lecture de la voie des gaz
    gazActuel = pulseIn(PIN_GAZ, HIGH, 25000);
    if (gazActuel == 0) gazActuel = NEUTRE_CENTRE; // Sécurité si perte de signal

    // 2. Vérification de la zone morte (Véhicule au neutre)
    bool maintenantAuNeutre = (gazActuel >= (NEUTRE_CENTRE - ZONE_MORTE)) &&
    (gazActuel <= (NEUTRE_CENTRE + ZONE_MORTE));

    // 3. Calcul de la décélération rapide (Drag Brake violent)
    int deltaGaz = gazPrecedent - gazActuel;

    // --- LOGIQUE DES FEUX STOP ---

    // CAS A : Déclenchement sur Drag Brake violent (même en mouvement)
    if (gazPrecedent > (NEUTRE_CENTRE + ZONE_MORTE) && deltaGaz > SEUIL_DRAG_BRAKE_SEVERE) {
    feuxStopActifs = true;
    }

    // CAS B : Gestion de l'arrêt au neutre avec temporisation aléatoire
    if (maintenantAuNeutre) {
    if (!auNeutre) {
    // Transition vers le neutre :
    auNeutre = true;
    feuxStopActifs = true;
    tempsDebutNeutre = tempsActuel;

    // Tirage au sort d'une durée aléatoire entre 3 et 10 secondes
    delaiExtinctionStop = random(DELAI_STOP_MIN, DELAI_STOP_MAX + 1);
    } else {
    // Si on reste au neutre, vérifier si le délai tiré au sort est dépassé
    if (tempsActuel - tempsDebutNeutre >= delaiExtinctionStop) {
    feuxStopActifs = false; // Extinction après le délai aléatoire
    }
    }
    } else {
    // Si le véhicule réaccélère (marche avant ou arrière)
    auNeutre = false;

    // Si l'accélération est stabilisée ou en hausse, éteindre les feux stop
    if (deltaGaz <= 0) {
    feuxStopActifs = false;
    }
    }

    // 4. Application de l'état sur la LED
    digitalWrite(PIN_STOP, feuxStopActifs ? HIGH : LOW);

    // Mémorisation de la position pour le prochain cycle
    gazPrecedent = gazActuel;


    delay(20); // Petite pause pour stabiliser la boucle (~50 Hz)
    }
     
  20. sawblade

    sawblade membre
    Membre du personnel MODERATEUR

    Inscrit:
    7 Novembre 2009
    Messages:
    11 921
    J'aime reçus:
    16 802
    Voila, c'est plus ou moins la logique que je vise.
    Vous suivez, les copains (y)
     

Partager cette page