======= Boîtier Interactif : Apprendre la profondeur dans un tableau ======= * **Porteur(s) du projet** : Lise Irmann (DNMADE DG2), Damien MUTI DESGROUAS (Prof. de Numérique) * **Date** : 03/2021 * **Contexte** : projet "Mallette pédagogique" au Musée des Beaux Arts - 2021 * **Fichiers** : {{ :wiki:flossmanuals:boitier-interactif-01:liseirrmann_dispositifs.pdf |}} * **Lien** : * **Capteurs/Actionneurs** : * 1 Capteur de distance Ultrasonic Grove (mettre le lien...) * 2 Potentiomètres Grove * 2 Sliders (potentiomètres) Grove * 1 bouton poussoir Grove * 1 module LED Grove * 1 LED ---- ====== Intentions : explication du projet et objectifs ====== ==== Le Contexte ==== Commande de malette pédagogique pour les enfants de 5-6 ans de la part du musée de Beaux Arts de Marseille. Chaque élève propose des dispositifs qui pourraient s’insérer dans la malette, en se basant sur un ou plusieurs des tableaux proposés par le musée. ==== Les Objectifs ==== Permettre aux enfants d’appréhender la profondeur, la perspective, qui sont des concepts difficiles pour leur âge. Qu’est ce qui est grand? Petit? Loin? Près? Apprendre aux enfants à se repérer dans l’espace. ==== Le Dispositif ==== Une tablette / un ordinateur tactile affiche le tableau d’Émile LOUBON. Elle est reliée à un boitier Arduino, avec des sliders, des potentiomètres et un capteur de distance. Ceux-ci permettent à l’utilisateur de déplacer un personnage dans le tableau. Il peut le faire de deux différentes manières : - de haut en bas : le personnage change de taille et de saturation - de droite à gauche : le personnage ne change pas Ainsi, le médiateur peut se servir du boitier pour démontrer la perspective aux enfants. Ensuite, les enfants volontaires peuvent passer à tour de rôle pour s’en servir pendant que leurs camarades se servent du kit tampographique. ====== Plans et schémas de fonctionnement ====== {{ :wiki:flossmanuals:boitier-interactif-01:dispositifs_dn3.2.jpg?nolink |}} ====== Programmes ====== ===== Arduino ===== Le montage Arduino et le programme associé a pour but de mesurer les valeurs de plusieurs potentiomètres et sliders, d'un bouton et d'un capteur de distance à ultrason et de les envoyer au programme Processing pour traitement. Le code est le suivant : {{ :wiki:flossmanuals:boitier-interactif-01:boitier_interactif_arduino.zip |}} #include "Ultrasonic.h" Ultrasonic ultrasonic(7); // capteur de distance int Slider1 = 0; // Slider 1 : xPerso byte Slider1Pin = A0; byte Slider2Pin = A1; // Slider 2 : yPerso int Potentiometre1 = 0; // Potentiomètre 1 : ChoixPerso byte Potentiometre1Pin = A2; int Potentiometre2 = 0; // Potentiomètre 2 : CouleurPerso byte Potentiometre2Pin = A3; int ChoixMesureDistance = 0; // Bouton poussoir - 0 : mesure de distance par slider 2 (yPerso). 1 : Mesure de distance par le capteur de distance byte boutonPoussoirPin = 2; // broche de lecture du bouton poussoir int Distance = 0; // distance lue sur le capteur de distance // Attention MeasureInCentimeters() renvoie un type "long" int inByte = 0; // incoming serial byte byte ledPin = 8; // broche de commande de la LED byte etat_bouton = 0; //La variable « etat_bouton » mémorise l’état HIGH ou LOW de la pate d’entrée byte old_etat_bouton = 0; //La variable « old_etat_bouton » mémorise l’ancien état de la variable « etat_bouton » byte etat_led = 0; //La variable « etat_led » mémorise l’état 1 (allumée) ou 0 (éteinte) de la led. boolean debug = false; boolean debug_Com_Serial = false; void setup() { // start serial port at 9600 bps: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } pinMode(boutonPoussoirPin, INPUT); // digital sensor is on digital pin D2 pinMode(ledPin, OUTPUT); // LED sur D7 if (!debug) { establishContact(); // send a byte to establish contact until receiver responds } } void loop() { gestionBouton(); // Si une donnée arrive dans le port série venant de processing = LIre les nouvelles valeurs des différents capteurs if (!debug) { // mode normal if (Serial.available() > 0) { // lecture de la donnée sur le port série : lettre A inByte = Serial.read(); if (inByte == 'A') { // vérification du caractère envoyé par Processing // mesure sur les différents capteurs gestionCapteurs(); // Envoi des valeurs lues sur les différents capteurs vers Processing envoiMesureVersProcessing(); if (debug_Com_Serial) { ///// Debug envoiMesureSurPortSerial(); } } } } else { // mode debug // mesure sur les différents capteurs gestionCapteurs(); // Envoi des valeurs lues sur les différents capteurs envoiMesureSurPortSerial(); delay(500); } } void establishContact() { while (Serial.available() <= 0) { Serial.print('A'); // send a capital A delay(300); } } void gestionBouton() { // mesure de l'état du bouton etat_bouton = digitalRead(boutonPoussoirPin); // lit et mémorise l’état en entrée de la pate 2 if ((etat_bouton == LOW) && (old_etat_bouton == HIGH)) { // si l’entrée 2 est à l’état LOW (bouton appuyé) et que juste précédemment le bouton est ouvert etat_led = 1 - etat_led ; // inverse l’état de la led delay(10); // patienter 10ms pour éviter les rebonds avant d’allumer la led } old_etat_bouton = etat_bouton; // sauvegarde la nouvelle valeur ChoixMesureDistance = etat_led ; // choix du capteur de distance Ultrason ou du Slider if (etat_led == 1) {//si la led doit être allumée digitalWrite(ledPin, HIGH); // allumer la LED } else { // si la led est éteinte digitalWrite(ledPin, LOW); // éteindre la LED } } void gestionCapteurs() { // lecture des données sur les différents capteurs. On divise par 4 pour ramener la valeur entre 0 et 255 Potentiometre1 = map(analogRead(Potentiometre1Pin), 0, 1023, 0, 3.5); //ChoixPerso : 0, 1, 2 (3 personnages). Attention : on a mis 3.5 car sinon, le déclenchement du 2ième personage s'effectuer trop tardivement // delay 10ms to let the ADC recover: delay(10); Potentiometre2 = analogRead(Potentiometre2Pin) / 4; //CouleurPerso // delay 10ms to let the ADC recover: delay(10); Slider1 = analogRead(Slider1Pin) / 4; // xPerso // delay 10ms to let the ADC recover: delay(10); // lecture de la distance avec le capteur ultrason ou le Slider 2 if (ChoixMesureDistance == 1) { // Capteur de distance ultrason Distance = map(ultrasonic.MeasureInCentimeters(), 0, 400, 0, 255); // mesure de la distanceentre 0 et 400cm et ramener cette valeur entre 0 et 255 } else { Distance = analogRead(Slider2Pin) / 4; //yPerso lu sur Slider 2 } } void envoiMesureVersProcessing() { Serial.write(Potentiometre1); //ChoixPerso Serial.write(Potentiometre2); //CouleurPerso Serial.write(Slider1); // xPerso Serial.write(Distance);//distance - y Perso } void envoiMesureSurPortSerial() { Serial.println(""); Serial.print("etat_led = "); Serial.print(etat_led); //etat_led Serial.print(" ChoixPerso = "); Serial.print(Potentiometre1); //ChoixPerso Serial.print(" CouleurPerso = "); Serial.print(Potentiometre2); //CouleurPerso Serial.print(" xPerso = "); Serial.print(Slider1); // xPerso Serial.print(" yPerso = "); Serial.print(Distance);//distance - yPerso Serial.println(""); } ===== Processing ===== ==== Programme initial ==== Le programme Processing a pour but de récupérer les différentes données envoyées par la carte Arduino. Chaque donnée constitue un paramètre d'affichage permettant de sélectionner un personnage, modifier sa position et sa taille dans une image. Le rendu est le suivant : {{ :wiki:flossmanuals:boitier-interactif-01:interface.png?800 |}} Le programme est le suivant : {{ :wiki:flossmanuals:boitier-interactif-01:boitier_interactifprocessing_2.zip |}} import processing.serial.*; int bgcolor; // Background color int fgcolor=255; // Fill color Serial myPort; // The serial port int NbData = 4; // nombre de données à récupérer de la carte Arduino int[] serialInArray = new int[NbData]; // Where we'll put what we receive int serialCount = 0; // A count of how many bytes we receive int ChoixPerso, CouleurPerso, ChoixMesureDistance, Distance, SaturPerso; // Starting position of the ball boolean firstContact = false; // Whether we've heard from the microcontroller float xPerso, yPerso, sPerso; PImage loubon, bouvier,img; color c =color(#192A76); int r = 25, g = 42, b=118; PImage imgMask; void setup() { size(1000,570); // Stage size noStroke(); // No border on the next thing drawn // Initialisation des variables ChoixPerso=0; // Potentiomètre 1 CouleurPerso = 0; //Potentiomètre 2 xPerso = 0.76*width; // Slider 1 yPerso = 0.84*height; // Slider 2 - Distance sPerso = 2000; //Taille du personnage SaturPerso = 255; bouvier = loadImage("Images/test2/BouvierCarre.png"); //Image du personnage loubon= loadImage("Images/Loubon_-_Vue_de_Marseille.jpg"); // Image du fond img = createImage(bouvier.width, bouvier.width, ARGB); image(loubon,0,0); colorMode(HSB); // Print a list of the serial ports, for debugging purposes: printArray(Serial.list()); imgMask = loadImage("Images/mask.png"); imgMask = createImage(bouvier.width, bouvier.width, ARGB); //création d'un masque pour effacer les anciennes images // I know that the first port in the serial list on my mac // is always my FTDI adaptor, so I open Serial.list()[0]. // On Windows machines, this generally opens COM1. // Open whatever port is the one you're using. String portName = Serial.list()[0]; myPort = new Serial(this, portName, 9600); } void draw() { /////////////////////////////////////// r@Lise : ajouter le graphisme... imageMode(CENTER); image(loubon,width/2,height/2); if (ChoixPerso == 0) { //Changer l'image selon la position du potentiomètre bouvier = loadImage("Images/test2/BouvierCarre.png"); img.mask(imgMask); //Masque l'image précédente lorsqu'on change d'image } if (ChoixPerso == 1){ bouvier = loadImage("Images/test2/ArbreCarre.png"); img.mask(imgMask); } if (ChoixPerso == 2){ bouvier = loadImage("Images/test2/MoulinCarre.png"); img.mask(imgMask); } for (int i = 0; i < bouvier.pixels.length; i++) { int r_i = (bouvier.pixels[i] >> 16) & 0xFF; // Faster way of getting red(argb) int g_i = (bouvier.pixels[i] >> 8) & 0xFF; // Faster way of getting green(argb) int b_i= bouvier.pixels[i] & 0xFF; // Faster way of getting blue(argb) if (r_i == r && g_i == g && b_i == b) { float h = CouleurPerso; //float h = 128; float s = SaturPerso; float b =brightness(bouvier.pixels[i]); //println("h =" + h + " s=" + s + " b=" +b); img.pixels[i] = color(h, s, b); } img.updatePixels(); } image(bouvier, xPerso, yPerso, sPerso, sPerso); //permet de garder les parties blanches de l'image image(img, xPerso, yPerso, sPerso, sPerso); } //for (int i = 0; i < img.pixels.length; i++) { // img.pixels[i] = color(0, 90, 102, i % img.width * 2); //} void serialEvent(Serial myPort) { // gestion des données envoyées par la carte // read a byte from the serial port: int inByte = myPort.read(); // if this is the first byte received, and it's an A, // clear the serial buffer and note that you've // had first contact from the microcontroller. // Otherwise, add the incoming byte to the array: if (firstContact == false) { if (inByte == 'A') { myPort.clear(); // clear the serial port buffer firstContact = true; // you've had first contact from the microcontroller myPort.write('A'); // ask for more } } else { // Add the latest byte from the serial port to array: serialInArray[serialCount] = inByte; serialCount++; // If we have NbData bytes: if (serialCount > NbData-1) { ChoixPerso=serialInArray[0]; CouleurPerso = serialInArray[1]; //rouge xPerso =0.2*width +2*serialInArray[2]; yPerso = 0.53*height + serialInArray[3]; sPerso = 12*serialInArray[3]; SaturPerso = serialInArray[3]; // print the values (for debugging purposes only): println(ChoixPerso + "\t" + CouleurPerso + "\t" + xPerso + "\t" + yPerso + "\t" + sPerso); // Send a capital A to request new sensor readings: Envoie d'une letter "A" pour signifier à la carte Arduino de renvoyer des données myPort.write('A'); // Reset serialCount: serialCount = 0; } } } ====== Réalisation de la maquette ====== vidéos, photos du making of...