Documentation

I. Structure du Code JavaScript

La bibliothèque JavaScript web_sciences permet de configurer une interface interactive pour des expérimentations en physique-chimie et SVT. En définissant quelques variables, vous pouvez personnaliser les mesures et les graphiques.

Le code JavaScript, doit être écrit sous la forme d'une chaîne de caractères. En fonction du type d'utilisation choisie, ce code sera inséré automatiquement dans une cellule Jupyter ou dans un fichier HTML.

1. Mode de fonctionnement (mode)

  • Définit la manière dont les données sont envoyées par la carte Arduino.
  • Valeurs possibles :
    • "point" : Arduino envoie une seule mesure sur demande.
    • "temporel" : Arduino envoie un flux continu de mesures.
    • "commande" : Arduino exécute des actions spécifiques basées sur l'interaction utilisateur.
mode = "temporel";

2. Commandes à envoyer (commandes)

  • Liste d'objets définissant les boutons de commande.
  • Chaque objet contient :
    • texte_bouton : texte affiché sur le bouton.
    • arduino : commande envoyée à la carte Arduino.
commandes = [{texte_bouton: "Charge", arduino: "charge"}, {texte_bouton: "Décharge", arduino: "decharge"}];

3. Séries de données (series)

  • Définit les séries de grandeurs mesurées par la carte Arduino, leur nom et leur unité.
  • Chaque objet contient :
    • grandeur : nom de la grandeur mesurée.
    • unite : unité correspondante.
  • Remarque : En mode temporel, la première série renvoyée par la carte est toujours le temps. Il n'est pas nécessaire de la préciser dans la variable series.
series = [{grandeur: "Uc", unite: "V"}, {grandeur: "E", unite: "V"}];

4. Axes du graphique (axes)

  • Configure les axes pour le graphique.
  • Chaque axe est défini par :
    • nom : label de l'axe.
    • unite : unité correspondante.
  • axes = [{nom: "Temps", unite: "s"}, {nom: "Uc", unite: "V"}];
  • Remarques spécifiques par mode :
    • Mode temporel :
      • L'abscisse est toujours le temps.
      • Les grandeurs en ordonnées sont les séries définies dans la variable series.
    • Mode point :
      • L'abscisse correspond toujours à la grandeur saisie par l'utilisateur (ex. : d (cm)).
      • Les grandeurs en ordonnées sont celles définies dans series.
    • Mode commande : Aucun graphique n'est généré.

5. Autres variables

  • La variable tableur est un boolean qui formatte la Copie presse papier au format tableur. Elle est intialisée à false par defaut.
    tableur = true;
  • La variable stop_possible est un boolean qui ajoute un bouton permettant de stopper les mesures dans le cas de mesures temporelles longues. Elle est intialisée à false par defaut.
    stop_possible = true;
  • La variable precision est un entier qui permet de préciser le nombre de chiffres significatifs utilisés par le script. Elle est intialisée à 3 par defaut.
    precision = 5;
  • La variable car_par_ligne est un entier qui permet de modifier le nombre de caractères par lignes utilisés chiffres significatifs utilisés dans la Copie presse papier Elle est intialisée à 70 par defaut.
    car_par_ligne = 85;

6. Exemples Complets

Commande avec boutons

mode = "commande";
commandes = [{texte_bouton: "Rouge", arduino: "rouge"},
             {texte_bouton: "Vert", arduino: "vert"},
             {texte_bouton: "Stop", arduino: "stop"}];

Mesures temporelles avec bouton de début des mesures

mode = "temporel";
commandes = [{texte_bouton: "Démarrer les mesures", arduino: "start"}];
series = [{grandeur: "Température", unite: "°C"}];
axes = [{nom: "Temps", unite: "s"}, 
        {nom: "Température", unite: "°C"}];
titre_graphe = "Loi phénoménologique de Newton";

Mesures ponctuelles avec bouton de mesure

mode = "point";
commandes = [{texte_bouton:"Mesure", arduino:"mesure"}];
series = [{grandeur: "P", unite: "hPa"}];
titre_graphe = "Loi de Mariotte";
axes = [{grandeur: "V", unite: "mL"}, {grandeur: "P", unite: "hPa"}];

II. Intégrer le Code JavaScript

1. Si vous souhaitez utiliser votre interface Web Sciences dans un Notebook Jupyter

  1. Importer la bibliothèque : Téléchargez web_sciences.py et placez-le dans le même dossier que votre notebook Jupyter. Puis, importez-le :
    from web_sciences import WebSciences
  2. Définir le code JavaScript : Rédigez votre code JavaScript dans une variable Python :
    my_init = """
    var mode = "point";
    var commandes = [{texte_bouton: "Mesure", arduino: "vas_y"}];
    var series = [{grandeur: "T", unite: "°C"}];
    """
  3. Afficher l'interface : Créez une instance de WebSciences et appelez la méthode affiche :
    interface = WebSciences(my_init)
    interface.affiche()

2. Si vous souhaitez intégrer votre interface Web Sciences dans un Fichier HTML distribuable aux élèves

  1. Préparer les fichiers requis : Téléchargez :
    • insere_code.py
    • web_sciences_complet.html
    Placez-les dans le même dossier.
  2. Modifier le fichier Python : Ouvrez insere_code.py et ajoutez votre code JavaScript :
    my_init = """
    mode = "point";
    commandes = [{texte_bouton: "Mesure", arduino: "vas_y"}];
    series = [{grandeur: "T", unite: "°C"}];
    """
  3. Spécifier le fichier de sortie : Indiquez le nom du fichier HTML dans le script Python :
    nom_fichier_html = "interface_arduino.html"
  4. Exécuter le script : Lancez le programme Python pour générer le fichier HTML :
    python insere_code.py
  5. Tester le fichier HTML : Ouvrez le fichier généré dans un navigateur. Il fournit une interface interactive prête à l’emploi.

Remarque :

Pour une utilisation avec un tableur, ajoutez la ligne suivante dans votre code JavaScript :

var tableur = true;

III. Codage des Programmes Arduino

Cette section explique comment coder les programmes Arduino nécessaires pour les trois modes de fonctionnement commande, point, et temporel. Chaque mode est présenté avec un exemple de code et des explications détaillées.

1. Mode commande

Le mode commande permet à l'Arduino de réagir à des commandes reçues via la liaison série. Il exécute des actions spécifiques et renvoie une réponse à chaque commande. Voici un exemple :

/* déclaration des constantes */
const int ledPin = 13; // Exemple : une LED connectée à la broche 13

void setup() {
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT); // Configure la broche comme sortie
    delay(1);
}

void loop() {
    if (Serial.available()) {
        String chaine = Serial.readString(); // Lecture de la commande
        if (chaine == "allume") {
            digitalWrite(ledPin, HIGH); // Allume la LED
            Serial.println("LED allumée");
        } else if (chaine == "eteint") {
            digitalWrite(ledPin, LOW); // Éteint la LED
            Serial.println("LED éteinte");
        } else {
            Serial.println("commande non reconnue"); // Réponse par défaut
        }
    }
}

Dans cet exemple :

  • Une LED est connectée à la broche 13.
  • Les commandes allume et eteint contrôlent l'état de la LED.
  • Toute commande non reconnue génère un message d'erreur.

2. Mode point

Le mode point réalise une mesure unique sur demande et renvoie la valeur mesurée. Par exemple :

const int capteurPin = A0; // Capteur connecté à la broche analogique A0

void setup() {
    Serial.begin(9600);
    delay(1);
}

void loop() {
    if (Serial.available()) {
        String chaine = Serial.readString();
        if (chaine == "mesure") {
            int valeurBrute = analogRead(capteurPin); // Lecture de la valeur
            float tension = valeurBrute * (5.0 / 1023.0); // Conversion en volts
            Serial.println(tension); // Envoie la mesure
        }
        else {
            Serial.println("commande non reconnue");
        }
    }
}

Ce code lit la valeur d'un capteur analogique sur la broche A0, la convertit en tension, et envoie la mesure via la liaison série.

3. Mode temporel

Le programme réalise des mesures périodiques à un intervalle fixe. Il transmet en continu les résultats au PC sous la forme d'une série temporelle. Voici un exemple pour deux capteurs connectés sur A0 et A1 :

#define TEMPS 10000 // Durée totale des mesures en ms (10 secondes)
#define N_PTS 100   // Nombre total de points à mesurer
	
unsigned long t_total = TEMPS;
unsigned long dt = t_total / N_PTS; // Intervalle de temps entre deux mesures en ms
	
// Définition des constantes pour le capteur
const int capteurPin_0 = A0; // Capteur connecté à la broche analogique A0
const int capteurPin_1 = A1; // Capteur connecté à la broche analogique A1
	
void setup() {
    Serial.begin(9600);
    pinMode(capteurPin, INPUT);
    delay(1);
}
	
void mesures() {
    unsigned long temps_depart = millis();
    unsigned long t_mesure;

    while (millis() - temps_depart <= t_total) {
        t_mesure = (millis() - temps_depart); // Temps écoulé depuis le début des mesures
        int u0 = analogRead(capteurPin_0); // Lecture de la valeur brute du capteur 0
        float C_0 = u0 * ... // Conversion capteur_0
        int u1 = analogRead(capteurPin_1); // Lecture de la valeur brute du capteur 1
        float C_1 = u1 * ... // Conversion capteur_1
        // Envoi des séries
        Serial.println(String(t_mesure) + "," + String(C_0) + "," + String(C_1));
        // Délai personnalisé pour respecter le temps entre deux mesures
        while ((millis() - temps_depart) - t_mesure < dt) {}
    }
    // ne pas oublier d'envoyer le mot "end"
    Serial.println("end");
}
	
void loop() {
    if (Serial.available()) {
        String chaine = Serial.readString();
        if (chaine == "mesures") {
            mesures(); 	
        } else {
            String message = "commande non reconnue";
            Serial.println(message);
        }
    }
}

Explications :

Macros définies :

  • #define TEMPS : Durée totale des mesures (modifiable selon les besoins).
  • #define N_PTS : Nombre de points à mesurer.

Initialisation :

  • dt = t_total / N_PTS : Calcule l'intervalle de temps entre deux mesures.

Fonction mesures :

  • Envoie les données sous forme de séries temporelles (temps écoulé + valeur mesurée).
  • Garantit un espacement constant entre les mesures avec une boucle d'attente contrôlée.

Boucle principale (loop) :

  • Attente d'une commande sur la liaison série.
  • La commande"mesures" lancent la série de mesures.
  • Toute commande non reconnue retourne un message d'erreur.

Cette structure rend le programme flexible pour différents types de mesures et optimise la précision des mesures temporelles.

4. Un cas particuliers : le mode temporel rapide

Le programme ci-dessous effectue des mesures rapides de tensions aux bornes d'un circuit RC et aux bornes du condensateur. La constante de temps du circuit RC est faible et la durée d'acquisition est courte (600ms). De façon à être rapide, le programme doit stocker les séries pendant les mesures pour les envoyer en bloc par la liaison série à la fin des mesures.

#define TEMPS 650
#define N_PTS 75
		
const int pin_led_condo = 3;
const int nb = N_PTS;
unsigned long t_total = TEMPS;
unsigned long temps[nb];
int mesures[nb][2];
 
void setup() {
    for (int i=0; i<nb; i++)
        temps[i] = -1;
    t_total = t_total * 1000;
    pinMode(pin_led_condo, OUTPUT);
    digitalWrite(pin_led_condo, LOW);
    Serial.begin(9600);
    delay(1);
}
		
void get_mesures(int command) {
    int n = 0;
    unsigned long dt = t_total / (N_PTS-1); 
    unsigned long temps_depart = micros();
    unsigned long t = temps_depart;
    if (command == 1) {
        digitalWrite(pin_led_condo, HIGH); 
    }
    else {
        digitalWrite(pin_led_condo, LOW); 
    }
    while (t - temps_depart <= t_total) {
        temps[n] = (micros() - temps_depart); // t en µs
        mesures[n][0] = analogRead(A0);
        mesures[n][1] = analogRead(A1);
        //un delai personnalisé pour optimiser la rapidité
        while ((micros() - temps_depart) - long(n*dt) <= dt) {}
        n = n + 1;
        t = micros();
    }
}

void envoie_mesures() {
    int n = 0;
    for(int i=0 ; i<nb; i++) {
        if (temps[i] != -1) {
            // le temps est convertit en ms pour compatibilité avec le programe de charge lente condo.ino
            Serial.println(String(float(temps[i])/1000) + "," + String(mesures[i][0]*5.0/1023) + "," + String(mesures[i][1]*5.0/1023));
            n = n+1;
    	}
    	delay(1);
    }
}	

void loop() {
    if (Serial.available()) {
        String chaine = Serial.readString();
        if (chaine=="charge") {
            get_mesures(1);
            envoie_mesures();
            Serial.println("end");    
        }
        else if (chaine=="decharge") {
            get_mesures(0);
            envoie_mesures();
            Serial.println("end");
        }
        else {
            Serial.println("commande non reconnue");
        }
    }   
}

Explication détaillée du programme

Ce programme permet de mesurer rapidement les tensions aux bornes d’un circuit RC (résistance et condensateur) ainsi qu'aux bornes du condensateur, avec une acquisition rapide et une transmission des données en bloc une fois l'acquisition terminée.

a. Déclaration des constantes et des variables globales

  • Macros :
    • #define TEMPS 650 : La durée totale des mesures est fixée à 650 ms.
    • #define N_PTS 75 : Le nombre de points à mesurer est fixé à 75.
  • Broches utilisées :
    • pin_led_condo : Applique la tension aux bornes du circuit. Permet de réaliser les différentes phases de charge et de décharge.
    • A0 : Entrée analogique pour mesurer la tension aux bornes du générateur.
    • A1 : Entrée analogique pour mesurer la tension aux bornes du condensateur.
  • Tableaux :
    • temps[nb] : Stocke les instants (en microsecondes) où les mesures sont effectuées.
    • mesures[nb][2] : Stocke les valeurs analogiques des tensions mesurées (colonne 0 pour A0 et colonne 1 pour A1).

b. Fonction setup()

  • Initialise les tableaux temps à -1.
  • Convertit t_total en microsecondes pour une meilleure précision.
  • Initialise la communication série avec une vitesse de transmission de 9600 bauds.

3. Fonction get_mesures()

  • Objectif : Effectuer les mesures à intervalle régulier et les stocker.
  • Paramètre :
    • command : Détermine si la mesure correspond à une charge (1) ou une décharge (0).
  • Déroulement :
    • Calcule l'intervalle entre deux mesures : dt = t_total / (N_PTS - 1) (en microsecondes).
    • Effectue les mesures dans une boucle :
      • Stocke l'instant courant dans temps[n].
      • Mesure les tensions analogiques et les enregistre dans mesures[n][0] et mesures[n][1].
      • Utilise une boucle d'attente pour garantir un espacement constant entre les mesures.

4. Fonction envoie_mesures()

  • Objectif : Envoyer les données mesurées en bloc via la liaison série.
  • Déroulement :
    • Parcourt les tableaux temps et mesures.
    • Convertit les temps en millisecondes pour compatibilité avec d'autres programmes.
    • Convertit les mesures analogiques en tensions réelles (valeurs entre 0 et 5 V).
    • Envoie chaque ligne au format CSV : <temps(ms)>,<tension_A0(V)>,<tension_A1(V)>.

5. Fonction loop()

  • Objectif : Réagir aux commandes reçues via la liaison série.
  • Déroulement :
    • Attente d'une commande (charge ou decharge).
    • Appelle get_mesures() en fonction de la commande reçue puis et envoie_mesures().
    • Envoie un message "end" à la fin des mesures.
    • Gère les commandes inconnues avec un message d'erreur.

Optimisations pour la rapidité

  • Stockage local des données pendant la mesure : Les mesures sont stockées dans les tableaux et envoyées après l'acquisition, ce qui minimise le temps d'attente.
  • Conversion des temps en millisecondes : Facilite la compatibilité avec d'autres programmes tout en conservant la précision.
  • Attente optimisée entre les mesures : La boucle d'attente assure une précision temporelle en microsecondes.

Points forts de ce programme

  • Rapidité : L'utilisation de micros() pour mesurer le temps permet une grande précision temporelle.
  • Flexibilité : Gère deux phases (charge et décharge) et peut facilement être adapté pour d'autres configurations.
  • Transmission efficace : Les données sont transmises en bloc, évitant les interruptions pendant l'acquisition.

Ce script est parfaitement adapté pour des situations nécessitant des mesures rapides et peut-être modifié facilement.