Après avoir expliqué comment les systèmes éducatifs ont pu assimiler les techniques numériques inventées par des personnalités du MIT comme Seymour PAPERT et Mitchel RESNICK, sans pour autant chercher à s’en accommoder, il me semble qu’il est temps de remonter nos manches et (ré)explorer ces outils pour comprendre ce qu’ils avaient de potentiellement révolutionnaire (… ou pas). Il y a sans doute quelques bonnes raisons pour que ces outils ne soient pas déployés comme espéré. On peut également s’interroger sur le fait que PAPERT ou RESNICK aient pu se laisser emporter par une forme d’enthousiasme geek, sans pour autant apporter une solution réellement viable dans l’école.
Certains auteurs se sont déjà chargés d’identifier les limites des approches éducatives avec les micromondes (Linard, 2002). Il ne s’agit pas ici de refaire cette analyse, mais bel et bien de savoir si quelque chose mérite d’être préservé dans les approches proposées par le MIT il y a un demi-siècle. En effet, si les micromondes présentent vraisemblablement un certain nombre d’écueils, il faut aussi constater qu’il sont souvent mal utilisés. C’est par exemple le cas avec les logiciels de géométrie dynamique, dont les usages usuels dépassent difficilement le tracé de figures en contexte de classe. Autrement dit, il parait difficile de savoir si ce sont les micromondes qui ne sont pas féconds, ou si c’est leur mésusage qui les a stérilisés. Il parait prématuré de jeter le bébé avec l’eau du bain.
Retour en 1980
Comme d’habitude, je m’évertuerai à démontrer qu’avant de sauter sur la première innovation gratifiée d’une épithète pédante à base de novlangue (un petit retour au G.é.r.a.r.d. ne peut pas faire de mal), il suffit parfois de regarder chez ces pionniers. Ceux-ci n’avaient, au début des années 80, que des ordinateurs en 8 bit, et il n’était pas question de mettre des effets de sapins de Noël partout dans les logiciels pour faire illusion de modernitude ou d’innovationisme.
L’histoire de la tortue promobile T2 Jeulin est intéressante à plus d’un titre. Évidemment, elle est une copie des tortues du MIT, peut-être même un peu dépassée techniquement. La Yellow Turtle est conçue en 1969 et Irving, la première tortue sans fil radiocommandée, voit le jour en 1972. En 1981, la tortue T2 est équipée d’un cordon ombilical qui disparaitra avec la T3 au profit d’une commande infrarouge (un choix évidemment discutable pour investir tout l’espace). Les tortues Jeulin sont le fruit d’une collaborations entre l’INRP, la société Jeulin et le centre de formation des professeurs techniques (CFPT) de Cachan. Dans mes pérégrinations sur le web, j’ai été très heureux de trouver ce dossier d’étude de la tortue par des élèves professeurs (voir ici) de Cachan.
Mais là n’est pas le plus important, cette tortue, tout en étant « logo compatible » en utilisant les ordinateurs Thomson, est aussi conçue pour s’éloigner sensiblement du logo afin d’être introduite en maternelle. Plus précisément, elle reprend quelques idées du Logo, comme la commande de déplacement associée à une distance, mais sans passer par la saisie du texte. Elle utilise alors, un codage binaire selon le bon vieux principe des cartes perforées.

Contrairement au logo, les cartes perforées permettent de débuter la programmation avant de maitriser l’écriture.
Certes, on peut discuter de l’ergonomie de ces cartes, notamment la trop faible différence entre les cartes avance de 1cm, de 10 cm et de 20 cm pour un non-lecteur, mais elles permettent le codage par un geste plus simple que l’écriture, fût-elle au clavier.
Un petit détour par les archives de Publimaths nous fait découvrir quelques pépites, dont ce compte rendu d’activité de 1985 en grande section. Les activités de repérage et de tracé sont visiblement complexes, comme l’indique l’extrait ci-dessous.

Où acheter une tortue en 2023?
Il existe des tortues de sol, mais elles sont « relativement chères » (par exemple les Thymio) ou très limitées (par exemple les Bee-bot). Aucune ne passe par la programmation par carte qui me parait intéressante à plus d’un égard, en particulier pour de jeunes enfants.
Il me semble également que fabriquer sa carte perforée codant un caractère ASCII n’est pas un geste anodin pour comprendre le fonctionnement d’un ordinateur. Entrer les commandes manuellement (au sens strict du terme) puis attendre leur exécution et la fin de celle-ci par la machine n’est pas anodin non plus. Je parlerai peut-être prochainement de la question « oubliée » de la séquentialité dans la programmation des objets technique.
Peu satisfait de l’offre commerciale, j’ai donc reconçu un robot sous Arduino pilotable pars carte perforées en m’appuyant sur les choix techniques de Jeulin de 1980. La fabrication de ce robot fera l’objet d’un prochain billet, mais, en attendant, voici quelques photographies et une petite vidéo de démonstration.


fichier STL et code
Les modèles pour l’impression 3D sont disponibles sur les liens ci-dessous.
Le code du robot tortue est disponible ci-dessous. Il faut paramétrer la largeur de l’empattement (variable après montage) et le diamètre des roues. Le réglage fin de ces paramètre donne de la précisionlors du tracé des figures.
/********************************************
*** Programme robot tortue ***
*** Alexandre BOOMS ***
*** CC-BY-NC-SA ***
********************************************/
/////////////////
//Bibliothèques//
/////////////////
#include <Servo.h> // bibliothèque servomoteur
#include <Adafruit_NeoPixel.h> // bibliothèque LED RGB
#include <SoftwareSerial.h> // Biliothèque BlueTooth
// configuration servo
int broche_servo = 7; // défini la broche du servomoteur
int angle_stylo_B = 0; // angle du servo avec le stylo en bas
int angle_stylo_H = 90; // angle du servo avec le stylo en bas
Servo Servo_stylo; // nom du servo
// Variables d'ecoute du port série
String ent_serie; // récupère la chaine de caractère entrée sur le port série
int ENTREE;
// Variables de déplacement
float dia_roue=59.9; // diamètre des roues (augmentation = diminution distance parcourue)
float empattement=112.9; // empattement (augmentation = augmentation de la rotation angulaire)
int Paspartour=512; // voir la documentation du réducteur
int duree_delai=8; // Attente entre pas in ms (10 mini)
int distance; // distance à parcourir
float AvanceParPas = (3.1415926 * dia_roue)/Paspartour; //calcule l'avance d'un pas
int steps ;
// Variables moteurs pas à pas
int broches_MoteurG[4] = { 2, 3, 4, 5};
int broches_MoteurD[4] = { 10, 11, 12, 13};
const byte dextro[4][4] = { //rotation sens horaire depuis le moteur vers l'extérieur
{HIGH, LOW, LOW, HIGH},
{LOW, LOW, HIGH, HIGH},
{LOW, HIGH, HIGH, LOW},
{HIGH, HIGH, LOW, LOW},
};
const byte levo[4][4] = { //rotation sens anti-horaire depuis le moteur vers l'extérieur
{HIGH, HIGH, LOW, LOW},
{LOW, HIGH, HIGH, LOW},
{LOW, LOW, HIGH, HIGH},
{HIGH, LOW, LOW, HIGH},
};
// configuration speaker
int broche_speaker = 6; // Broche du speaker
int speak = 0; // Flag pour empêcher le bip après l'appel d'une boucle sonore
// configuration led RGB
int broche_RGB =8; // Broche de la led mutlicolore
Adafruit_NeoPixel led_RGB = Adafruit_NeoPixel(1, broche_RGB, NEO_GRB + NEO_KHZ800);
// configuration du port série BlueTooth
int Rx=0;
int Tx=1;
SoftwareSerial BTmySerial(Rx, Tx); // RX, TX
/////////////////////
// INITIALISATION //
/////////////////////
void setup()
{
// Initialise le port série
BTmySerial.begin(9600); // ouvre le port série et fixe le débit à 9600 bauds
// initialisation le Servo_stylo
Servo_stylo.attach(broche_servo); // déclaration de la broche
pinMode(broche_servo, OUTPUT); // met la broche en mode entrée
stylo_H(); // monter le porte stylo en haut
digitalWrite (broche_servo, LOW); // coupe la broche du servo pour économiser les batteries
delay(200);
// initialisation Moteurs pas à pas
for(int pin=0; pin<4; pin++)
{
pinMode(broches_MoteurG[pin], OUTPUT); // met les broches du moteur gauche en mode sortie
digitalWrite(broches_MoteurG[pin], LOW); // coupe la broche du moteur gauche pour économiser les batteries
pinMode(broches_MoteurD[pin], OUTPUT); // met les broches du moteur droit en mode sortie
digitalWrite(broches_MoteurD[pin], LOW); // coupe la broche du moteur droit pour économiser les batteries
}
// configure le BT
BTConfig();
delay(1000);
BTmySerial.flush(); // Purge le buffer
// Fais bouger le robot
avance(5);
recule(5);
tournegauche(5);
tournedroite(5);
// joue la mélodie pour signaler que la tortue est prête
melodie();
}
/////////////////////
// PROGRAMME PCPAL //
/////////////////////
void loop()
{
// ecoute du port série
if (BTmySerial.available()) //ecoute le port série si reçoit une donnée
{
ent_serie = BTmySerial.readString(); // lit la chaine envoyée sur le port série
ENTREE = ent_serie.charAt(0); // extrait le premier caractère (sécurité pour traiter les commandes une à une);
BTmySerial.println(ENTREE); // lit le premier caractère dans le moniteur
if (ENTREE == 66){stylo_B();} // B= stylo bas
else if (ENTREE == 72){stylo_H();} // H = stylo haut
else if (ENTREE == 97){avance(0);} // a = avance d'un pas
else if (ENTREE == 115){avance(10);} // s = avance de 10 mm
else if (ENTREE == 65){avance(100);} // A = avance de 100 mm
else if (ENTREE == 70){avance(200);} // F = avance de 200 mm
else if (ENTREE == 114){recule(0);} // r = recul d'un pas
else if (ENTREE == 116){recule(10);} //t = recule de 10 mm
else if (ENTREE == 82){recule(100);} //R = recule de 10 mm
else if (ENTREE == 85){recule(200);} //U = recule de 10 mm
else if (ENTREE == 103){tournegauche(0);} // g = tourne d'un pas gauche
else if (ENTREE == 118){tournegauche(1);} // v = tourne 1 degré gauche
else if (ENTREE == 71){tournegauche(15);} // G = tourne 15 degré gauche
else if (ENTREE == 99){tournegauche(30);} // c = tourne 30 degré gauche
else if (ENTREE == 102){tournegauche(45);} // f = tourne 45 degré gauche
else if (ENTREE == 105){tournegauche(60);} // i = tourne 60 degré gauche
else if (ENTREE == 108){tournegauche(90);} // l = tourne 90 degré gauche
else if (ENTREE == 111){tournegauche(120);} // o = tourne 120 degré gauche
else if (ENTREE == 100){tournedroite(0);} // d = tourne d'un pas droite
else if (ENTREE == 117){tournedroite(1);} // u = tourne 1 degré droite
else if (ENTREE == 68){tournedroite(15);} // D = tourne 15 degré droite
else if (ENTREE == 98){tournedroite(30);} // b = tourne 30 degré droite
else if (ENTREE == 101){tournedroite(45);} // e = tourne 45 degré droite
else if (ENTREE == 104){tournedroite(60);} // h = tourne 60 degré droite
else if (ENTREE == 107){tournedroite(90);} // k = tourne 90 degré droite
else if (ENTREE == 110){tournedroite(120);} // n = tourne 120 degré droite
else if (ENTREE == 49){avantdroite(100);} // 1 = arc de cercle av dr rayon 100 mm
else if (ENTREE == 53){avantdroite(200);} // 5 = arc de cercle av dr rayon 200 mm
else if (ENTREE == 50){avantgauche(100);} // 2 = arc de cercle av g rayon 100 mm
else if (ENTREE == 54){avantgauche(200);} // 6 = arc de cercle av g rayon 200 mm
else if (ENTREE == 51){arrieregauche(100);} // 3 = arc de cercle ar g rayon 100 mm
else if (ENTREE == 55){arrieregauche(200);} // 7 = arc de cercle ar g rayon 200 mm
else if (ENTREE == 52){arrieredroite(100);} // 4 = arc de cercle ar dr rayon 100 mm
else if (ENTREE == 56){arrieredroite(200);} // 8 = arc de cercle ar dr rayon 200 mm
else if (ENTREE == 74){melodie();} // J = joue une mélodie
else if (ENTREE == 75){bip();} // K = bip
else if (ENTREE == 80){rale();} // P = rale
else if (ENTREE == 43){allume_blanc();} // + = allume la led en blanc
else if (ENTREE == 45){eteins_led();} // - = allume la led en blanc
else if (ENTREE == 33){allume_bleu();} // ! = allume la led en bleu
else if (ENTREE == 34){allume_vert();} // " = allume la led en vert
else if (ENTREE == 35){allume_rouge();} // # = allume la led en rouge
else if (ENTREE == 36){allume_violet();} // $ = allume la led en violet
else if (ENTREE == 37){allume_turquoise();} // % = allume la led en turquoise
else if (ENTREE == 38){allume_jaune();} // & = allume la led en jaune
else if (ENTREE == 88){test_carre();}
else if (ENTREE == 121){demi_tour();}
}
}
/////////////////////
// SOUS PROGRAMMES //
/////////////////////
/***************************************************************************
* gestion du porte sylo
***************************************************************************/
///////////////////////////////////
// relève la came du porte-stylo //
///////////////////////////////////
void stylo_H ()
{
digitalWrite (broche_servo, HIGH); // allume la broche du servo
delay(250);
Servo_stylo.write(angle_stylo_H); // met le servo sur la position haute
delay(250);
digitalWrite (broche_servo, LOW); // coupe la broche du servo (economie)
}
///////////////////////////////////
// baisse la came du porte-stylo //
///////////////////////////////////
void stylo_B () // baisse la came du porte-stylo en haut
{
digitalWrite (broche_servo, HIGH); // allume la broche du servo
delay(250);
Servo_stylo.write(angle_stylo_B); // met le servo sur la position basse
delay(250);
digitalWrite (broche_servo, LOW); // coupe la broche du servo (economie)
}
/***************************************************************************
* deplacement avant arrière
* utilise la sous routine "step" pour calculer le nombre de pas
***************************************************************************/
/////////////////////////////////////////
// avance de la distance définie en cm //
/////////////////////////////////////////
void avance (float distance)
{
int steps = step(distance); // affecte le nombre de pas à steps
for(int step=0; step<steps; step++) // boucle sur le nombre de pas moteurs à effectuer
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // avance le moteur gauche d'un pas
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // avance le moteur droit d'un pas
}
delay(duree_delai);
}
}
fin();
}
/////////////////////////////////////////
// recule de la distance définie en cm //
/////////////////////////////////////////
void recule (float distance) // recule de la distance définie en cm
{
int steps = step(distance);
for(int step=0; step<steps; step++) // boucle sur le nombre de pas moteurs à effectuer
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // recule le moteur gauche d'un pas
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // recule le moteur droit d'un pas
}
delay(duree_delai);
}
}
fin();
}
///////////////////////////////////////////
// sous routine "step" //
//établit le nombre de pas de déplacement//
///////////////////////////////////////////
int step(float distance) // retourne le nombre de pas pour réaliser la distance
{
int steps = distance / AvanceParPas; // calcule le nombre de pas
if (steps==0) // permet les avances d'un pas en affectant 0
{
steps=1;
}
return steps; // retourne le nombre de pas
}
/***************************************************************************
* rotation sur l'axe du stylo
* utilise la sous routine "stepR" pour calculer le nombre de pas
***************************************************************************/
////////////////////////////////////////////
// tourne sur la gauche d'un angle défini //
////////////////////////////////////////////
void tournegauche (float angle)
{
int steps = stepR(angle); // demande le calcul dun nombre de pas
for(int stepR=0; stepR<steps; stepR++) // boucle sur le nombre de pas moteurs à effectuer
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // recule le moteur gauche d'un pas
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // avance le moteur droit d'un pas
}
delay(duree_delai);
}
}
fin();
}
////////////////////////////////////////////
// tourne sur la droite d'un angle défini //
////////////////////////////////////////////
void tournedroite (float angle) // tourne sur la droite d'un angle défini
{
int steps = stepR(angle); // demande le calcul dun nombre de pas
for(int stepR=0; stepR<steps; stepR++) // boucle sur le nombre de pas moteurs à effectuer
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // avance le moteur gauche d'un pas
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // recule le moteur droit d'un pas
}
delay(duree_delai);
}
}
fin();
}
//////////////////////////////////////////////
// sous routine "stepR" //
//établit le nombre de pas pour la rotation //
//////////////////////////////////////////////
int stepR(float angle){
int stepsR = ((empattement * angle* 3.1415926)/360) * Paspartour / (dia_roue * 3.1415926); // Calcule le nombre de pas pour tourner d'un angle défini
if (stepsR==0) // permet les avances d'un pas en affectant 0
{
stepsR=1;
}
return stepsR; // retourne le nombre de pas
}
/***************************************************************************
* déplacement en 1/4 de cercle de diamètre défini
* utilise les sous routine "stepsext" et "stepint"
* pour calculer le nombre de pas pour chacune des roues
***************************************************************************/
//////////////////////////////////////////////
// 1/4 de cercle en avançant sur la droite //
//////////////////////////////////////////////
void avantdroite (float dia_rot)
{
float steps_ext = stepsext(dia_rot); // demande le nombre de pas de la roue extérieure
float steps_int = stepsint(dia_rot); // demande le nombre de pas de la roue intérieure
float ratio_init = steps_ext/abs(steps_int); // calcule le ratio entre les pas ext et les pas int
int step=0; // affecte 0 à step qui comptera les pas extérieurs
float ratio_dyn=0; // affecte 0 à ratio dyn qui permettra de gérer les avances différentielle entre roues
float test_ratio = ratio_init-1; // calcule test_ratio qui déclenchera l'avance de la roue intéreur quand le ratio sera correct
while( step<steps_ext)// lance la boucle tant que la roue extérieure n'aura pas finit le nombre de pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // fait avancer la roue gauche (ext)
}
delay(duree_delai);
}
ratio_dyn=ratio_dyn+1; // incrémente le ratio dynamique
step++; // incrémente le nombre de pas ext réalisés
if (ratio_dyn>= test_ratio) // si le ratio est atteint moins un pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // fait avancer la roue gauche (ext)
if (steps_int<=0) // si la roue intérieure doit reculer (diamètre du cercle <empattement du robot)
{
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // fait reculer la roue droite (int)
}
else
{
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // fait avancer la roue droite (int)
}
}
delay(duree_delai);
}
step++; // incrémente le nombre de pas ext réalisés
ratio_dyn= ratio_dyn-test_ratio; // calcule le ratio dyn (conserve les décimales pour les incrémenter de nouveau)
}
}
fin();
}
//////////////////////////////////////////////
// 1/4 de cercle en avançant sur la gauche //
//////////////////////////////////////////////
void avantgauche (float dia_rot)
{
float steps_ext = stepsext(dia_rot); // demande le nombre de pas de la roue extérieure
float steps_int = stepsint(dia_rot); // demande le nombre de pas de la roue intérieure
float ratio_init = steps_ext/abs(steps_int); // calcule le ratio entre les pas ext et les pas int
int step=0; // affecte 0 à step qui comptera les pas extérieurs
float ratio_dyn=0; // affecte 0 à ratio dyn qui permettra de gérer les avances différentielle entre roues
float test_ratio = ratio_init-1; // calcule test_ratio qui déclenchera l'avance de la roue intéreur quand le ratio sera correct
while( step<steps_ext) // lance la boucle tant que la roue extérieure n'aura pas finit le nombre de pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // fait avancer la roue droite (ext)
}
delay(duree_delai);
}
ratio_dyn=ratio_dyn+1; // incrémente le ratio dynamique
step++; // incrémente le nombre de pas ext réalisés
if (ratio_dyn>= test_ratio) // si le ratio est atteint moins un pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // fait avancer la roue droite (ext)
if (steps_int<=0) // si la roue intérieure doit reculer (diamètre du cercle <empattement du robot)
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // fait reculer la roue gauche (int)
}
else
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // fait avancer la roue droite (int)
}
}
delay(duree_delai);
}
step++; // incrémente le nombre de pas ext réalisés
ratio_dyn= ratio_dyn-test_ratio; // calcule le ratio dyn (conserve les décimales pour les incrémenter de nouveau)
}
}
fin();
}
//////////////////////////////////////////////
// 1/4 de cercle en reculant sur la droite //
//////////////////////////////////////////////
void arrieredroite (float dia_rot) // trace un 1/4 de cercle en reculant sur la droite
{
float steps_ext = stepsext(dia_rot); // demande le nombre de pas de la roue extérieure
float steps_int = stepsint(dia_rot); // demande le nombre de pas de la roue intérieure
float ratio_init = steps_ext/abs(steps_int); // calcule le ratio entre les pas ext et les pas int
int step=0; // affecte 0 à step qui comptera les pas extérieurs
float ratio_dyn=0; // affecte 0 à ratio dyn qui permettra de gérer les avances différentielle entre roues
float test_ratio = ratio_init-1; // calcule test_ratio qui déclenchera l'avance de la roue intéreur quand le ratio sera correct
while( step<steps_ext) // lance la boucle tant que la roue extérieure n'aura pas finit le nombre de pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // fait reculer la roue droite (ext)
}
delay(duree_delai);
}
ratio_dyn=ratio_dyn+1; // incrémente le ratio dynamique
step++; // incrémente le nombre de pas ext réalisés
if (ratio_dyn>= test_ratio) // si le ratio est atteint moins un pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // fait reculer la roue droite (ext)
if (steps_int<=0) // si la roue intérieure doit avancer (diamètre du cercle <empattement du robot)
{
digitalWrite(broches_MoteurG[pin], dextro[mask][pin]); // fait avancer la roue gauche (int)
}
else
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // fait reculer la roue gauche (int)
}
}
delay(duree_delai);
}
step++; // incrémente le nombre de pas ext réalisés
ratio_dyn= ratio_dyn-test_ratio; // calcule le ratio dyn (conserve les décimales pour les incrémenter de nouveau)
}
}
fin();
}
//////////////////////////////////////////////
// 1/4 de cercle en reculant sur la gauche //
//////////////////////////////////////////////
void arrieregauche (float dia_rot)
{
{
float steps_ext = stepsext(dia_rot); // demande le nombre de pas de la roue extérieure
float steps_int = stepsint(dia_rot); // demande le nombre de pas de la roue intérieure
float ratio_init = steps_ext/abs(steps_int); // calcule le ratio entre les pas ext et les pas int
int step=0; // affecte 0 à step qui comptera les pas extérieurs
float ratio_dyn=0; // affecte 0 à ratio dyn qui permettra de gérer les avances différentielle entre roues
float test_ratio = ratio_init-1; // calcule test_ratio qui déclenchera l'avance de la roue intéreur quand le ratio sera correct
while( step<steps_ext) // lance la boucle tant que la roue extérieure n'aura pas finit le nombre de pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // fait reculer la roue gauche (ext)
}
delay(duree_delai);
}
ratio_dyn=ratio_dyn+1; // incrémente le ratio dynamique
step++; // incrémente le nombre de pas ext réalisés
if (ratio_dyn>= test_ratio) // si le ratio est atteint moins un pas
{
for(int mask=0; mask<4; mask++)
{
for(int pin=0; pin<4; pin++)
{
digitalWrite(broches_MoteurG[pin], levo[mask][pin]); // fait reculer la roue gauche (ext)
if (steps_int<=0) // si la roue intérieure doit reculer (diamètre du cercle <empattement du robot)
{
digitalWrite(broches_MoteurD[pin], levo[mask][pin]); // fait avancer la roue droite (int)
}
else
{
digitalWrite(broches_MoteurD[pin], dextro[mask][pin]); // fait reculer la roue droite (int)
}
}
delay(duree_delai);
}
step++; // incrémente le nombre de pas ext réalisés
ratio_dyn= ratio_dyn-test_ratio; // calcule le ratio dyn (conserve les décimales pour les incrémenter de nouveau)
}
}
fin();
}
}
/////////////////////////////////////////////////////////////////////
// sous routine "stepext" //
//établit le nombre de pas pour la rotation pour la roue exterieure//
/////////////////////////////////////////////////////////////////////
int stepsext(float dia_rot)
{
float perim_ext= (3.1415926 * ((dia_rot) + (empattement)))/4; // calcule le périmètre parcouru par la roue extérieure
int steps_ext = perim_ext/AvanceParPas ; // nombre de pas nécessaires
return steps_ext;
}
/////////////////////////////////////////////////////////////////////
// sous routine "stepint" //
//établit le nombre de pas pour la rotation pour la roue exterieure//
/////////////////////////////////////////////////////////////////////
int stepsint(float dia_rot)
{
float perim_int= (3.1415926 * ((dia_rot) - (empattement)))/4; // calcule le périmètre parcouru par la roue intérieure
int steps_int = perim_int/AvanceParPas ; // nombre de pas nécessaires attention la valeur est négative si empattement < diamètre
return steps_int;
}
/***************************************************************************
* gestion du speaker
***************************************************************************/
//////////////////////
// Joue une mélodie //
//////////////////////
void melodie ()
{
int melody[] =
{
349,349,349,392,440,392,349,440,392,392,349, // Au clair de la lune
};
int duree[] =
{
200,200,200,200,500,500,200,200,200,200,200, // durée des notes
};
for (int thisNote = 0; thisNote < 11; thisNote++)
{
tone(broche_speaker, melody[thisNote], duree[thisNote]); // Joue la note
delay(duree[thisNote]+100); // permet de séparer les notes
noTone(broche_speaker); // stoppe le son
}
speak = 1 ; // Met le flag boucle sonore à 1
}
/////////////////
// Joue un bip //
/////////////////
void bip ()
{
tone(broche_speaker, 600, 250); // Bip
delay(300); // permet de séparer les notes
noTone(broche_speaker); // stoppe le son
speak = 1 ; // Met le flag boucle sonore à 1
}
///////////////////////
// Joue un bip grave //
///////////////////////
void rale ()
{
tone(broche_speaker, 140, 800); // Bip grave
delay(1000); // permet de séparer les notes
noTone(broche_speaker); // stoppe le son
speak = 1 ; // Met le flag boucle sonore à 1
}
/***************************************************************************
* gestion de la led multicolore
***************************************************************************/
///////////////////
//allume en blanc//
///////////////////
void allume_blanc ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(255,255,255)); //allume toutes les leds = blanc
led_RGB.show(); //allume la led
fin();
}
///////////////////
//allume en bleu //
///////////////////
void allume_bleu ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(0,0,255)); //allume B = bleu
led_RGB.show(); //allume la led
fin();
}
///////////////////
//allume en vert //
///////////////////
void allume_vert ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(0,255,0)); //allume G = vert
led_RGB.show(); //allume la led
fin();
}
///////////////////
//allume en rouge//
///////////////////
void allume_rouge ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(255,0,0)); //allume R = rouge
led_RGB.show(); //allume la led
fin();
}
////////////////////
//allume en violet//
////////////////////
void allume_violet ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(255,0,255)); //allume R+B = violet
led_RGB.show(); //allume la led
fin();
}
///////////////////////
//allume en turquoise//
///////////////////////
void allume_turquoise ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(0,255,255)); //allume G+B = turquoise
led_RGB.show(); //allume la led
fin();
}
///////////////////
//allume en jaune//
///////////////////
void allume_jaune ()
{
led_RGB.setBrightness(255); // luminosité max
led_RGB.begin(); //démarre l'allumage
led_RGB.setPixelColor(0, led_RGB.Color(125,125,0)); //allume G+B = turquoise
led_RGB.show(); //allume la led
fin();
}
/////////////////
//eteind la led//
/////////////////
void eteins_led ()
{
led_RGB.setBrightness(0);
led_RGB.begin();
led_RGB.setPixelColor(0, led_RGB.Color(0,0,0));
led_RGB.show();
fin();
}
/***************************************************************************
* gestion de la fin de transmission
***************************************************************************/
////////////////////
//Fin transmission//
////////////////////
void fin ()
{
BTmySerial.println ("@"); // envoir un arobace à la fin de la transmission
if (speak == 0) // vérfie si on ne sort pas d'une boucle sonore
{
bip ();
}
speak = 0;
}
/***************************************************************************
* programmes de test pour étalonner la Tortue
* permet de corriger le diamètre des roues (demi tour)
* permet de corriger l'empattement (carré)
***************************************************************************/
////////////////////////
// figure test carré //
// gestion des angles //
////////////////////////
void test_carre()
{
stylo_B();
avance(150);
tournegauche(90);
avance(150);
tournegauche(90);
avance(150);
tournegauche(90);
avance(150);
stylo_H();
bip();
bip();
}
////////////////////////
// figure test carré //
// gestion des angles //
////////////////////////
void demi_tour()
{
stylo_B();
avance(300);
stylo_H();
tournegauche(180);
stylo_B();
avance(300);
stylo_H();
bip();
bip();
}
/***************************************************************************
* configuration du BT
***************************************************************************/
void BTConfig()
{
BTmySerial.begin(9600); // Ouvre le port série à 900 Bauds
BTmySerial.print("AT"); // Ouvre le mode écriture
delay(400);
BTmySerial.print("AT+DEFAULT"); // Restaure la configuration d'usine
delay(2000);
BTmySerial.print("AT+NAMETortue"); // Nomme le BT Tortue
delay(400);
BTmySerial.print("AT+PIN1234"); // définit le code PIN pour se connecter
delay(400);
BTmySerial.print("AT+AUTH1"); // protocole d'authentification
delay(400);
BTmySerial.flush(); // Purge le buffer
}
Et la suite…
Il faut encore finaliser le lecteur de carte en lui offrant la possibilité d’enregistrer les cartes et rappeler les programmes et les sous-programmes. Une fois ce travail achevé, une page sera évidemment consacrée au lecteur de carte qui me semble prometteur pour travailler la culture informatique.