[Projet] Programmateur Pompes de Bassin

Si l'Arduino n'est plus alimenté, la RTC continue à fonctionner grace à une batterie ou une petite pile. La batterie peut assurer le fonctionnement de la RTC pendant une très longue période car elle ne consomme quasiment rien. Donc l'heure n'est pas perdue mais les fonctions de gestion des pompes ne sont évidemment plus utilisables.
Voir ici par exemple

On recommande souvent les modules utilisant la RTC DS3231 car elle dérive très peu dans le temps ce qui évite de devoir la mettre à l'heure trop souvent.
Ceci dit, si l'installation solaire est bien dimensionnée, cette situation ne devrait pas se produire.

OK. On ne pourra donc pas donner d'indication à propos de la technique à employer pour la mesure de la tension du panneau.

Oui je comprends. J'ai fait une croix sur cette fonction du coup.
Je vais aller au plus simple et gérer l'automatisation des 4 pompes en fonctions du moment de la journée.

Merci.

Ta solution semble la plus sûr car elle ne prend pas en compte les variantes lié à la météo, à la lumière de la lune, etc.
Je trouvais la solution du capteur de lumière plus "poétique" mais nettement moins précise. :stuck_out_tongue:

Je vais regarder tout ça.

Bonjour,

Petite mise à jour sur l'avancée de mon projet.
J'ai reçu et testé les différents éléments. Tout fonctionne indépendamment. (C'est déjà une bonne nouvelle).
J'ai également testé le code pour le calcul de l'éphéméride. Il marche incroyablement bien (Bravo à l'auteur).
Concernant le code, j'ai quelques difficultés notamment concernant le calcul de l'éphéméride. Je souhaiterais épurer le code pour ne garder que les valeurs de lever et de coucher du soleil du jour actuel.
Auriez vous une pistes sur ce que je dois absolument gardé hormis les différents calculs ?

Merci.

En C, seules les fonctions et variables utilisées sont prises en compte par le linker. Les autres sont laissées de côté.

Pour ma part j'utilise sun_rise() et sun_set(), deux fonctions standard de la librairie C.
https://www.nongnu.org/avr-libc/user-manual/group__avr__time.html
Il faut donner la position : set_position()
Je ne sais pas si cela va prendre plus de place ou pas.

Je suis désolé.

Je n'ai rien compris à ta première phrase. C'est quoi un linker ? Qu'est ce que ça signifie justement ?

J'ai essayé de m'imprégner de cette librairie mais j'ai encore du mal.
Comment récupérer la valeur du sun_set et l'attribuer à la variable sunset par exemple ?

Merci de ton aide.

Le linker est le logiciel qui permet de lier tous les objets au sein de l'exécutable, après la compilation. Il construit l'exécutable.

En considérant que tu utilises un RTC_DS3231 + RTClib :

#include <time.h>
#include <RTClib.h>

#define LATITUDE          47.25
#define LONGITUDE         6.033

RTC_DS3231 rtc;

void setup() {
  Serial.begin(115200);
  if (!rtc.begin()) {
    Serial.println(F("Couldn't find RTC"));
    return;
  }
  if (rtc.lostPower()) {
    Serial.println(F("RTC lost power"));
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  set_position(LATITUDE * ONE_DEGREE, LONGITUDE * ONE_DEGREE);
}

void loop() {
  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now);
  // afficher ensuite sunRise et sunSet
}

Mais si la librairie de bricoleau te convient, pourquoi chercher plus loin, et pourquoi épurer ?
J'utilise sun_rise() et sun_set() par habitude, car disponible aussi sur PC.

Merci de ton retour.

Le code que tu as écrit me semble très bien et suffisant pour ce que je souhaite en faire. :slight_smile:
En revanche, je dois mal m'y prendre car le moniteur série me donne le résultat suivant :

13/1/2022 (Thursday) - 9:37:29
Lever du Soleil : 1642058580
Coucher du Soleil : 1642089900

Voici le code que j'utilise :

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

#define LATITUDE          47.25
#define LONGITUDE         6.033

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

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

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

  if (rtc.lostPower()) {
    Serial.println("RTC lost power");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  
  set_position(LATITUDE * ONE_DEGREE, LONGITUDE * ONE_DEGREE);

  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now);
  
  // afficher ensuite sunRise et sunSet
    Serial.print(utc.day(), DEC);
    Serial.print('/');
    Serial.print(utc.month(), DEC);
    Serial.print('/');
    Serial.print(utc.year(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[utc.dayOfTheWeek()]);
    Serial.print(") - ");
    Serial.print(utc.hour(), DEC);
    Serial.print(':');
    Serial.print(utc.minute(), DEC);
    Serial.print(':');
    Serial.print(utc.second(), DEC);
    Serial.println();
  
  Serial.print("Lever du Soleil : ");
  Serial.println(sunRise);
  Serial.print("Coucher du Soleil : ");
  Serial.println(sunSet);
}

void loop() {
}

Est-il possible de simplifier la partie du code affichant la date et l'heure ?

La librairie de @bricoleau me convient mais je n'ai pas les compétences pour récupérer uniquement les deux horaires qui m'intéresse ainsi que la date du jour. La partie concernant les différents éphémérides sur plusieurs jours et différents endroits de la planète ne m'est pas utile.

Merci de ton aide.

Les deux variables que tu affiches sont du type time_t, le nombre de secondes écoulées depuis le 1/1/1970.
Il faut les transformer en structure :

Merci de ta réponse.
J'essaie de me familiariser avec localtime, struct tm, etc. Ce n'est pas facile... Le langage C, c'est pas simple. :sweat_smile:

J'arrive à ce code qui se complie correctement mais ne me donne rien :

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

#define LATITUDE          47.25
#define LONGITUDE         6.033

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

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

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

  if (rtc.lostPower()) {
    Serial.println("RTC lost power");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  
  set_position(LATITUDE * ONE_DEGREE, LONGITUDE * ONE_DEGREE);

  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now); 

  struct tm lever;
  time(&sunRise);
  lever=*localtime(&sunRise);

  struct tm coucher;
  time(&sunSet);
  coucher=*localtime(&sunSet);
  
  // afficher ensuite sunRise et sunSet
  
    Serial.print(utc.day(), DEC);
    Serial.print('/');
    Serial.print(utc.month(), DEC);
    Serial.print('/');
    Serial.print(utc.year(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[utc.dayOfTheWeek()]);
    Serial.print(") - ");
    Serial.print(utc.hour(), DEC);
    Serial.print(':');
    Serial.print(utc.minute(), DEC);
    Serial.print(':');
    Serial.print(utc.second(), DEC);
    Serial.println();
    
    Serial.print("Lever du soleil : ");
  Serial.print(lever.tm_hour);
  Serial.print(":");
  Serial.print(lever.tm_min);
  Serial.print(":");
  Serial.println(lever.tm_sec);
  Serial.print("Coucher du Soleil : ");
  Serial.print(coucher.tm_hour);
  Serial.print(":");
  Serial.print(coucher.tm_min);
  Serial.print(":");
  Serial.println(coucher.tm_sec);
}

void loop() {
}

Je ne comprend pas mon erreur. Ca doit tellement être évident...

Pourquoi faire ceci : Cela met forcément sunRise et sunSet à ZÉRO.

D'autre part :

  struct tm lever;
  lever=*localtime(&sunRise);  // tu fais une recopie de la structure
  Serial.print(lever.tm_hour);

// ceci serait moins consommateur
  struct tm *lever;
  lever=localtime(&sunRise);
  Serial.print(lever->tm_hour);
// etc.

Travailler avec des pointeurs serait plus économique.

  struct tm *lever;
  lever=localtime(&sunRise);
  Serial.print("Lever du soleil : ");
  Serial.print(lever->tm_hour);
  Serial.print(":");
  Serial.print(lever->tm_min);
  Serial.print(":");
  Serial.println(lever->tm_sec);
  struct tm *coucher;
  coucher=localtime(&sunSet);
  Serial.print("Coucher du Soleil : ");
  Serial.print(coucher->tm_hour);
  Serial.print(":");
  Serial.print(coucher->tm_min);
  Serial.print(":");
  Serial.println(coucher->tm_sec);

Ensuite, pour savoir s'il fait jour ou nuit, la comparaison ne nécessite pas de connaître les éléments. Il suffit de comparer now avec sunRise et sunSet. Si now est entre les deux, c'est qu'il fait jour :

  bool enJournee = now >= sunRise && now <= sunSet ? true : false;

Merci @hbachetti.
J'ai appris pas mal de choses grâce à toi. Cela dit je suis loin de tout comprendre encore.

Peux-tu m'éclairer sur ces interrogations :

  • A quoi servent les pointeurs exactement ? "lever->tm_hour" est un pointeur ? Je ne comprend pas le concept.
  • A quoi sert "*" ? Et pourquoi cela est plus économique de le mettre avant "lever" plutôt que devant "localtime" ?
  • La dernière ligne de code permet d'attribuer à la variable enJournée la valeur TRUE ou FALSE en fonction du moment de la journée. C'est bien ça ? Ca marche un peu comme un "If" ? Si oui pourquoi cette méthode plutôt que l'autre ?

Merci de tes explications.

lever est un pointeur.

Il reçoit l'adresse d'une structure tm interne à la fonction localtime().

Ici on affiche l'élément tm_hour de la structure pointée par lever.

C'est un peu perturbant, je sais.
Devant lever * sert à le déclarer en tant que pointeur.
Devant un pointeur * sert à récupérer le contenu de la variable pointée.

localtime(&sunRise) retourne l'adresse d'une struct tm : c'est donc un pointeur.
*localtime(&sunRise) représente le contenu de la variable dont l'adresse est retournée par localtime().

En faisant ceci tu recopies l'intégralité de la structure dans lever, ce qui représente pas mal de données (9 entiers).

Cette fois-ci le pointeur lever reçoit simplement l'adresse de la structure (2 octets).

Dans une structure les éléments sont accédés comme ceci : lever.tm_hour
En passant par un pointeur les éléments sont accédés comme ceci : lever->tm_hour

C'est un opérateur appelé ternaire, qui existe dans beaucoup de langages. C'est équivalent à un if / else. Simple habitude de ma part, je trouve l'écriture plus concise.

Donc si je vulgarise...

  struct tm *lever;

Cette ligne permet de déclarer lever comme étant un pointeur.
A quoi sert exactement "struct tm" ? Je veux dire, "lever" est un pointeur juste à cause du "*" ou du "struct tm *" ?

  lever=localtime(&sunRise);

Cette ligne permet d'attribuer à "lever" la structure tm lié à la ligne time_t sunRise = sun_rise(&now);.
En gros de transformer un nombre de seconde depuis le 1/1/1970 en heure, minutes, secondes, etc.

C'est certainement grossier.

Lever est un pointeur (*) sur une structure tm.

Oui, c'est ça, mais dans ton cas, cela ne sert à rien de transformer le nombre de secondes en H:M:S, mis à part pour afficher.

Merci encore de ces précisions.

J'ai ma première partie de programme. Il ne me reste plus qu'à créer la partie concernant le pilotage des 4 relais. Ce sera plus facile je penses.

Merci encore @hbachetti !

Un petit supplément à savoir quand même :
Tu as mis à l'heure la RTC en France. Actuellement nous sommes en hiver (GMT+1). En été l'heure française sera à GMT+2.
sun_rise() et sun_set() retournent une heure GMT.
Donc si tu veux que le résultat soit exact, il faut que tu mettes à l'heure la RTC avec l'heure GMT.

Merci. Je me posais justement la question du changement d'heure.
Est ce qu'il n'y a pas moyen d'automatiser ce changement d'heure ?

Voici mon code pour le moment :

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

//L'ephéméride peut être vérifier à cette adresse : https://ssp.imcce.fr/forms/visibility

//Latitude et Longitude de Crevin.
#define LATITUDE          47.9333
#define LONGITUDE         -1.6667
int i = 3;

//Initialisation du module DS3231.
RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  
  Serial.begin(9600);

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

//Mise à jour de la date et de l'heure en cas de panne d'alimentation. Mise à jour depuis la date et l'heure de la compilation.
  if (rtc.lostPower()) {
    Serial.println("RTC lost power");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 
  }
  
  //Définition de la position.
  set_position(LATITUDE * ONE_DEGREE, LONGITUDE * ONE_DEGREE);

 //Définition du lever et du coucher du soleil.
  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now); 
  
  //Affichage de la date et l'heure actuelle.
  Serial.print(utc.day(), DEC);
  Serial.print('/');
  Serial.print(utc.month(), DEC);
  Serial.print('/');
  Serial.print(utc.year(), DEC);
  Serial.print(" (");
  Serial.print(daysOfTheWeek[utc.dayOfTheWeek()]);
  Serial.print(") - ");
  Serial.print(utc.hour(), DEC);
  Serial.print(':');
  Serial.print(utc.minute(), DEC);
  Serial.print(':');
  Serial.print(utc.second(), DEC);
  Serial.println();
    
  //Affichage de l'heure de lever du soleil
  struct tm *lever;
  lever=localtime(&sunRise);
  Serial.print("Lever du soleil : ");
  Serial.print(lever->tm_hour);
  Serial.print(":");
  Serial.print(lever->tm_min);
  Serial.print(":");
  Serial.println(lever->tm_sec);

  //Affichage de l'heure du coucher du soleil
  struct tm *coucher;
  coucher=localtime(&sunSet);
  Serial.print("Coucher du Soleil : ");
  Serial.print(coucher->tm_hour);
  Serial.print(":");
  Serial.print(coucher->tm_min);
  Serial.print(":");
  Serial.println(coucher->tm_sec);
    
  bool enJournee = now >= sunRise && now <= sunSet ? true : false;

  if (enJournee == true) Serial.println ("C'est le jour. Le système fonctionne.");
  else Serial.println ("C'est la nuit. Le système ne  fonctionne pas.");
    }

void loop() {
  
  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now); 
  
  bool enJournee = now >= sunRise && now <= sunSet ? true : false;
  
  while (enJournee == true){  
  digitalWrite(i,LOW);
  Serial.print ("Démarrage de la pompe n°");
  Serial.println (i-2);
  delay(6000);
  digitalWrite(i, HIGH);
  i++;
  if (i==7) i = 3;
}
}

Il y a peut être des incohérences...
Concernant la partie suivante :

void loop() {

  time_t now;
  DateTime utc = rtc.now();
  now = utc.unixtime();
  time_t sunRise = sun_rise(&now);
  time_t sunSet = sun_set(&now); 
  
  bool enJournee = now >= sunRise && now <= sunSet ? true : false;

Je suis obligé de remettre ces lignes suivantes sinon les variables ne sont pas reconnues. Existe-t-il une autre solution ?

Entre deux pompes, il y a un délai d'activation de 15 minutes. Existe-t-il une façon plus jolie de l'écrire à part delay(900000); ? (Pour le moment c'est réglé à 6s pour mes tests).

Ce que tu cherches à faire est : mettre en route une pompe et l'arrêter au lever et coucher du soleil, pas à une heure précise. Donc l'heure GMT est parfaitement adaptée.

Ton code ne marchera pas, car l'heure n'est pas relue dans la boucle, et enJournee n'est pas recalculé.
La boucle while() n'a d'ailleurs aucun intérêt car la fonction loop() est elle-même appelée dans une boucle.