usage multiple de MsTimer2 ( bibliotheque interuption )

Bonjour ,

j ' ai besoin de conseils avisés car je galere un peu 8) .

est il possible de faire des interuptions multiples a l' aide de cette bibliotheque ?
j ' entends par là , gerer plusieurs timer d ' interuption ,qui arrete plusieurs fonctions .

voici l ' exemple integre dans la biliotheque :

/*
  MsTimer2 is a small and very easy to use library to interface Timer2 with
  humans. It's called MsTimer2 because it "hardcodes" a resolution of 1
  millisecond on timer2
  For Details see: http://www.arduino.cc/playground/Main/MsTimer2
*/
#include <MsTimer2.h>

// Switch on LED on and off each half second

#if ARDUINO >= 100
const int led_pin = LED_BUILTIN; // 1.0 built in LED pin var
#else
const int led_pin = 13; // default to pin 13
#endif


void flash()
{
  static boolean output = HIGH;
  
  digitalWrite(led_pin, output);
  output = !output;
}

void setup()
{
  pinMode(led_pin, OUTPUT);

  MsTimer2::set(500, flash); // 500ms period
  MsTimer2::start();
}

void loop()
{
}

dans mon code a moi , je n ' utilise pas tout a fait de la meme maniere , mais bon ca a toujours marché depuis plusieurs mois .

le code de la bibliotheque , fichier MsTimer2.cpp et MsTimer2.h sont en PJ si necessaire

dans mon code , je voudrais donc lancer 2 fois MStimer2 , avec 2 timers et interrompant 2 fonctions differentes .

c ' est possible selon vous , car apres de muiltiples essais ca ne marche pas :sleeping:

MsTimer2.cpp (6.78 KB)

MsTimer2.h (504 Bytes)

le timer va interrompre la tâche en cours, pas une fonction en particulier.

Si vous avez besoin de deux fréquences d'interruption, prenez le PGCD et comptez le nombre d'interruptions

par exemple si pour la tâche 1 vous devez faire quelque chose toutes les 500ms et pour la tâche 2 toutes les 750ms, le PGCD c'est 250 (2x250= 500, 3x250=750). Donc vous faites une interruption toutes les 250 ms et dans le code de l'interruption vous avez un compteur. Quand c'est un multiple de 2 vous savez que c'est l'interruption de la tâche 1 et quand c 'est un multiple de 3 pour la tâche 2 (les deux étant possible en même temps, pas de else dans le test)

Merci J-M-L , mais ce n ' est pas vraiment possible .

dans le cas ou je l' utilise l ' appel se fait par le nom de la fonction et ensuite on lance un timer , on ecrit une autre fonction de reset et la magie opere :smiley:

plus besoin de chrono , millis etc ...
j ' ai trouvé la solution pour implementer les usages multiples , cependant la methode n ' est pas des plus pratiques a mettre en oeuvre .

voici un bref exemple , je passe les declarations de variables et autre base , juste l initialisation , le lancement et la fonction d ' interuption :

void setup() {

MsTimer2::set(TIMER_V, resetTimer_V);

void resetTimer_V () { // attend la fin de TIMER_V pour effectuer le code .
 // on attend 30 sec puis on reinitialise les bouttons des volets en position of.
 MsTimer2::stop(); // arrete le timer
 reInitRelaisTable(0 , RELAY_NUMBER_V);
 delayMicroseconds(1000);
}

void ma_fonction_ki_va_bien () {
//blabla
MsTimer2::start();
//blabla
}


}

mais 2 problemes se posent :
on ne peut pas passer de parametres dans la fonction de resetTimer
donc on fait des copier coller ...

tres dommage car c ' est facile a mettre en oeuvre et a prendre en main , ca evite aussi de passer par chrono , millis() et les comparaisons de temps a la k.. :stuck_out_tongue:

rien compris :frowning: :o

commencez par expliciter l'objectif, ensuite on verra si un timer est la solution adaptée...

ok donc ,

pour le cas d' un arrosage autonome :kissing:

, je lance mon arrosage , j implante un timer , a la fin du timer l ' arrosage s' arrete .

iznobe:
plus besoin de chrono , millis etc ...

Attention, ça ne remplace pas du tout millis()...

La fonction est exécutée en interruption, donc elle doit être le plus courte possible et ne doit pas appeler de fonctionnement qui utilisent les interruptions (sauf à gérer explicitement les masquage/démasquage des interruptions).
En particulier pas de delayMicroseconds() et encore moins de delay()

C'est sympa pour faire clignoter un led ou actionner une sortie, mais il ne faut pas en demander trop.

iznobe:
ok donc ,
pour le cas d' un arrosage autonome :kissing:
, je lance mon arrosage , j implante un timer , a la fin du timer l ' arrosage s' arrete .

mouaiiis c'est un mauvais usage des interruption :slight_smile:

(encore plus si vous avez plusieurs tâches à gérer)

en fait ca marche plutot bien hors mis qu ' il ne faut pas faire de melange :grin:

on peut utiliser la fonction delay() , pas de probleme , la bibliotheque est basée sur micros() si j ' ai bien compris .

l ' inconvenient majeur de la procedure est qu on ne peut pas passer de parametre a la fonction de reset ( en tout cas j ' ai pas trouvé )

du coup il faut utiliser une variable globale ou plusieurs selon les cas que l ' on modifie a l ' interieur de celle-ci ,

ca peut vite devenir un probleme pour les cartes arduino a " gros programme " qui ont une limite memoire tres vite atteinte dans le cas d' utilisation de cette " library " sur des projets ambitieux .

J-M-L:
mouaiiis c'est un mauvais usage des interruption :slight_smile:

(encore plus si vous avez plusieurs tâches à gérer)

c ' est censé etre utiliser dans quel cas une interruption ??

iznobe:
c ' est censé etre utiliser dans quel cas une interruption ??

Hello

Clairement pas pour gérer un arrosage :slight_smile:

C'est plutôt pour gérer des événements externes avec une réactivité à la milliseconde, et en utilisant des routines extrêmement courtes (qques dizaines de microsecondes).

AMHA : apprendre à utiliser millis() est plus facile que maîtriser les interruptions.

Tenez j'ai retrouvé un bout de code que j'avais

mettez ça dans un fichier AsyncTask.h

#ifndef ASYNCTASK_H
#define ASYNCTASK_H

#include <Arduino.h>

typedef uint16_t t_commandID; // command idendifier is on 2 bytes (0x00 to 0xFFFF).
typedef void (*t_callback)(t_commandID); // same as "using t_callback = void (*)(t_commandID);"

template <uint16_t queueLength>
class AsyncTask
{
  private:
    struct t_queueCommand
    {
      t_callback    callback;    // a pointer to the function you want to execute after the delay is expired
      t_commandID   commandID;   // An ID for this command, used in callBack
      uint32_t      maxWait;     // how long to wait before the callback
      uint32_t      startTime;
      bool          active;
    };

    uint16_t _count;
    t_queueCommand _queueCommandList[queueLength];
    static const uint16_t _maxCommands = queueLength;

  public:
    // constructor
    AsyncTask()
    {
      _count = 0;
      for (uint16_t i = 0; i < _maxCommands; i++)
        _queueCommandList[i].active = false;
    }

    bool registerAsyncCommand(t_commandID id, uint32_t deltaT, t_callback cb) {

      // find the next empty slot
      int32_t nextSlot = -1;

      for (int32_t i = 0; i < _maxCommands; i++) {
        if (! _queueCommandList[i].active) {
          nextSlot = i; // found a position
          break;
        }
      }

      // register the command in that slot
      if (nextSlot != -1) {
        _queueCommandList[nextSlot].startTime = millis();
        _queueCommandList[nextSlot].commandID = id;
        _queueCommandList[nextSlot].callback = cb;
        _queueCommandList[nextSlot].maxWait = deltaT;
        _queueCommandList[nextSlot].active = true;
        _count++;
      } // else  // error no room left

      return (nextSlot != -1);
    }

    uint16_t updateQueue()
    {
      uint16_t taskExpired = 0;
      if (_count == 0) return false; // no command to execute
      for (uint16_t i = 0; i < _maxCommands; i++) {
        if (_queueCommandList[i].active) {
          if (millis() - _queueCommandList[i].startTime >= _queueCommandList[i].maxWait) {
            // trigger the callback
            _queueCommandList[i].callback(_queueCommandList[i].commandID);
            // mark it as done
            _queueCommandList[i].active = false;
            _count--;
            taskExpired++; // count number of completed tasks
          }
        }
      } // end for each possible task
      return taskExpired;
    }


    // utilities
    uint16_t maxCommands() const {
      return _maxCommands;
    }

    uint16_t queueCount() const  {
      return _count;
    }
};

#endif

c'est une petite classe qui permet de gérer pour vous N trucs "à faire plus tard"

N est un paramètre d'instanciation (c'est un template) de la classe (pour ne pas avoir à le câbler en dur) qui dit combien de tâches au max vous pouvez avoir en parallèle. par exempleAsyncTask<10> gestionnaireDeTache;définit un objet nommé gestionnaireDeTache capable de contenir 10 tâches.

Une tâche se définie par un identifiant (un nombre), un délai à attendre en ms et une fonction à appeler (un callback).

Quand vous voulez que quelque chose se lance plus tard vous appelez la méthode registerAsyncCommand() en passant un N° d'identifiant (qui sera passé au callback), un délai d'attente avant d'appeler le callback et le pointeur sur la fonction à appeler.

Dans la loop() vous n'avez qu'à appeler à chaque tour de loop updateQueue(); pour déclencher les callbacks au bon moment

par exemple on va l'utiliser comme ça

#include "AsyncTask.h"
AsyncTask<10> gestionnaireDeTache;

void callback1(t_commandID identifier)
{
  Serial.print(millis() / 1000);
  Serial.print(F("\tCall back #1 pour ID = "));
  Serial.println(identifier);
}

void callback2(t_commandID identifier)
{
  Serial.print(millis() / 1000);
  Serial.print(F("\tCall back #2 pour ID = "));
  Serial.println(identifier);
  gestionnaireDeTache.registerAsyncCommand(40, 5000UL, callback2); // on relance la tâche ID 40 pour dans 5 secondes
}

void setup()
{
  Serial.begin(115200);
  // on amorce avec 3 tâches futures dans 3, 6 et 9 secondes
  gestionnaireDeTache.registerAsyncCommand(10, 3000UL, callback1);
  gestionnaireDeTache.registerAsyncCommand(20, 6000UL, callback1);
  gestionnaireDeTache.registerAsyncCommand(30, 9000UL, callback2);
}

void loop()
{
  gestionnaireDeTache.updateQueue();
}

ce bout de code déclare dans le setup qu'on veut déclencher la fonction callback1 dans 3secondes, puis 6 secondes et la fonction callback2 dans 9 secondes

  // on amorce avec 3 tâches futures dans 3, 6 et 9 secondes
  gestionnaireDeTache.registerAsyncCommand(10, 3000UL, callback1);
  gestionnaireDeTache.registerAsyncCommand(20, 6000UL, callback1);
  gestionnaireDeTache.registerAsyncCommand(30, 9000UL, callback2);

les fonctions ne font qu'imprimer un petit message sur la console à 115200 bauds, et le callback2 remet une pièce dans la machine et redemande d'être rappelée 5 secondes plus tard en faisant  gestionnaireDeTache.registerAsyncCommand(40, 5000UL, callback2); // on relance la tâche ID 40 pour dans 5 secondes

donc on va avoir un appel au bout de 3s, 6s, 9s puis ensuite toutes les 5 secondes. la console série va afficher

[color=purple][color=red]3	Call back #1 pour ID = 10
6	Call back #1 pour ID = 20
9	Call back #2 pour ID = 30[/color]
14	Call back #2 pour ID = 40
19	Call back #2 pour ID = 40
24	Call back #2 pour ID = 40
29	Call back #2 pour ID = 40
[/color]

On voit en rouge les 3 premiers callbacks (avec leurs identifiant 10, 20 30) au bout des 3,6 et 9 secondes et ensuite toutes 5 secondes on a l'autre callback pour l'ID 40.

voilà avec ça plus besoin de vous casser la tête avec millis(). Au moment ou vous ouvrez une vanne vous enregistrez un callback dont l'ID sera le numéro de la vanne et le temps que vous voulez conserver la vanne ouverte et vous écrivez un petit callback qui ne fait que fermer la vanne reçue en paramètre. ça se déclenchera pour vous tout seul, du moment que vous appelez en début de loop la fonction updateQueue().

ouahhh , pas mal du tout !!!

c ' est pratiquement pareil , mais en mieux :smiley: et on peut meme passer un parametre identifier !!! coolll ca

juste une question a propos de la callback 2 pourquoi celle-ci est remanente ? alors que les 2 autres non

a cause de l ' imbrication ou j ' ai loupé un truc ?

Elle est rémanente parce qu’elle se relance toute seule dans le callback2 cf le

 gestionnaireDeTache.registerAsyncCommand(40, 5000UL, callback2); // on relance la tâche ID 40 pour dans 5 secondes

Et le paramètre de la fonction callback est de type t_commandID. Ce type est défini dans le .h par typedef uint16_t t_commandID;donc ici un entier sur 16 bits mais rien ne vous empêche de modifier ce typedef et mettre un uint32_t ou une structure et vous aurez alors en paramètre quelque chose de plus riche que juste un identifiant

ok , j ' avais bien zapper la subtilité du coup ...

j ' avais vu qu on apellait un callback dans un calback , mais pas fait gaffe que la fonction callback2 s' appelait elle meme ...

Merci pour l ' explication , bon ben du coup avec ca quand on a compris la subtilité , ca evite pas mal de ligne de code :smiley:

J-M-L:
donc ici un entier sur 16 bits mais rien ne vous empêche de modifier ce typedef et mettre un uint32_t ou une structure et vous aurez alors en paramètre quelque chose de plus riche que juste un identifiant

hum , je suis pas certain de pouvoir faire ce genre de chose :disappointed_relieved:

En fait on ne l’appelle pas immédiatement, on enregistre le fait qu’on voudrait que callback2 soit rappelé dans 5 secondes, ce qui aura pour effet quand ça se produit de le redemander à nouveau et donc de s’auto-entretenir

impecable votre classe est tout juste ce dont j' avais besoin !

Merci beaucoup :-[