Moteur pas à pas et minuteur

Bonjour,
Pour un projet que je suis en train de réaliser, j'aurais besoin de faire tourner un moteur pas à pas avec un minuteur.
Je sais à peu près contrôler le moteur et j'ai le code pour le minuteur qui fonctionne parfaitement.
Seulement voilà, je n'arrive pas à imbriquer les deux.
Le principe est de déterminer une durée à l'aide du minuteur et pendant la durée choisie le moteur doit tourner dans un sens, s'arrêter, tourner dans l'autre sens, s'arrêter et ainsi de suite jusqu'à a fin du chrono.
La cerise sur le gâteau serait de pouvoir avoir 2 vitesses de rotation selectionnables avec un spdt.
Si quelqu'un peut m'aider ça serait super.
Le moteur est un Nema 17 avec un A4988, le lcd un 1602 I2C, un petit buzzer, 4 boutons poussoirs et un nano 328P (clone).
Voici le code du minuteur :

#include <LiquidCrystal_I2C.h>
#include "Countimer.h"
Countimer tdown;
LiquidCrystal_I2C lcd (0x3F, 16, 2);
#include <EEPROM.h>

#define bt_set    A0
#define bt_up     A1
#define bt_down   A2
#define bt_start  A3

int time_s = 0;
int time_m = 0;
int time_h = 0;

int set = 0;
int flag1=0, flag2=0;

int relay = 5;
int buzzer = 11;

void setup() {

pinMode(bt_set,   INPUT_PULLUP);
pinMode(bt_up,    INPUT_PULLUP);
pinMode(bt_down,  INPUT_PULLUP);
pinMode(bt_start, INPUT_PULLUP);

pinMode(relay, OUTPUT);
pinMode(buzzer, OUTPUT);

lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("     UPB-1A   ");
lcd.setCursor(0,1);
lcd.print("   CHZ Timer");
tdown.setInterval(print_time, 999);
eeprom_read();
delay(2000);
lcd.clear();
}

void print_time(){
time_s = time_s-1;
if(time_s<0){time_s=59; time_m = time_m-1;}
if(time_m<0){time_m=59; time_h = time_h-1;}
}

void tdownComplete(){Serial.print("ok");}

//tdown.stop(); 

void loop(){
tdown.run();

if(digitalRead (bt_set) == 0){
if(flag1==0 && flag2==0){flag1=1;
set = set+1;
if(set>3){set=0;}
delay(100); 
}
}else{flag1=0;}

if(digitalRead (bt_up) == 0){
if(set==0){tdown.start(); flag2=1;}
if(set==1){time_s++;}
if(set==2){time_m++;}
if(set==3){time_h++;}
if(time_s>59){time_s=0;}
if(time_m>59){time_m=0;}
if(time_h>99){time_h=0;}
if(set>0){eeprom_write();}
delay(200); 
}

if(digitalRead (bt_down) == 0){
if(set==0){tdown.stop(); flag2=0;}
if(set==1){time_s--;}
if(set==2){time_m--;}
if(set==3){time_h--;}
if(time_s<0){time_s=59;}
if(time_m<0){time_m=59;}
if(time_h<0){time_h=99;}
if(set>0){eeprom_write();}
delay(200); 
}

if(digitalRead (bt_start) == 0){ flag2=1; 
  eeprom_read(); 
  digitalWrite(relay, HIGH); 
  tdown.restart(); 
  tdown.start();
}

lcd.setCursor(0,0);
if(set==0){lcd.print("  UPB-1A Timer  ");}
if(set==1){lcd.print(" Set Timer SEC  ");}
if(set==2){lcd.print(" Set Timer MIN  ");}
if(set==3){lcd.print(" Set Timer HOUR ");}

lcd.setCursor(4,1);
if(time_h<=9){lcd.print("0");}
lcd.print(time_h);
lcd.print(":");
if(time_m<=9){lcd.print("0");}
lcd.print(time_m);
lcd.print(":");
if(time_s<=9){lcd.print("0");}
lcd.print(time_s);
lcd.print("   ");

if(time_s==0 && time_m==0 && time_h==0 && flag2==1){flag2=0;
tdown.stop(); 
digitalWrite(relay, LOW);
digitalWrite(buzzer, HIGH);
delay(300);
digitalWrite(buzzer, LOW);
delay(200);
digitalWrite(buzzer, HIGH);
delay(300);
digitalWrite(buzzer, LOW);
delay(200);
digitalWrite(buzzer, HIGH);
delay(300);
digitalWrite(buzzer, LOW);
}

if(flag2==1){digitalWrite(relay, HIGH);}
else{digitalWrite(relay, LOW);}

delay(1);
}

void eeprom_write(){
EEPROM.write(1, time_s);  
EEPROM.write(2, time_m);  
EEPROM.write(3, time_h);  
}

void eeprom_read(){
time_s =  EEPROM.read(1);
time_m =  EEPROM.read(2);
time_h =  EEPROM.read(3);
}
[Countdown_Timer_Nano_MIFO.zip|attachment](upload://xtXxK7mN1DphqFGgy7KRSvuaYcs.zip) (3.0 KB)

Bonjour tummler

Une petite remarque concernant ton programme, il, faut l'indenter
image
ça facilite la lecture.

void eeprom_write(){
EEPROM.write(1, time_s);  
EEPROM.write(2, time_m);  
EEPROM.write(3, time_h);  
}

L'EEPROM a un nombre de cycle limité, 100'000, c'est beaucoup, mais limité quand même, c'est pourquoi il est préférable d'utiliser, à la place de EEPROM.write, EEPROM.update, ça peut économiser des cycles.

A la fin du minuteur, le cycle doit s'arrêter où qu'il soit, ou le cycle doit se terminer et donc revenir à la position de départ?

Cordialement
jpbbricole

Bonjour,
Merci pour votre réponse.
Première chose, j'aurais appris un truc, je ne connaissais pas le terme indentation.
Ensuite, je pense que le cycle peut s'arrêter là où il est, le plus important est que la durée soit respectée.

Bonjour tummler

Ce qui veut dire que la position de départ du MPAP a peu d'importance?

A+
Cordialement
jpbbricole

Oui, le moteur peut partir à n'importe quel moment du cycle. Si par exemple il fait trois tours dans un sens puis trois tours dans l'autre, la première rotation peut être d'un nombre de pas aléatoire tant qu'il reprend le cycle normal de trois tours la rotation suivante.
J'espère être clair.

Bonjour tummler

Donc il faut mémoriser sa position au moment de l'arrêt pour être sûre de rattraper le cycle au prochain départ.?

Q'en est-il de la position de départ au moment de la mise en route du système ou après un arrêt. Est-il prévu une position 0 physique?

Pendant le fonctionnement du MPAP, est-ce-que le programme est susceptible de faire autre chose?

A+
Cordialement
jpbbricole

Non, ce n'est pas nécessaire.

Non, le rôle du moteur est juste de faire tourner un axe, sa position n'a pas d'importance.

Pas pour l'instant mais j'aimerais par la suite avoir un menu lcd et aussi deux vitesses de rotation différentes.

Bonjour tummler

J'ai fait un programme d'essai pour voire si le "mouvement" te convient.
Toutes les 10 secondes et pendant 10 secondes (mpapSequTempo = 10000), il répète un mouvement CW CCW (mpapStepsMove = 200). Si le commutateur est sur on (spdPin == LOW), le MPAP passe en vitesse lente (mpapSpeedLow), sinon il est en vitesse rapide (mpapSpeedHigh)

Les connexions du driver A4988 et du SPD:

#define mpapDirPin 4     // DIR dsu driver A4988
#define mpapStepPin 5     // STEP dsu driver A4988

#define spdPin 6     // Pin du commutateur de vitesse

A adapter à ton installation

S'il y a un changement de vitesse (SPD), ce changement est effectif au début du prochain cycle CW CCW.
Au démarrage d'un cycle de 10 secondes, si un cycle CW CCW des dernières 10 secondes n'était pas terminé, il se termine. Si pendant la pause la vitesse a changé, il se terminera à la vitesse à laquelle il a débuté.

// https://www.arduino.cc/reference/en/libraries/accelstepper/
// https://www.pjrc.com/teensy/td_libs_AccelStepper.html

#include <AccelStepper.h>

#define mpapDirPin 4     // DIR dsu driver A4988
#define mpapStepPin 5     // STEP dsu driver A4988
AccelStepper mpap(1, mpapStepPin, mpapDirPin);

boolean mpapMoveCW = true;     // Sense horaire

unsigned long mpapSequTempo = 10000;     // Toutes les 3 secondes
unsigned long mpapSequTemp = millis() + mpapSequTempo;

const float mpapSpeedHigh = 300.0;     // Vitesse haute
const float mpapSpeedLow = 100.0;     // Vitesse basse
float mpapSpeed = 0;

int mpapStepsMove = 200;     // Déplacement du MPAAP en pas

boolean mpapSequOn = false;
#define spdPin 6     // Pin du commutateur de vitesse

void setup()
{
	Serial.begin(115200);

	pinMode(spdPin, INPUT_PULLUP);
}

void loop()
{
	if (millis()-mpapSequTemp >= mpapSequTempo)
	{
		mpapSequOn = !mpapSequOn;
		mpapSequTemp = millis();
	}

	if (mpap.distanceToGo() == 0)
	{
		mpapSpeed = digitalRead(spdPin) == LOW ? mpapSpeedLow : mpapSpeedHigh;
		mpap.setMaxSpeed(mpapSpeed);
		
		mpap.setAcceleration(mpapSpeed*10.0);
		if (mpapMoveCW)
		{
			mpap.moveTo(0);
			mpapMoveCW = false;
		}
		else
		{
			mpap.moveTo(mpapStepsMove);
			mpapMoveCW = true;
		}
	}

	if (mpapSequOn)
	{
		mpap.run();
	}
}

Pour intégrer ça dans ton programme (sans tout ce qui tourne autour du timer de 10 sec.), il faut mettre la variable mpapSequOn = true au démarrage de la temporisation et à false à la fin de la temporisation.
Je t'indiquerai comment intégrer le tout.

A+
Cordialement
jpbbricole

Merci !
J'essaye ça dès que possible.
Edit : j'essaie de comprendre le fonctionnement du code, j'approfondirai demain.
Bonne soirée.

C'est super, c'est vraiment ce qu'il me faut.
Par contre je ne comprends pas comment changer la durée pendant laquelle le moteur est arrêté. La pause n'a pas besoin d'être si longue.

Bonjour tummler

Oupsss, j'avais oublié ce point, quel est, en moyenne, ce temps d'arrêt?
La pause doit avoir lieu après CW et aussi après CCW, de telle façon que dans une suite de mouvements CW CCW, il y aie une pause après chaque mouvement?

Cordialement
jpbbricole

Un temps d'arrêt d'une seconde me semble bien.

Oui, j'aimerais que ça fasse CW-pause-CCW-pause.

Ok, merci

Bonjour tummler

J'ai ajouté une temporisation, j'ai fait simple, avec un simple delay(cwToCcwDelay), si cette temporisation gêne le déroulement de ton programme on peut faire plus élaboré.
Les modifications sont l'ajout d'une nouvelle variable et de la temporisation:

// https://www.arduino.cc/reference/en/libraries/accelstepper/
// https://www.pjrc.com/teensy/td_libs_AccelStepper.html

#include <AccelStepper.h>

#define mpapDirPin 4     // DIR dsu driver A4988
#define mpapStepPin 5     // STEP dsu driver A4988
AccelStepper mpap(1, mpapStepPin, mpapDirPin);

boolean mpapMoveCW = true;     // Sens horaire

unsigned long mpapSequTempo = 10000;     // Toutes les 3 secondes
unsigned long mpapSequTemp = millis() + mpapSequTempo;

const float mpapSpeedHigh = 300.0;     // Vitesse haute
const float mpapSpeedLow = 100.0;     // Vitesse basse
float mpapSpeed = 0;

int mpapStepsMove = 200;     // Déplacement du MPAAP en pas

boolean mpapSequOn = false;
#define spdPin 6     // Pin du commutateur de vitesse

int cwToCcwDelay = 1000;     // Delais entre 2  mouvement

void setup()
{
	Serial.begin(115200);

	pinMode(spdPin, INPUT_PULLUP);
}

void loop()
{
	if (millis()-mpapSequTemp >= mpapSequTempo)
	{
		mpapSequOn = !mpapSequOn;
		mpapSequTemp = millis();
	}

	if (mpap.distanceToGo() == 0)
	{
		mpapSpeed = digitalRead(spdPin) == LOW ? mpapSpeedLow : mpapSpeedHigh;
		mpap.setMaxSpeed(mpapSpeed);

		delay(cwToCcwDelay);
				
		mpap.setAcceleration(mpapSpeed*10.0);
		if (mpapMoveCW)
		{
			mpap.moveTo(0);
			mpapMoveCW = false;
		}
		else
		{
			mpap.moveTo(mpapStepsMove);
			mpapMoveCW = true;
		}
	}

	if (mpapSequOn)
	{
		mpap.run();
	}
}

Si cette modification joue, j'intégrerai la fonction dans ton programme.

A+
cordialement
jpbbricole

Le moteur tourne bizarrement.
Avec spd LOW il fait un tour CW, pause, un tour CCW, pause, un autre tour CW puis une longue pause (env 10 sec) puis un petit à-coup et il recommence.
Avec spd HIGH c'est dur à décrire j'ai fait une petite vidéo : spd high l - YouTube
A+

Jusqu'à la seconde 26 ça semble erratique! après le fonctionnement, en mode mpapSpeedHigh semble tout à fait normal.
S'il y a des à-coups à la fin des périodes mpapSequTempo, ça vient du fait que les périodes mpapSequTempo ne sont pas synchrones avec les mouvements CCW>CW, donc un mouvement CW>CCW peut juste commencer à la fin d'une période mpapSequTempo d'où quelques pas comme un à coups ou qu'il reste juste quelques pas pour terminer un mouvement CW>CCW et que ces quelques pas manquants sont terminés à la prochaine période mpapSequTempo, d'où un à-coups.

Pour ce qui est du fonctionnement erratique, je pencherai pour la connexion du SPD qui semble "en l'air"
As-tu mis le bon port dans la nouvelle version?

#define spdPin 6     // Pin du commutateur de vitesse

Chez moi, 6.

J'ai testé la version du post #14, sans problème.

A+
Cordialement
jpbbricole

Je reteste ça.
Du coup j'ai un doute sur le spd c'est bien ça (avec le milieu sur le pin 6, un au +5 et un au gnd) ?

Bonsoir tummler

Comme c'est initialisé PULL_UP

	pinMode(spdPin, INPUT_PULLUP);

Le point central sur GND et l'autre sur spdPin.
Le +5V n'est pas nécessaire.

A+
Cordialement
jpbbricole

Ok, je verrai ça demain.
Encore merci.

J'avais un problème d'alim, le moteur ne "broute" plus.