Variables globales dans fonction utilisée plusieurs fois

Bonjour à tous,

Où déclarer ces variables pour pouvoir utiliser ma fonction "blinker" plusieurs fois?

Si je les déclare en variables globales, je ne sais utiliser la fonction qu'une fois.
Si je les déclare dans la fonction, elles sont réécrites à chaque fois que le programme boucle et cela ne fonctionne plus.

J'ai cherché un peu partout mais je ne trouve pas de réponse.

unsigned long t3;
boolean in3_vieux;
boolean out3_vieux;
boolean out;

Voici le code:

// Global Variables
boolean led;
boolean bp;

unsigned long t3;
boolean in3_vieux;
boolean out3_vieux;
boolean out; 

//============================================================================================================================================================
void setup() {
 // déclaration des entrées avec résistance pullup interne
  pinMode(8, INPUT_PULLUP);
  Serial.begin(9600);// initialize serial:
}

/////////////////////////////BLINK////////////////////////////////
boolean blinker(boolean in, unsigned long temps)
{
  
  if((!out && out3_vieux)||(out && !out3_vieux)||(in && !in3_vieux)) t3= millis(); // si front output ou front mont input, on active minuterie
    out3_vieux = out; 
    in3_vieux = in;  
    if((t3+temps)<(millis())) out = !out; // si la minuterie est finie, on inverse sortie
    if(!in) out = false; // on force à 0 qd désactivé
    return (out);
}

///////////////////////////////////////////////////////////////////
void loop() {

bp = digitalRead(8);
led = blinker(!bp,500);
boolean led2 = blinker(!bp,1000);
boolean led3 = blinker(!bp,1500);
Serial.print(led); 
Serial.print(led2);
Serial.println(led3);
}

Merci d'avance

La portée d'une variable n'a rien à voir avec le fait d'appeler une fonction une fois ou plusieurs fois.

Tes variables led, led2 et led3 ne sont utilisées que dans la loop. Tu peux donc les déclarer comme locales dans la loop. Même chose pour bp.

Les autres sont utilisées par ta fonction et celle-ci doit conserver les valeurs à chaque appel. Deux possibilités pour ça :

  • Des variables globales
  • Des variables locales déclarées static (cherche la signification si tu ne connais pas)

Merci pour le "static", c'est ce que je cherchais, je ne connaissais pas.

Par contre, mon programme ne semble pas fonctionner, les 3 variables led, led2 et led3 devraient clignoter à des fréquences différentes mais ce n'est pas le cas.
Je ne trouve pas mon erreur.

// Global Variables
boolean led;
boolean bp;

//============================================================================================================================================================
void setup() {
 // déclaration des entrées avec résistance pullup interne
  pinMode(8, INPUT_PULLUP);
  Serial.begin(9600);// initialize serial:
}

/////////////////////////////BLINK////////////////////////////////
boolean blinker(boolean in, unsigned long temps)
{
static unsigned long t3;
static boolean in3_vieux;
static boolean out3_vieux;
static boolean out; 

  if((!out && out3_vieux)||(out && !out3_vieux)||(in && !in3_vieux)) t3= millis(); // si front output ou front mont input, on active minuterie
    out3_vieux = out; 
    in3_vieux = in;  
    if((t3+temps)<(millis())) out = !out; // si la minuterie est finie, on inverse sortie
    if(!in) out = false; // on force à 0 qd désactivé
    return (out);
}

///////////////////////////////////////////////////////////////////
void loop() {

bp = digitalRead(8);
led = blinker(!bp,500);
boolean led2 = blinker(!bp,1000);
boolean led3 = blinker(!bp,1500);
Serial.print(led); 
Serial.print(led2);
Serial.println(led3);
}

Pour info, comment faire avec des variables globales?

Les autres sont utilisées par ta fonction et celle-ci doit conserver les valeurs à chaque appel. Deux possibilités pour ça :

Des variables globales
Des variables locales déclarées static (cherche la signification si tu ne connais pas)

Merci!

Vous avez 3 LEDs qui vont avoir des états différents donc il faut une mémorisation PAR led... la fonction ici va se mélanger les pinceaux. Soit voit mettez un tableau static dans la fonction pour mémoriser ce qu’il se passe pour chaque led et vous passez le N° de led en param à la fonction, soit vous créez une classe qui représente votre objet led et ce sont vos variables d’instance qui servent à mémoriser l’état (soit vous faites 3 fonctions chacune avec Son état local static mais c’est moche)

À noter if((t3+temps)<(millis())) nest pas écrit correctement pour gérer les débordements, il faut travailler en soustraction if((milis()-t3)>=temps)

Est ce que le but est uniquement de faire clignoter des leds à des fréquences différentes, ou bien est-ce un exercice préalable à quelque chose de plus compliqué ?

Dans le premier cas, si tu lis l'anglais, je te conseille de lire le tuto ici, et aussi de t'intéresser aux machines d'état.

Bonjour, Merci beaucoup pour votre aide, j'apprécie beaucoup!

Pour le débordement, comme souvent, c'est logique mais il fallait y penser :wink: Merci

Soit voit mettez un tableau static dans la fonction pour mémoriser ce qu'il se passe pour chaque led et vous passez le N° de led en param à la fonction,

Ok, je vois comment faire mais ce n'est pas ce que je souhaite

soit vous créez une classe qui représente votre objet led et ce sont vos variables d'instance qui servent à mémoriser l'état

Si je comprends bien, cela signifie créer une bibliothèque?

(soit vous faites 3 fonctions chacune avec Son état local static mais c'est moche)

C'est ce que j'ai fait? Non?

Est ce que le but est uniquement de faire clignoter des leds à des fréquences différentes, ou bien est-ce un exercice préalable à quelque chose de plus compliqué ?

C'est bien un exercice préalable à quelque chose de plus compliqué.

J'utilise régulièrement des contrôleurs pour machines mobiles programmés avec Codesys en ST.Pour ceux qui connaissent, il y a en standard ces petites fonctions que j'utilise régulièrement et que j'aimerais créer pour les cartes Arduino (Ton Toff tempo pour passer à on ou off, R_trig détection de front montant, Blink clignoteur,... )

J'imaginais gagner du temps en créant ces petites fonctions que je pourrais utiliser partout dans mon programme... Je commence à douter :slight_smile:
Je suis un peu perdu, comment écrire une fonction qu'on peut utiliser plusieurs fois sans avoir d'interférences entre elles? Elles doivent être indépendantes les unes des autres.

Je comprends bien que si on utilise deux fois une fonction, il faut que les variables internes soient créées deux fois mais je pensais que cela se faisait automatiquement avec "static"?

(mon problème suivant sera de renvoyer plusieurs variables en sortie de fonction mais on verra cela plus tard;)

jefk:
Ok, je vois comment faire mais ce n'est pas ce que je souhaite
Si je comprends bien, cela signifie créer une bibliothèque?

Une classe n'est pas forcément une librairie. mais si vous créez quelque chose de réutilisable alors ça peut être utile. Par exemple pour simplifier la gestion des boutons, @bricoleau a créé une petite librairie qui contient une classe simpleBouton. De la même manière il a une librairie avec une classe simpleLed

jefk:
J'imaginais gagner du temps en créant ces petites fonctions que je pourrais utiliser partout dans mon programme... Je commence à douter :slight_smile:
Je suis un peu perdu, comment écrire une fonction qu'on peut utiliser plusieurs fois sans avoir d'interférences entre elles? Elles doivent être indépendantes les unes des autres.

Je comprends bien que si on utilise deux fois une fonction, il faut que les variables internes soient créées deux fois mais je pensais que cela se faisait automatiquement avec "static"?

(mon problème suivant sera de renvoyer plusieurs variables en sortie de fonction mais on verra cela plus tard;)

Quand vous déclarez une variable static dans une fonction, ça veut dire qu'elle va se souvenir de sa valeur entre 2 appels (c'est comme une variable globale dont le nom serait utilisable que localement dans la fonction). La fonction elle même ne crée pas de nouvelles variables à chaque appel, sinon comment voulez vous qu'elle sache quand réutiliser une des variables déjà créée...

Donc pas de magie, si vous voulez un état associé à une LED qui soit mémorisé, il faut pouvoir le stocker quelque part. une classe offre un bon niveau d'abstraction, mais un tableau (éventuellement de structures si vous avez plusieurs éléments à mémoriser) peut aussi être utilisé

Je pense qu'une solution à ton questionnement se trouve dans les bibliothèques de Bricoleau qui simulent du multithreading. Voir ce message, et la bibli Ordonnanceur

EDIT : je vois que J-M-L a pensé aussi à Bricoleau...

Sans parler de classes qui occupent plus de place en flash qu'une fonction (on n'est pas dans un PC) il faut simplement autant de "groupes de variables" qu'il y a de Del.

Soit c'est des variables globales.
Soit c'est des variables locales à la fonction loop() et qui sont passés aux fonctions par adresse au lieu d'être passées par valeur.

Remarque :
Avec le système setup() et loop() dans une boucle infinie, je ne vois pas la différence en occupation mémoire entre des variables globales et des variables locales à loop(), surtout si elle sont déclarée static, mais peut-être il y en a t-il une ?

68tjs:
Sans parler de classes qui occupent plus de place en flash qu'une fonction (on n'est pas dans un PC) il faut simplement autant de "groupes de variables" qu'il y a de Del.

Non non... faut pas croire que les classes c'est quelque chose de gourmand et cher...

Le compilateur est assez fort... Les fonctions prennent la même place, les variables idem... En fait utiliser une classe n'est pas vraiment plus coûteux réellement que d'écrire tout à la main. c'est juste une façon de structurer son code, la protection des variables etc. On peut faire mieux au prix de plus de code manuel et en comprenant bien l'architecture des micro et la théorie de compilation et optimisation....

Par exemple ce code avec une classe LED toute simple qui prend en paramètre le N° de pin et la demi période pour faire clignoter la led

class LED
{
  public:
    // constructor
    LED(const uint8_t aPin, uint32_t demiPeriode) : _pin(aPin), _demiPeriode(demiPeriode) {
      pinMode(aPin, OUTPUT);
    }
    void clignote() {
      if (millis() - chrono >= _demiPeriode) {
        digitalWrite(_pin, (digitalRead(_pin) == LOW) ? HIGH : LOW);
        chrono = millis();
      }
    }

    uint8_t _pin;
    uint32_t _demiPeriode;

  private:
    uint32_t chrono = 0;
};

const uint8_t ledPin1 = 13;
const uint8_t ledPin2 = 12;

LED led1(ledPin1, 100ul);
LED led2(ledPin2, 1000ul);

void setup() {}

void loop() {
  led1.clignote();
  led2.clignote();
}

ça va occuper 1604 octets de flash et 27 octets de mémoire RAM. Le setup() est réduit à sa plus simple expression et la loop() est super lisible... On comprend bien ce que l'on fait.


la même chose peut être effectuée bien sûr en déclarant les mémoires à la main

const uint8_t ledPin1 = 13;
const uint8_t ledPin2 = 12;

uint32_t demiPeriode1;
uint32_t demiPeriode2;

uint32_t chrono1 = 0;
uint32_t chrono2 = 0;

void setup() {
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  demiPeriode1 = 200ul;
  demiPeriode2 = 1000ul;
}

void loop() {
  if (millis() - chrono1 >= demiPeriode1) {
    digitalWrite(ledPin1, (digitalRead(ledPin1) == LOW) ? HIGH : LOW);
    chrono1 = millis();
  }

  if (millis() - chrono2 >= demiPeriode2) {
    digitalWrite(ledPin2, (digitalRead(ledPin2) == LOW) ? HIGH : LOW);
    chrono2 = millis();
  }
}

ça va occuper 1710 octets de flash et 25 octets de mémoire RAM et c'est pas top à lire... Les 2 octets de RAM gagnés sont parce que les pins sont constantes et l'optimisateur les a virées.

Dans le cas de la classe, on peut les modifier à l'exécution donc si on voulait être iso-fonctionnel, on déclarerait les pins pas en const mais en volatile et on retrouverait nos 27 octets.

--> avantage sur la flash pour l'approche par classe.


ou alors si on est un peu plus smart on part avec une boucle et des tableaux

const uint8_t ledPins[] = {13, 12};
const uint8_t nbLED = sizeof(ledPins) / sizeof(ledPins[0]);
uint32_t demiPeriode[] = {200ul, 1000ul};
uint32_t chrono[] = {0, 0};

void clignote(const uint8_t index)
{
  if (millis() - chrono[index] >= demiPeriode[index]) {
    digitalWrite(ledPins[index], (digitalRead(ledPins[index]) == LOW) ? HIGH : LOW);
    chrono[index] = millis();
  }
}

void setup() {
  for (byte i = 0; i < nbLED; i++) pinMode(ledPins[i], OUTPUT);
}

void loop() {
  for (byte i = 0; i < nbLED; i++) clignote(i);
}

ça va occuper 1564 octets de flash et 27 octets de mémoire RAM

ici on a tout fait à la main et réfléchit aux optimisations, on gagne 40 octets de mémoire flash (à mon avis parce que la fonction clignote() doit être optimisée et mise "inline" et potentiellement les boucles juste répétées car nbLED est petit et constant)

bref - une classe ce n'est pas abominablement coûteux, c'est même mieux que si on ne code pas très bien sans comprendre ce que fait l'optimiseur de code.

Merci pour les conseils!

Si j'ai bien compris, la meilleur solution pour mon cas est une classe?
J'ai donc essayé d'en faire une en partant de vos exemples et librairies mais c'est un peu compliqué pour mon niveau.
Je ne trouve pas d'exemple simple où il y a des variables en "entrée" et en "sortie" de la classe pour m'en inspirer

Voici où j'en suis pour le moment, je constate au moins 2 problèmes:
Si vous voyez d'autres erreurs, n'hésitez pas!

// Global Variables
boolean bp;
//////////////////////////////////////////////////////
 class BLINKER
{
  public:
    // constructor
    BLINKER( boolean in, unsigned long temps) : _in(in), _temps(temps) { 
    }
    void clignote() {
       if((!out && out_vieux)||(out && !out_vieux)||(_in && !in_vieux)) t= millis(); // si front output ou front mont input, on active minuterie
       out_vieux = out; 
       in_vieux = _in;  
       if((millis()-t)>=_temps) out = !out; // si la minuterie est finie, on inverse sortie
       if(!_in) out = false; // on force à 0 qd désactivé
      }
    
    byte _in;
    unsigned int _temps;
    boolean out;
    static boolean sortie(){return out;}
    
  private:
     unsigned long t;
     boolean in_vieux;
     boolean out_vieux;
     
};

BLINKER blk1(!bp, 500);
BLINKER blk2(!bp, 1000);
BLINKER blk3(!bp, 1500);

void setup() {
 pinMode(8, INPUT_PULLUP); // déclaration des entrées avec résistance pullup interne
 Serial.begin(9600);// initialize serial:
}

void loop() {
  bp = digitalRead(8);
  boolean led1 = blk1.clignote();
  boolean led2 = blk2.clignote();
  boolean led3 = blk3.clignote();
  Serial.print(led1); 
  Serial.print(led2);
  Serial.println(led3); 
}

Ceci devrait être dans loop car bp est une variable?

BLINKER blk1(!bp, 500);
BLINKER blk2(!bp, 1000);
BLINKER blk3(!bp, 1500);

Comment écrire ceci pour copier la variaible out de blk1 dans led1?

boolean led1 = blk1.clignote();

Bonjour,

Tu retournes out dans la fonction clignote()

   bool clignote() {
       if((!out && out_vieux)||(out && !out_vieux)||(_in && !in_vieux)) t= millis(); // si front output ou front mont input, on active minuterie
       out_vieux = out; 
       in_vieux = _in;  
       if((millis()-t)>=_temps) out = !out; // si la minuterie est finie, on inverse sortie
       if(!_in) out = false; // on force à 0 qd désactivé
       return out;
      }

Par contre, je ne vois pas ou tu commandes les leds. Regardes la classe LED donnée pas J-M-L au post #9

Par contre, je ne vois pas ou tu commandes les leds. Regardes la classe LED donnée pas J-M-L au post #9

C'était un exemple les led.
Ce qui m'intéresse, c'est de pouvoir faire clignoter des variables boolean à des fréquences différentes.
Désolé de vous avoir mal influencé avec le nom de mes variables.

jefk:
Ce qui m'intéresse, c'est de pouvoir faire clignoter des variables boolean à des fréquences différentes.
Désolé de vous avoir mal influencé avec le nom de mes variables.

C'est bien ce que fait la class de J-M-L

Bonjour,

C'est bien de la class de J-M-L que je me suis grandement inspiré.
Mais il y a quelques différences entre la mienne et celle de J-M-L:

J'ai une variable et pas une constante à envoyer à l'objet.
Je dois récupérer une variable de l'objet et pas piloter une sortie de la carte directement.

Je ne sais pas comment et à quel endroit déclarer les objets. Je suppose qu'il faut écrire quelque chose de ce genre dans main?
Dans les autres exemples, c'est fait à l'extérieur du main mais si je fais cela, l'état de bp ne sera pas actualisé.

BLINKER blk1(!bp, 500);
BLINKER blk2(!bp, 1000);
BLINKER blk3(!bp, 1500);

Pour récupérer une variable de l'objet, je fait ceci?

static boolean sortie(){return out;}

ou ceci?
"return out" dans la fonction clignote() + "boolean led1 = blk1.clignote();"
J'aurai bien l'état de out dans led1?

Désolé, c'est sûrement des bêtes questions mais je viens de découvrir les classes. Avez-vous un tuto simple à me conseiller?

Voici le code complet qui ne fonctionne pas pour le moment:(je ne comprends pas les erreurs de compilation)

// Global Variables
boolean bp;
//////////////////////////////////////////////////////
 class BLINKER
{
  public:
    // constructor
    BLINKER( boolean in, unsigned long temps) : _in(in), _temps(temps) {
    }
    void clignote() {
       if((!out && out_vieux)||(out && !out_vieux)||(_in && !in_vieux)) t= millis(); // si front output ou front mont input, on active minuterie
       out_vieux = out;
       in_vieux = _in; 
       if((millis()-t)>=_temps) out = !out; // si la minuterie est finie, on inverse sortie
       if(!_in) out = false; // on force à 0 qd désactivé
       return out;
      }
   
    byte _in;
    unsigned int _temps;
   
  private:
     unsigned long t;
     boolean in_vieux;
     boolean out_vieux;
     boolean out;
};

BLINKER blk1(!bp, 500);
BLINKER blk2(!bp, 1000);
BLINKER blk3(!bp, 1500);

void setup() {
 pinMode(8, INPUT_PULLUP); // déclaration des entrées avec résistance pullup interne
 Serial.begin(9600);// initialize serial:
}

void loop() {
  bp = digitalRead(8);
  boolean led1 = blk1.clignote();
  boolean led2 = blk2.clignote();
  boolean led3 = blk3.clignote();
  Serial.print(led1);
  Serial.print(led2);
  Serial.println(led3);
}

Merci

Un peu de logique : si je ne m'abuse ceci

(!out && out_vieux)||(out && !out_vieux)

équivaut à : XOR (out, out_vieux) soit

out ^ out_vieux

Pour les problèmes de compilation, ce serait plus simple pour nous si tu les postais avec le code...

Merci pour cette optimisation, je confirme que ça fait la même chose :slight_smile:

Voici le résultat de la compil:

sketch_mar12c.ino: In member function ‘void BLINKER::clignote()’:
sketch_mar12c.ino:17:15: error: return-statement with a value, in function returning 'void' [-fpermissive]
sketch_mar12c.ino: In function ‘void loop()’:
sketch_mar12c.ino:41:32: error: void value not ignored as it ought to be
sketch_mar12c.ino:42:32: error: void value not ignored as it ought to be
sketch_mar12c.ino:43:32: error: void value not ignored as it ought to be

Les erreurs sont aux lignes 17, 41 42 et 43. Tu peux voir les numéros des lignes en positionnant le curseur dans l'IDE : le numéro s'affiche en dessous de la fenêtre du code (en tout petit)

17:

return out;

Remplacer

void clignote()

par

boolean clignote()

41 et suivantes :

  boolean led1 = blk1.clignote();
  boolean led2 = blk2.clignote();
  boolean led3 = blk3.clignote();

Je pense que ces erreurs seront réglées automatiquement...

ensuite vous ne pouvez pas déclarer l'instance avec une variable et penser que quand la variable est mise à jour, l'instance sera mise à jour... (bp)

Bonjour,

Merci bien, je n'ai en effet plus d'erreur!

ensuite vous ne pouvez pas déclarer l'instance avec une variable et penser que quand la variable est mise à jour, l'instance sera mise à jour... (bp)

C'est ce qu'il me semblait mais comment faire alors? Je peux déclarer l'instance sans rien et ensuite dans loop lui envoyer les variables? Si oui, comment?

Merci bien.