[Conseil] Mémoire externe - Enregistrement et PlayBack de mouvements d'un servo

Bonjour à tous,

Je cherche depuis des mois à concrétiser un projet Arduino mais sans succès ... malgré mes efforts et le temps passé sur le net.
Je me retourne donc vers vous, en espérant que vous pourrez me donner un vrai coup de main ; j'y crois !
(je rappelle que mon niveau de connaissance est proche du néant en la matière; merci pour votre indulgence)

Mon projet Arduino :

1 Mémoriser (via un potentiomètre) une suite de mouvements données à un Servo moteur (compris entre 0 et 180° et d'une durée de 3 ou 4mn tout au plus).
2 Sauvegarder en temps réel cette "chorégraphie" sur une mémoire externe (microSD ou EEPROM).
3 disposer d'un bouton "record" qui permettrait de lancer cet enregistrement.
4 disposer d'un bouton "Play", qui permettrait de rejouer une seule fois l'animation du servomoteur exactement dans le même tempo qu'elle aura été enregistrée.
5 Une fois le sketch téléversé sur la carte, le montage final serait autonome et alimenté par des batteries.
6 Bonus ambitieux : imaginer rajouter par la suite, un ou plusieurs autres servomoteurs.

Il s'agit donc de pouvoir "donner vie" à un petit objet attaché à un Servo moteur.
Comme un automate, il reproduirait des mouvements fluides et précis préalablement enregistrés.

Mon matériel et environnement :

  • Arduino uno
  • EEPROM 24CL 1025KB
  • module carte Micro SD (4Go)
  • Windows 10
  • Version IDE: 1.8.13

Après avoir testé plusieurs tutos, je me suis concentré sur 3 d'entre eux qui se rapprochent de mon projet :

1 ) Avec carte micro SD:

Présenté par le canadien "Bill" : "Using SD Cards with Arduino - Record Servo Motor Movements"
https://dronebotworkshop.com/sd-card-arduino/

Son explication est particulièrement claire (et donc à ma portée).

Il propose une méthode en deux temps :
Un 1er code permet d'enregistrer les positions du servo sur la micro SD.
Un 2d code permet de lire cette même carte ; le servo moteur joue alors le playback.

Points positifs :

  • le Play back est joué une seule fois.
  • le bouton reset de l’Arduino joue le rôle du bouton "play" dès que la carte est mise sous tension.

Points négatifs :

  • deux sketchs à téléverser, donc chaque nouvel enregistrement nécessite la présence d'un pc.
  • plus embêtant, le servo moteur "rejoue" très rapidement les mouvements successifs enregistrés sans tenir compte des temps de pauses et des ralentissements initialement prévus.
    Cela anéantit totalement la "chorégraphie" souhaitée...

2 ) Avec EEPROM (méthode 1):

Dans cet autre tuto, "Bill" explique les atouts de l'EEPROM et son fonctionnement.
https://dronebotworkshop.com/eeprom-arduino/

Points positifs :

  • un seul sketch pour enregistrer puis lire le contenu de la mémoire.
  • les mouvements du servomoteur en playback correspondent parfaitement à ceux de l'enregistrement. La "chorégraphie" est donc bien respectée.

Points négatifs :

  • le playback est immédiatement rejoué sans pouvoir contrôler son lancement : manque un bouton "playback".
  • impossible pour moi de relancer ce playback lorsque la carte a été débranchée puis remise sous tension.

3 ) Avec EEPROM (méthode 2):

"EEEnthusiast" propose une autre approche :
"Arduino Tutorial #22 - EEPROM & Servo Memory Tutorial - Storing Servo Motion through I2C & Playback"

Points positifs :

  • comme précédemment, les mouvements du playback correspondent à ceux enregistrés.
  • Présence du bouton "Record".

Points négatifs :

  • les mêmes que dans le montage précédent, avec en plus le playback qui rejoué en boucle indéfiniment.

Voilà.... peut être que vous avez des solutions pour moi, et dans ce cas, je vous en suis déjà très reconnaissant ou .... peut-être que mon projet n'est qu'une utopie.
Merci d'avance à tous pour vos contributions.

1 Mémoriser (via un potentiomètre) une suite de mouvements données à un Servo moteur (compris entre 0 et 180° et d'une durée de 3 ou 4mn tout au plus).

pour avoir des idées, cf un post de kammo avec sa vidéo

pour le reste, une fois que les données arrivent en mémoire, savoir où les stocker et comment les rejouer n'est pas très complexe

Merci mille fois J-M-L pour votre intérêt !

Je connaissais en effet le post de "Kammo" qui rejoint l'astuce d'un jeune américain pour "hacker" un servomoteur:
onshouldersTv: Hacking a Servo Trimpot - YouTube et onshouldersTv: Servo Animation - YouTube

Kammo propose un enregistrement pratique pour quelques mouvements du servo moteur mais qui deviendrait vraiment trop fastidieux pour enregistrer un déplacement fluide et continue sur quelques minutes.

Un potentiomètre facilite donc la chose et est utilisé dans les 3 tutos cités dans mon premier message;
la sauvegarde et la lecture des positions du Servo sur carde SD est faisable mais c'est peut être plus simple sur une mémoire EEPROM.
En fait, là où je coince c'est comment modifier le sketch ci dessous pour:

  • supprimer le lancement automatique et en boucle du playback dès la fin de l'enregistrement.
  • insérer un bouton "playback" qui permettrait de lancer quand on le souhaite la lecture de la mémoire.
  • insérer un bouton "Record" dont la fonction est assurée ici par le bouton reset de l'Arduino.
#include <Servo.h>
Servo myServo;
byte myServoAngle = 0;
byte myServoAngleRead = 0;
#include "Wire.h"
#define EEPROM_I2C_ADDRESS 0x50
long lastWrite = 0;
int writePointer = 0;
int writeLimit = 200;
const int analogInPin = A3;
const int numOfInputs = 1;
const int inputPins[numOfInputs] = {8};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW};
bool inputFlags[numOfInputs] = {LOW};
int inputCounters[numOfInputs];
long lastDebounceTime[numOfInputs] = {0};
long debounceDelay = 10;
void setup() {
  for(int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  Serial.begin(9600);
  myServo.attach(9); 
  Wire.begin();
}
void loop() {
  setInputFlags();
  resolveInputFlags();
  resolveOutputs();
}
void setInputFlags() {
  for(int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for(int i = 0; i < numOfInputs; i++) {
    if(inputFlags[i] == HIGH) {
      inputCounters[i]++;
      inputFlags[i] = LOW;
    }
  }
}
void resolveOutputs() {
  myServoAngle = map(analogRead(A3), 0, 1023, 0, 180);
  switch(inputCounters[0]) {
    case 1:
      myServo.write(myServoAngle);
      break;
    case 2:
      myServo.write(myServoAngle);
      if(millis() - lastWrite > 100) {
        writeAddress(writePointer, myServoAngle);
        Serial.print("Writing =");
        Serial.print(writePointer);
        Serial.print(" value =");
        Serial.println(myServoAngle);
        writePointer++;
        lastWrite = millis();
      }
      if(writePointer > writeLimit) {
        inputCounters[0] = 3;
        writePointer = 0;
      }
      break;
    case 3:
      if(millis() - lastWrite > 100) {
        myServoAngleRead = readAddress(writePointer);
        Serial.print("Reading =");
        Serial.print(writePointer);
        Serial.print(" value =");
        Serial.println(myServoAngleRead);
        myServo.write(myServoAngleRead);
        writePointer++;
        lastWrite = millis();
      }
      if(writePointer > writeLimit) {
        writePointer = 0;
      }
  }
}
void writeAddress(int address, byte val)
{
  Wire.beginTransmission(EEPROM_I2C_ADDRESS);
  Wire.write((int)(address >> 8));   // MSB
  Wire.write((int)(address & 0xFF)); // LSB
  Wire.write(val);
  Wire.endTransmission();
}
byte readAddress(int address)
{
  byte rData = 0xFF;
  Wire.beginTransmission(EEPROM_I2C_ADDRESS);
  Wire.write((int)(address >> 8));   // MSB
  Wire.write((int)(address & 0xFF)); // LSB
  Wire.endTransmission();  
  Wire.requestFrom(EEPROM_I2C_ADDRESS, 1);  
  rData =  Wire.read();
  return rData;
}

emmanuel6440:
Voilà.... peut être que vous avez des solutions pour moi, et dans ce cas, je vous en suis déjà très reconnaissant ou .... peut-être que mon projet n'est qu'une utopie.
Merci d'avance à tous pour vos contributions.

Bonsoir
Par simplicité et en se limitant à une résolution de positionnement tenant sur un byte (28 ) et un taux d'acquisition/restitution à 100 Hz ,il y a besoin de 30.000 bytes (+/- 30 kO) de stockage pour ~ 5 minutes .

Vous avez raison Artouste, j'ai vu que Microchip Technology a sorti l'été dernier une EEPROM de 4 Mbits (la 25CSM04). Ca devrait être parfaitement suffisant :slight_smile:

un déplacement fluide et continu sur quelques minutes.

une interpolation par une succession de Courbes de Bézier par exemple permettrait de ne conserver que les points de contrôle pour définir un mouvement continu fluide. Ça prend beaucoup moins de place en mémoire bien sûr puisque l’on a que quelques points.

Il y a aussi la solution FLASH SPI, avec des capacités allant jusqu'à 256Mo (2Gbits).
La solution est très peu onéreuse.

S'il les projets cités n'ont pas de bouton playback, je ne vois pas ce qui empêche d'en ajouter un.

Le code cité en #2 (EEEnthusiast) donne mal à la tête.

Celui-ci me semble + compréhensible surtout pour quelqu'un qui dit que son "niveau de connaissance est proche du néant".

maxaddress doit être adapté à la longueur d'enregistrement désirée.

Les lignes 94 à 120 peuvent être coupées / collées dans une fonction record().
Le delay de 15ms doit être adapté à la fréquence de l'enregistrement.

Lignes 122 123 : à virer

Les lignes 125 à 151 peuvent être coupées / collées dans une fonction playback().
Le delay de 15ms doit être le même que précédemment.

Ensuite dans la loop il faut surveiller l'état de deux boutons et appeler record() ou playback() en fonction du bouton appuyé.

Ce ne sont pas les tutos BOUTON qui manquent sur le WEB.

Un immense merci pour vos différentes aides.
Elles m'ont invité à beaucoup de nouvelles lectures aujourd'hui.
Pour être honnête, la page concernant les courbes de Bézier a violement griffé mon petit cerveau :confused:
Tout cela reste très haut dessus de mes capacités de compréhension; hélas.

Je ne connaissais pas la mémoire Flash SPI ! Fantastique ! Je garde sous le coude cette possibilité pour m'y intéresser dès que j'en aurais fini avec l'EEPROM.

Et grâce aux conseils de "hbachetti", je devine un peu mieux la structure du sketch à faire.
Mais le chemin est encore long pour moi...
Depuis plusieurs heures, je tente de comprendre le travail de "DroneBot Workshop" et de "kammo" pour compiler un petit programme.
Le résultat est pour l'instant sans appel: l'application d'Arduino ne cesse de m'indiquer des bugs....

hello
j'avais fais ce prg pour un ami il y a quelques temps.

de mémoire:

enregistrement sur sd
vitesse moteur par potentiomètre ( de type linéaire et si possible bobiné pour éviter les "crachements")
enregistrement sur sd
4 BP, début enregistrement, fin enregistrement, début lecture, fin lecture
nota ils étaient montés en série comme sur le shield LCD ( avec les résistances)
1 inter : pour lecture en boucle si désiré.
1moteur pas à pas,
1 driver tb6660 sur lequel j'utilisais S1,S2,S3

utilisation:
_enregistrement:
tu lances l'enregistrement par appui sur le BP correspondant.
le moteur démarre et tu module sa vitesse en fonction de ce que tu veux.
la valeur du potentiomètre est enregistrée sur la sd toutes les 5 ou 10 ms( je ne sais plus)
tu stoppes l'enregistrement par une pression sur le BP arrêt enregistrement.

_lecture:
si tu veux une lecture en boucle, tu positionnes l'interrupteur sur la bonne position

tu lances la lecture par un appui sur le BP lecture
la sd est lue toutes les 5 ou 10 ms la vitesse du moteur s'en trouve initialisée et le moteur se déplace en fonction de la vitesse programmée par la lecture sur sd
tu peux stopper la lecture quand tu veux par un appui sur le BP arrêt lecture.

à toi de voir, si tu veux garder en SD ou adapter pour eeprom

enregist_play_uno_sd.zip (5.16 KB)

Bonsoir Dfgh et merci beaucoup pour cette nouvelle aide. Vraiment merci.
J'ai téléchargé et ouvert le fichier "enregist_play_uno_sd.ino" :
Nom de diou !!!!!!! pardon, mais un prozac m'a été immédiatement nécessaire !

Plus sérieusement, je mesure bien la complexité et la minutie de l'encodage. C'est sensationnel ! Quel travail !
Et je découvre donc à présent les possibilités d'un moteur pas à pas.... je n'ai pas du tout pensé à l'utiliser.
Du coup je suis allé regarder quelques vidéos à ce sujet et voilà qu'une autre dimension s'offre à moi !

Mais je devine aussi peut être quelques contraintes à utiliser ces moteurs:

  • les plus petits semblent avoir une très faible vitesse de rotation (démo faite par "RetroEtGeek" ici) ; or j'aurais besoin de pouvoir reproduire certains mouvements assez rapides.
  • ils nécessitent une carte de contrôle; cela peut augmenter sensiblement la taille de l'ensemble; (or la miniaturisation de l'ensemble a son importance)
  • leur alimentation demande du 12 v pour un modèle courant comme le Twotrees Nema17.

D'où 3 petites questions:

  • le driver tb6660 est il indispensable ou bien une carte de contrôle comme celle ci peut elle convenir ?
  • si le moteur Twotrees Nema17 est alimenté avec une seule pile de 9v; est ce que sa vitesse de rotation est altérée ?
  • le code "enregist_play_uno_sd.ino" est-il adaptable à des servomoteurs ?

hello
1/_tout dépends de la gamme de vitesse que tu désires.
de mémoire, j'avais du faire des tests pour changer les micro-pas en cours de route.
cela marchait avec leTB6660 en passant par des opto_coupleurs.
cela ne marchait pas avec des 4988.
je n'ai jamais utilisé un pas à pas avec le l298 ( uniquement avec des moteurs en continu.)
2/_la pile de 9V, si tu parles du vieux modèle rectangulaire, tu peux l'oublier.
regardes plutôt du coté des accus, en 12 V. et tu dois prendre en compte le nombre de moteurs ou servos que tu vas utiliser.
3/_oui, le code ne fait qu'enregistrer la position du potentiomètre à intervalles réguliers. donc que tu mettes un servo ou un moteur pas à pas importe peu.

complément sur le prg, le potentiomètre est utilisé pour donner le sens et la vitesse.
au milieu de sa course, il allume une led comme témoin de position neutre.
tourné sens horaire, il donne une consigne de vitesse lente qui augmente au fur et à mesire de la rotation horaire du potentiomètre.
et inversement, en partant du point neutre en sens trigonométrique.

nota: si tu n'es pas trop pressé, regardes chez Aliexpress, il y a du delai, mais les prix sont bas.

Encore mille mercis pour ces précieux conseils;
Je vais prendre le temps de passer commande de quelques composants et de réfléchir aux deux solutions possibles:

  • la première basée sur le travail de "Bill" (avec l'enregistrement des mouvements d'un servo sur EEPROM ou flash SPI )
  • la seconde basée sur le code "enregist_play_uno_sd" en tentant de l'adapter à mes besoins.

Pour ce dernier, j'ai encore une question: je comprends que le potentiomètre est là pour réguler la vitesse du moteur pas à pas, mais peut il aussi jouer sur son sens de rotation ?

dfgh:
au milieu de sa course, il allume une led comme témoin de position neutre.
tourné sens horaire, il donne une consigne de vitesse lente qui augmente au fur et à mesure de la rotation horaire du potentiomètre.
et inversement, en partant du point neutre en sens trigonométrique.

oui, c'est ce que je te précisais dans une réponse précédente
potentiomètre au milieu de sa course = pas de mouvement, consigne à zéro
rotation du potentiomètre en sens horaire = rotation du moteur en sens horaire et à vitesse croissante selon la position angulaire du potentiomètre
rotation du potentiomètre en sens anti horaire = rotation du moteur en sens anti horaire et à vitesse croissante selon la position angulaire du potentiomètre
si tu veux plusieurs versions de scenario, tu le fait sur plusieurs cartes sd
le moment venu, tu choisi ta carte et c'est parti.

C'est vraiment extraordinaire comme idée. Respect et bravo !
Je regarde du coté d'Aliexpress; cela me laisse le temps de commencer à comprendre comment devra être fait le circuit.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.