Récupérer et afficher les 10 dernières prise de température

quel est le problème ? ça affiche bien les 16 dernières lectures dans l'ordre. Mais comme je l'ai dit le code n'attend pas 30 minutes pour faire la prochaine lecture, le delay(3000); n'attend que 3 secondes... si vous voulez un code bloquant vous pouvez mettre delay(1800000ul); car 1 800 000 c'est 30 minutes en millisecondes. idéalement bien sûr il faudrait vérifier l'heure de la RTC plutôt.

Je parle de la date, quand je test mon capteur sur un autre programme j'ai bien la bonne heure .mais dans votre programme ,j'ai totalement autre chose (2010)

ah ok.

est-ce que vous voyez "RTC is NOT running, let's set the time" qui s'affiche au début ?

EDIT: vous n'avez pas remis le code d'initialisation de l'horloge ..

oui toujours même quand je laisse le programme tourner rien ne change,
Le rtc est branché sur mon ESP32 qui est branché à l'ordinateur

//BME280
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
unsigned long delayTime;

#include "RTClib.h"
RTC_DS1307 rtc;
const char* daysOfTheWeek[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

const byte NbValeurs = 16;
float tabTemp[NbValeurs];
byte positionDansLeTableau = 0;

  DateTime heure;

void printDate(DateTime& uneDate) {
  Serial.print(daysOfTheWeek[uneDate.dayOfTheWeek()]); Serial.write(',');
  Serial.print(uneDate.day());  Serial.write('/');
  if (uneDate.month() < 10) Serial.write('0');
  Serial.print(uneDate.month()); Serial.write('/');
  Serial.print(uneDate.year() - 2000); Serial.write('\t');

  if (uneDate.hour() < 10) Serial.write('0');
  Serial.print(uneDate.hour());
  Serial.print(':');
  if (uneDate.minute() < 10) Serial.write('0');
  Serial.print(uneDate.minute());
  Serial.print(':');
  if (uneDate.second() < 10) Serial.write('0');
  Serial.print(uneDate.second());
}

void printValues() {
  Serial.println("\nTempératures = ");
  for (byte i = 1; i <= NbValeurs; i++) {
    byte j = (NbValeurs + positionDansLeTableau - i) % NbValeurs;
    DateTime ancienneDate = heure - TimeSpan(30l * i * 60);
    Serial.print(j);
    Serial.write('\t');
    printDate(ancienneDate);
    Serial.write('\t');
    Serial.println(tabTemp[j]);
  }

}

void setup() {
  Serial.begin(115200);
  Serial.println();
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
  heure = rtc.now();
  
  while(!Serial);    // time to get serial running
    Serial.println(F("BME280 test"));
    unsigned status;
    status = bme.begin();  
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
        Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),16);
        Serial.print("        ID of 0x60 represents a BME 280.\n");
        while (1) delay(10);
    }
    
    Serial.println("-- Default Test --");
    Serial.println();

}

void loop() {
    Serial.print("Température = ");
    Serial.print(bme.readTemperature());
    Serial.println(" °C");
    
  heure = heure + TimeSpan(30l * 60); // 30 minutes plus tard
  tabTemp[positionDansLeTableau] = bme.readTemperature();
  positionDansLeTableau = (positionDansLeTableau + 1) % NbValeurs;
  printValues();
  delay(3000);
}

il manque

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

dans le setup pour initialiser la RTC (à faire avant if (! rtc.isrunning()))

Bonjour oasixm

Est-ce-que tu vois ta RTC avec un scan i2C?
Si non sur quelle pin sont connectés les signaux SDA et SCL?
Sur mon ESP32 TTGO, c'est SDA 21 et SCL 22
Essaies d'initialiser wire ainsi

Wire.begin(sdaPin, sclPin);

Ensuite essaies un nouveau scan i2C en initialisant wire de la même façon.

Cordialement
jpbbricole

Si tu as un Wi-Fi accessible il est possible de prendre l'heure via NTP

Après, c'est vrai que lorsque je t'ai proposé le code au départ, j'ai sous estimé le problème des huit heures. Il est plus simple de faire 3 tableaux : un pour la mesure, un pour l'heure et un pour la date.

1 Like

Bnjour oasixm

Pour le sport, je me suis prêté à l’exercice de lister les 10 dernières mesures.
Voilà le résultat de ma gamberge :

/*
    Name:       ARDFR_mormic10derniers.ino
    Created:	02.07.2021
    Author:     jpbbricole
*/
#include "RTClib.h"
RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

//------------------------------------- Journal des mesures
const int mesuresNombre = 10;

struct mesuresJournalDef
{String dateHeure; float pression;};
mesuresJournalDef mesuresJournal[mesuresNombre];

//------------------------------------- Tempo pour affichage
unsigned long displayTemps = millis();
unsigned long displayTempo = 2000;

#define btnJournal 12         // Bouton pour déclencher l'affichage du journal
	
void setup()
{
   Serial.begin(115200);
   pinMode(btnJournal, INPUT_PULLUP);
   
   if (! rtc.begin()) {
	   Serial.println("Couldn't find RTC");
	   Serial.flush();
	   abort();
   }

   if (! rtc.isrunning()) {
	   Serial.println("RTC is NOT running, let's set the time!");
	   // When time needs to be set on a new device, or after a power loss, the
	   // following line sets the RTC to the date & time this sketch was compiled
	   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
	   // This line sets the RTC with an explicit date & time, for example to set
	   // January 21, 2014 at 3am you would call:
	   // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
   }
}

void loop()
{
    if (millis()-displayTemps >= displayTempo)
    {
		mesureDansJournal();

		Serial.print(mesuresJournal[0].dateHeure);
		Serial.print("\tp: ");
		Serial.println(mesuresJournal[0].pression);
		
		displayTemps = millis();		
    }
	
	if (digitalRead(btnJournal) == LOW)
	{
		journalListe();
		
		while(digitalRead(btnJournal) == LOW){}
		Serial.println("");
	}
}

void mesureDansJournal()
{
	//--------------------------------- Décalage du journal vers "le bas"
	for (int j = mesuresNombre-1; j > 0; j --)
	{
		mesuresJournal[j] = mesuresJournal[j-1];
		
	}
	
	mesuresJournal[0].dateHeure = dateHeure();
	mesuresJournal[0].pression = pressionMesure();
}

/*-------------------------------------------------------------------
	Affichage du journal des mesures
	Le journal est décalé de telle façon que la dernière mesure 
	se trouve en tete
'*-------------------------------------------------------------------
*/
void journalListe()
{
	Serial.println("\n\tJournal des mesures");

	for (int j = 0; j < mesuresNombre; j ++)
	{
		Serial.print(mesuresJournal[j].dateHeure);
		Serial.print("\tp: ");
		Serial.println(mesuresJournal[j].pression);
	}
		
}

String dateHeure()
{
	String retVal = "";
	
	DateTime now = rtc.now();
	
	retVal = (String)daysOfTheWeek[now.dayOfTheWeek()] + ", "; 
	retVal += now.timestamp(2) +"/";     // Date
	retVal += now.timestamp(1);     // Heure
	
	return retVal;
}

float pressionMesure()
{
	static float pression = 12.124;
	pression += 0.13;
	
	if (pression > 22.0)
	{
		pression = 12.1;
	}
	return pression;
}

Les données, date et mesures se trouvent dans un tableau sous forme de structure mesuresJournal[mesuresNombre].
Les mesures de pression se font dans float pressionMesure(). Elles sont bidon, je n’ai pas ce capteur.
Tout les tant de temps la date et la pression sont mis dans le journal et ceci de telle façon que la dernière mesure se trouve en tête de la liste (indice [0]).
Pour déclencher les mesuresNombre dernières mesures, j’ai attribué un bouton (btnJournal), quand il y a LOW sur l’entrée la liste des mesures s’affiche dans le moniteur.

A ta disposition pour touts renseignements.

Cordialement
jpbbricole

Salut @jpbbricole

L'avantage de décaler les éléments a chaque lecture c'est que le tableau reste ordonné et il suffit donc d'enregistrer la nouvelle lecture à la position 0. L'autre avantage, c''est que c'est simple à lire.

Un inconvénient c'est le coût du décalage en terme de temps CPU et impact mémoire (ici vous recopiez par duplication les chaînes qui représentent l'heure par exemple). Avec 10 mesures, ça doit rester gérable mais quand on commence à vouloir conserver 500 mesures c'est plus problématique.

L'idée si on est contraint en mémoire ou performance est donc de garder dans une variable le "zéro relatif" du tableau et en faire ce que l'on appelle un tableau circulaire. Imaginons que l'on garde 5 valeurs et que le tableau ait été rempli case après case (depuis la case 0).

  • les 5 dernières valeurs sont donc aux indices 4, 3, 2, 1 et 0 et la prochaine case à remplir est à l'indice 0
  • au coup suivant, les 5 dernières valeurs sont aux indices 0, 4, 3, 2 et 1et la prochaine case à remplir est à l'indice 1
  • au coup suivant, les 5 dernières valeurs sont aux indices 1, 0, 4, 3 et 2 et la prochaine case à remplir est à l'indice 2
  • au coup suivant, les 5 dernières valeurs sont aux indices 2, 1, 0, 4 et 3 et la prochaine case à remplir est à l'indice 3
  • ...
    --> en mémorisant donc uniquement cette position de "la prochaine case à remplir" et en la faisant tourner (et revenir à 0 une fois arrivé au bout du tableau) on n'a plus à décaler les éléments ➜ c'est l'idée des autres codes proposés et la fonction modulo (%) permet de gérer le retour à 0 de cet indice.

Cela dit, pour un petit besoin de mémorisation et si le code ne tourne pas trop longtemps (risques possibles liés à la classe String et toutes les concaténations que vous effectuez) ça va fonctionner très bien.

PS/ pour rendre plus efficace votre code tant au niveau mémoire nécessaire que performance lors du décalage, vous pourriez stocker uniquement un uint32_t pour l'heure de mesure (la fonction dateHeure() retournerait simplement now. unixtime(); et c'est la fonction journalListe() qui se chargerait de convertir la date en texte, uniquement pour l'écriture sur écran.

Bonjour J-M-L

Je m'y était "attelé" mais mes vieux neurones n'avaient rien sorti de bon donc, solution de facilité... Mais je vais quand appliquer cette solution.

Excellente idée.
Dès que j'ai un moment, je vais appliquer ces 2 modifications.

Cordialement
jpbbricole

Bonjour

Voilà la version corrigée, que dis-je améliorée.
La structure du journal a légèrement changé.
de

struct mesuresJournalDef
{String dateHeure; float pression;};
mesuresJournalDef mesuresJournal[mesuresNombre];

à

struct mesuresJournalDef
{DateTime now;  float pression;};
mesuresJournalDef mesuresJournal[mesuresNombre];

Le programme (l'explication de post #48 est toujours valable)

/*
    Name:       ARDFR_mormic10derniers.ino
    Created:	02.07.2021
    Author:     jpbbricole
*/
#include "RTClib.h"
RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

//------------------------------------- Journal des mesures
const int mesuresNombre = 10;

struct mesuresJournalDef
{DateTime now;  float pression;};
mesuresJournalDef mesuresJournal[mesuresNombre];

int journalIndex = 0;     
int journalIndexListe[mesuresNombre];

//------------------------------------- Tempo pour affichage
unsigned long displayTemps = millis();
unsigned long displayTempo = 2000;

#define btnJournal 12         // Bouton pour déclencher l'affichage du journal
	
void setup()
{
	Serial.begin(115200);
	pinMode(btnJournal, INPUT_PULLUP);
   
	if (! rtc.begin()) {
		Serial.println("Couldn't find RTC");
		Serial.flush();
		abort();
	}

	if (! rtc.isrunning()) {
		Serial.println("RTC is NOT running, let's set the time!");
		rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
	}

	for (int i = 0; i < mesuresNombre; i ++)
	{
		journalIndexListe[i] = -1;     // Index inutilisé
	}

	journalIndex = -1;
	mesureDansJournal();
}

void loop()
{
    if (millis()-displayTemps >= displayTempo)
    {
		mesureDansJournal();
		journalListeOne(journalIndexListe[journalIndex]);
		
		displayTemps = millis();		
    }
	
	if (digitalRead(btnJournal) == LOW)
	{
		journalListe();
		
		while(digitalRead(btnJournal) == LOW){}
		Serial.println("");
	}
}

void mesureDansJournal()
{
	journalIndex += 1;
	journalIndex = journalIndex >= mesuresNombre ? 0 : journalIndex;

	mesuresJournal[journalIndex].now = rtc.now();
	mesuresJournal[journalIndex].pression = pressionMesure();
	
	//--------------------------------- Décalage de l'index du journal vers "le bas"
	for (int i = mesuresNombre-2; i >= 0; i --)
	{
		journalIndexListe[i+1] =journalIndexListe[i];
	}
	journalIndexListe[0] = journalIndex;
}

/*-------------------------------------------------------------------
	Affichage du journal des mesures
	Le journal est décalé de telle façon que la dernière mesure 
	se trouve en tete
'*-------------------------------------------------------------------
*/
void journalListe()
{
	Serial.println("\n\tJournal des mesures");

	for (int i = 0; i < mesuresNombre; i ++)
	{
		if (journalIndexListe[i] != -1)     // Si index utilisé
		{
			journalListeOne(i);
		}
	}
		
}

void journalListeOne(int journalIndex)
{
	Serial.print(mesuresJournal[journalIndexListe[journalIndex]].now.timestamp(2) +"/");
	Serial.print(mesuresJournal[journalIndexListe[journalIndex]].now.timestamp(1) +"\t p:");
	Serial.println(mesuresJournal[journalIndexListe[journalIndex]].pression);
}

String dateHeure()
{
	String retVal = "";
	
	DateTime now = rtc.now();
	
	retVal = (String)daysOfTheWeek[now.dayOfTheWeek()] + ", "; 
	retVal += now.timestamp(2) +"/";     // Date
	retVal += now.timestamp(1);     // Heure
	
	return retVal;
}

float pressionMesure()
{
	static float pression = 12.124;
	pression += 0.13;
	
	if (pression > 22.0)
	{
		pression = 12.1;
	}
	return pression;
}

Cordialement
jpbbricole

1 Like

Bravo, je suis fasciné par les programmes que vous faites si rapidement pendant que moi je galère :slight_smile:
J'ai donc essayé ton programme j'ai ce message d'erreur qui ressort .
pour le mettre avec mon capteur de température je dois changer le float pressionMesure () c'est ça ?

C:\Users\Commercial\Documents\Arduino\test_temp-histo3\test_temp-histo3.ino: In function 'String dateHeure()':
test_temp-histo3:107:28: error: invalid conversion from 'int' to 'DateTime::timestampOpt' [-fpermissive]
   retVal += now.timestamp(2) +"/";     // Date
                            ^
In file included from C:\Users\Commercial\Documents\Arduino\test_temp-histo3\test_temp-histo3.ino:6:0:
C:\Users\Commercial\Documents\Arduino\libraries\RTClib/RTClib.h:148:10: note:   initializing argument 1 of 'String DateTime::timestamp(DateTime::timestampOpt)'
   String timestamp(timestampOpt opt = TIMESTAMP_FULL);
          ^
test_temp-histo3:108:28: error: invalid conversion from 'int' to 'DateTime::timestampOpt' [-fpermissive]
   retVal += now.timestamp(1);     // Heure
                            ^
In file included from C:\Users\Commercial\Documents\Arduino\test_temp-histo3\test_temp-histo3.ino:6:0:
C:\Users\Commercial\Documents\Arduino\libraries\RTClib/RTClib.h:148:10: note:   initializing argument 1 of 'String DateTime::timestamp(DateTime::timestampOpt)'
   String timestamp(timestampOpt opt = TIMESTAMP_FULL);
          ^
exit status 1
invalid conversion from 'int' to 'DateTime::timestampOpt' [-fpermissive]

remplacez dans le code de @jpbbricole

au début la définition de daysOfTheWeek

const char* daysOfTheWeek[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

et ensuite les 2 fonctions par

void journalListeOne(int journalIndex)
{
  Serial.print(mesuresJournal[journalIndexListe[journalIndex]].now.timestamp(DateTime::TIMESTAMP_DATE));
  Serial.write('/');
  Serial.print(mesuresJournal[journalIndexListe[journalIndex]].now.timestamp(DateTime::TIMESTAMP_TIME));
  Serial.print(F("\t p:"));
  Serial.println(mesuresJournal[journalIndexListe[journalIndex]].pression);
}

String dateHeure()
{
  String retVal = "";

  DateTime now = rtc.now();

  retVal = (String) daysOfTheWeek[now.dayOfTheWeek()] ;
  retVal += ", ";
  retVal += now.timestamp(DateTime::TIMESTAMP_DATE);    // Date
  retVal += "/";
  retVal += now.timestamp(DateTime::TIMESTAMP_TIME);    // Heure

  return retVal;
}

niquel ça fonctionne mais voici le résultat
image
ça lit les valeurs et ne les sauvegardes pas il me semble

Bonjour J-M-L

Pour le const char* ça m'a échappé, mais pour le reste, ça change quoi?

Cordialement
jpèbbricole

Bonjour oasixm

Tu voudrais les sauvegarder où?

Cordialement
jpbbricole

Bonjour,
L'objectif final est que ça sauvegarde mes valeurs de température toute les 30 minutes, puis je fais un bouton et quand j'appuie sur ce bouton ça les envoies par Bluetooth .
Ducoup pour le moment je voulais un programme qui me sauvegardais mes valeurs toute les 30 minutes pendant 8 heures soit 16 valeurs .
Mais il faudrait que ça sauvegarde les 8h et 8h30 pas 8h23 sinon ça s'actualise trop.
Tu as compris ? :slight_smile: j'explique mal

le const char * c'est juste de l'optimisation mémoire

pour le reste la méthode timestamp() attend un paramètre de type timestampOpt
c'est un type énuméré

bien que les valeurs associées aux éléments soient 0,1 et 2 le compilateur C++ est en droit de refuser une conversion d'un entier vers ce type et c'est le message d'erreur qui a été donné.

invalid conversion from 'int' to 'DateTime::timestampOpt'

En utilisant DateTime::TIMESTAMP_DATE et DateTime::TIMESTAMP_TIME on fournit bien un paramètre du type attendu et le compilateur est content.

la séparation des print() en plusieurs lignes au lieu de faire '+' c'est pour éviter de créer des sous string temporaires qui n'apportent rien de bon (morcellement mémoire possible, allocation de gros buffers pour stocker la chaîne intermédiaire, ralentissement du code)

Bonjour J-M-L

J'avais essayé de mettre TIMESTAMP_FULL, TIMESTAMP_TIME et TIMESTAMP_DATE et j'avais eu des erreurs, c'est pourquoi j'avais mis 1 et 2 et ça a passé, d'où provient ce 2 interprétation du compilateur?

Cordialement
jpbbricole

Oui, comme l'enum est embarqué au sein de la classe, sa visibilité est locale et donc les mots clés ne sont pas connus en dehors.
Pour que le compilateur sache de quoi on parle, Il faut donc rajouter la classe devant, donc écrire DateTime::TIMESTAMP_DATE et pas juste TIMESTAMP_DATE.

ça peut passer suivant la version de l'IDE ou le type de carte utilisée (ie la version du compilateur). La norme C++ dit que ce n'est pas OK mais certains compilateurs sont flexibles (car en C ça se faisait souvent - je crois qu'il faut aussi fixer le type de l'énumération sans doute et faire un static cast sinon)