Clignotant à temps inegaux et longs avec millis()

Bonjour aux bonnes volontés!

Pour remplacer un système de minuterie à 555, je voudrais utiliser un arduino nano.
Le but est d'avoir une durée d'allumage de plusieurs minutes et des temps d'attente de plusieurs heures..
pour ce faire j'ai le sketch suivant :

Le problème est que si c'est ok pour des petites valeurs de durée (ici 5 secondes) et attente (ici 32 secondes), le fonctionnement devient fantaisiste des que "Attente" vaut 33 secondes!
Il y a un dépassement de capacité d'une variable quelque part alors que j'ai tout passé les variables en unsigned long!
Qu'est ce qu'il ne vas pas?
Merci! pour votre attention!

#define PIN 4
unsigned long Duree = 5*1000;
unsigned long Attente= 32*1000;
unsigned long TempsFutur;
unsigned long TempsActuel;

void setup() {
  Serial.begin(9600);
  pinMode(PIN, OUTPUT);
}

void loop() {
   TempsActuel = millis();
  if (TempsActuel > TempsFutur) {
    if (digitalRead(PIN)) {
      digitalWrite(PIN, LOW);
      TempsFutur = TempsActuel + Attente;
    } else {
      digitalWrite(PIN, HIGH);
      TempsFutur = TempsActuel + Duree;
    }
  }
Serial.print(F("Valeur durée Allumée= "));
Serial.println(Duree); // valeur analogique lue;
//Serial.println(TempsActuel); // valeur analogique lue; 
Serial.print(F("durée éteinte= "));
Serial.println(Attente); // valeur analogique lue;
//Serial.println(TempsFutur); // valeur analogique lue;
}

Par défaut les nombres sont des int. Si on veut avoir des constantes unsigned long, il faut le dire:
unsigned long Attente= 32*1000UL;
si un des deux nombre est UL, le résultat le sera aussi

sauf 32*1000

1 Like

Merci! Ca fonctionne!
Je croyais à tort qu'une fois la valeur "attente" déclarée en UL en début de ligne , il n'y avait pas besoin de redéclarer après la valeur que j'y mettais car c'était implicite!....
ca m'explique aussi des dysfonctionnements d'autres sketches que j'ai sur le feu!
merci encore!

Maintenant , pour ne pas avoir à entrer "en dur" la valeur Durée, j'ai mis un potentiomètre de 10K avec son curseur sur A0 , (je ferai la même chose pour l'attente).
mais là encore il y a des commutations fantaisistes alors que les valeurs normalisées par map() sont coherentes... ca manque de debuggueur en mode trace!!
quelqu'un aurait une idée?

sais//
// But : temporisation répétitive tant que l'Arduino est alimenté.
//----- Durée allumage de la LED (ou d'un relais de puissance)  fonction de la position du curseur du potentiometre  en A0
// durée etalonnée en secondes. commutable à terme en minutes par un petit interrupeur sur une entrée digitale 
//----- Attente entre  allumages de la LEd fonction de la position du curseur du potentiometre  en A1
//      attente etalonnée en minutes. commutable à terme en heures par un petit interrupeur sur ue entrée digitale
//      pouvant aller jusqu'à 24 heures
//*

#define PIN 4
unsigned long Duree ;
unsigned long Attente= 30*1000UL;
unsigned long TempsFutur = 0UL;
unsigned long TempsActuel= 0UL;


int pinPotDuree = A0; // la broche d'entrée pour le potentiomètre Duree
unsigned long valPotDuree = 0UL; //Variable pour stocker la valeur provenant du potentiomètre Duree





void setup() {
  Serial.begin(9600);
  pinMode(PIN, OUTPUT);
}

void loop() {

// Lit la valeur du potentiomètre Duree
unsigned long valPotDuree=analogRead(pinPotDuree); 
  
 //convertion valeur duree de 0 à 1023 du CAN en intervalles de 1 à 60 valeurs 
unsigned long Duree = map(valPotDuree,0,1023,1,60);
  
   TempsActuel = millis();
  if (TempsActuel > TempsFutur) {
    if (digitalRead(PIN)) {
      digitalWrite(PIN, LOW);
      TempsFutur = TempsActuel + Attente;
    } else {
      digitalWrite(PIN, HIGH);
      TempsFutur = TempsActuel + Duree;
    }
  }
Serial.print(F("Valeur durée Allumée= "));
Serial.println(Duree); // valeur analogique lue;
Serial.println(TempsActuel); // valeur analogique lue; 
Serial.print(F("durée éteinte= "));
Serial.println(Attente); // valeur analogique lue;
Serial.println(TempsFutur); // valeur analogique lue;
}issez ou collez du code ici

Si Duree vaut entre 1 et 60ms ou secondes? TempsActuel est en ms, donc Duree doit l'être aussi! Du coup temps longs, c'est pas vraiment ça! C'est pas plutôt
TempsFutur = TempsActuel + Duree*1000;
ou
unsigned long Duree = map(valPotDuree,0,1023,1,60000UL);

[edit]
Attention map utilise des long (signés) et il ne faut pas dépasser
unsigned long Duree = map(valPotDuree,0,1023,1,131200UL);
si on ne veut pas de débordements. Si on veut aller deux fois plus haut, il faut faire la conversion sans map.

valPotDuree et Duree sont définis 2 fois, une fois comme variable globale et une fois comme variable locale.
Dans le contexte ce n'est pas gênant mais il y a des cas où cela pourrait conduire à des erreurs difficile à localiser.

Merci pour les infos de correction.
J'ai modifié comme vous me l'indiquez: variable globale et tenu compte de la cohérence en millis.
J'ai rajouté un potentiomètre 10k en A1 pour le temps d'attente en plus de celui de durée.
Mais le comportement n'est pas celui attendu ! c'est un vulgaire clignotant!. j'ai du faire une grosse erreur quelque part!


// But : temporisation répétitive tant que l'Arduino est alimenté.
//----- Durée allumage de la LED (ou d'un relais de puissance)  fonction de la position du curseur du potentiometre  en A0
// durée etalonnée en secondes. commutable à terme en minutes par un petit interrupeur sur une entrée digitale 
//----- Attente entre  allumages de la LEd fonction de la position du curseur du potentiometre  en A1
//      attente etalonnée en minutes. commutable à terme en heures par un petit interrupeur sur une entrée digitale
//      pouvant aller jusqu'à 24 heures
//*

#define PIN 4

unsigned long Duree ;
unsigned long Attente;
unsigned long TempsFutur = 0UL;
unsigned long TempsActuel= 0UL;


int pinPotDuree = A0; // la broche d'entrée pour le potentiomètre Duree
unsigned long valPotDuree = 0UL; //Variable pour stocker la valeur provenant du potentiomètre Duree

int pinPotAttente= A1; // la broche d'entrée pour le potentiomètre Attente
unsigned long valPotAttente = 0UL;//Variable pour stocker la valeur provenant du potentiomètre Attente


void setup() {
  Serial.begin(9600);
  pinMode(PIN, OUTPUT);
}

void loop() {

// Lit la valeur du potentiomètre Duree
unsigned long valPotDuree=analogRead(pinPotDuree); 
 //convertion valeur duree de 0 à 1023 du CAN Duree en intervalles de 1 à 60 valeurs 
unsigned long Duree = map(valPotDuree,0,1023,1,60000UL);

// Lit la valeur du potentiomètre Attente
unsigned long valPotAttente=analogRead(pinPotAttente); 
//convertion valeur duree de 0 à 1023 du CAN Duree en intervalles de 1 à 60 valeurs 
unsigned long Attente = map(valPotDuree,0,1023,1,60000UL); 
  
   TempsActuel = millis();
  if (TempsActuel > TempsFutur) {
    if (digitalRead(PIN)) {
      digitalWrite(PIN, LOW);
      TempsFutur = TempsActuel + Attente;
    } else {
      digitalWrite(PIN, HIGH);
      TempsFutur = TempsActuel + Duree;
    }
  }
Serial.print(F("Valeur durée Allumée= "));
Serial.println(Duree); // valeur analogique lue;
 
Serial.print(F("durée éteinte= "));
Serial.println(TempsActuel); // valeur analogique lue; 

}

C'est-à-dire?

D’abord dans le sketch que j'ai précédemment envoyé il y a deux erreurs grossières: j'ai répété les 2 lignes concernant la durée. J'ai corrigé, et voici le sketch qui tourne:
Le potar durée repond correctement (valeur coherente, le potar attente, donne des temps d'attente vraiment bizarres. peut etre s'agit il d'un mauvais choix de plage dans les arguments de la fonction val(). je cherche et je teste.. mais pas bon!

// But : temporisation répétitive tant que l'Arduino est alimenté.
//----- Durée allumage de la LED (ou d'un relais de puissance)  fonction de la position du curseur du potentiometre  en A0
// durée etalonnée en secondes. commutable à terme en minutes par un petit interrupeur sur une entrée digitale 
//----- Attente entre  allumages de la LEd fonction de la position du curseur du potentiometre  en A1
//      attente etalonnée en minutes. commutable à terme en heures par un petit interrupeur sur ue entrée digitale
//      pouvant aller jusqu'à 24 heures
//*

#define PIN 4
unsigned long Duree ;
unsigned long Attente= 30*1000UL;
unsigned long TempsFutur = 0UL;
unsigned long TempsActuel= 0UL;


int pinPotDuree = A0; // la broche d'entrée pour le potentiomètre Duree
//unsigned long valPotDuree = 0UL; //Variable pour stocker la valeur provenant du potentiomètre Duree





void setup() {
  Serial.begin(9600);
  pinMode(PIN, OUTPUT);
}

void loop() {

// Lit la valeur du potentiomètre Duree
unsigned long valPotDuree=analogRead(pinPotDuree); 
  
 //convertion valeur duree de 0 à 1023 du CAN en intervalles de 1 à 60 valeurs 
unsigned long Duree = map(valPotDuree,0,1023,1,60000UL);
  
   TempsActuel = millis();
  if (TempsActuel > TempsFutur) {
    if (digitalRead(PIN)) {
      digitalWrite(PIN, LOW);
      TempsFutur = TempsActuel + Attente;
    } else {
      digitalWrite(PIN, HIGH);
      TempsFutur = TempsActuel + Duree;
    }
  }
Serial.print(F("Valeur durée Allumée= "));
Serial.println(Duree/1000); // valeur analogique lue;
 
Serial.print(F("durée éteinte= "));
Serial.println(Attente/1000); // valeur analogique lue;


//Serial.println(TempsActuel); // valeur analogique lue;
//Serial.println(TempsFutur); // valeur analogique lue
}
;

Quand j'ecris :

unsigned long Duree = map(valPotDuree,0,1023,5,60000UL);
unsigned long Attente = map(valPotAttente,0,1023,10,60000UL); 

Je dois obtenir valeur min de 5 secondes pour la durée
et valeur min de 10 secondes pour l'attente , c'est bien cela?

Bonsoir:

Quand j'ecris :

unsigned long Duree = map(valPotDuree,0,1023,5000UL,60000UL);
J'obtiens bien valeur min de 5 secondes et valeur max de 60 secondes
et quand j'ecris :

unsigned long Attente = map(valPotAttente,0,1023,10000UL,60000UL); 
```je n'obtiens pas val min 10secondes, et val max 60secondes
quelle est la cause de cette diablerie?

Si c'est le code juste au-dessus (#9) tu ne lis pas le second potentiomètre.

mais si! voici le code : il y a bien une lecture du potar . cf le code ci après.

//
// But : temporisation répétitive tant que l'Arduino est alimenté.
//----- Durée allumage de la LED (ou d'un relais de puissance)  fonction de la position du curseur du potentiometre  en A0
// durée etalonnée en secondes. commutable à terme en minutes par un petit interrupeur sur une entrée digitale 
//----- Attente entre  allumages de la LEd fonction de la position du curseur du potentiometre  en A1
//      attente etalonnée en minutes. commutable à terme en heures par un petit interrupeur sur une entrée digitale
//      pouvant aller jusqu'à 24 heures
//*

#define PIN 4

unsigned long Duree ;
unsigned long Attente;
unsigned long TempsFutur = 0UL;
unsigned long TempsActuel= 0UL;


int pinPotDuree = A0; // la broche d'entrée pour le potentiomètre Duree
unsigned long valPotDuree = 0UL; //Variable pour stocker la valeur provenant du potentiomètre Duree

int pinPotAttente= A1; // la broche d'entrée pour le potentiomètre Attente
unsigned long valPotAttente = 0UL;//Variable pour stocker la valeur provenant du potentiomètre Attente


void setup() {
  Serial.begin(9600);
  pinMode(PIN, OUTPUT);
}

void loop() {

// Lit la valeur du potentiomètre Duree
unsigned long valPotDuree=analogRead(pinPotDuree); 
 //convertion valeur duree de 0 à 1023 du CAN Duree en intervalles de 1 à 60 valeurs 
unsigned long Duree = map(valPotDuree,0,1023,5000UL,60000UL);

// Lit la valeur du potentiomètre Attente
unsigned long valPotAttente=analogRead(pinPotAttente); 
//convertion valeur Attente de 0 à 1023 du CAN Attente en intervalles de 1 à 60 valeurs 
unsigned long Attente = map(valPotAttente,0,1023,10000UL,60000UL); 
  
   TempsActuel = millis();
  if (TempsActuel > TempsFutur) {
    if (digitalRead(PIN)) {
      digitalWrite(PIN, LOW);
      TempsFutur = TempsActuel + Attente;
    } else {
      digitalWrite(PIN, HIGH);
      TempsFutur = TempsActuel + Duree;
    }
  }
Serial.print(F("Valeur durée Allumée= "));
Serial.println(Duree/1000); // valeur analogique lue;
 
Serial.print(F("durée éteinte= "));
Serial.println(Attente/1000); // valeur analogique lue; 

}

bonsoir
voici ce que j'obtiens pour une position mediane du potar Attente.
Si pour la Duree la valeur obtenue est correcte avec ce que je mesure au chronomètre, le temps d'attente est incohérent et instable : sans que j'actionne le potar attente, la valeur affichée varie d'une boucle à l'autre et est instable comme s'il y avait un débordement de capacité dans une variable. A l'oscilloscope il n'y a pas de bruit (crachotements) sur le curseur du potar...
Quelqu'un aurait-il une idée?

Capture d’écran du 2022-03-09 18-47-11

Il y a une erreur que je ne vois pas. Plutôt comme si le code que tu donnes n'est pas celui qui fait l'affichage. Quelle que soit la tension sur la broche pinPotAttente que le potar crache ou non qu'il soit branché ou pas, si on exécute les trois lignes:

unsigned long valPotAttente=analogRead(pinPotAttente); 
unsigned long Attente = map(valPotAttente,0,1023,10000UL,60000UL); 
Serial.println(Attente/1000); // valeur analogique lue;

la première met dans valPotAttente un nombre compris entre 0 et 1023
la deuxième met dans alors dans Attente une valeur comprise entre 10'000 et 60'000
la dernière ne peut qu'afficher un nombre compris entre 10 et 60. On ne devrait pas pouvoir voir ni 100 ni 400.

Attention, comme l'a déjà signalé @fdufnews dans le post #9 des références sont définies deux fois. Maintenant c'est valPotDuree, Duree, valPotAttente et Attente

Attention aussi, il vaut mieux éviter de calculer des instants "futurs" car cela pose un problème à chaque débordement de millis() . Il est vrai que pour le voir if faut attendre 49 jours, mais c'est une bonne habitude à prendre. Pour le voir, je vais supposer que Attente et Duree valent toutes deux 5 jours et que temps actuel est de 10 jours. TempsFutur prendra alors les valeurs 5jours, 10 jours, 15 jours...
Mais quand TempsActuel vaut 45 jours, faire
TempsFutur = TempsActuel + Attente;
devrait donner 50 jours, mais a cause du débordement, on aura 1 jour (toutes les valeurs supérieures à 49 jours sont diminuées de 49 jours. millis() aussi fera 47, 48, 49, 0, 1, 2jours...)
et du coup la comparaison TempsActuel > TempsFutur sera vrai immédiatement.

Il faut comparer des intervalles de temps, genre
`TempsActuel -TempsDerniereAction > Attente
En faisant ainsi, si on a un débordement sur TempsActuel, il y aura aussi un débordement lors de la soustraction et la comparaison fonctionne. Par exemple si TempsDerniereAction vaut 49 jours, la condition sera fausse pour TempsActuel=49jours. Le lendemain TempsActuel a augmenté d'un jour, mais il y a débordement (on repart à 0 si on dépasse 49 jours) Temps actuel vaut donc 0. A gauche de la comparaison, on a 0-49 ce qui fait 1 car la soustraction aussi déborde. Et la comparaison est bonne.

Bonjour patrig

Est-ce que les GND des potentiomètres et celui de l'Arduino sont bien connectés ensemble?
De telles variations sont typiques de ce type "d'oubli".

Cordialement
jpbbricole

Avec ces trois lignes:

unsigned long valPotAttente=analogRead(pinPotAttente); 
unsigned long Attente = map(valPotAttente,0,1023,10000UL,60000UL); 
Serial.println(Attente/1000); // valeur analogique lue;

Pour afficher 100 et 400, il faudrait qu'analogRead donne des valeurs de l'ordre de 3000! Même avec des erreurs de masses ce n'est pas croyable.

Bonjour vileroi

Oui, juste!
J'ai essayé son programme et il ne fait pas les variations signalées par @patrig
Les seules choses "contestables" sont

unsigned long Duree = map(valPotDuree,0,1023,5000UL,60000UL);
unsigned long Attente = map(valPotAttente,0,1023,10000UL,60000UL);
map(), c'est des int.

la même chose pour
unsigned long valPotDuree=analogRead(pinPotDuree);
c'est aussi des int.
Mais ça marche ainsi :wink:

Dans un cas comme celui-ci, avec des temps longs, il est mieux de tout "travailler" en secondes donc en int et d'ajuster, en toute fin , en millisecondes en faisant un * 1000UL.

Cordialement
jpbbricole

Bonjour à tous!
Ca marche! J'ai trouvé cette nuit !
J'ai recâblé en dur cette nuit avec fer à souder et Tynol.
Le défaut était vachard. Il y avait une microrupture dans la bande de masse de la breadboard d'origine chinoise achetée sur Amazon. Défaut qui n'apparaissait qu'en enfonçant fortement la pinoche du câble Dupont. Le défaut (microcoupure) est mesurable au multimètre en mettant les pointes de test dans un des trous consécutifs des bandes d'alimentation + et - puis en poussant fortement avec une petite pinoche! C'est de la camelote donc. J'ai ressorti d'anciennes breadboards bleues achetées autrefois chez Pentasonic dont les contacts sont en bronze argenté et qui n'ont pas ce défaut de qualité.
Attention donc à ces breadboards chinoises!
maintenant, il reste à remplacer les potars par des contacteurs à galette genre Jeanrenaud et resistances discrètes puis à etalonner en temps..;
mon montage servira à faire une minuterie analogue à une minuterie d'arrosage , et à une autre pour distributeur de nourriture dans un grand bassin, et à generer des effets de marée dans des bacs..
merci à tous pour votre aide et vos conseils éclairés!

Justement ce lien donne le contraire:
For the mathematically inclined, here’s the whole function
long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Ce sont des long signés, mais ici on est loin des problèmes de signes.

De toutes façon, un jour ou l'autre une conversion est faite. En mettant UL ici, c'est nous qui le demandons avant.

Je ne suis pas convaincu, car de 1024 valeurs, on passerait à 50 valeurs seulement. Pourquoi perdre les autres? Ainsi écrit on a moins de paliers.

Je ne m'explique pas les valeurs 100 et 400! Mais c'est souvent une erreur que l'on corrige sans faire exprès (une mauvaise version possible).