Aide slider motoriser 2

Bonjour !

Topic 2 car j’avais déjà fait appel à vous il y a plus de 4 mois et m’aviez remis sur de bon rail :

Je reviens avec un slider avec sa forme finale .

Mais comme vous l’aurez compris , au niveau programme … pff … je galère comme un âne !

Quelques infos :

Slider de 1m en alu piloter via Bluetooth par une appli Android fait avec App Inventor 2
Slider avec un moteur type Nema 17 avec réducteur ( venant d’un scanner : rapport puissance/vitesse idéal)
Moteur piloté par un driver A4998
Driver piloté par un Arduino nano
Le tout alimenter par une batterie Lipo 2S 7,4v 2500mAh

Partie de mon programme android :

Le design :

Le programme Arduino pour test :

#include <Arduino.h>
#include "BasicStepperDriver.h"
#include <SoftwareSerial.h>

#define MOTOR_STEPS 200                             //1.8 degrees/pas
#define RPM 200                                     //vitesse moteur
#define MICROSTEPS 1                                //Pas complet
#define DIR 11                                      // pin DIR
#define STEP 10                                     // pin STEP

BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
SoftwareSerial mySerial ( 6 , 7 ) ;                 // RX, TX

char nbphoto = "";
char interval = "";
char distance = "";
String trame = "";


void setup() {

Serial.begin(9600);  
mySerial.begin(9600);

stepper.begin(RPM, MICROSTEPS);

}

void loop() {  
    
trame = mySerial.readStringUntil(',');
nbphoto = trame.toFloat();
Serial.println (nbphoto);
trame = mySerial.readStringUntil(',');
interval = trame.toFloat();
Serial.println (interval);
trame = mySerial.readStringUntil('f');
distance = trame.toFloat();
Serial.println (distance);

  for ( int i = 0 ; i < nbphoto ; i++ )
    if (nbphoto > 1)
   {
    stepper.move(distance);               // Déplacement du chariot de X distance
     delay(500);
     digitalWrite(12, HIGH);              // Déclenchement de la photo
     delay(300);
     digitalWrite(12, LOW);               // arrêt du déclenchement
     delay(interval);                     // interval entre 2 photos
   }
   }

Voila , voila …

Maintenant mes soucis :confused: :

J’ai du beaucoup de mal à comprendre les nuances et la gestions série ( et surtout les différentes types de données )
J’ai fait cette ébauche de programme pour essayé de comprendre comment ça marche .

Mais j’ai des soucis de conversions en char et String … je vois dans mon moniteur série que j’envoie en faite avec mon prog android , par exemple :

37,122,100f ( , est le séparateur et le f pour fin de trame )

L’arduino à l’air de bien séparer la trame vers les variables , mais quand il println le résultat , je vois :

%
z
d

Je sais que le soucis vient de ma gestion de type de variable , mais j’ai beau lire les articles et tuto qui traite de cela … une fois qu’il faut appliqué à mon programme …pfff.

1- comment et quel type de variable utiliser , sachant que certaines valeurs pourront s’étendre de - 350 à 350 ?

2- ya t-il une meilleur méthode pour traité une trame ? je pense que oui , mais simple à comprendre pour un noob en c comme moi ?

3 - je devrais créer une fonction pour le traitement de ma trame ? si oui quand on appelle une fonction , a t-on les résultats de cette fonctions obligatoirement ou il faut t-il faire quelques chose en plus de l’appeler ? ( pas évidant à formuler celle-la … )

4- auriez-vous des conseilles ? je part pas dans la bonne direction ?

Merci aux âmes charitables qui pourront m’aider !

Bonsoir,

Tu déclares des variables de type char

char nbphoto = "";
char interval = "";
char distance = "";

Donc println te renvoi un caractère

Ok , j'ai donc changé mes déclarations en long .

long nbphoto = "";
long interval = "";
long distance = "";

Cela fonctionne , mais est-ce le meilleur choix ?

Merci :slight_smile:

Disons que c'est loin d'être le meilleur.

Je te conseil dans un premier temps d'aller faire un tour ici ou tu trouveras des réponses pour les types de variables disponibles, les fonctions et le reste. :wink: ensuite ici pour une première approche simple.

Si vous voulez comprendre comment bien écouter le port série vous pouvez jeter un oeil à mon petit tuto sur le sujet

En prenant donc la fonction qui écoute une ligne jusqu’à un marqueur de fin, que j’adapte dans votre cas à ‘f’ puisque c’est ce que vous envoyez à la fin de la ligne, et pour voir ce que vous recevez de votre programme Android, essayez d’exécuter cela:

#include <SoftwareSerial.h>
SoftwareSerial BTSerie ( 6 , 7 ) ;                 // RX, TX

const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'

const char marqueurDeFin = 'f';

boolean ecouter()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageEnCours = true;

  while (BTSerie.available() && messageEnCours) {
    int c = BTSerie.read();
    if (c != -1) {
      switch (c) {
        case marqueurDeFin:
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageEnCours = false;
          break;
        default:
          if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageEnCours;
}

void setup() {
  Serial.begin(115200);
  BTSerie.begin(9600);
}

void loop() {
  if (! ecouter()) {
    Serial.print(F("Message = ["));
    Serial.print(message);
    Serial.println(F("]"));
  }
}

votre BT reste comme il est mais passez le moniteur Série Arduino à 115200 bauds.

Une fois appairé, envoyez vos messages depuis votre smartphone et vous devriez voir dans la console ce qui est exactement envoyé. copiez le résultat de la console et postez le ici.

Bonjour zehypnos

Tiens un adepte de App Inventor 2 une super machine à programmer!

zehypnos:
1- comment et quel type de variable utiliser , sachant que certaines valeurs pourront s’étendre de - 350 à 350 ?

2- ya t-il une meilleur méthode pour traité une trame ? je pense que oui , mais simple à comprendre pour un noob en c comme moi ?

Pour ce qui est du traitement de la trame avec
trame = mySerial.readStringUntil(',');
c'est l'idéal pour ce qui est ce genre de transmission de paramètres séparés par des délimiteurs.
Pour ce qui est de ton utilisation des variables, c'est une autre paire de manches!!! Va jeter un oeil de ce côté.

char nbphoto = "";
char interval = "";
char distance = "";
String trame = "";

Je suppose que nbphoto, interval, distance sont des entiers donc

int nbphoto = 0;
int interval = 0;
int distance = 0;

String trame = "";
Est bon, tu vois c'est encourageant :slight_smile:

Par conséquences

nbphoto = trame.toInt();
Serial.println (nbphoto);
trame = mySerial.readStringUntil(',');
interval = trame.toInt();
Serial.println (interval);
trame = mySerial.readStringUntil('f');
distance = trame.toInt();

Essaies déjà ça.

Cordialement
jpbbricole

J-M-L:
Une fois appairé, envoyez vos messages depuis votre smartphone et vous devriez voir dans la console ce qui est exactement envoyé. copiez le résultat de la console et postez le ici.

Merci , j'ai tester est voici le retour sur le moniteur :

Message = [6,100,10]

:slight_smile: Merci à tous pour vos commentaires !

Merci pour le lien jpbbricole , il est clair et explique bien les différents types de variables .

Ce que j'ai appris:

J'ai donc déclarer mes variables en INT car suffisant pour mon utilisation ( nombres entier et pas plus de 32767 positif ou négatif )

et grâce au Toint(); je convertis la String de trame en entier dans une variable int .

Cela fait des semaines que je galère , et en une fois vous m'avez fait beaucoup avancé ! merci !

J'ai un semblant de programme qui fonctionne ! :slight_smile: ça fait du bien !
Comme dis jpbbricole c'est encourageant .

Je vais tenter d'intégrer cela proprement et continuer mon programme .

Pensez-vous qu'il soit judicieux de mettre cette récupération de trame en fonction , car j'aurais dans l'idée de faire une config en mode photo et une config en mode vidéo , du coup j'aurais 2 récupérations de trame différente , genre sous mon loop :

 int tramephoto();

trame = mySerial.readStringUntil(',');    
nbphoto = trame.toInt();
Serial.println (nbphoto);
return (nbphoto);
trame = mySerial.readStringUntil(',');
interval = trame.toInt();
Serial.println (interval);
return (interval);
trame = mySerial.readStringUntil('f');
distance = trame.toInt();
Serial.println (distance);
return (distance);

Puis dans mon loop quand j'ai besoin de la variable nbphoto j'appelle ma fonction comme cela :

int nbphoto = tramephoto();

J'ai bien compris ? :confused:

Bonjour zehypnos

zehypnos:
J'ai bien compris ? :confused:

Euh... non, pas tout à fait, mais c'est bien d'essayer et ça me plait.

Ce qu tu essaies de faire c'est de créer une fonction qui soit utilisable un peu partout dans un programme, c'est une excellente idée, ça rend le programme plus lisible et plus facile à gérer.
Premièrement, une fonction s'écrit comme ceci:

int extractionUneDonnée(int parametreEventuel)
{
  int valeurAretourner;
  ......
  Création valeurAretourner
  ......
  return valeurAretourner
}

Regardes tout ça sur ce super site.

Ensuite, une fonction s'arrête au premier return et donc, tu l'aura compris, ne peut retourner qu'une valeur.

Dans ton cas, comme tu en as plusieurs et qu'elles sont déclarées "tout en haut" donc globales donc visibles partout (ouf...) tu peux créer une fonction qui ne renvoie rien (void) mais qui met ces variables à jour et que tu invoquera ainsi, par exemple:
extractionDesDonnees();

et tu l'écrira ainsi:

void extractionDesDonnees()
{
 trame = mySerial.readStringUntil(',');
 nbphoto = trame.toFloat();

 trame = mySerial.readStringUntil(',');
 interval = trame.toFloat();

 trame = mySerial.readStringUntil('f');
 distance = trame.toFloat();
}

et tu l'invoquera ainsi:

void loop()
{

 extractionDesDonnees();

 Serial.println (nbphoto);
 Serial.println (interval);
 Serial.println (distance);
}

Une fois que cela fonctionnera, on pourra "affiner" tout ça, mais c'est un très bon début.
Je n'ai pas testé "en vrai", tiens moi au courant.

Bonne journée
Cordialement
jpbbricole

zehypnos:
Merci , j’ai testé est voici le retour sur le moniteur :

Message = [6,100,10]

Ok donc bonne nouvelle votre interface Android fonctionne (et envoie des entiers en format texte lisible ASCII)

Si vous voulez évitez la classe String et les fonctions associées (cette classe risque de morceller la mémoire et à terme Risque de faire planter votre arduino suivant l’usage - donc cette classe n’est pas recommandée sur les petits micros-contrôleurs) utilisez sscanf()

Voici votre code avec cette approche:

/* ------- LE MOTEUR -------- */
#include <Arduino.h>
#include "BasicStepperDriver.h"

#define MOTOR_STEPS 200                             //1.8 degrees/pas
#define RPM 200                                     //vitesse moteur
#define MICROSTEPS 1                                //Pas complet
#define DIR 11                                      // pin DIR
#define STEP 10                                     // pin STEP

BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);

/* ------- L'APPAREIL PHOTO -------- */
#define DECLENCHEUR 12

/* ------- LE BLUETOOTH -------- */
#include <SoftwareSerial.h>
SoftwareSerial BTSerie ( 6 , 7 ) ;                 // RX, TX

const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'

const char marqueurDeFin = 'f';

boolean ecouter()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageEnCours = true;

  while (BTSerie.available() && messageEnCours) {
    int c = BTSerie.read();
    if (c != -1) {
      switch (c) {
        case marqueurDeFin:
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageEnCours = false;
          break;
        default:
          if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageEnCours;
}


/* ------- LE CODE PRINCIPAL -------- */
void setup()
{
  Serial.begin(115200);
  BTSerie.begin(9600);
  pinMode(DECLENCHEUR, OUTPUT);
  stepper.begin(RPM, MICROSTEPS);
}

void loop()
{
  int nbphoto ;
  int intervalle;
  int distance;

  if (! ecouter()) {

    sscanf(message, "%d,%d,%d", &nbphoto, &intervalle, &distance );

    /* JUSTE POUR INFO ON PEUT AFFICHER CE QUE L'ON A RECU */
    Serial.print(F("Nouvelle Commande = ["));
    Serial.print(message);
    Serial.print(F("]  -> Nb Photos="));  Serial.print(nbphoto); 
    Serial.print(F(", Intervalle=")); Serial.print(intervalle); 
    Serial.print(F(", Distance=")); Serial.println(distance);
    
    /* On exécute la nouvelle demande */
    for ( int i = 0 ; i < nbphoto ; i++ ) {
      stepper.move(distance);         // Déplacement du chariot de X distance
      delay(500);
      digitalWrite(DECLENCHEUR, HIGH);  // Déclenchement de la photo
      delay(300);                       // temps d'appui sur le déclencheur (à mon avis pas besoin que ce soit aussi long)
      digitalWrite(DECLENCHEUR, LOW);   // arrêt du déclenchement
      delay(intervalle);                // intervalle entre 2 photos
    }
  }
}

Au passage vous aviez oublié de déclarer la pin du DECLENCHEUR en output, je l’ai rajouté et j’ai viré le
if (nbphoto > 1)Parce que si vous demandez 1 seule photo alors vous n’en prendriez aucune et s’il y en a 1 au moins alors la boucle for va se charger pour vous de faire ce qu’il faut.

A noter vous bougez le charriot avant de faire la photo, ne devriez vous pas prendre une photo - puis avancer le charriot? ça vous permettrait de mettre l’appareil à la bonne position pour la première photo de votre série.

L’appel à stepper.move() est bloquant et vous attendez ensuite 500ms après la fin du mouvement moteur pour déclencher la photo, c’est peut être un peu juste pour que l’appareil soit stabilisé. L’attelage aura une inertie qui sera d’autant plus importante que la charge (donc appareil+objectif) sera lourde. Si vous trouvez que vos photos ne sont pas super nettes, prévoyez d’allonger un peu ce temps de repos avant déclenchement pour que le système soit bien au repos et qu’il ne reste aucune vibration.

Concernant le timing - vous avez le temps déplacement moteur + les 500ms + les 300ms avant de faire votre delay(intervalle); → donc l’intervalle que vous envoyez ne sera pas franchement respecté. si vous n’êtes pas à 1s près, ça ne pose aucun soucis mais si intervalle doit être du même ordre que la seconde, alors il faut sans doute tenir compte du temps perdu ailleurs et attendre moins longtemps à la fin de la boucle.

En combinant ces 2 commentaires, une approche serait de prendre la photo, bouger le charriot ensuite et attendre l’intervalle (éventuellement recalculé). Comme cela vous serez stabilisé pour la prochaine photo sans perdre plus de temps.

Enfin les 300ms d’appui sur le déclencheur, c’est peut être un peu long mais ça dépend de votre appareil. J’ai un Canon 5D et je me suis fabriqué plusieurs systèmes automatisés de photos et ça déclenche bcp plus vite que cela.

Pour la netteté - si votre appareil supporte L’option verrouillage du miroir - vous pourriez envisager aussi avoir un mode et déclencher deux appuis avec un petit temps d’attente entre les deux (cf cet article sur le flou de bougé dû aux vibrations de l’appareil photo au moment de la remontée du miroir).

Si ça vous intéresse - suivant le type de time-lapse que vous faites - on peut aussi bricoler (j’ai pris une télécommande chinoise à 2€ pour récupérer le câble) un système de contrôle qui permet de faire le demi appui sur le déclencheur comme ça vous avez une mise au point par photo. (bien sûr parfois on ne veut pas cela, on a tout mis en mode manuel pour que les photos soient toutes similaires)

remote.png

cable.png

→ on récupère les 3 fils que l’on pilote par un opto-coupleur

Cela dit, si vous comptez juste utiliser ça ponctuellement ou que l’arduino redémarrera souvent et ne doit pas tourner des jours sans rebooter alors vous êtes sans doute tranquille avec la classe String et le code de JP. ==> Je suppose que vos séances photos sont de quelques heures au plus et que vous envoyez une commande et ensuite laissez l’arduino piloter le rail et l’appareil puis vous éteignez le tout - donc ça devrait aller avec les readStringUntil(). Si un jour vous avez des instabilités ou commencez à faire des programmes plus conséquents ou qui doivent tourner pendant des jours, évitez ces fonctions.

(Quand vous maîtriserez les fonctions un peu plus vous verrrez qu’il y a atoi() qui prend encore bcp moins de place mémoire programme que sscanf - on gagnera plus de 1,5 K octets, sur de gros programme ça peut être utile! - et qui sait lire un entier)

:o wouahou ! merci pour tout cela ! bon j'ai du boulot , à lire et relire vos message et les liens pour assimllé tout cela !

Merci J-M-L pour tes conseils , je vais etudier tout cela , car oui je vais essayer d'avoir le plus de stabilité possible au moment de la photo , surtout que je suis adepte des timelaspes de nuit , avec des temps de pose plutôt longue .

J'utilise déja sur la platine que je me suis faite un optocoupleur pour déclencher l'appareil photo :

Pour l'appuis de déclenchement, effectivement cela dois être un peu long , je ferais des tests plus court que 300ms .

J'utilise un canon 6DMKII et je fait toujours mes timelaspes entirement en manuel , sinon trop de risque que la mise au point foire ou un simple nuage modifie mes réglages d'exposition , et lorsque néccessaire je bloque le mirroir .

Pour le restes du programme effectivement , il n'est pas bien "ficelé" , mais ce n'est pas du tout mon programme definitif , celui-ci été fait pour m'apprendre à gérer la récupération de trame ou faire des tests .Voila pourquoi effectivement je me retrouve avec des mouvements juste avant de faire une photo ou autres .

Encore merci à tous , je vais tenté de tout assimilé , je reviens vers vous dès que je trouve plus par moi même ou pour vous tenir au courant de mes avancées .

Joli montage - ça fait propre !

Oui c'est assez cool et gratifiant quand on voit le résultat ensuite (et si vous intégrez une RTC vous pouvez faire des photos sans vous lever à 2h du matin :slight_smile: )

Quand vous faites de la pause longue de nuit - en quel mode vous vous mettez?

Si vous êtes en "Pause B"

vous êtes si je me souviens bien en mode Minuteur Bulb désactivé par défaut

et donc il faut tenir le bouton appuyé aussi longtemps que vous souhaitez exposer --> dans votre cas ce ne serait que 300ms.

La durée du minuteur Bulb peut s'activer et se règle manuellement

et il n'est plus nécessaire de maintenir enfoncé le déclencheur pendant la pose longue

Perso je préférais le premier mode comme ça je peux piloter aussi par Arduino la durée d'exposition en simulant le bouton enfoncé (une cellule de mesure de luminosité (TSL2561 par exemple) peut aider à prendre une décision)

J-M-L:
Perso je préférais le premier mode comme ça je peux piloter aussi par Arduino la durée d'exposition en simulant le bouton enfoncé (une cellule de mesure de luminosité (TSL2561 par exemple) peut aider à prendre une décision)

Merci :slight_smile:

Mais là on part sur de la programmation qui est loin de mon niveau !

En tout cas je prend note de toute vos conseils ! et je ne savias pas que l'on pouvais minuté le mode Bulb , c'est trés interessant !

c'est fou tous ce qu'on apprend quand on est plus tout seul ... :wink:

:slight_smile:

bonnes photos et bricolages !

au fait sur ce point

zehypnos:
je fais toujours mes timelaspes entirement en manuel , sinon trop de risque que la mise au point foire ou un simple nuage modifie mes réglages d’exposition

oui - mais si un jour vous voulez bricoler un système qui détecte la présence d’un animal et déclencher, faire la mise au point ça peut être pas mal - c’est pour ça que j’avais bidouillé mon câble, certaines fois vous ne voulez pas être trop proche de l’action :slight_smile: