Adafruit-multi-tasking-the-arduino-part-1

De VoWiki
Aller à : Navigation, rechercher
AdfLs.JPGLe multitache Arduino – Part 1
Created by Bill Earl
AdfMltpageDeGarde.JPG
Last updated on 2015-10-13 07:20:09 AM EDT

Original :

https://learn.adafruit.com/multi-tasking-the-arduino-part-1


Traduction en français : J.Soranzo


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 ».

PhotoEnsembleFinale.JPG

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 processeur.Ainsi 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);
    myservo.attach(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
        myservo.write(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
    {
        myservo.write(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:


WiringLed un.JPG

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.
http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/
// 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 :

2LEDwiring.JPG

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()
{
    led1.Update();
    led2.Update();
}

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.

Microcontrollers Lights and Action bb.png


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 <http://barraganstudio.com>
// 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()
{
    myservo.attach(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
        myservo.write(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
    {
        myservo.write(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)
    {
        servo.attach(pin);
    }
    void Detach()
    {
        servo.detach();
    }
    void Update()
    {
        if((millis() - lastUpdate) > updateInterval) // time to update
        {
            lastUpdate = millis();
            pos += increment;
            servo.write(pos);
            Serial.println(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)
    {
        servo.attach(pin);
    }
    void Detach()
    {
        servo.detach();
    }
    void Update()
    {
        if((millis() - lastUpdate) > updateInterval) // time to update
        {
            lastUpdate = millis();
            pos += increment;
            servo.write(pos);
            Serial.println(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()
{
    Serial.begin(9600);
    sweeper1.Attach(9);
    sweeper2.Attach(10);
}
void loop()
{
    sweeper1.Update();
    sweeper2.Update();
    led1.Update();
    led2.Update();
    led3.Update();
}


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 :


TousCompletAvecBP.JPG

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)
    {
        servo.attach(pin);
    }
    void Detach()
    {
        servo.detach();
    }
    void Update()
    {
        if((millis() - lastUpdate) > updateInterval)  // time to update
        {
            lastUpdate = millis();
            pos += increment;
            servo.write(pos);
            Serial.println(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() 
{ 
    Serial.begin(9600);
    sweeper1.Attach(9);
    sweeper2.Attach(10);
} 
void loop() 
{ 
    sweeper1.Update();
    if(digitalRead(2) == HIGH)
    {
        sweeper2.Update();
        led1.Update();
    }
    led2.Update();
    led3.Update();
} 


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.


PhotoEnsembleFinale.JPG

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.