Projet Arduino pour Horloger (amateur et peu fortuné)

FORUMAMONTRES › Forums › Forum de discussions sur les montres vintages et sujets techniques › Projet Arduino pour Horloger (amateur et peu fortuné)

15 sujets de 61 à 75 (sur un total de 81)
  • Auteur
    Messages
  • #420004805978
    Torzio
    Membre

    Lorsque je rends une pendule à un client, le réglage est dégrossi. c’est à lui de le peaufiner.
    La gravité « g », influe sur la période et n’est pas la même partout. Elle varie beaucoup en fonction de l’altitude du lieu. En théorie, la même pendule au rez-de-chaussée ne va pas régler de la même façon au 15 è étage.
    La période varie aussi avec la pression atmosphérique et la température. Tout ceci en plus de l’isochronisme fragile du pendule, perturbé par les frottements constants de l’échappement, plus ou moins accentués en fonction de l’armage du ressort et de la qualité du rouage et ou de l’état d’usure, qui transmet la force irrégulièrement.
    Je dis à mes client que ce n’est pas une pendule à quartz et qu’une variation d’environ 2 minutes par semaine est acceptable.

    Je reste persuadé que la solution la plus simple est de mesurer une trentaine d’oscillations du balancier, de faire la moyenne. De faire un petit tableur Excel, comme le mien, mais adapté aux nombres d’alternances/heures.

    J’ai réalisé un tel montage, qui fonctionne parfaitement, mais il attend dans un carton que je trouve le temps et le courage de faire un capteur en fourche où le balancier va couper un rayon laser.

    @+
    JC

    #420004805979
    Cadrature
    Membre
    Torzio a écrit:
    J’ai réalisé un tel montage, qui fonctionne parfaitement, mais il attend dans un carton que je trouve le temps et le courage de faire un capteur en fourche où le balancier va couper un rayon laser.

    @+
    JC

    Pour ce montage, je peux aider.
    C’est assez simple et le quartz monté sur l’Arduino suffit car il ne s’agit pas de relever des temps longs.

    Le capteur existe tout fait: LM393 Module de détection de vitesse

    sinon, on peut monter une led laser et une photorésistance à distance l’une de l’autre.

    Montage:
    Arduino Uno
    Alimentation Arduino
    Capteur LM393
    Affichage LCD 20×4 (branché en communication série, c’est-à-dire en A4 et A5)
    Led + résistance 220homs à 1k

    On peut brancher tout ça avec des fils en l’air, c’est assez simple.

    Le programme en C++ doit ressembler à ça:
    Bon, je ne vois pas le moyen de mettre du code ici…

    Cadrature

    #420004805980
    Torzio
    Membre
    Cadrature a écrit:
    Pour ce montage, je peux aider.
    C’est assez simple et le quartz monté sur l’Arduino suffit car il ne s’agit pas de relever des temps longs.

    Le capteur existe tout fait: LM393 Module de détection de vitesse

    sinon, on peut monter une led laser et une photorésistance à distance l’une de l’autre.

    Je veux bien un schéma avec les composants qui vont bien. Je préfère le laser à l’infra rouge. J’ai eu des problème d’interférences avec ce dernier. Je ne veux pas d’un rayon pulsé, cela fausse la mesure.

    Je ne suis pas d’accord avec le quartz Arduino. Il est très loin d’avoir une précision horlogère. Sa fréquence peut varier de quelques PPM. Lorsque l’on fait une mesure sur une courte durée, il faut une base de temps très précise. Il suffit de mesurer un signal précis et de déterminer une variable d’ajustement pour corriger l’écart de fréquence de l’Arduino.

    Voici une photo de mon périodemètre :

    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Pzorio10

    Le problème est que le capteur a l’émetteur et le récepteur cote à cote. Le signal doit donc être réfléchi. ce qui fausse les mesures si le balancier n’est pas parfaitement plat. Il faut donc que je fasse un capteur en fourche, avec un rayon laser non pulsé. J’ai acheté, sur Aly Express, des émetteurs et capteurs laser. Mais aucune doc avec. J’ai fait des essais en 5 V, cela a un peu fumé. Il faut que je tente en 3,5 V.
    @+
    JC

    #420004805981
    Cadrature
    Membre

    OK

    Pour les composants:

    Une plaque à essai
    Des fils pour plaque à essais
    1 Arduino Uno
    1 LED
    1 Résistance (entre 720 et 1000 ohm)
    1 DS3231
    1 LCD2004
    1 LMS393 2010

    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Img_2183
    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Img_2184

    Montage:

    a)
    Brancher GND et 5V sur les lignes d’alimentation de la plaque
    LED avec la résistance en série (- petite broche et + grande broche)

    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Img_2185

    B)
    Brancher LCD et DS3231
    sur la ligne d’alimentation Vcc et GND
    sur deux lignes de la plaque: SDA et SCL
    Relier SDA à la broche SDA de l’Arduino
    Relier SCL à la broche SCL de l’Arduino
    C’est une communication série avec 2 appareils

    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Img_2186

    C)
    Brancher le détecteur IR sur la ligne d’alimentation et sur 2 de l’Arduino

    Projet Arduino pour Horloger (amateur et peu fortuné) - Page 3 Img_2187

    Cadrature

    #420004805982
    Cadrature
    Membre

    La suite: le code

    Parfaitement d’accord à propos du quartz de l’Arduino qui n’est pas très fiable pour chronométrer.
    Encore moins sur vous utilisez directement le microcontrôleur sans quartz.

    Le composant DS3231 permet de produire une horloge sur Arduino.

    Le composant que j’utilise (LMS393 2010) est simplement un système de coupure IR. Je n’ai jamais eu de pb d’interférence avec.
    Il me sert en général à détecter des événements rapides (moins d’une milliseconde)

    Je fais un petit code avec ce montage et vous montre (si j’arrive à mettre du code dans
    les pages du forum)

    Cadrature

    #420004805983
    Cadrature
    Membre

    Bon, du code à adapter en fonction de vos besoins.
    Les formules mathématiques sont faciles à ajouter.
    L’affichage se fait avec rtc.print()

    Pour cet exemple, je n’ai fait que la moyenne des alternances (je crois qu’on dit dans ce cas oscillation, mais ne suis plus si sûr).

    Je tente encore de mettre du code

    Cadrature

    /*
    * This program is free software: you can redistribute it and/or modify it under the terms of the GNU
    * General Public License as published by the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
    * the GNU General Public License for more details.
    *
    * Cadrature 2024
    */

    #include <Arduino.h>

    // Constants
    #include <LiquidCrystal_I2C.h>
    #include <Wire.h>
    #include « RTClib.h »

    #define IR_PENDULUM_PIN 2 // IR sensor for the pendulum
    #define GREEN_LED 8

    // LCD setup
    LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20×4 display

    // RTC setup
    RTC_DS3231 rtc;

    // Time synchronization
    unsigned long lastSyncMillis = 0;
    DateTime rtcAtLastSync;
    unsigned long SYNC_INTERVAL_MS = 6000; // Synchronization interval with RTC

    unsigned long pendulumOscillations = 0;
    unsigned long pendulumMs = 0;
    unsigned long lastPendulumMs = 0;
    unsigned long PENDULUM_DEBOUNCE_MS = 700; // Debounce time for pendulum detection
    unsigned long sumPendulumMs = 0; // Accumulates the durations of pendulum oscillations
    bool pendulumDetected = false;

    bool greenState = false;
    unsigned long greenStartMs = 0;
    const unsigned long GREEN_DURATION = 20;

    // Display update timing
    unsigned long lastDisplayUpdateMs = 0;
    const unsigned long DISPLAY_UPDATE_INTERVAL_MS = 1009;

    unsigned long currentTimeMs;

    //////////////////////////////
    // Synchronize current millis with RTC
    //////////////////////////////
    unsigned long getCurrentMillis()
    {
    unsigned long currentMillis = millis();

    if (currentMillis – lastSyncMillis >= SYNC_INTERVAL_MS)
    {
    rtcAtLastSync = rtc.now();
    lastSyncMillis = currentMillis;
    }

    unsigned long elapsedMillis = currentMillis – lastSyncMillis;
    DateTime currentTime = rtcAtLastSync + TimeSpan(elapsedMillis / 1000);
    return (currentTime.unixtime() * 1000) + (elapsedMillis % 1000);
    }

    /////////////////////////
    // Interrupt for counting pendulum oscillations
    /////////////////////////
    void IRPendulumISR()
    {
    if (currentTimeMs – pendulumMs > PENDULUM_DEBOUNCE_MS)
    {
    greenState = true;
    digitalWrite(GREEN_LED, HIGH);
    greenStartMs = currentTimeMs;
    lastPendulumMs = pendulumMs;
    pendulumMs = currentTimeMs;
    pendulumDetected = true;
    }
    }

    /////////////////////////
    // Initialization routine
    /////////////////////////
    void setup()
    {
    // Démarre la communication série pour le débogage
    Serial.begin(9600);
    Serial.println(« Initialisation en cours… »);

    // Démarre le bus I2C
    Wire.begin();
    delay(1000);
    Serial.println(« I2C démarré »);

    // Démarre le module RTC
    if (!rtc.begin())
    {
    Serial.println(« Erreur : Impossible de trouver le module RTC. »);
    exit(0);
    }
    Serial.println(« RTC initialisé »);

    // Initialisation de l’écran LCD
    lcd.init();
    lcd.backlight(); // Active le rétroéclairage de l’écran
    Serial.println(« LCD initialisé »);

    // Setup RTC
    lastSyncMillis = millis();
    rtcAtLastSync = rtc.now();

    pinMode(GREEN_LED, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(IR_PENDULUM_PIN), IRPendulumISR, FALLING);

    // LCD init
    lcd.init();
    lcd.backlight();
    lcd.clear();
    lcd.setCursor(0, 0);

    if (!rtc.begin())
    {
    lcd.print(« Couldn’t find RTC »);
    exit(0);
    }

    if (rtc.lostPower())
    {
    lcd.print(« RTC lost power, »);
    lcd.setCursor(0, 1);
    lcd.print(« let’s set the time! »);
    // Réglage automatique de l’heure à partir de l’heure de compilation
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    delay(2000);
    }

    currentTimeMs = pendulumMs = getCurrentMillis();
    }

    /////////////////////////
    // Main loop
    /////////////////////////
    void loop()
    {

    currentTimeMs = getCurrentMillis();

    if (greenState && currentTimeMs – greenStartMs >= GREEN_DURATION)
    {
    greenState = false;
    digitalWrite(GREEN_LED, LOW);
    }

    if (pendulumDetected)
    {
    pendulumDetected = false;
    sumPendulumMs += pendulumMs – lastPendulumMs;
    pendulumOscillations++;
    }

    // Display timing
    if (currentTimeMs – lastDisplayUpdateMs >= DISPLAY_UPDATE_INTERVAL_MS)
    {
    lastDisplayUpdateMs = currentTimeMs;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(sumPendulumMs / pendulumOscillations);
    lcd.print(« ms »);
    }
    }

    #420004805984
    Torzio
    Membre

    Bonjour Cadrature,

    Pour info, Une oscillation = deux alternances. En horlogerie, depuis l’origine, on parle d’alternances/heure pour compter les balanciers. Chaque alternance correspond à un tic ou un tac. Avec l’arrivée du quartz, certains wokistes avant l’heure, ont tenté de désigner la période des balanciers en Hertz. Cette mauvaise habitude semble être passée.

    Merci pour votre code, je vais essayer de le comprendre. Je ne suis qu’un amateur dans ce domaine.
    Je l’ai vérifié avec l’IDE et il semble y avoir un problème à la ligne 82.

    Voici mon code :

    Code:
    /*
    **********************************************************************
    * Periodemetre.
    * Mesure la periode d’un balancier pendulaire
    * et de la convertit en alternances/heure
    * Jean-Claude Sulka avril 2017
    * ********************************************************************
    */
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>

    int CAPTEUR = 11; // Capteur optique connecte à la broche 11.
    int AH; // Alternances pas heure.
    int Compteur = 0; // Compte le nombre de mesure.
    int Moyenne = 0; // Moyenne des mesures effectuees.
    unsigned long Duree; // Duree d’une oscillation.
    unsigned long Heure = 3579981002; // nombre de millisecondes dans 1 heure
    unsigned long Total = 0; // Total des Alternances/Heure mesurees.

    LiquidCrystal_I2C lcd(0x27, 16, 2);
    //LiquidCrystal_I2C lcd(0x3F, 16, 2);

    void setup()
    {
    Serial.begin (115200);
    Serial.println (" sulka.fr");
    pinMode (CAPTEUR,INPUT);
    lcd.init();
    lcd.backlight();
    lcd.setCursor(2, 0);
    lcd.print("PERIODEMETRE");
    lcd.setCursor(4, 1);
    lcd.print("J-C SULKA ");

    delay(500);
    }

    void loop()
    {
    Duree = pulseIn(CAPTEUR,HIGH,50000000);
    Duree = Duree / 2;
    AH = Heure / Duree;

    Compteur = Compteur + 1;
    Total = Total + AH;
    Moyenne = Total / Compteur;

    // ——————–Affichage LCD—–

    lcd.clear();
    lcd.setCursor(3,0);
    lcd.print(AH);
    lcd.setCursor(9,0);
    lcd.print ( "A/H");

    lcd.setCursor(0,1);
    lcd.print("M = ");
    lcd.setCursor(5,1);
    lcd.print(Moyenne);
    lcd.setCursor(13,1);
    lcd.print (Compteur);

    Serial.println (AH,Compteur);
    delay (50);
    }

    @+
    JC

    #420004805985
    Cadrature
    Membre
    Torzio a écrit:
    Bonjour Cadrature,

    Merci pour votre code, je vais essayer de le comprendre. Je ne suis qu’un amateur dans ce domaine.
    Je l’ai vérifié avec l’IDE et il semble y avoir un problème à la ligne 82.

    Voici mon code :

    @+
    JC

    Merci pour votre code. En effet l’approche est différente.
    C’est quoi la ligne 82 et le message d’erreur ? Je vais regarder.

    Concernant votre code:
    Je ne connaissais pas pulseIn()
    Vous mesurez la durée pendant laquelle le signal n’est pas interrompu. C’est-à-dire entre la fin de la coupure laser
    et le début d’une autre coupure laser sur une oscillation complète. Avec 50s au pire des cas, je crois que vous couvrez
    tous les besoins en pendules de clocher 🙂

    Mais même en mettant le détecteur à l’extrême limite, cela va anéantir la mesure du temps passé lors de la coupure.
    Je m’explique: on peut décomposer l’oscillation en deux phases: la phase où le laser n’est pas coupé (un grand aller-retour) et la phase où il est
    coupé.
    Vous pourriez cumuler ce pulseIn() par un autre: pulseIn(CAPTEUR,LOW,1000000)
    Je me trompe ?

    Un autre problème vient: pulseIn va provoquer une attente qui empêche une autre mesure. Comme vous en avez qu’une seule, ce n’est pas un problème. Mais si vous voulez, comme je fais, mesurer le temps réel sur une horloge de référence, cela va poser problème.

    C’est pourquoi j’ai plutôt opter pour une interruption.
    Une interruption en informatique, c’est un truc qui arrête votre ordinateur quoi qu’il fasse et qui prend la main.
    Dans mon code, à chaque fois que le faisceau est coupé, c’est-à-dire que la broche « IR_PENDULUM_PIN » passe de 5v à 0v le code de « IRPendulumISR » est lancé.
    Ce qui s’écrit attachInterrupt(digitalPinToInterrupt(IR_PENDULUM_PIN), IRPendulumISR, FALLING);

    Et si vous regardez le code « IRPendulumISR »:

    Code:
    void IRPendulumISR()
    {
      if (currentTimeMs – pendulumMs > PENDULUM_DEBOUNCE_MS)
      {
      greenState = true;
      digitalWrite(GREEN_LED, HIGH);
      greenStartMs = currentTimeMs;
      lastPendulumMs = pendulumMs;
      pendulumMs = currentTimeMs;
      pendulumDetected = true;
      }
    }

    On attend un certain temps (PENDULUM_DEBOUNCE_MS) depuis la dernière fois qu’on a détecté cette interruption pour éviter un effet de clignotement à l’approche du balancier.

    Puis on fait ce qu’on a à faire:
    – Allumer la LED verte
    – dire que la dernière détection du pendule c’était pendulumMs
    – dire ensuite que pendulumMs c’est maintenant
    – dire qu’on a détecté le pendule.

    Cadrature

    #420004805986
    Cadrature
    Membre

    J’ai enfin compris comment un met du code ici.
    Mon code avec version coupure laser pour balancier et sonnerie
    Ce code est à améliorer et à adapter !

    Code:
    /*
     * This program is free software: you can redistribute it and/or modify it under the terms of the GNU
     * General Public License as published by the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
     * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
     * the GNU General Public License for more details.
     *
     * Cadrature 2024
     */

    #include <Arduino.h>

    // Constants
    #include <LiquidCrystal_I2C.h>
    #include <Wire.h>
    #include « RTClib.h »

    #define IR_PENDULUM_PIN 2 // IR sensor for the pendulum
    #define IR_CHIME_PIN 3 // IR sensor for the chime strikes
    #define GREEN_LED 8
    #define RED_LED 9
    #define BLUE_LED 10
    #define BUTTON_PIN 4

    // LCD setup
    LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20×4 display

    // RTC setup
    RTC_DS3231 rtc;

    // Time synchronization
    unsigned long lastSyncMillis = 0;
    DateTime rtcAtLastSync;
    unsigned long SYNC_INTERVAL_MS = 6000; // Synchronization interval with RTC

    unsigned long pendulumOscillations = 0;
    unsigned long pendulumMs = 0;
    unsigned long lastPendulumMs = 0;
    unsigned long PENDULUM_DEBOUNCE_MS = 700; // Debounce time for pendulum detection

    unsigned long CHIME_DELAY_MS = 1 * 3600000 – 10 * 60000; // 24 hours – 10 minutes
    unsigned long measuredChimeIntervalMs = 0;
    unsigned long REFERENCE_CHIME_INTERVAL_MS = 1 * 3600000; // Reference time for 24 hours

    bool greenState = false;
    unsigned long greenStartMs = 0;
    const unsigned long GREEN_DURATION = 20;

    bool redState = false;
    unsigned long redStartMs = 0;
    const unsigned long red_DURATION = 60;

    bool blueState = false;
    unsigned long blueStartMs = 0;
    const unsigned long blue_DURATION = 60;

    // Display update timing
    unsigned long lastDisplayUpdateMs = 0;
    const unsigned long DISPLAY_UPDATE_INTERVAL_MS = 1009;

    // IR detection
    bool chimeDetected = false;
    bool pendulumDetected = false;

    unsigned long currentTimeMs;
    unsigned long t0;

    unsigned long lastChimeMs;

    // Frequency and timing calculations
    unsigned long deltaFirstPendulum; // Time between the first chime and the first pendulum oscillation
    unsigned long deltaLastPendulum; // Time between the last pendulum oscillation and the second chime

    unsigned long sum = 0; // Accumulates the durations of pendulum oscillations
    double targetFrequency; // Desired frequency

    enum State
    {
      STATE_INIT,
      STATE_WAITING_FIRST_CHIME,
      STATE_MEASURING,
      STATE_CALIBRATING
    };

    enum State state;

    //////////////////////////////
    // Synchronize current millis with RTC
    //////////////////////////////
    unsigned long getCurrentMillis()
    {
      unsigned long currentMillis = millis();

      if (currentMillis – lastSyncMillis >= SYNC_INTERVAL_MS)
      {
      rtcAtLastSync = rtc.now();
      lastSyncMillis = currentMillis;
      }

      unsigned long elapsedMillis = currentMillis – lastSyncMillis;
      DateTime currentTime = rtcAtLastSync + TimeSpan(elapsedMillis / 1000);
      return (currentTime.unixtime() * 1000) + (elapsedMillis % 1000);
    }

    /////////////////////////
    // Interrupt for counting pendulum oscillations
    /////////////////////////
    void IRPendulumISR()
    {
      if (currentTimeMs – pendulumMs > PENDULUM_DEBOUNCE_MS)
      {
      greenState = true;
      digitalWrite(GREEN_LED, HIGH);
      greenStartMs = currentTimeMs;
      lastPendulumMs = pendulumMs;
      pendulumMs = currentTimeMs;
      pendulumDetected = true;
      }
    }

    /////////////////////////
    // Detection of chime strikes
    /////////////////////////
    void IRChimeISR()
    {
      redState = true;
      digitalWrite(RED_LED, HIGH); // Turn on the red LED
      redStartMs = currentTimeMs;
      if (state == STATE_WAITING_FIRST_CHIME || currentTimeMs – lastChimeMs >= CHIME_DELAY_MS)
      {
      blueState = true;
      digitalWrite(BLUE_LED, HIGH); // Turn on the blue LED
      blueStartMs = currentTimeMs;
      chimeDetected = true;
      }
    }

    /////////////////////////
    // Initialization routine
    /////////////////////////
    void setup()
    {
      // Start the serial port
      Serial.begin(57600);

      // Setup RTC
      lastSyncMillis = millis();
      rtcAtLastSync = rtc.now();

      // Start the I2C interface
      Wire.begin();

      pinMode(BUTTON_PIN, INPUT_PULLUP);
      pinMode(GREEN_LED, OUTPUT);
      pinMode(RED_LED, OUTPUT);
      pinMode(BLUE_LED, OUTPUT);
      attachInterrupt(digitalPinToInterrupt(IR_PENDULUM_PIN), IRPendulumISR, FALLING);
      attachInterrupt(digitalPinToInterrupt(IR_CHIME_PIN), IRChimeISR, FALLING);

      // LCD init
      lcd.init();
      lcd.backlight();
      lcd.clear();
      lcd.setCursor(0, 0);

      if (!rtc.begin())
      {
      lcd.print(« Couldn’t find RTC »);
      exit(0);
      }

      if (rtc.lostPower())
      {
      lcd.print(« RTC lost power, »);
      lcd.setCursor(0, 1);
      lcd.print(« let’s set the time! »);
      rtc.adjust(DateTime(1970, 1, 1, 0, 0, 0));
      delay(2000);
      }

      lcd.print(« Waiting start button »);
      state = STATE_INIT;
    }

    /////////////////////////
    // Main loop
    /////////////////////////
    void loop()
    {
      currentTimeMs = getCurrentMillis();

      if (greenState && currentTimeMs – greenStartMs >= GREEN_DURATION)
      {
      greenState = false;
      digitalWrite(GREEN_LED, LOW);
      }

      if (redState && currentTimeMs – redStartMs >= red_DURATION)
      {
      redState = false;
      digitalWrite(RED_LED, LOW);
      }

      if (blueState && currentTimeMs – blueStartMs >= blue_DURATION)
      {
      blueState = false;
      digitalWrite(BLUE_LED, LOW);
      }

      switch (state)
      {
      case STATE_INIT:
      if (pendulumDetected)
      {
      pendulumDetected = false;
      pendulumOscillations++;

      if (pendulumOscillations == 20)
      {
      lcd.setCursor(0, 0);
      lcd.print(« time for 20 osc.: »);
      lcd.setCursor(0, 1);
      lcd.print(currentTimeMs – t0);
      lcd.print( » « );
      t0 = currentTimeMs;
      pendulumOscillations = 0;
      }
      }

      if (digitalRead(BUTTON_PIN) == LOW)
      {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(« Waiting first chime »);
      state = STATE_WAITING_FIRST_CHIME;
      delay(1000);
      }

      break;

      case STATE_WAITING_FIRST_CHIME:
      if (digitalRead(BUTTON_PIN) == LOW)
      {
      state = STATE_CALIBRATING;
      }
      if (chimeDetected)
      {
      chimeDetected = false;
      lastChimeMs = currentTimeMs;
      pendulumOscillations = 0;
      state = STATE_MEASURING;
      }
      break;

      case STATE_MEASURING:

      if (pendulumDetected)
      {
      pendulumDetected = false;
      pendulumOscillations++;

      if (deltaFirstPendulum == 0)
      {
      deltaFirstPendulum = currentTimeMs – lastChimeMs;
      }
      }

      if (chimeDetected)
      {
      chimeDetected = false;

      deltaLastPendulum = currentTimeMs – lastPendulumMs;

      // Calculate the measured delay between two chimes
      measuredChimeIntervalMs = currentTimeMs – lastChimeMs;

      targetFrequency = (REFERENCE_CHIME_INTERVAL_MS – deltaFirstPendulum – deltaLastPendulum) / (pendulumOscillations – 1);

      // Update the time of the last chime
      lastChimeMs = currentTimeMs;
      pendulumOscillations = 0;
      state = STATE_CALIBRATING;
      }

      // Display timing
      if (currentTimeMs – lastDisplayUpdateMs >= DISPLAY_UPDATE_INTERVAL_MS)
      {
      lastDisplayUpdateMs = currentTimeMs;
      lcd.clear();
      lcd.setCursor(0, 0);
      if ((currentTimeMs – lastChimeMs >= CHIME_DELAY_MS))
      {
      lcd.print(« Waiting second chime »);
      }
      else
      {
      lcd.setCursor(0, 0);
      lcd.print((currentTimeMs – lastChimeMs < CHIME_DELAY_MS) ? (CHIME_DELAY_MS – (currentTimeMs – lastChimeMs)) / 1000 : 0);
      lcd.print(« s »);
      lcd.setCursor(0, 1);
      lcd.print(« Before second chime »);
      }
      }
      break;

      case STATE_CALIBRATING:

      if (digitalRead(BUTTON_PIN) == LOW)
      {
      sum = 0;
      pendulumOscillations = 0;
      }

      if (pendulumDetected)
      {
      pendulumDetected = false;
      sum += pendulumMs – lastPendulumMs;
      pendulumOscillations++;
      }

      // Display timing
      if (currentTimeMs – lastDisplayUpdateMs >= DISPLAY_UPDATE_INTERVAL_MS)
      {

      lastDisplayUpdateMs = currentTimeMs;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(« Osc: »);
      lcd.print(pendulumMs – lastPendulumMs);
      lcd.setCursor(0, 1);
      lcd.print(« avrg osc: »);
      if (pendulumOscillations > 0)
      {
      lcd.print(sum / pendulumOscillations);
      }
      else
      {
      lcd.print(« N/A »); // Évite une division par zéro
      }
      lcd.setCursor(0, 2);
      lcd.print(« delta chime: »);
      lcd.print(measuredChimeIntervalMs);
      lcd.setCursor(0, 3);
      lcd.print(« target frq: « );
      lcd.print(targetFrequency);
      }
      break;
      }
    }

    Code pour montage montré plus haut:

    Code:
    /*
     * This program is free software: you can redistribute it and/or modify it under the terms of the GNU
     * General Public License as published by the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
     * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
     * the GNU General Public License for more details.
     *
     * Cadrature 2024
     */

    #include <Arduino.h>

    // Constants
    #include <LiquidCrystal_I2C.h>
    #include <Wire.h>
    #include « RTClib.h »

    #define IR_PENDULUM_PIN 2 // IR sensor for the pendulum
    #define GREEN_LED 8

    // LCD setup
    LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20×4 display

    // RTC setup
    RTC_DS3231 rtc;

    // Time synchronization
    unsigned long lastSyncMillis = 0;
    DateTime rtcAtLastSync;
    unsigned long SYNC_INTERVAL_MS = 6000; // Synchronization interval with RTC

    unsigned long pendulumOscillations = 0;
    unsigned long pendulumMs = 0;
    unsigned long lastPendulumMs = 0;
    unsigned long PENDULUM_DEBOUNCE_MS = 700; // Debounce time for pendulum detection
    unsigned long sumPendulumMs = 0; // Accumulates the durations of pendulum oscillations
    bool pendulumDetected = false;

    bool greenState = false;
    unsigned long greenStartMs = 0;
    const unsigned long GREEN_DURATION = 20;

    // Display update timing
    unsigned long lastDisplayUpdateMs = 0;
    const unsigned long DISPLAY_UPDATE_INTERVAL_MS = 1009;

    unsigned long currentTimeMs;

    //////////////////////////////
    // Synchronize current millis with RTC
    //////////////////////////////
    unsigned long getCurrentMillis()
    {
      unsigned long currentMillis = millis();

      if (currentMillis – lastSyncMillis >= SYNC_INTERVAL_MS)
      {
      rtcAtLastSync = rtc.now();
      lastSyncMillis = currentMillis;
      }

      unsigned long elapsedMillis = currentMillis – lastSyncMillis;
      DateTime currentTime = rtcAtLastSync + TimeSpan(elapsedMillis / 1000);
      return (currentTime.unixtime() * 1000) + (elapsedMillis % 1000);
    }

    /////////////////////////
    // Interrupt for counting pendulum oscillations
    /////////////////////////
    void IRPendulumISR()
    {
      if (currentTimeMs – pendulumMs > PENDULUM_DEBOUNCE_MS)
      {
      greenState = true;
      digitalWrite(GREEN_LED, HIGH);
      greenStartMs = currentTimeMs;
      lastPendulumMs = pendulumMs;
      pendulumMs = currentTimeMs;
      pendulumDetected = true;
      }
    }

    /////////////////////////
    // Initialization routine
    /////////////////////////
    void setup()
    {
      // Démarre la communication série pour le débogage
      Serial.begin(9600);
      Serial.println(« Initialisation en cours… »);

      // Démarre le bus I2C
      Wire.begin();
      delay(1000);
      Serial.println(« I2C démarré »);

      // Démarre le module RTC
      if (!rtc.begin())
      {
      Serial.println(« Erreur : Impossible de trouver le module RTC. »);
      exit(0);
      }
      Serial.println(« RTC initialisé »);

      // Initialisation de l’écran LCD
      lcd.init();
      lcd.backlight(); // Active le rétroéclairage de l’écran
      Serial.println(« LCD initialisé »);

      // Setup RTC
      lastSyncMillis = millis();
      rtcAtLastSync = rtc.now();

      pinMode(GREEN_LED, OUTPUT);
      attachInterrupt(digitalPinToInterrupt(IR_PENDULUM_PIN), IRPendulumISR, FALLING);

      // LCD init
      lcd.init();
      lcd.backlight();
      lcd.clear();
      lcd.setCursor(0, 0);

      if (!rtc.begin())
      {
      lcd.print(« Couldn’t find RTC »);
      exit(0);
      }

      if (rtc.lostPower())
      {
      lcd.print(« RTC lost power, »);
      lcd.setCursor(0, 1);
      lcd.print(« let’s set the time! »);
      // Réglage automatique de l’heure à partir de l’heure de compilation
      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
      delay(2000);
      }

      currentTimeMs = pendulumMs = getCurrentMillis();
    }

    /////////////////////////
    // Main loop
    /////////////////////////
    void loop()
    {

      currentTimeMs = getCurrentMillis();

      if (greenState && currentTimeMs – greenStartMs >= GREEN_DURATION)
      {
      greenState = false;
      digitalWrite(GREEN_LED, LOW);
      }

      if (pendulumDetected)
      {
      pendulumDetected = false;
      sumPendulumMs += pendulumMs – lastPendulumMs;
      pendulumOscillations++;
      }

      // Display timing
      if (currentTimeMs – lastDisplayUpdateMs >= DISPLAY_UPDATE_INTERVAL_MS)
      {
      lastDisplayUpdateMs = currentTimeMs;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(sumPendulumMs / pendulumOscillations);
      lcd.print(« ms »);
      }
    }

    Cadrature

    #420004805987
    Torzio
    Membre

    {
    Cadrature a dit :
    Mais même en mettant le détecteur à l’extrême limite, cela va anéantir la mesure du temps passé lors de la coupure.
    Je m’explique: on peut décomposer l’oscillation en deux phases: la phase où le laser n’est pas coupé (un grand aller-retour) et la phase où il est
    coupé.
    Vous pourriez cumuler ce pulseIn() par un autre: pulseIn(CAPTEUR,LOW,1000000)
    Je me trompe ?
    Un autre problème vient: pulseIn va provoquer une attente qui empêche une autre mesure. Comme vous en avez qu’une seule, ce n’est pas un problème. Mais si vous voulez, comme je fais, mesurer le temps réel sur une horloge de référence, cela va poser problème.
    }

    Je ne vois pas où est le problème. Mon code fonctionne parfaitement bien, mais, comme je l’ai déjà dit, j’ai des problème avec des balanciers aux lentilles sculptées, la réflexion du rayon ne se fait pas toujours au bon moment, pour peu que le balancier n’ait pas un déplacement parfaitement régulier, d’avant en arrière. Comme je l’ai dit, cela provient du capteur qui fonctionne par réflexion. Il faut que j’équipe mon montage d’un capteur avec un faisceau laser qui sera coupé par la lentille du balancier.
    Le fait d’utiliser Pulsein() permet à l’Arduino de se consacrer uniquement à la mesure d’une oscillation., qui ne sera pas polluée pas du « temps machine ». Une seule oscillation sur deux sera mesurée. Les calculs et l’affichage sont effectuées pendant l’oscillation « muette », pour ne pas polluer la mesure.
    Il faut bien sûr positionner le capteur afin de prendre la mesure sur la partie la plus large du balancier. et laisser de la marge, pour ne pas avoir un faux déclenchement.

    @+
    JC

    #420004805988
    Cadrature
    Membre
    Torzio a écrit:
    {
    Cadrature a dit :
    Mais même en mettant le détecteur à l’extrême limite, cela va anéantir la mesure du temps passé lors de la coupure.
    Je m’explique: on peut décomposer l’oscillation en deux phases: la phase où le laser n’est pas coupé (un grand aller-retour) et la phase où il est
    coupé.
    Vous pourriez cumuler ce pulseIn() par un autre: pulseIn(CAPTEUR,LOW,1000000)
    Je me trompe ?
    Un autre problème vient: pulseIn va provoquer une attente qui empêche une autre mesure. Comme vous en avez qu’une seule, ce n’est pas un problème. Mais si vous voulez, comme je fais, mesurer le temps réel sur une horloge de référence, cela va poser problème.
    }

    Je ne vois pas où est le problème. Mon code fonctionne parfaitement bien, mais, comme je l’ai déjà dit, j’ai des problème avec des balanciers aux lentilles sculptées, la réflexion du rayon ne se fait pas toujours au bon moment, pour peu que le balancier n’ait pas un déplacement parfaitement régulier, d’avant en arrière. Comme je l’ai dit, cela provient du capteur qui fonctionne par réflexion. Il faut que j’équipe mon montage d’un capteur avec un faisceau laser qui sera coupé par la lentille du balancier.
    Le fait d’utiliser Pulsein() permet à l’Arduino de se consacrer uniquement à la mesure d’une oscillation., qui ne sera pas polluée pas du « temps machine ». Une seule oscillation sur deux sera mesurée. Les calculs et l’affichage sont effectuées pendant l’oscillation « muette », pour ne pas polluer la mesure.
    Il faut bien sûr positionner le capteur afin de prendre la mesure sur la partie la plus large du balancier. et laisser de la marge, pour ne pas avoir un faux déclenchement.

    @+
    JC

    Le code fonctionne, mais il mesure seulement un temps où la cellule reçoit la lumière, pas l’autre temps qui ne me semble pas négligeable. Mais
    je n’ai peut-être pas compris le montage. Ou alors je n’ai pas compris pulseIn().

    pulseIn() calcule bien les temps, mais empêchera d’autres évènements.
    S’il est tout seul ça va, mais si vous mesurez d’autres choses en parallèle, les problèmes viendront.
    C’est l’avantage des interruptions qui captent un signal haut ou un signal bas avec un anti-rebond.
    Cela fonctionnerait bien avec votre montage à réflexion, même avec des lentilles
    compliquées.

    Pour le montage avec un laser. Il me semble que le plus simple est la coupure d’un faisceau en effet. La réflexion (à choisir pour une montre par exemple) est plus délicate.
    Un système à collimation et miroir irait certainement, mais c’est bien compliqué à mettre en oeuvre.

    Pour un montage où le capteur est distant de l’émetteur, on peut monter un laser à point rouge par exemple avec une photorésistance.
    Les composants existent tout faits à prix modiques.
    Un faisceau infra-rouge ou un détecteur de distance optique, à laser, à induction n’est pas recommandé s’il est à impulsions.
    Vous l’aviez justement souligné.

    Cadrature

    #420004805989
    Torzio
    Membre

    @Cadrature,

    Vous avez mis le doute dans mon esprit !
    Ce qui est étonnant, c’est que les valeurs lues me semblaient parfaitement cohérentes.

    J’avais même ajusté le nombre de micro secondes en 1 heure pour corriger l’erreur du quartz de l’Arduino utilisé :
    unsigned long Heure = 3579135248; // nombre de microsecondes dans 1 heure

    J’ai fait ce montage il y a bien longtemps, je vais m’y replonger dès que possible.

    J’ai trouvé une autre façon de faire, qu’en pensez-vous ?

    Code:
    // mesure temps courts

    const int buttonPin = 2;

    unsigned long startTime;
    unsigned long endTime;

    void setup() {
    pinMode(buttonPin, INPUT_PULLUP);
    Serial.begin(9600);
    }

    void loop() {
    if (digitalRead(buttonPin) == LOW) {
    startTime = millis();
    while (digitalRead(buttonPin) == LOW) {
    // Attendre que le bouton soit relâché
    }
    endTime = millis();

    unsigned long duration = endTime – startTime;
    Serial.print("Durée du bouton enfoncé : ");
    Serial.print(duration);
    Serial.println(" ms");
    }
    }

    Mon objectif est de mesurer une oscillation (deux alternances) et de ne faire que ça, pour ne pas fausser la mesure par du temps machine. Pendant l’alternance suivante, l’Arduino s’occupe des calculs et de l’affichage.

    @+
    JC

    #420004805990
    Cadrature
    Membre

    Bonsoir

    Dans l’esprit ce code est bon. Il pose un petit problème avec un bouton et même avec une coupure lumière.
    Au moment précis de l’événement, il peut se passer un phénomène imperceptible: celui du « rebond ».
    Quand le faisceau passe à peine, ou que le bouton touche à peine, il s’allume et s’éteint plusieurs fois en quelques microsecondes, voire millisecondes.
    Donc la boucle while risque d’être interrompue au tout début.

    Pour résoudre ce problème on utilise un délai après la détection, mais cela fausse le résultat. Il faudra alors corriger ce délai.

    L’astuce est d’utiliser un temps de « rebond », par exemple de 20ms et plus de délai.

    Code:
    unsigned long startTime;
    unsigned long endTime;
    int buttonPin = 2;

    const unsigned long debounceDelay = 20; // temps de rebond de 20ms
    unsigned long lastDebounceTime = 0;

    int lastState = LOW;
    int state;

    void setup()
    {
      pinMode(buttonPin, INPUT_PULLUP);
      Serial.begin(9600);
    }

    void loop()
    {

      state = digitalRead(buttonPin);

      if (state != lastState)
      {

      unsigned long now = millis();

      if (now – lastDebounceTime > debounceDelay)
      {
      lastDebounceTime = now;
      if (state == LOW)
      {
      startTime = now;
      }
      if (state == HIGH)
      {
      endTime = now;
      unsigned long duration = endTime – startTime;
      Serial.print("Durée du bouton enfoncé : ");
      Serial.print(duration);
      Serial.println(" ms");
      }
      }
      }

      lastState = state;
    }

    Cadrature

    #420004805991
    Torzio
    Membre

    Merci Cadrature pour ces précieuses infos que je vais essayer de mettre en pratique/

    En attendant, une petite vidéo de mon périodemètre, que j’ai remis en action. Les résultats sont cohérents, sauf quelques fois des résultats bizarres. Peut-être un problème de rebond ?
    Est ce qu’un condensateur de 100 nf pourrait résoudre le problème ?

    https://sulka.fr/videos/periodemetre.mp4

    @+
    JC

    #420004805992
    Cadrature
    Membre

    Cela dépasse mes compétences, je n’y connais pas grand chose en électronique. Mon niveau bac scientifique bien ancien…
    Mais oui une capacité branchée en parallèle de la photorésistance va permettre de provoquer un délai de latence et filtrer les pics.
    Il faudrait essayer.
    Le calcul est donné par t = R . C où R est la résistance de la photocell (10k en général)

    100nF . 10k = une milliseconde

    Je ne sais pas pour le reste et si le montage est fiable.

    Cadrature

15 sujets de 61 à 75 (sur un total de 81)
  • Vous devez être connecté pour répondre à ce sujet.