Problème avec millis et servomoteur

Bonjour,
Je travaille sur une arduino nano. Tous les ports de la nano sont utilisés dans diverses fonctions (LED, afficheurs, LDR, ...).
Je souhaite utiliser la fonction millis() mais je n'arrive pas à faire tourner mon servomoteur de 30° dès lors que j'appuie sur le bouton poussoir.
J'ai cru comprendre que les servomoteurs avaient une horloge indépendante et pouvait générer des pbs lorsqu'utilisés avec millis.
Merci de m'aider à régler le problème!

int pin_button = 4; 
int pin_servo = 13; 
unsigned long tempsOuverture;

#include "Servo.h"
Servo servo; 


void setup() {
  Serial.begin(9600); 
  pinMode(pin_button, INPUT); 
  servo.attach(pin_servo); 


void loop() {
  millis();
  servo.write(120); 
  if (digitalRead(pin_button) == HIGH) { 
    Serial.println("ON");
    tempsOuverture=millis();
    if ((millis() - tempsOuverture)> 6000) {
      servo.write(90);
    }
  }
}


Bonjour titan65

Il faut séparer les 2 fonctions, la première qui "lit" le bouton et qui fait tourner le servo:

	//--------------------------------- Partie bouton
	if (servoMillis == 0 && (digitalRead(pin_button) == HIGH))      // Si servo pas actif et bouton pressé
	{
		Serial.println("ON");
		servo.write(120);     // Rotation du servo
		servoMillis = millis();     // Démarrage du chrono
	}

La 2ème qui contrôle si le chrono a démarré et s'il est arrivé à échéhance et qui remet le servo au départ:

	//--------------------------------- Partie temporisation
	if (servoMillis > 0 && (millis() - servoMillis >= servoTempo))     // Si servo actif et arrivé à échéhence
	{
		Serial.println("OFF");
		servo.write(90);     // Servo mis au repos
		
		servoMillis = 0;     // Servo plus en action
	}

Le tout est "orchestré" par 2 variables:

unsigned long servoTempo = 6000;     // Temps de l'action du servo
unsigned long servoMillis = 0;     // Chrono pour l'action du servo, si > 0 = servo en action

et le tout donne ceci:

int pin_button = 3;
int pin_servo = 13;
unsigned long tempsOuverture;

#include <Servo.h>
Servo servo;

unsigned long servoTempo = 6000;     // Temps de l'action du servo
unsigned long servoMillis = 0;     // Chrono pour l'action du servo, si > 0 = servo en action

void setup()
{
	Serial.begin(9600);
	pinMode(pin_button, INPUT);
	servo.attach(pin_servo);
	
	servo.write(90);     // Servo mis au repos
}

void loop()
{
	//--------------------------------- Partie bouton
	if (servoMillis == 0 && (digitalRead(pin_button) == HIGH))      // Si servo pas actif et bouton pressé
	{
		Serial.println("ON");
		servo.write(120);     // Rotation du servo
		servoMillis = millis();     // Démarrage du chrono
	}

	//--------------------------------- Partie temporisation
	if (servoMillis > 0 && (millis() - servoMillis >= servoTempo))     // Si servo actif et arrivé à échéhence
	{
		Serial.println("OFF");
		servo.write(90);     // Servo mis au repos
		
		servoMillis = 0;     // Servo plus en action
	}
}

A ta disposition pour toutes questions :wink:

Cordialement
jpbbricole

1 Like

Les quelques micros secondes qui s'écoulent entre ces deux lignes ne risquent pas de dépasser les 6 s...

La logique de ton code est à revoir : que cherches-tu à obtenir ?

Merci à vous 2 pour votre réactivité.
J'ai testé ta proposition jpbbricole, çà fonctionne déjà mieux que ce que j'avais fait, mais il y a encore des pbs :

  • sur le moniteur série, lorsque j'appuie sur le bouton poussoir, il s'affiche une série alternée de ON et OFF, alors que le moteur passe de 90 à 120 une seule fois. Le programme semble lire les boucles tout le temps que je reste appuyé sur le bouton

  • sinon, la réaction du programme est aléatoire, l'alternance du moteur peut fonctionner plusieurs fois d'affilée, à chaque fois que j'appuie sur le bouton, mais le système peut tout aussi bien se figer sur 120 et je suis obligé de redémarrer l'arduino...
    Comment se fait-il que ce ne soit pas stable?

Bonjour titan65

As tu une résistance de PULLDOWN sur l'entrée pin_button, 10k ferait l'affaire.

ou mieux, passe en mode PULLUP
image
Ainsi tu bénéficies de la résistance de PULLUP interne au port de l'Arduino.
Les changements sont:

le câblage du bouton: un côté à GND et l'autre sur pin_button
l'initialisation du bouton: pinMode(pin_button, INPUT_PULLUP);
la lecture du bouton: digitalRead(pin_button) == LOW)

La version PULLDOWN est:

int pin_button = 3;
int pin_servo = 10;
unsigned long tempsOuverture;

#include <Servo.h>
Servo servo;

unsigned long servoTempo = 6000;     // Temps de l'action du servo
unsigned long servoMillis = 0;     // Chrono pour l'action du servo, si > 0 = servo en action

void setup()
{
	Serial.begin(9600);
	pinMode(pin_button, INPUT_PULLUP);
	servo.attach(pin_servo);
	
	servo.write(90);     // Servo mis au repos
}

void loop()
{
	//--------------------------------- Partie bouton
	if (servoMillis == 0 && (digitalRead(pin_button) == LOW))      // Si servo pas actif et bouton pressé
	{
		Serial.println("ON");
		servo.write(120);     // Rotation du servo
		servoMillis = millis();     // Démarrage du chrono
	}

	//--------------------------------- Partie temporisation
	if (servoMillis > 0 && (millis() - servoMillis >= servoTempo))     // Si servo actif et arrivé à échéhence
	{
		Serial.println("OFF");
		servo.write(90);     // Servo mis au repos
		
		servoMillis = 0;     // Servo plus en action
	}
}

Cordialement
jpbbricole

Le montage fonctionne avec la version "pull-Down" sans la résistance.
J'avais à l'origine monté une résistance comme un montage en diviseur de tension mais avec une résistance manifestement inadaptée (1000ohm).

Merci beaucoup pour ta réactivité et compétence!

Bonjour titan65

C'est avec plaisir :smile: :

L'avantage du PULLUP est, que si l'on a plusieurs, switches boutons ou autres sur un frontal, il n'est pas nécessaire d'amener un potentiel à ces contacts, seul le GND est nécessaire et si le milieu de fonctionnement n'est pas trop perturbé, ce qui est la plus part du temps le cas, les PULLUP internent suffisent donc pas de composants externes.

Bonne continuation.
Cordialement
jpbbricole

Rebonjour,
Je suis confronté à un nouveau problème avec le même programme...
Cette fois, je souhaite comparer une valeur (issue d'une mesure via LDR) à d'autres :

  • entre 1 et 5, je multiplie par 1,25
  • entre 5 et 15, je multiplie par 1,5
  • ...
    Les deux premières fonctions if fonctionnent bien, mais les autres affichent un résultat faux (ex : pour 250*3 donne 30000 !)

Dès que la valeur en entrée est trop grosse, le programme bug.
Merci de m'aider.

int pin_button = 4;
int pin_servo = 13;
float temps_s;
float temps_ms; // conversion pour l'arduino


// librairies
#include <Servo.h>
#include <math.h>
Servo servo; // création de l'objet "servo"


unsigned long tempsAfficheur;     // Temps de l'action de l'afficheur
unsigned long servoTempo = 6000;     // Temps de l'action du servo
unsigned long servoMillis = 0;     // Chrono pour l'action du servo, si > 0 = servo en action

void setup()
{
  Serial.begin(9600);
  pinMode(pin_button, INPUT_PULLUP);
  
  servo.attach(pin_servo);
  servo.write(120);     // Servo mis au repos
  
}

void loop()
{
  //--------------------------------- Partie bouton
  if (servoMillis == 0 && (digitalRead(pin_button) == LOW))      // Si servo pas actif et bouton pressé
  {
    tempsAfficheur=millis();
    Serial.println("ON");
    
    temps_s=250;
    Serial.println("temps mesure:");
    Serial.println(temps_s);
    
    // correction non-réciprocité 
    // neocatalys.fr/blog/fabriquez-votre-stenope-partie-3-la-prise-de-vue/
    // photoclub-opti-son.be/2021/06/leffet-schwarzschild-ou-lechec-de-reciprocite-en-photographie-argentique/
    
    if ((temps_s  >= 1) && (temps_s  < 5) ) temps_s=temps_s*1.25;
    if ((temps_s  >= 5) && (temps_s  < 15) ) temps_s=temps_s*1.5;
    if ((temps_s  >= 15) && (temps_s  < 45) ) temps_s=temps_s*2;
    if ((temps_s  >= 45) && (temps_s  < 120) ) temps_s=temps_s*2.5; 
    if ((temps_s  >= 120) && (temps_s  < 300) ) temps_s=temps_s*3; 
    if ((temps_s  >= 300) && (temps_s  < 600) ) temps_s=temps_s*4;  
    if ((temps_s  >= 600) && (temps_s  < 1200) ) temps_s=temps_s*5;  
    if ((temps_s  >= 1200) && (temps_s  < 2400) ) temps_s=temps_s*6;   
    if (temps_s  >= 2400 ) temps_s=temps_s*8;
      
    temps_s = round(temps_s);
    Serial.println("temps corrige:");
    Serial.println(temps_s);
    temps_ms=temps_s*1000;
    
    
       
    servo.write(90);     // Rotation du servo
    servoMillis = millis();     // Démarrage du chrono
  }

  //--------------------------------- Partie temporisation
  if (servoMillis > 0 && (millis() - servoMillis >= temps_ms))     // Si servo actif et arrivé à échéhence
  {
    Serial.println("OFF");
    servo.write(120);     // Servo mis au repos
    
    servoMillis = 0;     // Servo plus en action
  }
}

bonjour,
inverse l'ordre de tes tests
(ou utilise if ... else)

1 Like

Même réponse que @5_cylindres.
2503 fait 750 qui est supérieur à 600 et inférieur à 1200.
750
5 fait 3750 qui est supérieur à 2400,
Donc 3750*8 fait bien 30000.
le compte est bon :slight_smile:

Pour ce genre de chose, tu gagnerais du temps à mettre des sorties sur le moniteur série.
Par exemple des Serial.println(temps_s); entre chaque IF

Si j'avais découvert ce forum plus tôt, j'aurais gagné tellement de temps...
Merci bcp pour votre réactivité!

Je cherche à ouvrir un obturateur d'appareil photo pendant une durée calculée à partir de la tension aux bornes d'une photorésistance (temps de pose de l'ordre de la minute).

Je pensais mon programme terminé, mais malgré mes modifs, il demeure très instable.
Il peut fonctionner quelques fois, mais de façon aléatoire il peut aussi buguer définitivement (m'obliger à éteindre l'arduino pour recommencer) ou bien le servo rester bloqué à 120 au delà de la valeur calculée dans temps_ms, ou bien encore activer le servo pendant qq millisecondes seulement...

Il semble qu'il y ait des problèmes de compatibilité dans la gestion des tâches entre millis() et le bouton poussoir, ou quelque chose comme çà.

Mais je vous avoue que là je suis complètement perdu...
Je vous joins le code entier.

Merci de m'aider à nouveau

// paramètres divers
int pin_button = 4;
int pin_LDR = A0; // port numérique lié à la photorésistance
int pin_servo = 13;
float valeur; //variable de type entier « valeur » (nombre compris en 0 et 1023)
float Rp; //valeur de la photorésitance (en ohm)
float temps_s;
float temps_ms; // conversion pour l'arduino

// paramètres afficheur
int digit1 = 10; 
int digit2 = 11; 
int digit3 = 12; 
int segA = 9; 
int segB = 2; 
int segC = 3;
int segD = 5;
int segE = 6; 
int segF = 8; 
int segG = 7; 

// librairies
#include <Servo.h>
#include <math.h>
Servo servo; // création de l'objet "servo"

unsigned long tempsAfficheur;     // Temps de l'action de l'afficheur
unsigned long servoTempo = 6000;     // Temps de l'action du servo
unsigned long servoMillis = 0;     // Chrono pour l'action du servo, si > 0 = servo en action

void setup()
{
  Serial.begin(9600);
  pinMode(pin_button, INPUT_PULLUP);
  pinMode(pin_LDR, INPUT); // réglage du port de la photorésistance en mode ENTREE
  servo.attach(pin_servo);
  servo.write(120);     // Servo mis au repos
  // afficheur :
  pinMode(segA, OUTPUT);
  pinMode(segB, OUTPUT);
  pinMode(segC, OUTPUT);
  pinMode(segD, OUTPUT);
  pinMode(segE, OUTPUT);
  pinMode(segF, OUTPUT);
  pinMode(segG, OUTPUT);
  pinMode(digit1, OUTPUT);
  pinMode(digit2, OUTPUT);
  pinMode(digit3, OUTPUT);
}

void loop()
{
  //--------------------------------- Partie bouton
  if (servoMillis == 0 && (digitalRead(pin_button) == LOW))      // Si servo pas actif et bouton pressé
  {
    tempsAfficheur=millis();
    Serial.println("ON");
    valeur = analogRead(pin_LDR);
    //Serial.println(valeur);
    Rp=10000*(1024/valeur-1); // calculée à partir du pont diviseur
    //Serial.println(Rp);
    temps_s=0.0005741*pow(Rp,1.35); // caractéristique de la photorésistance
    Serial.println("temps mesure:");
    Serial.println(temps_s);
    
    
    if (temps_s  >= 2400 ) temps_s=temps_s*8;
    if ((temps_s  >= 1200) && (temps_s  < 2400) ) temps_s=temps_s*6; 
    if ((temps_s  >= 600) && (temps_s  < 1200) ) temps_s=temps_s*5;
    if ((temps_s  >= 300) && (temps_s  < 600) ) temps_s=temps_s*4;
    if ((temps_s  >= 120) && (temps_s  < 300) ) temps_s=temps_s*3; 
    if ((temps_s  >= 45) && (temps_s  < 120) ) temps_s=temps_s*2.5;
    if ((temps_s  >= 15) && (temps_s  < 45) ) temps_s=temps_s*2;
    if ((temps_s  >= 5) && (temps_s  < 15) ) temps_s=temps_s*1.5;
    if ((temps_s  >= 1) && (temps_s  < 5) ) temps_s=temps_s*1.25;
    
    temps_s = round(temps_s);
    Serial.println("temps corrige:");
    Serial.println(temps_s);
    temps_ms=temps_s*1000;
    while( (millis() - tempsAfficheur) < 4000) {
      displayNumber(temps_s);
    }
    
       
    servo.write(90);     // Rotation du servo
    servoMillis = millis();     // Démarrage du chrono
  }

  //--------------------------------- Partie temporisation
  if (servoMillis > 0 && (millis() - servoMillis >= temps_ms))     // Si servo actif et arrivé à échéhence
  {
    Serial.println("OFF");
    servo.write(120);     // Servo mis au repos
    
    servoMillis = 0;     // Servo plus en action
  }
}

void displayNumber(int toDisplay) {
#define DISPLAY_BRIGHTNESS  1000
#define DIGIT_ON  HIGH
#define DIGIT_OFF  LOW

  for(int digit = 3 ; digit > 0 ; digit--) {
    
    //Turn on a digit for a short amount of time
    switch(digit) {
      case 1:
        digitalWrite(digit1, DIGIT_ON);
        break;
      case 2:
        digitalWrite(digit2, DIGIT_ON);
        break;
      case 3:
        digitalWrite(digit3, DIGIT_ON);
        break;
      }
    
    //Turn on the right segments for this digit
    lightNumber(toDisplay % 10);
    toDisplay /= 10;
    
    delayMicroseconds(DISPLAY_BRIGHTNESS); //Display this digit for a fraction of a second (between 1us and 5000us, 500 is pretty good)

    
    //Turn off all digits
    digitalWrite(digit1, DIGIT_OFF);
    digitalWrite(digit2, DIGIT_OFF);
    digitalWrite(digit3, DIGIT_OFF);    
  }
}

//Given a number, turns on those segments
//If number == 10, then turn off number
void lightNumber(int numberToDisplay) {

#define SEGMENT_ON  LOW
#define SEGMENT_OFF HIGH

  switch (numberToDisplay){

  case 0:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 1:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 2:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 3:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 4:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 5:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 6:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 7:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 8:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 9:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 10:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;
  }
}

Bonjour titan65

Comment as-tu alimenté ton servo, sur la pin 5v de l'Arduino ?
Si oui, prends une alimentation 5v externe comme un chargeur USB, en mettant son GND avec le GND de l'Arduino.

Cordialement
jpbbricole

Pour assurer le coup, j'alimente entre le pin VIN et le GROUND de l'arduino avec une batterie li-ion 7,4V.
La puissance est suffisante pour alimenter l'afficheur et le servo.
(cela dit, lorsque je désactive l'afficheur qui est susceptible de consommer pas mal, le programme est instable aussi...)

Y a-t-il un problème avec la manière dont j'alimente le système?
Il faut que l'arduino soit indépendant d'un ordi, et je m'étais aperçu que d'alimenter avec une batterie 5V ne suffisait pas pour servo et afficheur, et faisait planter...

Quoiqu'il en soit, mon programme est trop aléatoire pour que je puisse l'utiliser en l'état.
Est-ce un problème de programmation?
Merci de m'aider; je suis perdu.

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