Skip to main content

Data logger 2

Un modèle de documentation minimal pour tous les types de projets. Toutes les catégories ci-dessous doivent être renseignées, même de façon succincte.
IMPORTANT : Merci de sélectionner le / les tags adéquats dans le menu de droite, et de ne pas créer de nouveau tag.
Les fichiers sources doivent idéalement être joints à cette page grâce à l'icône trombone du menu de droite.
Des hésitations sur comment bien documenter et utiliser l'interface ? Consultez le tutoriel "Comment documenter"

Informations

  • Caroline Sreng
  • Adresse mail : caroline.vann_sreng@sorbonne-universite.fr
  • Service civique au Fablab 2024/2025
  • 01/10/2024- 2025

Contexte

Il s'agit d'un projet transversal Ă  l'interface des espaces prototypage et biologie/chimie, pour lequel les langages informatiques C++ et Python, l'Ă©lectronique et la chimie seront mobilisĂ©s. 

Objectifs

Un objectif serait de pouvoir confectionner au Fablab un boĂ®tier Ă©lectronique permettant d'afficher les informations sur la prise de mesures de tempĂ©rature, de pH, et d'autres valeurs physiques relevĂ©es lors de travaux pratiques effectuĂ©s dans le cadre d'enseignement secondaire/supĂ©rieure. Les valeurs des mesures seraient affichĂ©es sur un ordinateur par une communication par le port USB pour une première version et  par une application via un rĂ©seau internet, pour une version amĂ©liorĂ©e. Dans un second temps, Ă  partir d'un ordinateur, des seuils et des alertes seraient mises en place lorsque des valeurs souhaitĂ©es seraient atteintes. De la mĂŞme manière que prĂ©cĂ©demment, une première version vise Ă  instaurer une communication par le port USB.


image-1653061695508.jpeg

Ajouter au moins une image de votre projet

Matériel

Boîte contenant notre boîtier

  • Ă  voir ultĂ©rieurement (partie non traitĂ©e encore)

Electronique

  • un m5Core2
  • des fils Ă©lectriques
  • un câble USB-C vers port m5Core2
  • un breadboard
  • un ordinateur (logiciel Arduino IDE, Python, VSCode)
  • une sonde de tempĂ©rature DS18B20, (une sonde de pH de type SEN0161 par DFROBOT)
  • des rĂ©sistances

Machines utilisées

à voir ultérieurement (partie non traitée encore)

Construction

(Fichiers, photos, code, explications, paramètres d'usinage, photos, captures d'écran...)

Étape 1

Partie informatique/électronique

Une première Ă©tape est de se familiariser avec l'Ă©lectronique et les langages informatiques C++ et Python. 

Étape 2

----

Étape 3

----

Journal de bord

Avancée du projet à chaque étape, difficultés rencontrées, modifications et adaptations (facultatif pour les petits projets)

01/10/2024 -~mi-novembre 2024

J'ai commencĂ© Ă  me familiariser avec l'Ă©lectronique/les microcontrĂ´leurs Arduino en lisant : arduino-premiers-pas-en-informatique-embarquee.pdf  . J'ai Ă  peu près tout lu dans les grandes lignes exceptĂ©e la partie sur les Ă©crans LCD. J'ai aussi utilisĂ© Tinkercad pour tester certains branchements de manière virtuelle et aussi IRL une breadboard, un microcontrĂ´leur Arduino Uno et des LED. 

image.png

image.png

mi-novembre 2024 - 6/01/2025

Etant donnĂ© qu'il serait pertinent de crĂ©er une communication par l'utilisation d'une application entre un microcontrĂ´leur et un ordinateur, je me suis tournĂ©e vers un M5Core2 qui possède de nombreuses caractĂ©ristiques comme un Ă©cran, la possibilitĂ© de configurer entre autre une alarme, les connexions bluetooth et WI-FI. De plus, pour la crĂ©ation d'une application, il serait prĂ©fĂ©rable d'utiliser un autre langage informatique que le C++, je me suis tournĂ©e vers le langage Python. J'ai donc commencĂ© Ă  me familiariser avec le langage en lisant le livre Apprenez Ă  programmer en Python par Vincent Le Goff aux Editions Eyrolles (4ème Ă©dition). En parallèle, j'ai regardĂ© des vidĂ©os sur youtube en faisant des exercices de cas. J'ai aussi fait des branchements entre un M5core2 et un capteur de tempĂ©rature, en rĂ©cupĂ©rant les donnĂ©es de tempĂ©rature sur le terminal de sĂ©rie de l'Arduino IDE et mettant des seuils depuis ce terminal de sĂ©rie. 

Voici une photo du branchement (alimentation 3.3V, GPIO 27 et ground) :

image.pngimage.png

Le code C++ saisi sur Arduino IDE: 

#include <M5Core2.h>
#include <OneWire.h>
#include <DallasTemperature.h>


// Pin du capteur
#define ONE_WIRE_BUS 27


// Créer une instance OneWire
OneWire oneWire(ONE_WIRE_BUS);


// Passer l'instance OneWire Ă  DallasTemperature
DallasTemperature sensors(&oneWire);


// Déclarer la constante seuil par défaut à 20°C
float seuil = 20;


void setup() {
  // Initialiser M5Core2
  M5.begin();
  M5.Axp.SetSpkEnable(true); //activer le haut-parleur du m5core2  
  // Initialise la communication sĂ©rie
  Serial.begin(115200);
  Serial.println("Programme dĂ©marrĂ©. Tapez un nouveau seuil ou 'exit' pour quitter.");
  Serial.print("Seuil actuel : ");
  Serial.println(seuil);


  // Initialiser le capteur
  sensors.begin();


  // Initialiser l'affichage
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.print("TempĂ©rature : ");
}


void loop() {
  // Demander les tempĂ©ratures
  sensors.requestTemperatures();
 
  // Lire la tempĂ©rature en degrĂ©s Celsius
  float temperatureC = sensors.getTempCByIndex(0);


  // Afficher la tempĂ©rature sur l'Ă©cran
  M5.Lcd.setCursor(0, 30);
  M5.Lcd.fillRect(0, 30, 240, 40, BLACK); // Effacer la ligne prĂ©cĂ©dente
  M5.Lcd.print(temperatureC);
  M5.Lcd.print(" °C");


  // Afficher la tempĂ©rature sur le terminal sĂ©rie
  Serial.print("TempĂ©rature actuelle : ");
  Serial.print(temperatureC);
  Serial.println(" °C");


  // VĂ©rifie si des donnĂ©es sont disponibles sur le port sĂ©rie
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n'); // Lit l'entrĂ©e jusqu'Ă  un retour Ă  la ligne


    // Permet Ă  l'utilisateur de quitter le programme
    if (input.equalsIgnoreCase("exit")) {
      Serial.println("Programme terminĂ©.");
      while (true); // ArrĂŞte le programme
    } else {
      // Convertir l'entrĂ©e en float et mettre Ă  jour le seuil
      float newSeuil = input.toFloat();
      if (newSeuil != 0 || input.equals("0")) { // VĂ©rifie si la conversion est valide
        seuil = newSeuil;
        Serial.print("Nouveau seuil dĂ©fini : ");
        Serial.println(seuil);
      } else {
        Serial.println("EntrĂ©e invalide. Veuillez entrer un nombre valide.");
      }
    }
  }


  // VĂ©rifier si la tempĂ©rature dĂ©passe le seuil
  if (temperatureC > seuil) {
    M5.Lcd.setCursor(0, 80);
    M5.Lcd.setTextColor(RED);
    M5.Lcd.fillRect(0, 80, 240, 40, BLACK); // Effacer la ligne prĂ©cĂ©dente
    M5.Lcd.println("ALERTE : TempĂ©rature Ă©levĂ©e !");
    Serial.println("ALERTE : TempĂ©rature Ă©levĂ©e !");
    M5.Axp.SetVibration(true);  // Open the vibration.  
    delay(1000);
    M5.Axp.SetVibration(false);  // Open the vibration.  
    delay(1000);
  } else {
    M5.Lcd.setCursor(0, 80);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.fillRect(0, 80, 240, 40, BLACK); // Effacer la ligne prĂ©cĂ©dente
    M5.Lcd.println("TempĂ©rature OK.");
    Serial.println("TempĂ©rature OK.");
  }


  // Attendre 2s avant la prochaine lecture
  delay(2000);
}

J'ai ensuite compilé et téléchargé le script vers le M5Core2.

6/01/2025-27/02/2025

Après m'être familiarisé avec le langage Python, j'ai commencé à me pencher sur la communication des données via le port série. Il existe une bibliothèque du langage de programmation Python appelée Pyserial qui permet ceci. J'ai réussi à faire afficher dans le terminal de l'éditeur de code VScode les valeurs de températures mesurées toutes les 2 secondes et également à saisir des valeurs de seuil.

Voici le code à saisir en langage Python dans un éditeur de code comme VScode:

import serial
import threading


# Configuration du port série
PORT = 'COM9'  # Remplacez par le port sĂ©rie de votre M5Core2, port 9 pour moi
BAUD_RATE = 115200  # Taux de transmission utilisĂ© par le M5Core2.


def lire_temperature(ser):
    """
    Fonction qui lit les donnĂ©es reçues sur le port sĂ©rie en continu
    et les affiche dans le terminal.
    """
    while True:
        try:
            # VĂ©rifie s'il y a des donnĂ©es disponibles sur le port sĂ©rie.
            if ser.in_waiting > 0:
                # Lit une ligne complète, la dĂ©code et supprime les espaces/sauts de ligne inutiles.
                data = ser.readline().decode('utf-8').strip()
                # Affiche les donnĂ©es reçues (par exemple : tempĂ©rature mesurĂ©e).
                print(f"[M5Core2] {data}")
        except Exception as e:
            # En cas de problème lors de la lecture, affiche un message d'erreur.
            print(f"Erreur lors de la lecture du port sĂ©rie : {e}")
            break



def envoyer_seuil(ser):
    """
    Fonction qui permet d'envoyer un seuil de tempĂ©rature Ă  partir du terminal.
    L'utilisateur entre un seuil qui est envoyĂ© au M5Core2.
    """
    while True:
        try:
            # Demande Ă  l'utilisateur d'entrer un seuil ou de taper "exit" pour quitter.
            user_input = input("Entrez un seuil de tempĂ©rature (ou 'exit' pour quitter) : ")
            if user_input.lower() == 'exit':
                # Si l'utilisateur tape 'exit', on arrĂŞte la boucle.
                print("Fin de l'envoi de commandes.")
                break
            # Envoie la valeur entrĂ©e au M5Core2 via le port sĂ©rie.
            ser.write((user_input + '\n').encode('utf-8'))
        except Exception as e:
            # En cas de problème lors de l'envoi, affiche un message d'erreur.
            print(f"Erreur lors de l'envoi du seuil : {e}")
            break



def main():
    """
    Fonction principale qui initialise la connexion sĂ©rie, lance les threads
    pour la lecture et gère l'envoi des seuils.
    """
    try:
        # Ouvre la connexion sĂ©rie avec le port et le baud rate configurĂ©s.
        ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
        print("Connexion au M5Core2 Ă©tablie.\n")


        # DĂ©marre un thread sĂ©parĂ© pour lire les tempĂ©ratures en permanence.
        thread_lecture = threading.Thread(target=lire_temperature, args=(ser,))
        thread_lecture.daemon = True  # Assure que le thread s'arrĂŞte lorsque le programme principal termine.
        thread_lecture.start()


        # Lance la fonction d'envoi des seuils dans le thread principal.
        envoyer_seuil(ser)


    except serial.SerialException as e:
        # Si le port sĂ©rie ne peut pas ĂŞtre ouvert, affiche un message d'erreur.
        print(f"Erreur de connexion sĂ©rie : {e}")
    except Exception as e:
        # Capture d'autres erreurs potentielles et affichage.
        print(f"Erreur : {e}")
    finally:
        # Ferme la connexion sĂ©rie proprement Ă  la fin du programme.
        if 'ser' in locals() and ser.is_open:
            ser.close()
            print("Connexion sĂ©rie fermĂ©e.")



# Point d'entrée du programme.
if __name__ == "__main__":
    main()

Pour rĂ©cupĂ©rer les donnĂ©es, il faut tĂ©lĂ©verser le script ci-dessus dans l'Ă©diteur de code.  Veuillez Ă  faire attention Ă  ne pas laisser le terminal Arduino IDE ouvert sinon on ne pourra avoir accès au port sur VSCode. Si une erreur de type "Erreur de connexion sĂ©rie : could not open port 'COM9': Permission Error(13, 'Accès refusĂ©.', None, 5)" s'affiche, fermez le terminal sur Arduino IDE, dĂ©branchez le port USB  et rebranchez-le.

28/01/2025-30/01/2025

A prĂ©sent, afin d'utiliser les donnĂ©es pour des analyses, je vais essayer de rĂ©cupĂ©rer ces mesures de tempĂ©ratures (mesurĂ©es Ă  une frĂ©quence de 2 secondes ici) en fonction du temps. Pour cela, il faudrait enregistrer dans un fichier CSV ces donnĂ©es et faire une copie des log du M5Core2 sur une carte micro SD oĂą les donnĂ©es seront rĂ©cupĂ©rĂ©es. On pourra rĂ©cupĂ©rer ces donnĂ©es sur un ordinateur via cette carte micro SD. 

J'ai à nouveau modifié le code en C++ afin d'enregistrer les données sur la carte micro SD en affichant la date et l'heure.

#include <M5Core2.h>
#include <OneWire.h>          // Bibliothèque pour le capteur de température
#include <DallasTemperature.h> // Bibliothèque pour la gestion du capteur Dallas
#include <SD.h>               // Bibliothèque pour la lecture/écriture sur carte SD
#include <SPI.h>              // Bibliothèque pour la communication SPI

// Définition des broches
#define ONE_WIRE_BUS 27  // GPIO 27 pour le capteur
#define SD_CS_PIN 4      // GPIO 4 pour la carte SD

// Initialisation du capteur de température
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

// Seuil de température par défaut
float seuil = 30.0;

void setup() {
    M5.begin();
    M5.Axp.SetSpkEnable(true); // Activer le haut-parleur du M5Core2

    // Initialisation de la communication série
    Serial.begin(115200);
    Serial.println("Programme démarré. Tapez un nouveau seuil ou 'exit' pour quitter.");
    Serial.print("Seuil actuel : ");
    Serial.println(seuil);

    // Vérifier la présence de la carte SD
    if (!SD.begin(SD_CS_PIN)) {
        Serial.println("Échec de montage de la carte SD !");
        M5.Lcd.println("Carte SD absente !");
        return;
    } else {
        Serial.println("Carte SD détectée avec succès.");
    }

    // Initialisation du capteur de température
    sensors.begin();

    // Affichage initial sur l'écran
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.print("Température : ");
}

void loop() {
    sensors.requestTemperatures(); // Demander la température actuelle
    float temperatureC = sensors.getTempCByIndex(0); // Lire la température

    // Récupération de la date et heure actuelles
    RTC_TimeTypeDef TimeStruct;
    RTC_DateTypeDef DateStruct;
    M5.Rtc.GetTime(&TimeStruct);
    M5.Rtc.GetDate(&DateStruct);
    String dateTime = String(DateStruct.Date) + "/" + String(DateStruct.Month) + "/" + String(2000 + DateStruct.Year) + " " +
                      String(TimeStruct.Hours) + ":" + String(TimeStruct.Minutes) + ":" + String(TimeStruct.Seconds);

    // Ouverture du fichier sur la carte SD et enregistrement des données
    File dataFile = SD.open("/temp_data.txt", FILE_APPEND);
    if (dataFile) {
        dataFile.print(dateTime);
        dataFile.print(" - Température : ");
        dataFile.print(temperatureC);
        dataFile.println(" °C");
        dataFile.close();
        Serial.println("Donnée enregistrée sur la carte SD !");
    } else {
        Serial.println("Erreur lors de l'ouverture du fichier pour l'enregistrement !");
    }

    // Mise à jour de l'affichage sur l'écran
    M5.Lcd.setCursor(0, 30);
    M5.Lcd.fillRect(0, 30, 240, 40, BLACK); // Effacer la ligne précédente
    M5.Lcd.print(temperatureC);
    M5.Lcd.print(" °C");

    // Affichage sur le terminal série
    Serial.print("Température actuelle : ");
    Serial.print(temperatureC);
    Serial.println(" °C");

    // Vérification d'une entrée utilisateur sur le port série
    if (Serial.available() > 0) {
        String input = Serial.readStringUntil('\n'); // Lire l'entrée jusqu'au retour à la ligne

        if (input.equalsIgnoreCase("exit")) {
            Serial.println("Programme terminé.");
            while (true);
        } else {
            float newSeuil = input.toFloat();
            if (newSeuil != 0 || input.equals("0")) {
                seuil = newSeuil;
                Serial.print("Nouveau seuil défini : ");
                Serial.println(seuil);
            } else {
                Serial.println("Entrée invalide. Veuillez entrer un nombre valide.");
            }
        }
    }

    // Vérification du seuil de température et alerte
    if (temperatureC > seuil) {
        M5.Lcd.setCursor(0, 80);
        M5.Lcd.setTextColor(RED);
        M5.Lcd.fillRect(0, 80, 240, 40, BLACK);
        M5.Lcd.println("ALERTE : Température élevée !");
        Serial.println("ALERTE : Température élevée !");
        M5.Axp.SetVibration(true);
        delay(1000);
        M5.Axp.SetVibration(false);
    } else {
        M5.Lcd.setCursor(0, 80);
        M5.Lcd.setTextColor(WHITE);
        M5.Lcd.fillRect(0, 80, 240, 40, BLACK);
        M5.Lcd.println("Température OK.");
        Serial.println("Température OK.");
    }

    delay(2000); // Pause de 2 secondes avant la prochaine mesure
}

Attention, il faudrait formater la date et l'heure. Lorsque l'on ouvre le fichier txt, on observe qu'elles sont incorrectes. De plus, lorsque l'on regarde les temps auxquels les mesures sont prises, il y a des intervalles de 3s et non de 2s comme d'après notre code. Probablement que les mesures enregistrées dans le fichier TXT sur la carte SD ne correspondent pas à celles enregistrées dans le fichier CSV sur l'ordinateur.

J'ai modifié le code en Python afin d'enregistrer les données sur un fichier CSV.

#enregistrer les données sur un fichier csv 
import serial
import threading
from datetime import datetime
import os

# Configuration du port série
PORT = 'COM9'  # Remplacez par votre port série
BAUD_RATE = 115200  # Taux de transmission utilisé par le M5Core2
os.chdir('chemin oĂą vous voulez enregistrer votre fichier')
DATA_CSV = "temperatures v2 CS.csv"  # Nom du fichier pour enregistrer les données

def enregistrer_donnees(fichier, temperature):
    """
    Enregistre une température avec un horodatage dans un fichier avec extension `.csv`.
    """
    try:
        with open(fichier, mode='a') as file:
            # Récupère l'horodatage actuel
            horodatage = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            # Écrit les données sous forme de texte
            file.write(f"{horodatage} - Température : {temperature} °C\n")
            
    except Exception as e:
        print(f"Erreur lors de l'écriture dans le fichier : {e}")

def lire_temperature(ser):
    """
    Lit les données reçues sur le port série et les affiche tout en les enregistrant.
    """
    while True:
        try:
            # Vérifie s'il y a des données disponibles sur le port série
            if ser.in_waiting > 0:
                # Lit une ligne complète et supprime les espaces inutiles
                data = ser.readline().decode('utf-8').strip()
                print(f"[M5Core2] {data}")  # Affiche les données reçues
                # Enregistre les données dans le fichier .usd
                enregistrer_donnees(DATA_CSV, data)
        except Exception as e:
            print(f"Erreur lors de la lecture du port série : {e}")
            break

def envoyer_seuil(ser):
    """
    Permet à l'utilisateur d'envoyer un seuil de température via le terminal.
    """
    while True:
        try:
            user_input = input("Entrez un seuil de température (ou 'exit' pour quitter) : ")
            if user_input.lower() == 'exit':
                print("Fin de l'envoi de commandes.")
                break
            ser.write((user_input + '\n').encode('utf-8'))
        except Exception as e:
            print(f"Erreur lors de l'envoi du seuil : {e}")
            break

def main():
    """
    Programme principal qui gère la connexion série, la lecture et l'enregistrement des données.
    """
    try:
        # Ouvre la connexion série
        ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
        print("Connexion au M5Core2 établie.")
        print(f"Les données seront enregistrées dans le fichier : {DATA_CSV}\n")

        # Lance un thread pour lire les températures
        thread_lecture = threading.Thread(target=lire_temperature, args=(ser,))
        thread_lecture.daemon = True
        thread_lecture.start()

        # Permet d'envoyer des seuils de température
        envoyer_seuil(ser)

    except serial.SerialException as e:
        print(f"Erreur de connexion série : {e}")
    except Exception as e:
        print(f"Erreur : {e}")
    finally:
        # Ferme le port série proprement
        if 'ser' in locals() and ser.is_open:
            ser.close()
            print("Connexion série fermée.")

# Point d'entrée du script
if __name__ == "__main__":
    main()
31/01/2025- 5/02/2025

J'ai modifiĂ© les scripts prĂ©cĂ©dents de sorte fixer un intervalle de mesure toutes les 2 secondes. En remplaçant la fonction delay (cette fonction arrĂŞt le script pendant un moment et le temps qui reprenne, on aurait une frĂ©quence variable de mesure de tempĂ©rature), j'ai assignĂ© un intervalle de 2s par const unsigned long. Pour pallier les soucis d'horodatage, je vais numĂ©roter chaque prise de mesure.

Voici le code C++:

#include <M5Core2.h>         // Bibliothèque pour utiliser le M5Core2
#include <OneWire.h>         // Pour la communication avec le capteur de température
#include <DallasTemperature.h>  // Pour gérer le capteur DS18B20
#include <SD.h>              // Pour l'utilisation de la carte SD
#include <SPI.h>             // Interface SPI pour la communication avec la SD

// Définition des broches
#define ONE_WIRE_BUS 27   // Broche de connexion du capteur DS18B20
#define SD_CS_PIN 4       // Broche de sélection de la carte SD

// Initialisation du capteur de température
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

// Seuil de température par défaut (modifiable via le terminal série)
float seuil = 30.0;

// Fichier pour l'enregistrement des données sur la carte SD
File dataFile;

// Variables pour la gestion du timer
unsigned long previousMillis = 0;  // Stocke le temps de la dernière mesure
const unsigned long interval = 2000; // Intervalle entre chaque mesure (2 secondes)

// Gestion du nombre de mesures enregistrées
int mesureCount = 0;  // Compteur de mesures

void setup() {
    M5.begin();  // Initialisation du M5Core2
    M5.Axp.SetSpkEnable(true); // Active le haut-parleur du M5Core2

    Serial.begin(115200);  // Démarre la communication série
    Serial.println("Démarrage du programme...");
    
    // Ajout d'un délai de 5 secondes avant de commencer la prise de mesures
    Serial.println("Attente de 5 secondes avant la prise de mesures...");
    delay(5000);

    Serial.println("Programme démarré. Tapez un nouveau seuil ou 'exit' pour quitter.");
    Serial.print("Seuil actuel : ");
    Serial.println(seuil);

    // Vérifie si la carte SD est bien insérée et fonctionne
    if (!SD.begin(SD_CS_PIN)) {
        Serial.println("Échec de montage de la carte SD !");
        M5.Lcd.println("Carte SD absente !");
        
    }
    Serial.println("Carte SD détectée avec succès.");

    // Initialisation du capteur de température
    sensors.begin();
    int capteurCount = sensors.getDS18Count();  // Récupère le nombre de capteurs détectés
    Serial.print("Capteurs détectés : ");
    Serial.println(capteurCount);
    M5.Lcd.println(capteurCount);

    // Vérification de l'ouverture du fichier SD pour stocker les mesures
    if (capteurCount > 0) {
        dataFile = SD.open("/temp_data.txt", FILE_APPEND);
        if (!dataFile) {
            Serial.println("Erreur lors de l'ouverture du fichier !");
        }
    }

    // Affichage initial sur l'écran du M5Core2
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.print("Température : ");
}

void loop() {
    unsigned long currentMillis = millis();  // Récupère le temps actuel

    // Vérifie si l'intervalle de 2 secondes est écoulé
    if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;  // Mise Ă  jour du timer

        // Vérifie si le fichier SD est ouvert avant d'écrire dedans
        if (dataFile) {
          // Récupération de la date et de l'heure actuelle
          RTC_DateTypeDef DateStruct;
          RTC_TimeTypeDef TimeStruct;
          M5.Rtc.GetDate(&DateStruct);
          M5.Rtc.GetTime(&TimeStruct);
          // Récupération des millisecondes actuelles
          unsigned long currentMillis = millis() % 1000;  
          // Construction de la chaîne de date et heure
          String date = String(DateStruct.Date) + "/" + 
              String(DateStruct.Month) + "/" + 
              String(2000 + DateStruct.Year) + " " + 
              String(TimeStruct.Hours) + ":" + 
              String(TimeStruct.Minutes) + ":" + 
              String(TimeStruct.Seconds) + ":" + 
              String(currentMillis);
          // Affichage de la date et heure complète
          Serial.println(date);
            // Demande une mesure de température au capteur
            sensors.requestTemperatures();
            float temperatureC = sensors.getTempCByIndex(0);

            // Vérifie si la mesure est valide (-127°C signifie capteur non détecté)
            if (temperatureC == -127.00) {
                Serial.println("Erreur : Capteur de température non détecté !");
                M5.Lcd.setCursor(0, 30);
                M5.Lcd.fillRect(0, 30, 240, 20, BLACK);
                M5.Lcd.println("Erreur Capteur !");
            } else {
                mesureCount++;  // Incrémente le compteur de mesures

                // Enregistre la mesure sur la carte SD
                dataFile.print("Mesure ");
                dataFile.print(mesureCount);
                dataFile.print(" - ");
                dataFile.print(date);
                dataFile.print(" - Température : ");
                dataFile.print(temperatureC);
                dataFile.println(" °C");
                dataFile.flush();  // Sauvegarde immédiate sur la carte SD

                // Affichage dans le terminal série
                Serial.print("Mesure ");
                Serial.print(mesureCount);
                Serial.print(" - Température : ");
                Serial.print(temperatureC);
                Serial.println(" °C");

                // Mise à jour de l'affichage sur l'écran M5Core2
                M5.Lcd.setCursor(0, 30);
                M5.Lcd.fillRect(0, 30, 240, 20, BLACK);
                M5.Lcd.print("Mesure ");
                M5.Lcd.print(mesureCount);
                M5.Lcd.print(": ");
                M5.Lcd.print(temperatureC);
                M5.Lcd.println(" °C");

                // GESTION DE L'ALERTE 
                if (temperatureC > seuil) {
                    Serial.println("ALERTE : Température élevée !");
                    
                    // Affiche un message en rouge sur l'écran du M5Core2
                    M5.Lcd.setTextColor(RED);
                    M5.Lcd.setCursor(0, 100);
                    M5.Lcd.fillRect(0, 100, 240, 20, BLACK);
                    M5.Lcd.println("Alerte Température !");

                    // Active la vibration du M5Core2 pour signaler l'alerte
                    M5.Axp.SetVibration(true);
                    delay(500);
                    M5.Axp.SetVibration(false);
                }
            }
        }
    }

    // Vérifie si l'utilisateur envoie une commande via le terminal série
    if (Serial.available() > 0) {
        String input = Serial.readStringUntil('\n');  // Lit la commande
        input.trim();  // Supprime les espaces inutiles

        // Si l'utilisateur tape "exit", on ferme le fichier et arrĂŞte le programme
        if (input.equalsIgnoreCase("exit")) {
            Serial.println("Fermeture du fichier et arrĂŞt du programme...");
            dataFile.close();
        } else {
            // Sinon, on essaie de modifier le seuil de température
            float newSeuil = input.toFloat();
            if (newSeuil != 0 || input.equals("0")) {
                seuil = newSeuil;
                Serial.print("Nouveau seuil défini : ");
                Serial.println(seuil);
            } else {
                Serial.println("Entrée invalide. Veuillez entrer un nombre valide.");
            }
        }
    }
}

Voici le code Python:

import serial
import threading
from datetime import datetime
import os

# Configuration du port série pour la connexion avec le M5Core2
PORT = 'COM9'  # Remplacez par le bon port COM
BAUD_RATE = 115200  # Taux de transmission défini sur le M5Core2

# Définition du chemin du fichier CSV où les données seront enregistrées
os.chdir('C:\\Users\\fablabuser\\Documents\\Python CS\\Logiciel TP')
DATA_CSV = "temperatures v2-5 CS.csv"  # Nom du fichier CSV

# Variable de contrôle pour gérer l'exécution des threads
mesures_actives = True

def enregistrer_donnees(fichier, temperature):
    """
    Enregistre une température avec un horodatage dans un fichier CSV.

    :param fichier: Nom du fichier CSV où enregistrer les données.
    :param temperature: Donnée de température à enregistrer.
    """
    try:
        with open(fichier, mode='a') as file:
            horodatage = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
            file.write(f"{horodatage} - data: {temperature} °C\n")
    except Exception as e:
        print(f"Erreur lors de l'écriture dans le fichier : {e}")

def lire_temperature(ser):
    """
    Lit les données reçues sur le port série, les affiche et les enregistre.

    :param ser: Objet Serial pour la communication avec le M5Core2.
    """
    global mesures_actives
    while mesures_actives:
        try:
            if ser.in_waiting > 0:
                data = ser.readline().decode('utf-8').strip()
                print(f"[M5Core2] {data}")  
                enregistrer_donnees(DATA_CSV, data)
        except Exception as e:
            print(f"Erreur lors de la lecture du port série : {e}")
            break

def envoyer_seuil(ser):
    """
    Permet à l'utilisateur d'envoyer un seuil de température au M5Core2 ou d'arrêter les mesures.

    :param ser: Objet Serial pour la communication avec le M5Core2.
    """
    global mesures_actives
    while mesures_actives:
        try:
            user_input = input("Entrez un seuil de température (ou 'exit' pour arrêter) : ")
            
            if user_input.lower() == 'exit':
                print("ArrĂŞt des mesures en cours...")

                # Envoi de la commande "stop" au M5Core2 pour arrĂŞter l'enregistrement sur la carte SD
                ser.write(b"stop\n")
                
                # Arrêt des mesures côté PC
                mesures_actives = False
                break

            # Envoi du seuil au M5Core2
            ser.write((user_input + '\n').encode('utf-8'))
        
        except Exception as e:
            print(f"Erreur lors de l'envoi du seuil : {e}")
            break

def main():
    """
    Programme principal qui gère la connexion série, la lecture et l'enregistrement des données.
    """
    global mesures_actives
    try:
        # Ouverture de la connexion série avec le M5Core2
        ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
        print("Connexion au M5Core2 établie.")
        print(f"Les données seront enregistrées dans : {DATA_CSV}\n")

        # Lancement du thread pour la lecture des températures
        thread_lecture = threading.Thread(target=lire_temperature, args=(ser,))
        thread_lecture.daemon = True
        thread_lecture.start()

        # Gestion de l'envoi des seuils et de l'arrĂŞt
        envoyer_seuil(ser)

    except serial.SerialException as e:
        print(f"Erreur de connexion série : {e}")
    except Exception as e:
        print(f"Erreur : {e}")
    finally:
        # ArrĂŞt du thread proprement
        mesures_actives = False
        
        # Fermeture propre du port série
        if 'ser' in locals() and ser.is_open:
            ser.close()
            print("Connexion série fermée.")

# Exécution du script principal
if __name__ == "__main__":
    main()

Une chose Ă  noter est que l'on peut arrĂŞter l'enregistrement des donnĂ©es sur le fichier CSV mais pas sur la carte micro SD et les mesures continuent Ă  se poursuivre d'après l'affichage sur l'Ă©cran du M5Core2. Il serait intĂ©ressant d'encore modifier les codes de telle sorte. 

10/02/2025- ?/02/2025

Une autre Ă©tape est de pouvoir dĂ©clarer les ports sur lesquels seront branchĂ©s les diffĂ©rents capteurs et les diffĂ©rentes librairies Ă  exploiter en fonction des capteurs utilisĂ©s. 

Une autre étape encore serait de faire un graphique en utilisant les données à partir d'un script.