Une solution pour ne pas avoir de problème avec millis() sur plus de 49 Jours

Portrait de brossden

Lorsque on veut positionner une temporisation dans un programme Arduino il existe deux solutions.

  • La première c'est avec la fonction 'delay(x)' mais le problème c'est qu'avec cette solution le programme attend que le délais 'x' soit passer pour continuer à exécuter le code. Dans ce cas il ne fait plus rien et dès que le temps 'x' est écoulé le programme saute à la ligne suivante jusqu'au prochain 'delay(y)' s'il y en a un. Donc dans beaucoup de cas 'delay(x)' ne convient pas.
  • La seconde c'est d'utiliser millis(). Cette instruction renvoi le temps en milliseconde depuis que le module Arduino a commencé à exécuter le code avec lequel il a été programmé.
    Pour utiliser cette solution il faut attribuer à une variable de type 'unsigned long' la valeur de millis() + la valeur du délais à respecter en milliseconde. Dans un deuxième temps on compare cette variable à 'millis()' et si 'millis()' est supérieure ou égale à celle-ci on exécute le code prévu.

Exemple :  Faire-clignoter-des-leds-des-frequences-differentes-ou-synchro

Comme toute les bonnes choses ont une fin la valeur de millis() fini par atteindre la valeur maximum du compteur 32 bits soit 0xFFFFFFFF ou  4,294,967,295 ce qui correspond à un peu plus de 49 jours. Exactement : 49 jours 17 heures 2 minutes 47 secondes 295 millisecondes, pour être précis !

Pour le programmes qui n'ont pas vocation à s'exécuter aussi longtemps il n'y a pas de soucis, mais pour ceux dont le temps de fonctionnement est supérieur à ce délais... c'est une autre affaire.
Que se passe t'il ? Et bien le compteur une fois arrivé à 4,294,967,295 soit 0xFFFFFFFF en hexa il repasse à 0 avec un flag de débordement. Comme je veux éviter ici de rentrer dans les détails disons que ce n'est pas facile à gérer.
Il serait plus simple de remettre à zéro ce millis() absolument quand on le désire, mais aucune instruction n'existe pour cette opération. 'millis() = 0;' ne fonctionne pas !
En cherchant sur le Net, comme je ne suis pas le seul à avoir rencontré ce problème, j'ai trouvé un code qui initialise ce millis() à volonté !

Je vous livre donc la solution non commentée (car elle n'est pas de mon cru !) dans un petit exemple qui initialise millis() toutes les 10 secondes (à vous de voir pour changer ce délais à votre convenance) c'est juste une façon de voir comment exploiter ce code.

extern volatile unsigned long timer0_overflow_count;
extern volatile unsigned long timer0_millis;
uint8_t old_SREG;

void ResetMillis() {
  old_SREG = SREG;
  cli();
  timer0_millis = 0;
  timer0_overflow_count = 0;
  SREG = old_SREG;
}
void setup()
{
  Serial.begin(230400);
}
void loop()
{
  Serial.println(millis());
  delay(1000);
  if (millis() > 10000)
    ResetMillis();

}

Attention que toutes vos temporisations soient écoulées avant d'utiliser 'ResetMillis() ' !!!!

Bonne journée à tous !

Portrait de Walter

bonjour à tous,

Du coup ca te force à faire des calcules et à mémoriser le nombre de fois que tu as réinitialiser ton compteur de milliseconde ?

Tu pourrais utiliser un "Timer" qui t'incrémente un compteur de minutes au lieu d'utiliser celui des millisecondes, je suppose que dans le cas évoqué on n'a pas besoin d'être précis à la second sur le délais passé?

Portrait de brossden

Bonjour Walter

Je ne vois pas ce que tu veux dire ?

Pourquoi des calculs ?

Comment ferais tu pour faire clignoter une led toutes les 7 secondes et une seconde toute les 13 secondes ?

Cela indéfiniment ??

De plus un compteur qui compterais tous le 49 jours .... pas trop pénible !

Sur un compteur 32 bits cela représente plus de 576 millions d'années, il y aura longtemps que je n'aurai plus mal aux dents !! :o)

Les dix secondes que j'ai prises dans mon exemple c'était pour vérifier la remise à zéro de millis()

Portrait de Walter

Pour les calcules, il faut bien recalculer le temps qu'il reste avant échéance entre chaque millis() > 10000
Oui pas besoin de compter sur un entier, un byte est déjà  très bien.

Code qui utilise le timer 2 et l'interruption sur l'overflow du timer.
C'est exactement la même chose que toi, sauf qu'il n'y a plus de code actif.

int led1 = 2;
int led7 = 4;
int led13 = 6;
int enableLed1 = HIGH;
int enableLed7 = HIGH;
int enableLed13 = HIGH;
int varCompteur = 0; //Compteur de seconde
int cptLed7 = 7; // Compteur led 7s
int cptLed13 = 13; // Compteur led 13s

 

void setup() {
pinMode(led1, OUTPUT);
pinMode(led7, OUTPUT);
pinMode(led13, OUTPUT);

 

digitalWrite(led1, enableLed1);
digitalWrite(led7, enableLed7);
digitalWrite(led13, enableLed13);
Serial.begin(115200);

 

cli();
bitClear (TCCR2A, WGM20);
bitClear (TCCR2A, WGM21);
TCCR2B = 0b00000110;
TIMSK2 = 0b00000001;
sei();
}

 

ISR(TIMER2_OVF_vect) {
TCNT2 = 256 - 250; // 250 x 16 µS = 4 ms
if (varCompteur++ > 250) { // 250 * 4 ms = 1000 ms
varCompteur = 0;
cptLed7 --;
cptLed13 --;
if (cptLed7 == 0) {
enableLed7 = !enableLed7;
digitalWrite(led7, enableLed7);
cptLed7 = 7;
Serial.print("LED7\n");
}
if (cptLed13 == 0) {
enableLed13 = !enableLed13;
digitalWrite(led13, enableLed13);
cptLed13 = 13;
Serial.print("LED13\n");
}
digitalWrite(led1, enableLed1);
enableLed1 = !enableLed1;
}
}

 

void loop(){
//Rien a faire c'est l'interrupt qui bosse :)
}
Portrait de brossden

Tu n'as rien trouvé de plus compliqué ?

Je te rappelle que les membres de ce site ne sont pas des développeurs confirmés mais en général des débutants dans le domaine de la programmation et en C de surcroît qui et loin d'être le plus simple !

Portrait de Walter

Ha si après il y a des trucs vraiment compliqué si on cherche :)

Je te répondais précisément.
Après je pense personnellement que lorsque l'on à un problème de l'ordre de ne plus pouvoir ce satisfaire des fonctions de base et devoir jouer et bidouiller avec les registres du timer 0, autant le faire correctement et ne pas encombrer la loop.

Sinon, je ne suis pas très habitué au librairie arduino mais il semble, qu'il y a des librairies qui fournissent des objets Timers prêt à l'emploi (comme celle-ci je pense ? https://playground.arduino.cc/Code/SimpleTimer).

Après je suis bien d'accord avec toi sur le C, c'est pourquoi je développe en lua.
Ce qui aurait donné un truc du genre :

tmr.alarm( timerId, dly, 1, function()
    local battery = adc.read(0)
    print("System voltage (mV):", battery)
end)

Portrait de brossden

Je suis de ceux qui préfèrent se passer au maximum des libraries car d'une part on ne sait pas vraiment ce que l'on fait, j'ai trouvé pas mal de fakes dans des libraries et surtout on n'a pas l'intelligence de ce que l'on réalise. J'ai horreur des plats tout prêt à déguster parce on nous fait avaler tout un tas de saloperies dont la presse aime s'en faire écho ! Alors le fait maison j'adore mais sans complications inutiles !! Je 'étais responsable de projet et j'ai vu des choses insensées notamment avec ma méthode Merise qui était le top à cette époque !

Portrait de Walter

Pour le coup, j'aime bien aussi maîtriser ce que je fait et surtout comprendre.
Je ne vois pas ou tu veux en venir et qui y a t-il d'insensée à utiliser ce que tout µC propose en standard pour ne pas encombrer inutilement la loop?

De mon point vue les choses sont plutôt simple, soit tu veux gérer les choses et comprendre ce qu'il ce passe et tu fais l'effort de chercher à comprendre comment cela fonctionne, soit tu n'a pas les moyens(connaissance ou temps ou autres) et tu prends une librairie qui ferra le travaille pour toi, mais peut être mal ou à moitié.
Mais bien souvent pour un débutant qui n'a pas toujours le temps et encore moins les compétences, le travaille sera mieux fait par une librairie reconnue et largement utilisée.
C'est bien pour ça que arduino fonctionne si bien, je pense.

Portrait de brossden

Tu as tout à fait raison ! Mais tu nous as parlé d'objets connectés tu peux nous faire part des tes réalisations ?!  

Portrait de Walter

Je ne vois pas ou tu veux en venir ?
Quel est le rapport avec ce qui précède ?
Pourquoi prendre ce ton inquisiteur ?

J'ai fais une sonde(température, humidité) qui pousse ses résultats à une plateforme nagio.
Finalement je l'ai modifiée pour pousser sur un broker MQTT.
Je me sers aussi de ce code, pour faire un testeur de capacité de batterie, mais il me manque un MOSFET et une résistance pour le finaliser.

Je voudrais que la sonde soit autonome, donc je suis entrain d'essayer de faire recharger la batterie par une cellule photovoltaïques.

voilà ma petite réalisations d'objet connecté, certes comparé à tes réalisations cela ne va pas très loin, mais on fait ce que l'on peut en fonction de ses possibilités!

Portrait de brossden

Inquisiteur ? Je ne vois pas en quoi te demander de tes réalisations reflète de sentiment de passage à la question !

Dans ta présentation tu nous as parlé des objets connectés que tu faisais mais comme tu n'as jamais été plus loin pour nous intéresser à ta passion, je te demandais simplement de développer un peu.

Mais comme tu aimes utiliser des termes ésotériques (Nagios, MQTT etc.…)  je pense que tu n'as pas très bien compris que nous sommes ici sur un site de vulgarisation des modules Arduino et sincèrement je vois pas ce qu'un logiciel de surveillance couplé à un serveur qui regroupe moult informations pourrait intéresser les novices qui constitue la grande partie des membres de ce site. L'assembleur est quand même assez loin de la programmation courante des modules qui constituent la demande courante des membres présents ici. Donc excuse-moi de ne pas aller plus loin dans l'intérêt que je portais à ton travail qui est d'un niveau beaucoup trop haut pour nous, enfin je ne parle qu'en mon nom. Et par pitié pour la suite essaye de te placer à un niveau nettement inférieur avec des explications qui permettraient à la plus part de comprendre ce que tu dis.

Bonne journée à tous

Portrait de Walter

Désolé je ne comprend pas ta première phrase, mon sentiment concernant ton "ton inquisiteur", viens du fais que tu me demande quels sont mes projets d'objet connecté, dans un fils de discutions qui n'a rien à voir (limitation de la fonction millis) et sans qu'il n'y ait au préalable un quelconque lien avec ce qui précède, si ce n'est que de suggéré que je suis mal venu de donner mon avis.

Peut être cela vient-il de moi, mais j'ai du mal à te suivre, tu me pose des questions, au quel je répond et après te me reproche d'y répondre sous prétexte que les utilisateurs du forum n'ont pas le niveau pour les comprendres et qu'ils n'ont que faire de ce type de projet.
Je te rappel que c'est pas moi qui ais proposé de montrer ce que je fais comme projet, mais bien toi qui me le demande.
Tout est tout excusé de ne pas porter plus d'intérêt à mon travail, je n'ai jamais jugé que celui(mon travail) n'avait un intérêt particulier qui nécessiterait de devoir le présenter.

Par contre il faudra que tu me montre ou je propose de faire de l'assembleur?
Surtout que je le répète, je suis plus adepte du lua qui est de l'interprété et plus simple que le C.

Après on peut discuter sur le fait que le protocol MQTT soit ésotérique sur un esp8266.

Mais j'aurais tendance à te dire que certains de tes propos sur le fil de discutions "Synthetiseur"
sont aussi pour moi des propos ésotérique.

Le "Bulk" est un filtre à décade tout ce qu'il y a de plus simple pour filtrer les fréquences de plus en plus élevées. Pour lisser l'alimentation de 9 volts et éviter des harmoniques et autres fréquence parasites.
...

Portrait de brossden

Je n'avais aucune arrière pensée en te demandant ce que tu faisais comme objets connecté, mais j'ai vite compris que tu n'avais pas envie de parler avec des mots simple.

Pour ce qui est de "Bulk" c'est pour répondre à Miserness mais ce n'est pas moi qui en ait parlé en premier !!

J'ai utilisé le mot d'assembleur car car quand tu attaques les registres du processeur tu n'en es plus très loin !!!

Sur "Ce" je termine là cette conversation et à l'avenir j'éviterai toute dialogue avec toi !

Bonne journée !

Portrait de Walter

Ce n'ai pas Bulk dont je parlais mais "filtre à décade" et globalement tes explications que tu qualifie du B.A-BA, mais qui pour moi ne sont pas très parlante.
Ce n'est pas un reproche, mais plutôt une analogie avec tes remarques me concernant.

J'ai bien commencé mes messages avec le fait que j'avais du mal à te suivre et donc savoir le but de tes questions et si tu voulais vraiment un développement des mes réponses à tes reproches.
 

Sur l'assembleur l'ironie de la chose et que c'est toi qui utilise le registre SREG dans ton code de ton premier message :)

Je serais ravie de répondre à toute question ouverte sur un des sujets évoqués dans ce fil de discutions qui pourrait intéresser quelqu'un, de la même manière que je l'ai fait quelque fois sur d'autre fil de discutions.
Ou je ne pense pas que l'on puisse traiter mes réponse d'ésotérique ou "snobante" ?
 

Et j'insiste, je pense que les Timer et Interruption sont très appropriés à ton sujet principal sur millis().
Voici le lien dont est issue le code que j'ai posté :
https://www.locoduino.org/spip.php?article84

Si quelqu'un a des questions qui ne sont pas clairement décries dans cet excellent article, je serait ravie d'essayer d'y répondre.

Bonne journée

Portrait de brossden

Tu ne sais peut être pas lire !

Je vous livre donc la solution non commentée (car elle n'est pas de mon cru !)

C'est juste une fonction pour supprimer l'inconvénient de millis() !!! pas un code complet !

Portrait de Walter

Peut être effectivement,
Peut être que tu ne veux pas écouter ce que l'on veut te dire!

En tous cas avec des peut êtres, il est plus facile de ce moquer d'autrui que d'admettre ces propres faiblesses.

Pour tout t'avouer je ne vois pas le rapport entre le fait que tu me reproche d'utiliser des registres que toi même tu propose dans le code que tu met en avant, avec le fait que je sois illettré.
Comme en plus de ne pas savoir lire, je ne sais pas écrire, je reformule:
Je trouve ironique que tu propose du code qui utilise exactement ce que tu me reproche!

Portrait de SPY

Que pensez vous de ce code  ?

void setup() {
  Serial.begin(230400);
  pinMode(13, OUTPUT);
  pinMode(7, OUTPUT);
}
void loop() {
  if (millis() % 7000 == 0) {
    digitalWrite(7, digitalRead(7) ^ 1);
    Serial.println("LED 7 = " + String(digitalRead(7)) + " Secondes : " + String(millis() / 1000));
  }
  if (millis() % 13000 == 0) {
    digitalWrite(13, digitalRead(13) ^ 1);
    Serial.println("LED 13 = " + String(digitalRead(13)) + "       Secondes : " + String(millis() / 1000));
  }
  delayMicroseconds(800);
}
Portrait de Walter

Si tu te content de faire clignoter les LED, je pense qu'il fait très bien le travaille.
Par contre si tu a des actions cumulées au dessus des 1000 µs, il faudra l'adapter non ?

Portrait de SPY

Je pensais que le problème  était pour des Leds à chaque problème sa solution !

Les interruptions ne perturbent en rien le reste du programme au niveau de la microseconde ?

Portrait de Walter

Oui bien sûre, suivant ce que tu fait dans ta callback et le temps que tu y passe, tu le prend au reste.
Mais dans ce cas peut être que le code perturbable, ne devrait pas être dans le corps, mais dans un Timer.
Mais comme tu le dis, cela dépend du contexte que tu as.

Portrait de SPY

Oui c'est là que les Timers sont intéressants mais je ne vois pas en quoi les clignotements des LEDs nécessite leurs utilisations. Alors pourquoi ?

Portrait de Walter

Non, tu as tout fait raison le clignotement des LEDs en soit ne nécessite pas de timer/interruption.

Portrait de gaetan BERBIE

bonjour quelqu'un peut me dire comment simplement réinitialiser la fonction "millis"

c'est pour faire un compteur de temps sans delay donc non bloquant.

Portrait de Walter

Bonjour,

la fonction ResetMillis décrite tout en haut fait exactement cela, remet à zéro le décompte des millisecondes.
Il suffit de l'appeler au moment de l'incrémentation.

Personnellement pour faire un compteur non bloquant j'utiliserais plutôt un objet Timer
https://playground.arduino.cc/Code/ArduinoTimerObject

en modifiant la fonction de l'exemple ​PrintHello1 pour incrémenter ton compteur,

void PrintHello1(){
		moncompteur ++;
}
Portrait de gaetan BERBIE

merci beaucoup ça fonctionne 

Portrait de brossden

lol