Aide DS1307 système abonnement

Bonjour !

Après recherche sur internet et sur le forum je n'ai pas trouvé ma réponse même si je pense que des gens ont déjà fait ce que je cherche à faire.

Du coup, j'utilise simplement un Wemos D1 mini avec une horloge RTC DS1307 et une led WS2812 et le principe de ce que je veux faire est très simple:
J'ai une variable qui correspondra à une durée, et cette durée définira une date de fin entre l'heure à laquelle elle aura été défini et donc logiquement l'heure actuelle + la durée.

Par ex pour une durée de 10 jours, si je la programme aujourd'hui, la date de fin sera dans 10 jours à partir de maintenant (j'espère être clair désolé si ça par un peu en cacahuète) .
Et donc en au moment où la date de fin aura été atteinte j'allume ma led d'une certaine couleur, etc...

Mais le problème que j'ai, est que je n'arrive pas à trouver un moyen de définir une date fixe tout en pouvant plus tard la modifier selon une durée précise.
Car le plus logique pour moi était de faire comme ça :
(J'utilise la librairie RTC.lib pou gérer mon horloge RTC)

 Date_Fin = (now + TimeSpan(Durée,0,0,0)); 

Ce qui devrait me donner une date de fin mais étant donnée que "now" correspond à l'heure, il n'est pas fixe et continue d'avancer donc ma Date de fin n'est elle aussi pas fixe alors que le but serait qu'elle le soit pour par ex comparait la date actuelle (now) à ma date de fin et donc savoir si le temps de la durée s'est écoulé.

Donc ma question est, comment la rendre fixe ? Ou y'a t'il un autre moyen plus simple de faire ce que je veux faire ?

Merci d'avance.

startTime = now;
Date_Fin = (startTime + TimeSpan(Durée,0,0,0));

now continue d'évoluer mais startTime reste fixe.

J'ai l'impression qu'il y a une confusion : now() évolue mais Date_Fin doit être évalué une fois (dans le setup() par exemple ou suite à un événement particulier (à préciser)

Ton instruction est valable mais ne doit pas être mise dans le loop() en tout cas pas sans en empêcher l'évaluation à chaque tour...

Si tu as déjà un code plus complet, peux-tu le poster ici ?

Merci pour vos réponses.

D'abord je n'avais pas trouvé pour le start time donc je vais essayer et si c'est comme tu le décris ça devrait régler le problème.

J'ai pensé à définir ma date de fin dans le setup mais le truc c'est que je dois pouvoir la modifier et dans ce cas si je me trompe pas (si elle est dans le setup) je ne pourrai pas ?

J'ai bien un code plus complet mais je ne pourrai l'envoyer que ce soir.

Mais en gros ce code créait simplement une date de fin avec la ligne que j'ai envoyé puis ensuite je comparais les jour de la date de fin et les jour de la date actuelle pour voir si ils étaient égaux ce qui voudrait dire que la date de fin aurait été atteinte, et du coup si oui j'allumais ma led etc...

Cela vous paraît correct de faire comme ça ?

C'est ça mais pour pouvoir t'aider plus il faudrait connaître la manière dont tu veux procéder pour la modifier : c'est dans cette partie là qu'il faut mettre à jour Date_Fin et dans le loop() tu compares now() avec Date_Fin

Alors je pourrai envoyer mon code d'ici 19h. Mais en gros la date de fin correspondrait simplement à une durée en jour reçu.

Donc au début je n'ai pas de durée puis dès que je la reçois exemple je reçois 10 jour, je calcule la date de fin, une fois la date de fin atteinte je remettrai cette durée à 0 en attendant d'en recevoir une nouvelle etc, ça serait un peu comme un abonnement.

Tu "d'abonnés" pendant 10 jours puis quand c'est fini tu dois refaire et ainsi de suite, vous comprenez ?

Voilà mon code actuel (qui ne fonctionne donc pas):

#include <FastLED.h>
#include "RTClib.h"

RTC_DS1307 rtc;

#define NUM_LEDS 1//Nombre de leds
#define DATA_PIN 15////Pin led

int Duree_abonnement = 90;//Durée de l'abonnement en jours
bool Etat_Abonnement = true;//EEtat de l'abonnement 

DateTime Date_Fin;

CRGB leds[NUM_LEDS];

void setup() {
  
  
  Serial.begin(9600);  
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }

}

void loop() {

  DateTime now = rtc.now();
  
  Date_Fin = (now + TimeSpan(Duree_abonnement,0,0,0));
 
  if(Etat_Abonnement == true){
    Serial.println("Abonnement actif");
  }

  if(Duree_abonnement != 0 && Etat_Abonnement == true){
    
    leds[0] = CRGB::Green;
    FastLED.show();
  }
  
  if(now.day() == Date_Fin.day()){
    leds[0] = CRGB::Orange;
    FastLED.show();
  }
}

Donc d'abord je définis ma date de fin selon la date actuel.
J'allume ma led en vert une fois que l'abonnement est actif.
Puis je suis censé l'allumer en vert une fois que la date de fin est atteinte en comparant les jours de la date actuel aux jours de la date de fin.(Ce qui ne marche pas car étant donné que ma date de fin avance en même temps que ma date actuelle, cela ne peut pas marcher).

Non, pas d'abord mais à chaque boucle de la loop() : il est bien là le problème.

C'est pour ça que je te demandais comment tu « renouvelles ton abonnement » ? C'est là que tu vas devoir définir la date de fin, pas dans la loop()

Oui effectivement, je te dis d'abord mais c'est bien le problème que j'ai.

C'est ce que j'expliquais dans mon message précédent.

Car j'ai pas précisé mais je dis "envoyée" car en gros la durée sera envoyé depuis une application mobile par bluetooth, je n'ai pas encore fait cette partie là car je pense que je n'en ai pas vraiment besoin et je me sert juste d'une variable avec une valeur définit soit 90 dans ce code pour la durée de l'abonnement car je veux d'abord avoir ce principe fonctionnel pour intégrer le bluetooth.

Et du coup une fois que ma durée aura expiré, donc lorsque la date de fin sera dépassée, je ferai passé l'état de mon abonnement à false puis je repasserai cette durée à 0 jusqu'à ce qu'une autre durée soit envoyée pour ensuite recommencer, etc...

Tu vois ?

Si : c'est là que tu devrais définir la date de fin, y compris pour la première exécution.

Bonjour, finalement j'ai trouvé un moyen plus simple et qui me parait correct pour rendre la date fixe en ne la définissant qu'une fois.(Je précise que ce bout de code se trouve dans le loop).

if(Date_Fin_Definie == false && Etat_Abonnement == true){
    Fin = (now + TimeSpan(0,0,1,0));
    Date_Fin_Definie = true;
  }
  if(Date_Fin_Proche_Definie == false && Etat_Abonnement == true){
    Proche_Fin = (Fin - TimeSpan(0,0,0,30));
    Date_Fin_Proche_Definie = true;
  }

Qu'en pensez vous ?

Bien sûr, je précise que Date_Fin_Definie et Date_Fin_Proche_Definie sont mises à l'état false avant.

Voilà le code en entier :

#include <FastLED.h>
#include "RTClib.h"

RTC_DS1307 rtc;

#define NUM_LEDS 1
#define DATA_PIN 15
#define CLOCK_PIN 13

int Duree_abonnement = 90;
bool Etat_Abonnement = true;

bool Date_Fin_Definie = false;
bool Date_Fin_Proche_Definie = false;

int Jours_avant_fin = 10;

DateTime Debut;
DateTime Fin;
DateTime Proche_Fin;

CRGB leds[NUM_LEDS];

void setup() {
  
  
  Serial.begin(9600);  
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }

}

void loop() {
  
  // calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future

  DateTime now = rtc.now();
  
  if(Date_Fin_Definie == false && Etat_Abonnement == true){
    Fin = (now + TimeSpan(0,0,0,60));
    Date_Fin_Definie = true;
  }
  if(Date_Fin_Proche_Definie == false && Etat_Abonnement == true){
    Proche_Fin = (Fin - TimeSpan(0,0,0,50));
    Date_Fin_Proche_Definie = true;
  }
  
  /*while(Etat_Abonnement == false ){
    leds[0] = CRGB::Yellow;
    FastLED.show();
    delay(500);
    leds[0] = CRGB::Black;
    FastLED.show();
    delay(500);
  }*/
    
  if(Etat_Abonnement == true){
    Serial.println("Abonnement actif"); 
  }

  if(Duree_abonnement != 0 && Etat_Abonnement == true && now < Fin && now < Proche_Fin){
    
    leds[0] = CRGB::Green;
    FastLED.show();
    Serial.println("Abonnement en cours");
  }

  if(now > Proche_Fin && now < Fin){
    Serial.println("Proche de la fin");
    leds[0] = CRGB::Orange;
    FastLED.show();
    delay(1500);
    leds[0] = CRGB::Black;
    FastLED.show();
    delay(1500);
    
  }

  if(now > Fin){
    Serial.println("Abonnement dépassé");
    leds[0] = CRGB::Red;
    FastLED.show();
  }
  
    Serial.print("Date actuelle: ");
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    Serial.println();
  
    Serial.print("Date de Fin: ");
    Serial.print(Fin.year(), DEC);
    Serial.print('/');
    Serial.print(Fin.month(), DEC);
    Serial.print('/');
    Serial.print(Fin.day(), DEC);
    Serial.print(' ');
    Serial.print(Fin.hour(), DEC);
    Serial.print(':');
    Serial.print(Fin.minute(), DEC);
    Serial.print(':');
    Serial.print(Fin.second(), DEC);
    Serial.println();
    Serial.println();

    Serial.print("Date de Fin Proche: ");
    Serial.print(Proche_Fin.year(), DEC);
    Serial.print('/');
    Serial.print(Proche_Fin.month(), DEC);
    Serial.print('/');
    Serial.print(Proche_Fin.day(), DEC);
    Serial.print(' ');
    Serial.print(Proche_Fin.hour(), DEC);
    Serial.print(':');
    Serial.print(Proche_Fin.minute(), DEC);
    Serial.print(':');
    Serial.print(Proche_Fin.second(), DEC);
    Serial.println();
    Serial.println();
    Serial.println();

   delay(1000);
  
}

Donc à ce niveau là maintenant, cela fonctionne mais maintenant j'ai un autre problème, il est possible que je perde l'alimentation, et avec ce code là, si l'alim est coupée, toute mes dates sont recalculées donc tout est faussé.

Pensez vous que je peux trouver un moyen pour éviter ça, peut être en sauvegardant une date de référence dans l'eeprom ? Mais je finirai peut être par être limité ?

Avez vous une autre idée ?

Peut-être en sauvegardant ces données dans la RAM disponible sur le DS1307 (en supposant qu'il soit monté sur un module alimenté par sa pile).

Un exemple (premier pris au hazard ) d'écriture/lecture dans la RAM du DS1307

Salut, effectivement, je n'avais pas pensé à la mémoire du DS1307 mais du coup ça me semble être la meilleur option. Maintenant, il faut que je trouve comment faire et d'abord qu'est ce que je garde.

Je me dis que le plus simple est de garder ma Date de fin et de Fin proche, soit les deux dates que j'utilise réellement, pour le now je le récupérerais automatiquement. Mais du coup il faudrait que je calcule ces dates puis que je les enregistre dans les mémoire et qu'en cas de coupure au redémarrage, mes variables reprennent les bonnes valeurs. Vous voyez ce que je veux dire ?

Car j'avoue que je suis un peu perdu pour coder ça.

Voilà le "principe de fonctionnement" que j'imagine:

-Calcul des dates
-Enregistrement dans la mémoire
-J'éxécute mon code...
-Si je perds l'alim -> Au moment où je la récupère je récupère les valeurs enregistrées puis je les redonne
-Et ça repart...

J'espère être compréhensible, je ne sais pas trop comment expliquer.

Si vous pensez qu'il y a des solutions plus simples pour faire ce que je veux je suis aussi ouvert.

Pas la peine d'enregistrer la date/heure Proche_Fin puisqu'elle dépends de la date/heure de fin d'abonnement.


Chaque fois qu'une date/heure de fin d'abonnement sera définie,
elle devra être enregistrée dans la RAM du module RTC.

Le module RTC étant auto-alimenté par sa pile,
il gardera en RAM la date/heure de fin d'abonnement (passée ou à venir)
tout en actualisant la date/heure courante.


Au démarrage (ou re-démarrage),
le programme n'aura qu'à lire la date/heure de fin d'abonnement qui a été enregistrée dans la RAM du module RTC ce qui lui permettra de définir la valeur de la date/heure Proche_Fin

Bonjour, j'ai pu avancer sur le code mais j'ai quand même quelques questions.


#include <FastLED.h>
#include "RTClib.h"

RTC_DS1307 rtc;

#define NUM_LEDS 1
#define DATA_PIN 15
#define CLOCK_PIN 13

int Duree_abonnement = 90;
bool Etat_Abonnement = true;

bool Date_Fin_Definie = false;
bool Date_Fin_Proche_Definie = false;

int Jours_avant_fin = 10;

DateTime Debut;
DateTime Fin;
DateTime Proche_Fin;

CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(9600);  
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }
}

void loop() {

  // calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
  
  DateTime now = rtc.now();

  
  
 
   Calcul_Date_Fin();
   Calcul_Date_Fin_Proche();
  /*if(Date_Fin_Definie == false && Etat_Abonnement == true){
    Fin = (now + TimeSpan(0,0,0,60));
    Date_Fin_Definie = true;
  }*/
  
  
  /*while(Etat_Abonnement == false ){
    leds[0] = CRGB::Yellow;
    FastLED.show();
    delay(500);
    leds[0] = CRGB::Black;
    FastLED.show();
    delay(500);
  }*/
    
  /*if(Etat_Abonnement == true){
    Serial.println("Abonnement actif"); 
  }

  if(Duree_abonnement != 0 && Etat_Abonnement == true && now < Fin && now < Proche_Fin){
    
    leds[0] = CRGB::Green;
    FastLED.show();
    Serial.println("Abonnement en cours");
  }

  if(now > Proche_Fin && now < Fin){
    Serial.println("Proche de la fin");
    leds[0] = CRGB::Orange;
    FastLED.show();
    delay(650);
    leds[0] = CRGB::Black;
    FastLED.show();
    delay(650);
    
  }

  if(now > Fin){
    Serial.println("Abonnement dépassé");
    leds[0] = CRGB::Red;
    FastLED.show();
  }
  */
    Serial.print("Date actuelle: ");
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    Serial.println();
  
    

    Serial.print("Date de Fin Proche: ");
    Serial.print(Proche_Fin.year(), DEC);
    Serial.print('/');
    Serial.print(Proche_Fin.month(), DEC);
    Serial.print('/');
    Serial.print(Proche_Fin.day(), DEC);
    Serial.print(' ');
    Serial.print(Proche_Fin.hour(), DEC);
    Serial.print(':');
    Serial.print(Proche_Fin.minute(), DEC);
    Serial.print(':');
    Serial.print(Proche_Fin.second(), DEC);
    Serial.println();
    Serial.println();
    Serial.println();

   delay(1000);
  
}


void Calcul_Date_Fin(){
  DateTime now = rtc.now();
  if(Date_Fin_Definie == false && Etat_Abonnement == true){
    Fin = (now + TimeSpan(0,0,0,60));
    Date_Fin_Definie = true;
    saveDateTimeToNVRAM(Fin);
    saveBoolToNVRAM(Date_Fin_Definie);
    
  }
}


void Calcul_Date_Fin_Proche(){
  if(Date_Fin_Proche_Definie == false && Etat_Abonnement == true){
    Proche_Fin = (Fin - TimeSpan(0,0,0,50));
    
    Date_Fin_Proche_Definie = true;
    
    saveDateTimeToNVRAM(Proche_Fin);
    saveBoolToNVRAM(Calcul_Date_Fin_Proche);
  }
}


void saveDateTimeToNVRAM(DateTime dt) {
  // Convertir l'objet DateTime en un tableau d'octets
  uint8_t data[sizeof(DateTime)];
  uint8_t* ptr = (uint8_t*)&dt; // Obtenez un pointeur vers l'objet DateTime
  
  memcpy(data, ptr, sizeof(DateTime)); // Copiez les données de l'objet DateTime dans le tableau d'octets
  
  // Écrire les données dans la NVRAM du DS1307
  rtc.writenvram(0, data, sizeof(data));
}


void saveBoolToNVRAM(bool value) {
  // Convertir la variable booléenne en un tableau d'octets
  uint8_t data[sizeof(bool)];
  memcpy(data, &value, sizeof(bool)); // Copier la valeur de la variable booléenne dans le tableau d'octets
  
  // Écrire les données dans la NVRAM du DS1307
  rtc.writenvram(sizeof(DateTime), data, sizeof(data)); // Utiliser une adresse différente pour éviter les conflits avec les autres données 
}

Pour l'instant j'ai juste à réussi à ajouter deux fonctions pour écrire les données dans la Ram de l'horloge, sauf que j'ai pas mal utilisé Chat GPT en essayant quand même de faire attention, et aussi car je ne trouve pas trop de vidéo/tuto sur le sujet. Donc déjà est ce que la manière dont fonctionnent les fonctions : " saveBoolToNVRAM" et "saveDateTimeToNVRAM" vous semble bonne ? Car j'avoue que je n'ai pas encore tout compris de ce qui est utilisé.

Maintenant, j'ai aussi essayer avec Chat GPT de créer une fonction qui vérifierai si il y a des données présentent dans la RAM et qui renverrai une valeur booléenne en fonction, voilà le résultat:

bool Verification_NVRam() {
  const int NVRAM_SIZE = 64; // Taille de la mémoire NVRAM du DS1307
  uint8_t data[NVRAM_SIZE]; // Tableau pour stocker les octets lus depuis la NVRAM
  
  // Lire les octets depuis la NVRAM
  rtc.readnvram(0, &data[0], NVRAM_SIZE); // Passer un pointeur vers le premier élément du tableau
  
  // Vérifier si les octets contiennent des données significatives
  for (int i = 0; i < NVRAM_SIZE; i++) {
    if (data[i] != 0) {
      return true; // Données significatives trouvées
    }
  }
  
  return false; // Aucune donnée significative trouvée
}

Déjà cela vous semble correct ?
Car j'ai l'impression que ça l'est mais j'ai quand même une petite erreur à la compilation: invalid conversion from 'uint8_t*' {aka 'unsigned char*'} to 'uint8_t' {aka 'unsigned char'} [-fpermissive]

Pour laquelle je ne trouve pas de solutions et d'ailleurs, Chat GPT n'en trouve pas non plus... Donc je ne sais pas si ça vaut le coup de garder ça.

Puis ensuite je vais avoir besoin de lire les infos dans la RAM pour pouvoir logiquement les utiliser dans le code, je n'ai pas encore fait cette partie mais après quelques tests pour voir ce que ça donnait, j'ai fait quelques bout de code, bien sûr avec l'aide de Chat GPT, mais j'ai des erreurs similaires à celle d'avant donc j'ai l'impression que le problème revient assez souvent.

Voilà tout, j'espère ne pas vous avoir perdu, et je suis ouvert toute aide ou proposition.

Bonjour, je ne veux pas forcer ou quoi mais j'ai toujours pas trouver de solution, et j'aimerais avancer donc j'avoue que je voudrais bien de l'aide si possible s'il vous plaît. Bien sûr, si besoin je peux apporter plus d'informations ou clarifier des choses.

Bonjour

Je débute complètement en programmation C++.


J'ai fait un test d'une sauvegarde dans la nvram d'une date/heure au format unixtime
et j'ai pu ensuite faire une lecture de la valeur qui avait été sauvegardée dans la nvram.

#include "RTClib.h"

RTC_DS1307 RTC;

uint32_t dansRTC;
uint32_t dansNvram;

uint8_t  posDansNvram = 0; //position dans la nvram

void setup() {

  Serial.begin(9600); 
  RTC.begin();
 
  DateTime now = RTC.now();
  
  dansRTC = now.unixtime();

  // Affichage pour contrôle
  Serial.print("    À écrire  = ");
  Serial.println(dansRTC);

  // Enregistrement pour tests d'une valeur unixtime dans la nvram
  RTC.writenvram(posDansNvram, (uint8_t*)&dansRTC, sizeof(dansRTC)); 

  // Lecture de la valeur unixtime sauvegardée précédement dans la nvram
  RTC.readnvram((uint8_t*)&dansNvram, sizeof(dansNvram), posDansNvram); 
  
  // Affichage pour contrôle
  Serial.print("Dans la nvram = ");
  Serial.println(dansNvram);
}

void loop () {
  // Vide pour ce test
}

Lorsque je regarde le prototyper de la fonction j'ai ça, donc pour moi, tes arguments sont dans le déordre
void readnvram(uint8_t *buf, uint8_t size, uint8_t address);

Il n'y a que 56 octets disponibles dans la NVRAM du DS1307 (pdf de 216Kio)

Merci pour vos réponses.

Effectivement, je vois bien que les arguments ont l'air d'être placés bizarrement par rapport au code de @amic au prototype de la fonction qu'a envoyé @fdufnews .
Mais ce qui est bizarre c'est que sur l'ide lorsque je compile, il n'y a pas de problèmes à ce niveau là donc c'est un peu étonnant ? Vous pensez que cela peut avoir un rapport avec l'erreur ?

Au niveau de l'espace disponible ça me semble bon sachant que je veux enregistrer deux éléments de la class DateTime et deux variables bool.

Soit d'après ce que j'ai trouvé en tout 16 octets.
(Les deux bools font 2 octets en tout (1 chacun)
Puis les date time sont composés de 1 uint_16_t et 5 uint_8_t soit 2 octets + 5 octets * 2 = 14 octets + les deux des variables bool). Donc si je me suis pas trompé à ce niveau ça devrait être bon ?