IRRemote, analogWrite(), LED RGB bloquées dans un switch case

Bonjour à tous,

je réalise un petit projet pour amuser mon filleul et aussi pour m'amuser :smiley: J'aime bien les défis donc, je débute avec Arduino :sweat_smile: mais je comprends plus ou moins ce que je fais malgré un parcours plutôt autodidacte. Je repars de différents bouts de codes que j'ai adapté.

Le projet : Eclairage d'un meuble à l'aide de 9 LED RGB contrôlées par télécommande IR.

Le programme

#include <IRremote.hpp> 

#define RED 9
#define BLUE 10
#define GREEN 11
int IR_RECEIVE_PIN = 3;  // Réception signal IR
int redValue = 255;
int greenValue = 255;
int blueValue = 255;
boolean x = 0;

void setup()  // Initialisation
{
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  //séquence d'intialisation
  analogWrite(RED, 0);
  analogWrite(GREEN, 255);
  analogWrite(BLUE, 255);
  delay(500);
  analogWrite(RED, 255);
  analogWrite(GREEN, 0);
  analogWrite(BLUE, 255);
  delay(500);
  analogWrite(RED, 255);
  analogWrite(GREEN, 255);
  analogWrite(BLUE, 0);
  delay(500);
  Serial.begin(9600);
  Serial.println("IR Receiver Button Decode");
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  //Initialisation réception IR

} /*--(end setup )---*/

void loop() /*----( LOOP: RUNS CONSTANTLY )----*/
{
  if (IrReceiver.decode())  // have we received an IR signal?

  {
    translateIR();
    IrReceiver.resume();  // receive the next value
  }
} /* --(end main loop )-- */



/*-----( Fonction )-----*/
void translateIR()  // Actions en fonction du code IR reçu

{
  switch (IrReceiver.decodedIRData.decodedRawData)  //lecture du code
  {
    case 0xBA45FF00:  // POWER
      Serial.println("POWER");
      analogWrite(RED, 255);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 255);
      break;
    case 0xB847FF00:
      Serial.println("FUNC/STOP");
      /*int i = 0;
      while (i < 10) {
        i += 1;*/
        clignote();
      //}
      break;
    case 0xB946FF00: Serial.println("VOL+"); break;
    case 0xBB44FF00: Serial.println("FAST BACK"); break;
    case 0xBF40FF00:
      Serial.println("PLAY/PAUSE");
      x = 0;
      do {
        loopRGB();
        Serial.println("loopRGB en cours");
        if (IrReceiver.decode())  // have we received an IR signal?

        {
          IrReceiver.resume();  // receive the next value
        }
        IrReceiver.printIRResultShort(&Serial);

        if (IrReceiver.decodedIRData.decodedRawData != 0xBF40FF00) {  // Vérification du code IR reçu, différent de Play/pause ?
          x = true;
          Serial.println("Je suis dans le if");
        }

      } while (x == false);
      Serial.println("Sortie du do while");
      break;
    case 0xBC43FF00:
      Serial.println("FAST FORWARD");
      analogWrite(RED, random(256));
      analogWrite(GREEN, random(256));
      analogWrite(BLUE, random(256));
      break;
    case 0xF807FF00:
      Serial.println("DOWN");
      break;
    case 0xEA15FF00: Serial.println("VOL-"); break;
    case 0xF609FF00: Serial.println("UP"); break;
    case 0xE619FF00: Serial.println("EQ"); break;
    case 0xF20DFF00: Serial.println("ST/REPT"); break;

    case 0xE916FF00:
      Serial.println("0");
      analogWrite(RED, 0);
      analogWrite(GREEN, 0);
      analogWrite(BLUE, 0);
      break;

    case 0xF30CFF00:  //1
      Serial.println("1");
      analogWrite(RED, 255);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 0);
      break;
    case 0xE718FF00:
      Serial.println("2");
      analogWrite(RED, 0);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 255);
      break;
    case 0xA15EFF00:
      Serial.println("3");
      analogWrite(RED, 255);
      analogWrite(GREEN, 0);
      analogWrite(BLUE, 255);
      break;
    case 0xF708FF00:
      Serial.println("4");
      analogWrite(RED, 255);
      analogWrite(GREEN, 0);
      analogWrite(BLUE, 0);
      break;
    case 0xE31CFF00:
      Serial.println("5");
      analogWrite(RED, 0);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 0);
      break;

    case 0xA55AFF00:
      Serial.println("6");
      analogWrite(RED, 0);
      analogWrite(GREEN, 0);
      analogWrite(BLUE, 255);
      break;

    case 0xBD42FF00:

      Serial.println("7");
      analogWrite(RED, 127);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 127);
      break;

    case 0xAD52FF00:
      Serial.println("8");
      analogWrite(RED, 127);
      analogWrite(GREEN, 127);
      analogWrite(BLUE, 255);
      break;

    case 0xB54AFF00:
      Serial.println("9");
      analogWrite(RED, 255);
      analogWrite(GREEN, 127);
      analogWrite(BLUE, 127);
      break;

    case 0xFFFFFFFF: Serial.println("REPEAT"); break;

    default:
      Serial.println(" other button   ");
      //Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
      //IrReceiver.printIRResultShort(&Serial);

  }  // End Case

  delay(500);  // Do not get immediate repeat


}  //END translateIR


void loopRGB() {

#define delayTime 5  // fading time between colors
  redValue = 255;    // choose a value between 1 and 255 to change the color.
  greenValue = 0;
  blueValue = 255;

  for (int i = 0; i < 255; i += 1)  // fades out red bring green full when i=255
  {
    redValue -= 1;
    greenValue += 1;
    // The following was reversed, counting in the wrong directions
    // analogWrite(RED, 255 - redValue);
    //analogWrite(GREEN, 255 - greenValue);
    analogWrite(RED, redValue);
    analogWrite(GREEN, greenValue);
    delay(delayTime);
  }

  redValue = 0;
  greenValue = 255;
  blueValue = 255;

  for (int i = 0; i < 255; i += 1)  // fades out green bring blue full when i=255
  {
    greenValue -= 1;
    blueValue += 1;
    // The following was reversed, counting in the wrong directions
    //analogWrite(GREEN, 255 - greenValue);
    //analogWrite(BLUE, 255 - blueValue);
    analogWrite(GREEN, greenValue);
    analogWrite(BLUE, blueValue);
    delay(delayTime);
  }

  redValue = 0;
  greenValue = 0;
  blueValue = 255;

  for (int i = 0; i < 255; i += 1)  // fades out blue bring red full when i=255
  {
    // The following code has been rearranged to match the other two similar sections
    blueValue -= 1;
    redValue += 1;
    // The following was reversed, counting in the wrong directions
    // analogWrite(BLUE, 255 - blueValue);
    // analogWrite(RED, 255 - redValue);
    analogWrite(BLUE, blueValue);
    analogWrite(RED, redValue);
    delay(delayTime);
  }
}

void clignote() {  // on est d'accord, en l'état ça ne clignote pas encore :D 
  analogWrite(RED, random(256);
  analogWrite(GREEN, random(256);
  analogWrite(BLUE, random(256);
  }
}

Ce qui fonctionne :

  • J'ai passé le problème des calculs de résistances et tout s'allume correctement.
  • Je peux récupérer les valeurs de la télécommande et "voyager" dans un switch case
  • Je peux appeler une fonction loopRGB() pour faire varier la couleur des LED en continu (c'est beauuuu :wink: )
  • Je peux générer une valeur aléatoire pour le choix des couleurs via RANDOM()

Ce qui pose problème

  1. La fonction loopRGB() se lance bien, mais je n'arrive pas à continuer le programme une fois sorti de la fonction. J'ai essayé de n'appeler que la fonction sans succès. J'ai tenté un do while, j'arrive à en sortir mais le programme se bloque ensuite, il semble qu'il n'arrive plus à lire les valeurs de la télécommande. En tous cas, rien ne s'affiche en sortie.

Bout de code concerné :

case 0xBF40FF00:
      Serial.println("PLAY/PAUSE");
      x = 0;
      do {
        loopRGB();
        Serial.println("loopRGB en cours");
        if (IrReceiver.decode())  // have we received an IR signal?

        {
          IrReceiver.resume();  // receive the next value
        }
        IrReceiver.printIRResultShort(&Serial);

        if (IrReceiver.decodedIRData.decodedRawData != 0xBF40FF00) {  // Vérification du code IR reçu, différent de Play/pause ?
          x = true;
          Serial.println("Je suis dans le if");
        }

      } while (x == false);
      Serial.println("Sortie du do while");
      break;

2ème problème : lorsque j'appuie plusieurs fois sur le bouton pour changer de couleur, tout se bloque et je dois redémarrer l'arduino. Je ne sais pas trop où ça peut coincer.

Bout de code concerné :

case 0xBC43FF00:
      Serial.println("FAST FORWARD");
      analogWrite(RED, random(256));
      analogWrite(GREEN, random(256));
      analogWrite(BLUE, random(256));
      break;

Si vous avez quelque pistes, je suis preneur car là je ne sais plus quoi tester ou vers quelle ressource me tourner :wink:

Merci d'avance.

Il faudrait restructurer le code si vous voulez faire plusieurs choses à la fois : c'est typiquement une définition de programme qui se prête bien à la programmation par machine à états (cf mon tuto éventuellement)

les états correspondent aux animations (statiques ou dynamiques) et les évènements c'est le temps qui passe (pour une animation par exemple) et la réception d'une commande IR

Merci pour le lien. Donc si je comprends bien, je n'ai pas choisi la bonne structure de programme pour faire ce que je voulais ? On ne peut pas appeler une fonction dans un switch...case et continuer loop() par la suite?

Je vais lire votre article à tête reposée (donc pas aujourd'hui :sweat_smile:) et je fais un petit retour si j'arrive à appliquer.

Merci.
Je reste ouvert aux autres pistes et explications qui permettent de mieux comprendre pourquoi ça n'a pas fonctionné en l'état.

La fonction va s’exécuter et revenir dans le switch et le cours du programme va continuer et à un moment revenir à la loop.

Le souci fréquent c’est que la fonction appelée est bloquante (une animation sans fin) ou très longue ce qui fait que le code ne revient que très tardivement à la loop() pour tester le prochain appui d’un bouton ou autre moyen d’interaction avec l’utilisateur ce qui rend le programme peu utilisable

Il faut développer un code non bloquant et une approche par machine à états permet cela

1 Like

Bonjour,

après plusieurs lectures et relectures de votre article, je pense commencer à comprendre. Enfin... je crois :sweat_smile: : donc, il faudrait que je crée une fonction pour chaque bouton de télécommande et définir dans un enum l'ensemble des états possibles.

Dans mon premier code, j'utilisais IrReceiver.decodedIRData.decodedRawData pour naviguer dans un unique switch. J'associais 1 action à 1 bouton :
image

Un code IR reçu lançait la fonction translateIR(), ici avec l'action pour Power

void translateIR()  // Actions en fonction du code IR reçu

{
  switch (IrReceiver.decodedIRData.decodedRawData)  //lecture du code
  {
    case 0xBA45FF00:  // POWER
      Serial.println("POWER");
      analogWrite(RED, 255);
      analogWrite(GREEN, 255);
      analogWrite(BLUE, 255);
      break;
}

Tandis qu'ici la logique envisage toutes les actions en fonction de l'état précédent. Je n'ai illustré que OFF et "vert" par confort de lecture :
image

J'ai fait un essai avec uniquement le bouton Power qui devrait allumer ou éteindre les LED en vert.

//liste des états
enum { eteint,
       blanc,
       rouge,
       vert,
       bleu,
} EtatLED;

Avec comme fonction :

void power() {
  switch(EtatLED){
  case eteint:
    analogWrite(RED, 255);
    analogWrite(GREEN, 0);
    analogWrite(BLUE, 255);
    EtatLED = vert;
    break;
    case blanc:
    case rouge:
    case vert:
    analogWrite(RED, 255);
    analogWrite(GREEN, 255);
    analogWrite(BLUE, 255);
    EtatLED = eteint;
    case bleu:;
  }
}

Ca fonctionnerait si j'utilisais un seul bouton physique mais ici mon changement d'état se réalise sur un code IR réceptionné dans IrReceiver.decode()

Il faudrait donc que je puisse utiliser en tant que callback la valeur de Power = 0xBA45FF00 ?
Comme dans le tutoriel où 1 click enclenche une fonction et doubleclick une autre. Ici, la valeur hexadécimale renverrait vers Power et une autre valeur enclencherait une autre fonction. La logique est bonne ?

Si oui, ma question = comment utiliser cette valeur en tant que callback ? ou comment rentrer dans void power() en fonction du code IR reçu ??

Dans l'exemple du tutoriel, cela est défini dans void setup() mais je ne sais comment procéder...

Mon setup() et loop() actuels :

void setup()  // Initialisation
{
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  EtatInitial();
  Serial.begin(9600);
  Serial.println("IR Receiver Button Decode");
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  //Initialisation réception IR
} /*--(end setup )---*/

void loop() {
  if (IrReceiver.decode())  // have we received an IR signal?

  {
    //translateIR();        //On entre dans le switch case
    IrReceiver.resume();  // receive the next value
  }
} /* --(end main loop )-- */

Est-ce que c'est dans loop() que je dois appeler la fonction selon le code IR reçu ?
Merci d'avance pour toutes suggestions et pistes :slight_smile:

En continuant de lire la doc de la bibliothèque IRremote, je suis tombé sur ce problème d'incompatibilité de IRremote avec les fonctions comme analogWrite() car elles utilisent le même timer !

La solution est donc de modifier la bibliothèque en suivant cette solution.

Il faut modifier le fichier private/IRTimer.hpp , retrouver sa carte (dans mon cas ATMEGA2560) et modifier le timer2 par défaut par un des autres disponibles. J'ai pris 46 un peu au hasard mais ça fonctionne maintenant avec mon code de départ! :smiley:

code original dans IRTimer.hpp

// Arduino Mega
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#  if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4) && !defined(IR_USE_AVR_TIMER5)
//#define IR_USE_AVR_TIMER1   // send pin = pin 11
#define IR_USE_AVR_TIMER2     // send pin = pin 9
//#define IR_USE_AVR_TIMER3   // send pin = pin 5
//#define IR_USE_AVR_TIMER4   // send pin = pin 6
//#define IR_USE_AVR_TIMER5   // send pin = pin 46
#  endif

modifié en

// Arduino Mega
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#  if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4) && !defined(IR_USE_AVR_TIMER5)
//#define IR_USE_AVR_TIMER1   // send pin = pin 11
//#define IR_USE_AVR_TIMER2     // send pin = pin 9
//#define IR_USE_AVR_TIMER3   // send pin = pin 5
//#define IR_USE_AVR_TIMER4   // send pin = pin 6
#define IR_USE_AVR_TIMER5   // send pin = pin 46
#  endif

@J-M-L Merci pour les pistes proposées. Je reste intéressé par une solution pour la question précédente si jamais :wink:

OK pour la solution

pour la question, on effectue généralement dans la loop l'appel de réception des données IR puis si on a reçu quelque chose on stocke dans une variable ce que l'on a reçu pour ce tour de loop, puis on appelle la machine à états (comme cela les états qui ont besoin de consommer une commande IR l'auront de disponible).

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