Comment utilisé un interrupt dans une librarie

Bonjour à tous,

Je retourche mon code et je sépare ce dernier dans une librairie. Je fais donc une librairie pour lire un anénometre DEVIS.

Dans mon code initial, je fais ceci. Je commence à déclarer une variable volatile

 volatile unsigned long Rotations;         // (ANENO) cup rotation counter used in interrupt routine
volatile unsigned long ContactBounceTime; // /ANENO) Timer to avoid contact bounce in interrupt routine

Puis dans mon setup(), j'initie la pin qui va déclancher l'interrupt ainsi que le fonction qui va être appelée

pinMode(read_pin_wind_speed, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(read_pin_wind_speed), isr_rotation, FALLING);

QO: Est-ce que isr_rotation peux faire appel à une fonction void Ecoaneno::_isr_rotation () {} déclarée dans fichier.cpp?

puis ma fonction

// This is the function that the interrupt calls to increment the rotation count
void isr_rotation_aneno ()
{
  if ((millis() - ContactBounceTime) > 15 ) // debounce the switch contact.
  {
    Rotations++;
    ContactBounceTime = millis();
  }
}

Donc à chaque fois que ma girouette va faire un tour, la variable Rotations va incrémenter.

Voilà, ceci fonctionne.
J'ai donc écrit une librairie et je me pose la question comment déplacer ceci dans mon fichier.cpp

Je vais donc vous décrire comment je pense le faire et si vous pouviez me corriger, ca serait cool.

Premièrement, dans mon fichier.h, dans la partie public:, je pensais créer mes deux variables
Q1: Est-ce nécessaire qu'elle soit dans public, peuvent-elle être dans private?

public:
		Ecoaneno();
        volatile unsigned long Rotations;               // (ANENO) cup rotation counter used in interrupt routine
        volatile unsigned long ContactBounceTime;       // /ANENO) Timer to avoid contact bounce in interrupt
		int get_winddirection(int read_pin, bool debug);
		float get_windspeed(int read_pin_wind_speed, bool debug);
		//int WDdirection[18];
		int WindDirectionOffset = 0;                    // 0 : the van is directed to the north
		int WDdirection[18] {
    		// Analog value, direction direction (° degre)
    		0,    0,    // N
    		270,  45,   // NE
    		561,  90,   // E
    		642,  135,  // ES
    		743,  180,  // S
    		814,  225,  // SW
    		870,  270,  // W
    		920,  315,  // WN
    		987,  360,  // N
  		}; 

maintenant, je dois m'occuper de ces deux déclarations qui se trouve dans le setup()

pinMode(read_pin_wind_speed, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(read_pin_wind_speed), isr_rotation, FALLING);

Q2: Comment puis déplacer ces deux lignes soit dans mon fichier.h ou mon fichier.cpp?

Il faut savoir que déclare ma librairie ainsi

#include <EcoAneno.h>
Ecoaneno aneno;

Donc je ne passe aucun numéro d'une broche (pin) à ce niveau, donc sauf erreur de ma part, je ne peux rien faire à ce niveau (dans mon fichier.cpp)

Ecoaneno::Ecoaneno()
{
}

par contre, je fais appelle à la fonction float Ecoaneno::get_windspeed(int read_pin_wind_speed, bool debug){} ou je passe le numéro de la broche, mais sauf erreur de ma part, je ne peux définir le mode de ma broche dans cette fonction.

Si c'est bien juste, je devrais plus tôt m'orienter vers une solution qui se raproche de ceci (et adapter mon fichier.h en conséquence)

Ecoaneno aneno(14,true); //Dans mon fichier.ino

Ecoaneno(int read_pin_wind_speed, bool debug); dans mon fichier.h (public:)

// Dans mon fichier.cpp
Ecoaneno::Ecoaneno(int read_pin_wind_speed, bool debug)
{
  int _read_pin_wind_speed = read_pin_wind_speed;
  bool _debug = debug;
  pinMode(_read_pin_wind_speed, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(_read_pin_wind_speed), isr_rotation, FALLING);
}

puis j'ajoute en bas de mon fichier.cpp, la fonction isr_rotation.

En autre, ceci me permettrait de supprimer les deux paramètres de cette fonction
float get_windspeed(int read_pin_wind_speed, bool debug); et d'utiliser les variables privée _debug et _read_pin_wind_speed dans cette dernière

Est-ce que mon dernier raisonnement tient la route?, si oui que me proposeriez-vous de plus optimal?

Merci pour vos lumières

est-ce que ces variables doivent être accessibles depuis l'extérieur de votre classe ? si oui public si non private ou protected

il ne faut pas configurer des pins ou des trucs de bas niveau dans le constructeur car il est appelé avant que la fonction main() de l'arduino s'exécute et elle risque de défaire ce que vous avez fait

➜ généralement c'est pour cela que les classes qui ont des besoins spécifiques ont une fonction begin() dans laquelle vous faites la configuration

Non, le compilateur doit connaître l'instance (le contexte) sur laquelle s'appliquerait la fonction. Soit il faut qu'elle soit statique (méthode de classe) soit c'est une fonction générique
➜ dans ce cas toutes les instances partageraient la même interruption

Hello J-M-L,
en effet, mon problème se trouve actuellement là.
J'ai corrigé mon fichier.cpp de cette manière

Ecoaneno::Ecoaneno(int read_pin_wind_speed, bool debug)
{
  int _read_pin_wind_speed = read_pin_wind_speed;
  bool _debug = debug;
}

void Ecoaneno::begin(){
  pinMode(_read_pin_wind_speed, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(_read_pin_wind_speed), _isr_rotation, FALLING);
}

void Ecoaneno::_isr_rotation() {
  
  //if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact.
  //  Rotations++;
  //  ContactBounceTime = millis();
  //}

}

Dans mon fichier.h j'ai changé comme ceci (pour le moment _isr_rotation est public
(mes deux variable volatile, je regarderai plus tard pour les mettre private)

public:
		Ecoaneno(int red_pin, bool debug);
        void begin();
        void _isr_rotation();
        volatile unsigned long Rotations;               // (ANENO) cup rotation counter used in interrupt routine
        volatile unsigned long ContactBounceTime;       // /ANENO) Timer to avoid contact bounce in interrupt

et dans mon fichier.ino j'ai ceci


Ecoaneno aneno(pin_readWindSpeed, true);
aneno.begin(); // dans setup();

Soit il faut qu'elle soit statique (méthode de classe) soit c'est une fonction générique

Mais que veut tu dire par là? ceci void Ecoaneno::_isr_rotation() {} ne suffit pas dans mon fichier.cpp?? :upside_down_face: :upside_down_face:

Merciiii

[Edit]
J'ai essayé static void Ecoaneno::_isr_rotation() {} sans succès

si c'est bien comme cela que l'on déclarerait une fonction membre statique (de classe)

voici un wokwi avec la structure de base

le souci avec cette approche cependant c'est que la fonction statique ne peut accéder que des variables statiques (dites aussi variables de classe) et toutes les instances de votre classe Ecoaneno vont appeler la même fonction et accéder les mêmes variables.

Sur AVR il n'y a pas de paramètre passé à l'interruption (c'est possible sur ESP32 avec __attachInterruptFunctionalArg()) donc vous ne saurez pas quel compteur incrémenter dans la fonction sauf à regarder quelle interruption est servie

(il y a moyen de faire des trucs avec mais le jeu en vaut-il la chandelle... regardez GitHub - gfvalvo/NewEncoder: Rotary Encoder Library ou GitHub - PaulStoffregen/Encoder: Quadrature Encoder Library for Arduino pour voir un exemple sur l'approche)

Bonjour J-M-L,

Merci pour votre réponse et désolé de ne pas être revenu plus tôt.

Je me demande si je devrais continuer dans cette direction ou faire pus simple, en faisant un interrupt dans mon fichier ino, comme j'ai l'habitude de la faire. Mais dans ce cas, il me fera comme-même une variable de ma classe pour que je puisse transmettre le comptage des tours de ma girouette, à ma fonction float Ecoaneno::get_windspeed(int red_pin, bool debug){} pour faire le calcul.

Mais l'interêt d'avoir ma fonction ISR dans ma libraire est que l'utilisateur qui utilisatra ma libraitie, n'ait rien d'autre à faire que de renseigner la broche sur laquelle est lue les tours de ma girouette. J'aimerais donc continuer :slight_smile:

J'ai donc la même chose que dans votre wokwi, saud que dans le fichier.cpp, j'ai du chnager ceci
static void Ecoaneno::_isr_rotation(){} en void Ecoaneno::_isr_rotation(){}. Par contre, dans mon fichier.h, j'ai gardé static void _isr_rotation(); . Dans le premier cas, j'avais un message d'erreur

error: cannot declare member function 'static void Ecoaneno::_isr_rotation()' to have static linkage [-fpermissive]
101 | static void Ecoaneno::_isr_rotation(){
maintenant, je n'ai plus d'erreur...

Vous m'avez écrit:

le souci avec cette approche cependant c'est que la fonction statique ne peut accéder que des variables statiques (dites aussi variables de classe) et toutes les instances de votre classe Ecoaneno vont appeler la même fonction et accéder les mêmes variables

Si je continue avec ceci

void Ecoaneno::_isr_rotation(){
  
  //if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact.
    Rotations++;
    //ContactBounceTime = millis();
  //}

}

J'ai une erreur avec Rotations, qui doit donc est une variable statique. Par conséquent, ceci, dans mon fichier.h, est faux, n'est-ce pas?


private:
        bool _begug;
        int _read_pin_wind_speed;
        static void _isr_rotation();
        volatile unsigned long Rotations;

J'ai donc corrigé ainsi

private:
        bool _begug;
        int _read_pin_wind_speed;
        static void _isr_rotation();
        static unsigned long Rotations;

et mlaeureusement j'ai une erreur de référence

c:/users/pierrot/appdata/local/arduino15/packages/adafruit/tools/arm-none-eabi-gcc/9-2019q4/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: libraries\EcoAneno\EcoAneno.cpp.o: in function Ecoaneno::_isr_rotation()': C:\Users\pierrot\Documents\Arduino\libraries\EcoAneno/EcoAneno.cpp:108: undefined reference to Ecoaneno::Rotations'

PS: la variable Rotations n'est utilisée que dans la fonction float Ecoaneno::get_windspeed(int red_pin, bool debug){} et la fonction ISR void Ecoaneno::_isr_rotation(){}

Une variable static (qui incrémente) doit est lu ou modifiée d'une manière spécifique?

oui comme je l'ai dit avant, une fonction statique (de classe) ne peut utiliser que des variables statiques (de classe). Pour définir une variable de classe static on la déclare dans la classe et on alloue sa mémoire séparément.

j'ai modifié le wokwi pour vous montrer comme faire

le bouton avec ses rebonds va se comporter un peu comme l'aimant de votre anémomètre.

je n'ai pas mis de mutex ou bloqué les interruptions dans la classe quand je vais lire la valeur du compteur donc il y a un risque de retourner une mauvaise valeur si une interruption et incrémentation arrive au moment où on fait l'interrogation puisque cette opération n'est pas atomique. Dans un "vrai" code il faudra le faire. (sur AVR on peut se contenter de bloquer les interruptions pour faire une copie de la valeur et retourner cette copie, sur ESP il faudra prendre un mutex ou semaphore binaire)

je vous laisse regarder comment on déclare la variable de classe dans le .h et comment on l'alloue dans le .cpp

en pratique l'idée est séduisante et au prix de quelques contortions on peut faire ce que vous voulez (cf les liens précédents).

Une approche plus simple mais moins intégrée serait de mettre le callback (l'ISR) et le compteur dans le .ino. Comme cela si vous avez deux instances vous pouvez passer 2 fonctions différentes et deux compteurs. La classe servant à offrir une abstraction légère à la configuration des interruptions

voici un wokwi qui montre cela avec 2 boutons pour le coup

Bonjour J-M-L
Milles mercis pour ces explications et pour votre patience.
J'avour, j'ai du mal à trouver une solution.

Merci pour l'exemple du calllback, mais je pense que cela ne va pas répondre à mon besoin, car le fonction doit retourner la vitesse du vent et l'incémentation doit se faire dans ma librarire.cpp.

Sur ce, je pense que je n'ai pas assez fourni de code, je vais donc être plus complet. J'ai mis mon code sur github

NB: J'ai renommé Ecoaneno par Ecoanemo

Voici mon fichier.h et voici mon fichier.cpp

Pour revenir, dans mon fichier.ino, a un certain point, je veux connaitre la vitesse du vent. J'ai donc ceci qui va faire appel à cet demande

Ecoanemo anemo(pin_readWindSpeed, true);
anemo.begin();

Serial.println(F("Wind Speed:"));
anemo.get_windspeed();

ma fonction doit retourner la vitesse du vent.
Cette fonction, va mettre à zéro le compteur de rotation (Rotations), puis met en pause le script pendant 3 secondes , pour compter les interrupts.
Puis, il va faire le calcul avec la valeur de Rotations pour retourner la vitesse du vent en kmh.

Le anemo.begin() va donc activer le interrupt et définir la pin de lecture ici .

oui comme je l'ai dit avant, une fonction statique (de classe) ne peut utiliser que des variables statiques (de classe). Pour définir une variable de classe static on la déclare dans la classe et on alloue sa mémoire séparément.

Mais ne l'ai-je pas fait ici ?

Si elle renvoie une vitesse, il faut l'appeler comme ceci :

float vitesse = anemo.get_windspeed();

Si elle renvoie une vitesse, il faut l'appeler comme ceci :

float vitesse = anemo.get_windspeed();

Oui bien sûre.

Je suis arrivé à queque chose.
D'abors, j'ai supprimé cette ligne et je l''ai mise ici.
get_windspeed()
ISR

J'ai tourné mon code sur ma carte et ca fonctionne, mais j'aimerais bien que mon code soit bien évalué. Quand pensez-vous? Peut-on optimer ceci? (je ferai du nettoyage plus tard)

Tu peux prévoir un réglage pour fournir la vitesse en km/h ou en mph (selon que l'utilisateur est américain ou européen).
Tu peux aussi vérifier que la pin utilisée supporte bien les interruptions.

Ha oui. c'est des bonnes idées, mais je pensais au niveau de la fonction get_windspeed, et tout particulièrement au niveau du ISR (imterrupt). J'aimerais surtout que cela soit bien fait.

https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L10
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L11
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L122
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.h#L43
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L84

vous avez déclaré la variable (nom et type connu du compilateur) mais elle n'est pas définie (mémoire allouée). Pour cela il faut rajouter dans le .cpp

static unsigned long Ecoanemo:: Rotations;

c'est là que la mémoire est allouée.

pourquoi vous embêtez vous avec les interruptions si la bibliothèque est synchrone et bloque le code principal pendant 3 secondes à chaque appel? faites une boucle avec un échantillonnage pendant 3 secondes

l'idée des interruptions serait que le code s'auto-débrouille pour maintenir la vitesse moyenne du vent et que l'appel à anemo.get_windspeed() (moi je préfère anemo.getWindSpeed() en camel case car c'est ce que recommande la doc Arduino) donne cette vitesse directement

(sinon vous devriez attacher l'ISR uniquement pendant le délai de 3s puis la détacher sinon d'autres interruptions peuvent venir mettre bazar pendant vos calculs puisque vous n'avez pas de section critique)

Ta manière de calculer la direction du vent est assez compliquée. Si on regarde la courbe issue de tes données (tableau WDdirection du fichier .h) :

Ce n'est pas complètement linéaire, plutôt en deux parties linéaires.
Tu peux calculer la direction comme suit :

if (analogInput < 561) windDirection = int(analogInput * 0.16);
else windDirection = int(analogInput * 0.67 - 309);

Bonjour @J-M-L

D'accord je vois.
J'ai commenté ceci
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L10
et j'ai ajouté
https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.h#L36 et https://github.com/ecosensors/EcoAnemo/blob/main/EcoAnemo.cpp#L74

Hello lesept

Merci pour cet observation. Je n'ai pas encore passé en revu, cette partie, mais je vais le fait de suite. Je ne suis pas sûre que les différents anémoetre retorune les memes valeurs analogiques. C'est pourquoi, j'ai pensé mettre en place une partie pour les qualibrer. Je remarque aussi que mon code bug. J'ai peut-être ma recopier mon code d'un fichier à un autre. Je vais vérifier ceci durant cette semaine
J'ai plusieurs anémomètre et je vais comparer les valeurs anaogiques qu'ils retourne. Je reviens donner un feedback plus tard dans la semaine.
Merci

vous ne devriez pas conserver des trucs qui ne servent à rien en commentaire dans votre fichier

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.