Bonjour la communauté,
je bute sur un pb, certainement très bete, j'aimerai executer une sequence d'allumage d'une led, mais pas juste en off/on, mais une sequence temporelle qui allume et éteint la led, mon imperatif est de bosser avec millis() car d'autres action en parallèle
mais cela ne fonctionne pas comme attendu, en mode sequentiel cela devrait ressembler à
de 0 a 1000s = off
de 1000 a 1100 = on
de 1100 a 1200 = off
de 1200 a 1500 = on
etc ...
Il ne faut pas utiliser d'opérateur ternaire car dans les différents tests tu fais des actions contradictoires.
De plus tu te compliques la vie car tu fais un test puis à la ligne suivante le test inverse.
Il suffit d’enchaîner les tests quand le test précédent est faux. Ce qui donne;
if (currentMillis - timer2 < 1000)
digitalWrite(moteur, LOW);
else if (currentMillis - timer2 < 1100)
digitalWrite(moteur, HIGH);
else if (currentMillis - timer2 < 1200)
digitalWrite(moteur, LOW);
else if (currentMillis - timer2 < 1500)
digitalWrite(moteur, HIGH);
// etc
Merci pour vos retours, j'avais en effet essauyer avec des else if, mais dans tous les cas la condition est bonne
car imaginons millis() a 567, c'est < 1000 mais aussi < 1100 ...etc
Existe t'il une manière plus élégante de créer une sequence? et surtout plus fonctionnelle
merci à tous
Ainsi, pour ajouter un pas ou en enlever un, il suffit d'ajouter ou d'enlever une ligne à ce tableau, tout le reste se fait automatiquement.
Le bouton de déclenchement se trouve sur la pin seqBtnStartPin en mode PULLUP un côté du bouton à GND.
La commande du moteur sur moteurPin et l'état qui démarre le moteur est moteurOnEtat
Les temps, par rapport aux tiens sont beaucoup plus longs pour faciliter la démonstration.
Le programme:
const int moteurPin = 7; // Commande du nmoteur
const int moteurOnEtat = HIGH; // Etat pour commander la marche du moteur
struct seqPasMotDef // Structure de séquence moteur
{
const unsigned long pasTime;
const int moteurEtat;
};
seqPasMotDef seqPasMot[] =
{
{1000, moteurOnEtat},
{4000, !moteurOnEtat},
{7500, moteurOnEtat},
{12000, !moteurOnEtat},
};
const int pasNombre = sizeof(seqPasMot) / sizeof(seqPasMot[0]); // Nombre de pas dans la séquence
unsigned long seqPasTimeMillis = 0;
int pasNum = -1; // Sequence arrâtée
const int seqBtnStartPin = 2; // Bouton de déclenchement de la séquence
void setup()
{
Serial.begin(115200);
pinMode(moteurPin, OUTPUT);
digitalWrite(moteurPin, !moteurOnEtat); // Arrêt du moteur
pinMode(seqBtnStartPin, INPUT_PULLUP);
Serial.println("Nombre de pas de la sequence " + String(pasNombre));
}
void loop()
{
if (digitalRead(seqBtnStartPin) == LOW && pasNum == -1) // Si bouton start pressé et séquence arrêtée
{
pasNum = 0;
seqPasTimeMillis = millis();
Serial.println(F("Sequence Start"));
}
if (pasNum != -1 && (pasNum < pasNombre)) // Si séquence pas terminée
{
if (millis() - seqPasTimeMillis >= seqPasMot[pasNum].pasTime) // Si temps échu pour la séquence seqNum
{
Serial.println("Sequence " + String(pasNum +1) + " " + String(seqPasMot[pasNum].pasTime));
digitalWrite(moteurPin, seqPasMot[pasNum].moteurEtat); // Etat du moteur
pasNum ++;
}
}
if (pasNum >= pasNombre) // Si la séquence est terminée
{
Serial.println(F("Fin"));
digitalWrite(moteurPin, !moteurOnEtat); // Arrêter le moteur
pasNum = -1; // Séquence arrêtée
}
}
Avec cette façon de faire, la loop() est quasiment entièrement libre en permanence.
En balayant plusieurs post qui tournent autour des Timers, je m'étonne toujours que les demandeurs utilisent ponctuellement la méthode qui consiste à lire currentMillis alors qu'une classe Timer existe et qui permet de gérer plusieurs Timer sans se prendre la tête...
Le summum est d'y associer une fonction callback qui fera le travail à l'expiration dudit timer
Proposition qui n'engage que moi et que j'utilise en Langage C et C++, voire en Assembleur
Effectivement, mais on peut aussi se demander l'utilité d'utiliser un objet, pour faire un appel de fonction ?
Surtout quel est le gain, si on considère que l'on en vient à faire de l'assembleur.
faire un If (Timer.read() > 20000) est-il vraiment différent de faire if (millis() - startTime > 20000) ?
Pour moi les deux se valent.
Tout d'abord, une classe objet propose toujours plusieurs méthodes qui masquent l'implémentation sans avoir recours à des variables globales au gré du programme principal (variables globales à bannir à cause des accès concurrents si malheureusement utilisées dans des routines d'interruption).
Présentement, gérer un ou plusieurs timers au moyen d'une classe (que l'on peut compléter et enrichir à souhait) n'ajoute pas plus de complexité ni de code supplémentaire ou si peu.
Depuis des lustres, j'ai adopté une programmation événementielle matérielle (prise en compte des interruptions) et logicielle (accueil de fonctions callback) qui évite l'écriture d'un code à "la va que je te pousse" qui finit par "tomber en marche" mais inmaintenable car tout est mélangé (traitement et présentation des données notamment contraire à une programmation structurée et lisible par tous).
S'agissant de la remarque "Surtout quel est le gain, si on considère que l'on en vient à faire de l'assembleur" ... on en vient pas à faire heureusement de l'assembleur mais c'était pour préciser que le modèle s'applique à tous les langages même au plus près du µC.
Pour finir, ce modèle de programmation s'applique particulièrement bien à la problématique de la gestion de machines d'états que l'on retrouve fréquemment tout au long des fils de discussion
NB: Sauf erreur de ma part, la plupart des librairies proposées dans l'écosystème Arduino s'appuient sur des classes objets dont la lecture et l'inspiration devraient être mises en exergues pour le développement d'un projet quelles que soient sa complexité et les compétences des intervenants...
Une variable global, reste une variable global, que celle-ci soit de type prédéfini ou objet.
Donc tu as le même problème d'accès concurrentielle sur les objets.
Non, on ne peut pas dire que cela ajoute de la complexité ou du code supplémentaire.
Mais très probablement une occupation mémoire plus importante, enfin cela dépend bien évidement du code sans objet.
Dans ton objet Timer, les variables privés occupent 3 entiers 32bits, si je ne me suis pas planté, au lieu d'un seul pour l'utilisation directe de millis.
Alors on est d'accord, que pour ce cas cela ne va pas changer la face du monde, donc encore moins ton programme
Alors là, je ne veux pas te vexer, mais je pense que tu parle pour toi.
L'écriture de programme séquentielle traditionnel, n'implique en rien de l'écriture de code à "la va que je te pousse" ou que les tombes en marche!!!
Je ne sais pas ou tu as appris à coder comme cela, le preuve de programme ou autre théorie de ce genre, ne sont pas réservé à la programmation évènementielle.
J'aurais de plus tendance à dire, qu'il est bien plus simple d'écrire du code non évènementielle et le code est bien plus facile à lire.
Après la programmation évènementielle à aussi ses avantages,
L'avantage principal étant la possibilité très simple que le code qui est fournis par une librairie peut facilement interagir avec le code de l'utilisateur.
Je ne vois pas trop en quoi cela te force à quoique se soit.
mais effectivement le delay, c'est mal
Je ne connais pas la programmation assembleur qui utilise l'objet, peut être que je manque de connaissance sur ce sujet.
Ce que je voulais dire, c'est que je ne vois pas trop le rapport entre la programmation objet et l'assembleur et que les deux ont des paradigmes un peu différent.
Je ne suis pas spécialement d'accord, lorsque tu fais une machine à état finis, c'est que tu veux maitriser l'état de ta machine.
Je ne trouve pas que l'évènementielle soit particulièrement simple à utiliser, pour maitriser tes états et je ne parle pas de la lisibilité du code.
Oui, je pense aussi que c'est une écrasante majorité.
Par contre non cela ne devraient pas forcément être mis en exergues quelque soit la complexité du projet.
Au contraire cela devrait être mis en exergues pour les programmes assez compliqué ou collaboratif.
Après, il ne faut pas se méprendre, je ne dis pas qu'il ne faut pas faire de la programmation objet.
La programmation objet et tout aussi valable que de la programmation sans objet.
La programmation objet est très bien, de même pour l'évènementiel.
au début c'est bien de comprendre comment on fait de la gestion asynchrone et maitriser millis() est une étape importante
Ensuite une fois que l'on a compris on peut éventuellement encapsuler cela mais l'abstraction fournie par la classe que vous mentionnez n'est pas géniale (et elle est assez mal écrite)
Je vois que le sujet intéresse et je pense, sauf erreur de ma part, que nous sommes d'accord sur les multiples paradigmes de programmation que chacun applique (peut-être sans le savoir ) en fonction de ses attentes et surtout de ses réussites dans la mise en oeuvre de son projet.
Je n'insisterai pas sur les avantages/inconvénients de ceux-ci, le principal est que chacun y trouve son compte.
@ terwal: Je ne connais pas la programmation assembleur qui utilise l'objet, peut être que je manque de connaissance sur ce sujet.
Il va de soit qu'il est hors de question de programmer en objet en langage assembleur, je faisais simplement allusion à l'utilisation de fonctions callback applicables quel que soit le langage de programmation.
@ J-M-L: Ensuite une fois que l'on a compris on peut éventuellement encapsuler cela mais l'abstraction fournie par la classe que vous mentionnez n'est pas géniale (et elle est assez mal écrite)
Entièrement d'accord, cette classe Timer prise en exemple ne demande qu'à être modifiée et compléter, voire totalement réécrite
Oui il n’y a pas qu’une seule façon de résoudre un problème. L’usage d’une bibliothèque est parfois utile pour masquer une certaine complexité et d’autres fois cela rajoute une abstraction pour pas grand chose.
Par exemple j’utilise toujours une bibliothèque pour la gestion des boutons (avec ou sans callback suivant le besoin) mais toujours millis() directement. Si j’ai besoin de plusieurs “timers” cela veut dire que j’ai plusieurs objets qui ont besoin de gérer leur état et dans ce cas je crée une classe pour mes objets et millis() est intégré dans la classe parce que c’est très simple.
Après chacun fait comme il veut. Le seul intérêt de la bibliothèque que vous pointez à mon avis est la possibilité de mettre en pause le timer (mais ça mange de la RAM pour rien si on n’en a pas besoin).