Adafruit-multi-tasking-the-arduino-
part-1
Aller à la navigationAller à la recherche
Le multitache Arduino – Part 1
Created by Bill Earl
Last updated on 2015-10-13 07:20:09 AM EDT
Original :
[Link]
Traduction en français : [Link]
Sommaire
1Vue d'ensemble
o 1.1Des projets mieux et plus gros
2Laisser tomber le delay()
o 2.1Vous rappelez-vous Blink ?
o 2.2Et sweep aussi ?
3Utiliser millis() pour gérer les timing
o 3.1Devenir un gardien du temps !
o 3.2Clignoter sans delay
o 3.3Bienvenu dans la Machine
o 3.4Machine + Etats = Machine d'états
o 3.5Et maintenant 2 en 1
o 3.6Merci m'sieur ! Je peux en avoir une autre ?
4Une solution plus classe
o 4.1Mettez un peu de POO dans votre boucle.
o 4.2Définir une classe
o 4.3Maintenant utilisons
o 4.4Moins c'est plus !
5Un balayage plus propre
o 5.1Que pouvons nous faire d'autre avec cela ?
o 5.2Et vous en voulez combien ?
6Tous ensembles maintenant !
o 6.1Nous voulons votre entrée aussi
7Conclusion
Vue d'ensemble
Des projets mieux et plus gros
Une fois maîtrisés les basiques clignoteurs de LED, capteurs simples et les
aller-et-retours de servo,
il est temps de passer à des projets plus gros et mieux. Cela fait appel
généralement à des petits
bouts de sketchs qu'on essai de faire fonctionner ensembles. La première chose
que vous allez rapidement
découvrir est que certains de ces croquis qui fonctionnent parfaitement seuls ne
marchent pas du tout ensembles.
L'ARDUINO est un processeur vraiment très simple ne comportant pas d' OS
(Operating system) et peut seulement exécuté un programme à la fois.
Contrairement à votre ordinateur personnel ou un Raspberry Pi, l'ARDUINO n'a
aucun moyen de charger et d'exécuté de multiples programmes.
Cela ne signifie pas que nous ne pouvons pas gérer de multiples tâches avec
un ARDUINO.
Nous avons juste besoin d'utiliser une approche différente.
Comme, il n'y a pas d'OS, nous devons le faire « à la main ».
Laisser tomber le delay()
La première chose que vous devez faire est d'arrêter d'utiliser delay().
Utiliser delay() pour contrôler les timing est probablement une des première
qu'on apprend quand on commence à expérimenter avec ARDUINO.
Gérer les timing avec delay() est simple et directe, mais cela devient source de
problème quand on veut ajouter de nouvelles fonctionnalités.
Le problème est que la fonction delay() est un « attente occupé » qui
monopolise tout le processeur.
Pendant l'exécution de delay(), vous ne pouvez pas répondre à une entrée,
vous ne pouvez traiter aucune données et vous ne pouvez changer aucune
sortie.
Delay() occupe 100% du [Link] si une partie de votre code utilise un
delay(), tout le reste est mort pendant sa durée.
Vous rappelez-vous Blink ?
/*
Blink
Turns on an LED on for one second, then off for one second,
repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most
Arduino boards.
// give it a name:
int led = 13;
// the setup routine runs once when you
press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again
forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the
voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the
voltage LOW
delay(1000); // wait for a second
}
Le simple croquis Blink passe près de tous son temps dans la fonction delay().
Ci-bien que le processeur ne peut rien faire d'autre.
Et sweep aussi ?
Sweep utilise le delay() pour contrôler la vitesse de balayage. Si vous essayez
de combiner le croquis Blink de base avec l'exemple de balayage de servo.
Vous vous appercevrez qu'il alterne entre clignotament et balayge. Mais il ne fait
pas les 2 simultanément.
#include <Servo.h>
// Pin 13 has an LED connected on most Arduino
boards.
// give it a name:
int led = 13;
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most
boards
int pos = 0; // variable to store the servo position
void setup()
{
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
[Link](9); // attaches the servo on pin 9 to the servo
object
}
void loop()
{
digitalWrite(led, HIGH); // turn the LED on (HIGH is the
voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the
voltage LOW
delay(1000); // wait for a second
for(pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to
180 degrees
{ // in steps of 1 degree
[Link](pos); // tell servo to go to position
in variable 'pos'
delay(15); // waits 15ms for the servo to
reach the position
}
for(pos = 180; pos>=0; pos-=1) // goes from 180 degrees to 0
degrees
{
[Link](pos); // tell servo to go to position
in variable 'pos'
delay(15); // waits 15ms for the servo to
reach the position
}
}
Alors comment contrôler les timing sans utiliser la fonction delay ?
Utiliser millis() pour gérer les timing
Devenir un gardien du temps !
Une technique simple pour gérer le temps est de faire un "ordonanceur" et de
garder un oeil sur l'horloge.
Au mieu d'un monde contament arrêté par delay, vous vérifier régulièrement
l'horloge et ainsi vous savez
quand il est temp d'agir. Moyennat quoi, le processeur est libre pour les autres
tâches. Un exemple très
simple est le croquis BlinkWithoutDelay qui est fourni avec l'IDE ARDUINO.
Le code sur cette page utilise le schéma de câblage ci-dessous:
Clignoter sans delay
Voici le croquis BlinkWithoutDelay
/* Blink without Delay
Turns on and off a light emitting diode(LED) connected to a digital
pin, without using the delay() function. This means that other code
can run at the same time without being interrupted by the LED code.
The circuit:
* LED attached from pin 13 to ground.
* Note: on most Arduinos, there is already an LED on the board
that's attached to pin 13, so no hardware is needed for this example.
created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen
This example code is in the public domain.
[Link]
*/
// constants won't change. Used here to
// set pin numbers:
const int ledPin = 13; // the number of the LED pin
// Variables will change:
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0; // will store last time LED was updated
// the follow variables is a long because
the time, measured in miliseconds,
// will quickly become a bigger number than
can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop()
{
// here is where you'd put code that needs
to be running all the time.
// check to see if it's time to blink
the LED; that is, if the
// difference between the current time
and last time you blinked
// the LED is bigger than the interval
at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last
time you blinked the LED
previousMillis = currentMillis;
// if the LED is off
turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with
the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
Qu'est ce qu'il y a d'intéressant là dedans ?
A première vue, BlinkWithoutDelay ne semble pas un croquis très intéressant.
Cela semble juste un façon bien compliquée de faire clignoter une LED.
Toutefois, BlinkWithoutDelay illustre un concept très important connu sous le
nom de Machine d'états (State Machine).
Au lieu d'utiliser delay pour gérer les temps de clignotement. BlinkWithoutDelay
se souviens de l'état courant
de la LED et de la dernière fois qu'elle a changé d'état. A chaque passage dans
la boucle loop,
il regarde l'horloge millis() pour voir s'il est temps de changer la LED à nouveau.
Bienvenu dans la Machine
Jetons un œil à une variante de blink encore un peu plus intéressante qui
possède des temps d'allumage et d'extinction différents. Appelons ce dernier
« FlashWithoutDelay ».
// These variables store the flash
pattern
// and the current state of the LED
int ledPin = 13; // the number of the LED pin
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was
updated
long OnTime = 250; // milliseconds of on-time
long OffTime = 750; // milliseconds of off-time
void setup()
{
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop()
{
// check to see if it's
time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >=
OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the
time
digitalWrite(ledPin, ledState); // Update the
actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >=
OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the
time
digitalWrite(ledPin, ledState); // Update the
actual LED
}
}
Machine + Etats = Machine d'états
Notez que nous avons des variables pour conserver l'état allumer et l'état éteint
et des variables pour
conserver la trace de quand ces changement se sont produits. Ceci est la partie
Etats de la machine d'états.
Nous avons également du code qui vérifie quand et comment il est nécessaire
de changer. C'est la partie Machine.
A chaque tour de la boucle loop, nous exécutons la machine et la machine
prend en charge les changements d'état.
Dans la suite, nous allons voir comment combiner de multiples machines d'états
et les exécuter de manière concurrente.
Et maintenant 2 en 1
Il est temps maintenant de faire du multitâche ! Tout d'abord câblons une LED
de plus comme dans le diagramme suivant :
Maintenant, nous créons une seconde machine d'états pour la deuxième LED
qui flashe à des rythme complètement différents.
En utilisant 2 machines d'états séparées, on peut faire clignoter les LEDs de
manière complètement indépendante l'une de l'autre.
Quelque chose qu'il est très compliqué de faire en utilisant la version avec
delay.
// These variables store the flash pattern
// and the current state of the LED
int ledPin1 = 12; // the number of the LED pin
int ledState1 = LOW; // ledState used to set the LED
unsigned long previousMillis1 = 0; // will store last time LED
was updated
long OnTime1 = 250; // milliseconds of on-time
long OffTime1 = 750; // milliseconds of off-time
int ledPin2 = 13; // the number of the LED pin
int ledState2 = LOW; // ledState used to set the
LED
unsigned long previousMillis2 = 0; // will store last time LED
was updated
long OnTime2 = 330; // milliseconds of on-
time
long OffTime2 = 400; // milliseconds of off-
time
void setup()
{
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
}
void loop()
{
// check to see if it's
time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState1 == HIGH) && (currentMillis - previousMillis1 >=
OnTime1))
{
ledState1 = LOW; // Turn it off
previousMillis1 = currentMillis; // Remember the
time
digitalWrite(ledPin1, ledState1); // Update the
actual LED
}
else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >=
OffTime1))
{
ledState1 = HIGH; // turn it on
previousMillis1 = currentMillis; // Remember
the time
digitalWrite(ledPin1, ledState1); // Update
the actual LED
}
if((ledState2 == HIGH) && (currentMillis - previousMillis2 >=
OnTime2))
{
ledState2 = LOW; // Turn it off
previousMillis2 = currentMillis; // Remember
the time
digitalWrite(ledPin2, ledState2); // Update
the actual LED
}
else if ((ledState2 == LOW) && (currentMillis - previousMillis2 >=
OffTime2))
{
ledState2 = HIGH; // turn it on
previousMillis2 = currentMillis; //
Remember the time
digitalWrite(ledPin2, ledState2); //
Update the actual LED
}
}
Merci m'sieur ! Je peux en avoir une autre ?
Vous pouvez ajouter plus de machines d'états jusqu'à ce qu vous n'ayez plus de
mémoire ou de GPIO.
Chaque machine d'états à ces propre vitesse de clignotement.
En tant qu'exercice, éditez le code ci-dessus pour ajouter une troisième
machine d'états.
Premièrement dupliquez toutes les variables d'états et le code d'un des
machines.
Renommez ensuite toutes les variables pour éviter les conflits avec la
première machine.
Ce n'est pas très compliqué à faire. Mais cela peut sembler bien fastidieux de
recopier le même code encore et encore.
Il doit y avoir un moyen plus efficace de faire cela !
Il y a d'autres façon de gérer cette complexité. Il y a d'autres techniques de
programmation qui sont
à la fois plus simple et plus efficaces. Dans la page suivante, nous allons
introduire quelques unes
des possibilités les plus avancées du langage de programmation Arduino.
Une solution plus classe
Jetons un œil au dernier croquis. Comme vous pouvez le voir, il est très répétitif.
Le même code est dupliqué presque mot pour mot pour chaque LED. La seule
chose qui change un peu est le nom des variables.
Ce code est un candidat de choix un peu de Programmation Orientée Objet
(POO).
Mettez un peu de POO dans votre boucle.
Le langage Arduino est une variante du C++ qui supporte la Programmation
Orientée Objet.
En utilisant les caractéristiques de POO du langage, nous pouvons regrouper
ensemble toutes
les variables d'états et les fonctions pour le clignotement de la LED dans une
classe C++.
Ce n'est pas très difficile à faire. Nous avons déjà écrit tout le code pour cela.
Nous avons seulement besoin de le remballer dans une classe.
Définir une classe
Nous commençons par déclarer une classe « Flasher » :
Ensuite nous y ajoutons toutes les variables du croquis FlashWithoutDelay.
Comme elles font parties de la classe, elles sont maintenant connue sous le
nom de variables membres.
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current
state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED
was updated
}
Ensuite nous ajoutons un constructeur. Il a le même nom que la classe et son
boulot est d'initialiser les variables.
class Flasher
{
// Class Member Variables
// These are initialized at
startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the
current state
int ledState; // ledState used to set
the LED
unsigned long previousMillis; // will store last time
LED was updated
// Constructor - creates a
Flasher
// and initializes the member
variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
};
Pour finir, nous prenons notre loop et la transformons en une fonction
membre nommée « Update() ». A notre qu'elle est identique à la fonction loop.
Seul le nom change.
class Flasher
{
// Class Member Variables
// These are initialized at
startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current
state
int ledState; // ledState used to set the
LED
unsigned long previousMillis; // will store last time LED
was updated
// Constructor - creates a Flasher
// and initializes the member variables
and state
// and initializes the member variables
and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see
if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >=
OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember
the time
digitalWrite(ledPin, ledState); // Update
the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis
>= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; //
Remember the time
digitalWrite(ledPin, ledState); // Update
the actual LED
}
}
};
En réarrangeant simplement notre code existant en une classe Flasher, nous
avons encapsulé toutes les variables ( les états ) et toutes les fonctionnalités
( la machine ) pour faire flasher la LED.
Maintenant utilisons
Pour chaque LED que nous voulons faire clignoter, nous créons
une instance de la classe Flasher en appelant le constructeur.
Et à chaque passage dans la boucle loop nous n'avons besoin que d'appeler la
Update() pour chaque instance de Flasher.
Il n'est pas nécessaire de dupliquer le code complet de la machine d'états.
Nous avons juste à appeler une autre instance de la classe Flasher !
class Flasher
{
// Class Member Variables
// These are initialized at
startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current
state
int ledState; // ledState used to set the
LED
unsigned long previousMillis; // will store last time LED
was updated
// Constructor - creates a
Flasher
// and initializes the
member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
//
check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >=
OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; //
Remember the time
digitalWrite(ledPin, ledState); //
Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis
>= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis;
// Remember the time
digitalWrite(ledPin, ledState);
// Update the actual LED
}
}
};
Flasher led1(12, 100, 400);
Flasher led2(13, 350, 350);
void setup()
{
}
void loop()
{
[Link]();
[Link]();
}
Moins c'est plus !
C'est tout – chaque nouvelle LED ne nécessite que 2 lignes de code
supplémentaire !
Ce code est plus court et plus facile à lire. Et comme il n'y a pas de code
dupliqué, il produit des compilation plus petites ! Ce qui vous laisse plus de
précieuse mémoires pour faire d'autres choses !
Un balayage plus propre
Que pouvons nous faire d'autre avec cela ?
Appliquons ces mêmes principes à du code pour servo et réalisons quelques
actions.
Premièrement, connectons quelques servos sur notre breadboard (NdT : la
fameuse planche à pain) comme indiqué ci-dessous.
Et tant qu'on y est ajoutons un troisième LED également.
Voici le code standard de Sweep Servo. Notez qu'il fait appel au redouté delay.
Nous prendrons les parties dont nous avons besoin pour créer la machine
d'états « Sweeper ».
// Sweep
// by BARRAGAN
<[Link]
// This example
code is in the public domain.
#include <Servo.h>
Servo myservo; // create servo
object to control a servo
// a maximum of
eight servo objects can be created
int pos = 0; // variable to store
the servo position
void setup()
{
[Link](9); // attaches the
servo on pin 9 to the servo object
}
void loop()
{
for(pos = 0; pos < 180; pos += 1) // goes from 0
degrees to 180 degrees
{ // in steps of 1
degree
[Link](pos); // tell servo
to go to position in variable 'pos'
delay(15); // waits
15ms for the servo to reach the position
}
for(pos = 180; pos>=1; pos-=1) // goes from
180 degrees to 0 degrees
{
[Link](pos); // tell servo
to go to position in variable 'pos'
delay(15); // waits 15ms
for the servo to reach the position
}
}
La classe Sweeper ci-dessous encapsule les actions de balayage, mais utilise la
fonction millis() pour gérer les temps un peu comme le fait la classe Flasher
pour les LEDs.
Nous avons également besoin d'ajouter les fonctions Attach() et Detach() pour
associer le servo à une broche spécifique ?
class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each
interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
[Link](pin);
}
void Detach()
{
[Link]();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
[Link](pos);
[Link](pos);
if ((pos >= 180) || (pos <= 0)) // end of
sweep
{
// reverse
direction
increment = -increment;
}
}
}
};
Et vous en voulez combien ?
Maintenant nous pouvons instancier autant de Flasher et de Sweeper que nous
le souhaitons.
Chaque instance de Flasher nécessite 2 lignes de code :
Une pour déclarer l'instance.
Une pour appeler Update dans la boucle Loop.
Chaque instance de Sweeper nécessite juste 3 lignes de code :
Une pour déclarer l'instance.
Une pour l'attacher à une broche dans le setup
Une pour appeler Update dans le Loop.
#include <Servo.h>
class Flasher
{
// Class Member
Variables
// These are
initialized at startup
int ledPin; // the number of
the LED pin
long OnTime; // milliseconds
of on-time
long OffTime; // milliseconds
of off-time
// These
maintain the current state
int ledState; // ledState used
to set the LED
unsigned long previousMillis; // will store
last time LED was updated
// Constructor -
creates a Flasher
// and
initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see
if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >=
OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the
time
digitalWrite(ledPin, ledState); // Update the
actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis
>= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the
time
digitalWrite(ledPin, ledState); // Update the
actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each
interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
[Link](pin);
}
void Detach()
{
[Link]();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
[Link](pos);
[Link](pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(15);
Sweeper sweeper2(25);
void setup()
{
[Link](9600);
[Link](9);
[Link](10);
}
void loop()
{
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
}
Maintenant nous avons 5 tâches indépendantes qui tournent en continu sans
interférence. Et notre boucle Loop ne fait que 5 lignes de code ! Dans la suite
nous allons ajouter un bouton de manière à pouvoir intéragir avec certaines de
ces tâches.
Tous ensembles maintenant !
Nous voulons votre entrée aussi
L'autre problème avec delay() pour gérer les timing est que les entrées
utilisateur comme
les appuis sur des boutons on tendance à être ignorées parce que le processeur
ne peut vérifier
l'état d'un bouton quand il est dans un delay(). Avec notre manière de gérer les
timing basée sur millis(),
le processeur est libre de vérifier l'état d'un bouton ou de tout autre entrée
régulièrement.
Cela nous permet d'écrire des programmes complexes qui font pleins de choses
en même temps mais qui restent réactifs.
Nous allons démontrer ceci en ajoutant un bouton :
Le code ci-dessous vérifie l'état du bouton à chaque passage dans la loop Led1
et Sweeper2 s'arrêtent quand le bouton est appuyé.
#include <Servo.h>
class Flasher
{
// Class Member
Variables
// These are initialized
at startup
int ledPin; // the number of the LED
pin
long OnTime; // milliseconds of on-
time
long OffTime; // milliseconds of off-
time
// These maintain the
current state
int ledState; // ledState used to set
the LED
unsigned long previousMillis; // will store last time LED was
updated
// Constructor - creates
a Flasher
// and initializes the
member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see
if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >=
OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; //
Remember the time
digitalWrite(ledPin, ledState); // Update
the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis
>= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; //
Remember the time
digitalWrite(ledPin, ledState); // Update
the actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
[Link](pin);
}
void Detach()
{
[Link]();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
[Link](pos);
[Link](pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(15);
Sweeper sweeper2(25);
void setup()
{
[Link](9600);
[Link](9);
[Link](10);
}
void loop()
{
[Link]();
if(digitalRead(2) == HIGH)
{
[Link]();
[Link]();
}
[Link]();
[Link]();
}
Les 3 LED clignotent à leur propre vitesse. Les 2 Servo balayent à leur propre
vitesse. Mais quand on presse sur le bouton, sweeper 2 et led1 s'arrêtent dans
leur état jusqu'à ce qu'on relâche le bouton.
Comme il n'y a pas de delay dans la boucle, la réponse à l'entrée bouton est pratiquement
instantanée.
Ainsi nous avons 5 tâches s’exécutant de manière indépendantes avec entrée
utilisateur.
Il n'y a pas de delay pour bloquer le processeur. Et notre code Orienté Objet
efficace nous laisse plein de place pour des extensions !
Conclusion
Dans ce guide nous avons démontré qu'il est effectivement possible avec un
Arduino de jongler avec de multiples tâches indépendantes tout en restant
réactif aux événements externes comme des entrées utilisateur.
Nous avons appris gérer les timing avec millis() plutôt qu'avec delay() de
manière à libérer le processeur pour accomplir d'autres choses.
Nous avons appris à définir des tâches entant que machine d'états qui
peuvent s'exécuter indépendamment des autres machines d'état en même
temps.
Et nous avons appris comment encapsuler ces machines d'états dans des
classes C++ de manière à garder notre code simple et compacte.
Ces techniques ne vont pas changer votre Arduino en super calculateur. Mais
elles vous aiderons à tirer le meilleur de ce petit mais « surprenament » puissant
petit processeur.
Dans la partie 2 de cette série, nous poursuivrons sur ces techniques et
explorerons d'autres façons de faire en sorte que votre Arduino soit réactif aux
événements externes tout en gérant de multiples tâches.
Catégorie :
Programmation