Code d'un projet horloge

Merci beaucoup , ce n'est donc pas possible de changer la valeur toutes les minutes dans L'EEPROM car il n'est pas fait pour s'actualiser a cette fréquence ? Je vais lire ça

L'ATMega4809 de votre Nano Every a 256 octets de mémoire EEPROM. On peut écrire en moyenne jusqu'à 100,000 fois dans un de ces octets, ensuite vous n'êtes plus sûr que ça va fonctionner car vous aurez "grillé" cette case mémoire et si vous écrivez toutes les minutes alors 100,000 minutes de fonctionnement c'est un peu moins de 70 jours....

  • soit il vous faut metre un composant de stockage non volatile qui n'a pas une si "petite" limitation ➜ la FRAM pour Ferroelectric Ram permet de faire cela, vous trouverez des modules facile d'emploi à vendre et ils offrent 10,000,000,000,000 écritures... (plus de 19 millions d'années donc à une écriture par minute)

  • soit vous rajoutez le circuit de sauvegarde qui fait que vous n'écrivez qu'en cas de panne de courant et vous modifiez le code en conséquence et vous pourrez supporter 100,000 pannes de courant.


si vous voulez en savoir plus : L'écriture en EEPROM génère une usure en raison de la structure physique des cellules de mémoire, qui utilisent des transistors à grille flottante pour stocker les données. Chaque cycle d'écriture ou d'effacement provoque une dégradation progressive de la couche isolante de la grille flottante due aux charges électriques qui s'y accumulent. Cette dégradation finit par rendre la cellule incapable de conserver correctement les données, limitant ainsi la durée de vie de l'EEPROM à un nombre défini de cycles d'écriture/effacement, généralement autour de 100 000 à 1 000 000 cycles selon le fabricant.

Notez que la mémoire flash (qui sert à stocker votre code) est limitée à environ 10 000 cycles d'écriture/effacement en raison de la technologie utilisée pour stocker les données. Comme l'EEPROM, la Flash utilise des transistors à grille flottante, mais avec une architecture optimisée pour la densité et le coût plutôt que pour la durabilité. Donc elle vit moins longtemps. Les cartes (ESP32, ...) qui émulent l'EEPROM en effectuant un stockage en flash ont donc moins de durée de vie si on écrit toujours au même endoit. C'est pour cela que sur ESP32 par exemple il vaut mieux utiliser la bibliothèque Prefernces de Espressif qui fait de la répartition de l'usure (wear leveling).

J'y vois plus clair merci a vous. L'idée du condensateur est très intéressante mais me parait bien compliqué au vue de mon niveau , je pense que je vais donc me servir uniquement du DS3132 pour augmenter la précision , de toute façon l'horloge est très simple a remettre a l'heure.

Vu le temps dont vous disposez, c'est une bonne idée. vous n'aurez pas besoin de lire l'heure exacte de l'horloge ni de la mettre à jour, ce qui vous intéressera c'est simplement le temps qui passe. Je vous conseille la bibliothèque RTClib et d'utiliser les classes DateTime et TimeSpan qui vous permettent de représenter le temps et de faire des calculs sur les temps.

  • Au boot vous lisez l'heure dans un DateTime dans une variable tempsPrecedent

  • la loop lis l'heure dans un DateTime maintenant et fait la différence stockée dans un TimeSpan deltaT = maintenant - tempsPrecedent;

  • vous testez comparez ensuite le nombre de secondes dans deltaTavec 60 pour voir si une minute est passée et si oui, vous avancez d'un cran l'aiguille et mettez à jour tempsPrecedent avec maintenant .

voici un exemple (sans l'horloge) qui montre comment effectuer les calculs

#include <RTClib.h>

const int32_t uneMinute = 60; // secondes
DateTime tempsPrecedent(2025, 1, 26, 12, 0, 0); // année, mois, jour, heure, minute, seconde
DateTime maintenant(2025, 1, 26, 12, 0, 40);

void setup() {
  Serial.begin(115200);
  TimeSpan deltaT = maintenant - tempsPrecedent;

  Serial.print("∆t = ");
  Serial.print(deltaT.totalseconds());
  Serial.println(" s");

  if (deltaT.totalseconds() >= uneMinute) {
    Serial.println("il y a plus d'une minute, avancer les aiguilles d'un cran");
  } else {
    Serial.println("il y a moins d'une minute");
  }
}

void loop() {}

bien sûr vous n'utiliserez pas des dates "en dur", vous pouvez lire l'heure actuelle en faisant simplement

  DateTime maintenant = rtc.now();

autre option, l'horloge peut être configurée pour avoir une broche qui bouge à 1Hz, ça vous permettrait de compter les secondes et au bout de 60s faire avancer le moteur.

Merci beaucoup pour votre aide précieuse je vais essayer de refaire un programme a partir de ça.

J'ai refait un programme qui me semble fonctionnel mais il m'indique que la commande "rtc" de la ligne

DateTime maintenant = rtc.now();

est non défini alors que j'ai inclus la bibliothéque juste en haut

oui il faut définir l'instance

en début de code

#include <RTClib.h>
RTC_DS3231 rtc;

dans le setup() il faut l'initialiser

void setup () {
  Serial.begin(115200);

  if (! rtc.begin()) {
    Serial.println("RTC introuvable");
    Serial.flush();
    while (1) yield();
  }

  if (rtc.lostPower()) {
    Serial.println("la RTC a perdu sa mémoire, on règle avec la date et heure de compilation!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

...

jetez un oeil aux exemples de la bibliothèque

➜ celui de la DS3231

Oui, c'est jouable, @hbachetti en a déjà parlé ou mis en oeuvre, je crois.
Mais pour paraphraser @yalow1, aura t-il le temps de mettre en place un tel dispositif ?

Enfin tout ça pour dire que ce n'est pas si simple que le pensait @yalow1

Trouver donc l'erreur dans cette suite logique qui fait que l'holroge avance d'une minute toutes les secondes :

void setup() {
  pinMode(port[0], OUTPUT);//pas important
  pinMode(port[1], OUTPUT);//pas important
  pinMode(port[2], OUTPUT);//pas important
  pinMode(port[3], OUTPUT);//pas important
  rotate(-10); //pas important
  rotate(10); //pas important
  DateTime tempsPrecedent = rtc.now();
 delay (60000);//je ferai un truc plus propre après
}

void loop() {
  DateTime maintenant = rtc.now();
  TimeSpan deltaT = maintenant - tempsPrecedent;
  
 
 if (deltaT.totalseconds() >= uneMinute) {
    rotate(256);
    DateTime tempsPrecedent = rtc.now();

    delay (1000);
  }
}

Tu ne donnes pas le code en entier, donc à priori uneMinute = 1 et pas 60 ?

non uneMinute = 60s

const int32_t uneMinute = 60;

tempsPrecedent est une variable locale au bloc de code du if et elle est inconnue à l'extérieur.

Et il semblerait que tu en ais défini une autre dans setup().

Le plus simple serait de la définir en global.

Je peine a saisir la richesse de vos propos. Pourriez vous reformuler ?

lisez scope

passez les en variables globales

DateTime tempsPrecedent, maintenant; 

void setup() {
  pinMode(port[0], OUTPUT);//pas important
  pinMode(port[1], OUTPUT);//pas important
  pinMode(port[2], OUTPUT);//pas important
  pinMode(port[3], OUTPUT);//pas important
  rotate(-10); //pas important
  rotate(10); //pas important
  tempsPrecedent = rtc.now();
 delay (60000);//je ferai un truc plus propre après
}

void loop() {
  maintenant = rtc.now();
  TimeSpan deltaT = maintenant - tempsPrecedent;
  
 
 if (deltaT.totalseconds() >= uneMinute) {
    rotate(256);
    tempsPrecedent = rtc.now();
    delay (1000);
  }
}

Si tu définis une variable dans un bloc de code(entre {}), celle-ci est connu que uniquement de ce bloc.
donc la fonction setup, connais une variable tempsPrecedent et le bloc dans la fonction loop connais une variable tempsPrecedent, mais il ne s'agit pas de la même variable.
De plus lorsque tu utilises la variable tempsPrecedent dans l'instruction

  TimeSpan deltaT = maintenant - tempsPrecedent;

c'est encore une autre variable.

D'alleurs je suppose que tu as aussi une variable global tempsPrecedent, car sinon ton code ne doit pas compiler.

Ha je vois merci beaucoup

C'est bien le cas

Merci infiniment pour tout ce que vous faites c'est plus clair , je vais voir tout ça demain , il ne faut pas que je me couche trop tard

Bonjour , je continue a essayer d'avancer pour trouver ou ça bloque et je me pose une question , j'utilise ce code :

#include <RTClib.h>
RTC_DS3231 rtc;
#include <ds3231.h>
// Please tune the following value if the clock gains or loses.
// Theoretically, standard of this value is 60000.

// Motor and clock parameters
// 2048 * 90 / 12 = 15360
#define STEPS_PER_ROTATION 15360 // steps for a full turn of minute rotor
DateTime tempsPrecedent = rtc.now(); // année, mois, jour, heure, minute, seconde
DateTime maintenant = rtc.now();

// wait for a single step of stepper
int delaytime = 4;
// ports used to control the stepper motor
// if your motor rotate to the opposite direction, 
// change the order as {2, 3, 4, 5};
int port[4] = {2, 3, 4, 6};
int seq[4][4] = {
  {  LOW,  LOW, HIGH,  LOW},
  {  LOW,  LOW,  LOW, HIGH},
  { HIGH,  LOW,  LOW,  LOW},
  {  LOW, HIGH,  LOW,  LOW}
};
const int32_t uneMinute = 60; // seconde
void rotate(int step) {
  static int phase = 0;
  int i, j;
  int delta = (step > 0) ? 1 : 3;
  int dt = 20;

  step = (step > 0) ? step : -step;
  for(j = 0; j < step; j++) {
    phase = (phase + delta) % 4;
    for(i = 0; i < 4; i++) {
      digitalWrite(port[i], seq[phase][i]);
    }
    delay(dt);
    if(dt > delaytime) dt--;
  }
  // power cut
  for(i = 0; i < 4; i++) {
    digitalWrite(port[i], LOW);
  }
}

void setup() {
  pinMode(port[0], OUTPUT);//pas important
  pinMode(port[1], OUTPUT);//pas important
  pinMode(port[2], OUTPUT);//pas important
  pinMode(port[3], OUTPUT);//pas important
  
}


void loop() {
   maintenant = rtc.now();
  TimeSpan deltaT = maintenant - tempsPrecedent;
  Serial.begin ( 9600 );
  Serial.println(deltaT.totalseconds());
  Serial.println("Hello world");
 if (deltaT.totalseconds() >= uneMinute) {
    rotate(256);
    tempsPrecedent = rtc.now();
  }
  delay(1000);
}

Comme vous pouvez le voir j'ai ajouter l'instruction

  Serial.println("Hello world");

pour confirmer mes doutes , cette ligne est dans le bloc void loop donc elle serait censé se répéter en boucle pourtant même en attendant l'inscription ne s'est afficher qu'une fois sur le moniteur série. Et l'horloge ne bouge pas quand je téléverse le code.
Toute réponse est bonne a prendre.

c'est un peu le foutoir :slight_smile:

essayez ce code dans le simulateur et regardez les différences par rapport à votre code

le simulateur ne gère pas la DS3231 donc j'ai mis un DS307 à la place, il y a un #define au début pour choisir mettez 1 au lieu de 0 pour la DS3231

J'ai mis 3 secondes au lieu de 60 car la flemme d'attendre une minute

const uint32_t uneMinute = 3; // devrait être 60 mais pour le test on met 3 secondes

➜ faudra remettre 60

le moteur de wokwi n'est pas le votre, donc il va tourner mais sans rapport avec ce que votre code fait avec vos moteur.
Le moteur fera n'importe

click to see the code
#include <RTClib.h>

#define USING_DS3231 0 // mettre à 1 pour la DS3231

#if USING_DS3231
RTC_DS3231 rtc;
#else
RTC_DS1307 rtc;
#endif

// Motor and clock parameters
// 2048 * 90 / 12 = 15360
#define STEPS_PER_ROTATION 15360 // steps for a full turn of minute rotor

// ports used to control the stepper motor
// if your motor rotate to the opposite direction,
// change the order as {2, 3, 4, 5};
const byte port[] = {2, 3, 4, 6};
const uint32_t delaytime = 4; // wait for a single step of stepper
const int seq[4][4] = {
  { LOW,  LOW, HIGH,  LOW},
  { LOW,  LOW,  LOW, HIGH},
  {HIGH,  LOW,  LOW,  LOW},
  { LOW, HIGH,  LOW,  LOW}
};

const uint32_t uneMinute = 3; // devrait être 60 mais pour le test on met 3 secondes

DateTime tempsPrecedent;

void rotate(int step) {
  static int phase = 0;
  int i, j;
  int delta = (step > 0) ? 1 : 3;
  int dt = 20;

  step = (step > 0) ? step : -step;
  for (j = 0; j < step; j++) {
    phase = (phase + delta) % 4;
    for (i = 0; i < 4; i++) {
      digitalWrite(port[i], seq[phase][i]);
    }
    delay(dt);
    if (dt > delaytime) dt--;
  }
  // power cut
  for (i = 0; i < 4; i++)digitalWrite(port[i], LOW);
}

void setup() {
  Serial.begin (115200);

  for (byte i = 0; i < 4; i++) pinMode(port[i], OUTPUT);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (true) yield();
  }

#if USING_DS3231
  if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // DS3231
#else
  if (! rtc.isrunning())rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // DS1307
#endif
  tempsPrecedent = rtc.now();
}


void loop() {
  DateTime maintenant = rtc.now();
  TimeSpan deltaT = maintenant - tempsPrecedent;
  if (deltaT.totalseconds() >= uneMinute) {
    rotate(256);
    tempsPrecedent = rtc.now();
  }
}

J'ai essayer ce programme et il me semble un peu mieux , ce qui a bizarre c'est que la valeur de uneMinute semble influer sur le temps mais pas de manière précise : si je rentre 20s le moteur tournera 13s après que la simulation soit lancé