contrôler musique avec encodeur rotatif

Bonjour à tous,

j'aimerais réaliser une sorte de boîte à musique connectée à mon ordinateur, c'est-à-dire une boîte avec manivelle qui lorsqu'on la tourne cela déclenche la musique et si l'on s'arrête, la musique s'arrête aussi.

J'ai réussi à trouver un tutoriel qui permet de réaliser un bouton de commande avec un encodeur rotatif permettant de contrôler la fonction pause/play avec mon arduino Leonardo.

Voilà le code:

#include <ClickEncoder.h>
#include <TimerOne.h>
#include <HID-Project.h>

#define ENCODER_CLK A0 
#define ENCODER_DT A1
#define ENCODER_SW A2

ClickEncoder *encoder; // variable encodeur
int16_t last, value; // variables rotation

void timerIsr() {
  encoder->service();
}

void setup() {
  Serial.begin(9600); 
  Consumer.begin(); // clavier initalisation
  encoder = new ClickEncoder(ENCODER_DT, ENCODER_CLK, ENCODER_SW); 

  Timer1.initialize(1000); 
  Timer1.attachInterrupt(timerIsr); 
  last = -1;
} 

void loop() {  
  value += encoder->getValue();

 
  if (value != last) { // nouvelle valeur = encodeur tourne
   
      Consumer.write(MEDIA_PLAY_PAUSE); // 
     

    Serial.print("Encoder Value: "); 
    Serial.println(value);
    delay(500);
    
  }

  
  

}

Là je sais que si le arduino détecte un mouvement, ça me fait juste du pause/play sans arrêt (c'est évident vu que c'est une boucle) et c'est pas vraiment mon but haha.

J'aimerais bien que lorsque l'arduino détecte un mouvement (valeur qui change sur me moniteur), il appuie une seule fois sur play, tant que les valeurs changent, rien ne se passe mais par contre, lorsque l'on arrête de tourner l'encodeur rotatif, on met sur pause (lorsque les valeurs qui s'affichent sur mon moniteur ne changent plus au bout de deux.)

Si vous aviez une petite piste pour m'aider ce serait génial Merci !!

il faut détecter le premier mouvement, noter l'heure (avec millis()) et envoyer la commande PLAY.

ensuite il faut surveiller

  • les mouvements, à chaque mouvement vous mettez à jour l'heure du dernier mouvement.
  • si la différence de temps entre le dernier mouvement et l'heure courante est trop grande (par exemple vous n'avez pas tourné la manivelle depuis 1seconde) alors envoyer STOP

une petite "machine à état" en terme de structuration du code permet de faire cela simplement (cf mon tuto éventuellement)

un exemple de code avec une autre librairie pour encodeur rotatif plus simple d'emploi

#include <Encoder.h> // https://www.pjrc.com/teensy/td_libs_Encoder.html
Encoder encodeurRotatif(2, 3); // attaché sur pins 2 (Encodeur DT) et 3 (Encodeur CLK)

#include <simpleBouton.h> // http://forum.arduino.cc/index.php?topic=375232.0 la librairie de @bricoleau
simpleBouton boutonEncodeur(4);//Cablage : pin 4 --- Encodeur SW

long positionEnCours;
unsigned long dernierTick = 0;
const unsigned long delaiMax = 500;

enum : byte {REPOS, TOURNE} etat = REPOS;

void etatInitial()
{
  encodeurRotatif.write(0); // valeur initiale du compteur
  positionEnCours  = 0L;
  etat = REPOS;
}

void setup()
{
  Serial.begin(115200);
  etatInitial();
}

void loop()
{
  // la librairie envoie 2 ticks par cran sur mon encodeur
  // je divise donc par 2 (décale à droite d'un bit) pour avoir le bon compte. 
  // certains encodeurs envoient 4 ticks donc faudrait faire >> 2 dans ce cas, 
  // d'autres qu'un seul tick, auquel cas il faut virer carrément le >> 1
  long nouvellePosition = encodeurRotatif.read() >> 1; 

  switch (etat) {
    case REPOS:
      if (nouvellePosition > positionEnCours) { // on a commencé à tourner côté croissant 
        Serial.println(nouvellePosition);
        Serial.println(F("PLAY"));
        etat = TOURNE;
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      break;
    case TOURNE:
      if (nouvellePosition > positionEnCours) { // on continue à tourner
        Serial.println(nouvellePosition);
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      if (millis() - dernierTick >= delaiMax) { // ça fait trop longtemps qu'on n'a pas tourné
        Serial.println(F("STOP"));
        etatInitial();
      }
      break;
  }

  if (boutonEncodeur) Serial.println(F("appui bouton"));
}

j'ai aussi mis du code pour détecter si on appuie sur le bouton de l'encodeur rotatif - au cas où vous voudriez faire que chose de plus; si pas besoin vous pouvez bien sûr enlever

#include <simpleBouton.h> // http://forum.arduino.cc/index.php?topic=375232.0 la librairie de @bricoleau
simpleBouton boutonEncodeur(4);//Cablage : pin 4 --- Encodeur SW

ainsi que le test en fin de loop()

  if (boutonEncodeur) Serial.println(F("appui bouton"));

Merci pour votre aide!! Je ne connaissais pas la fonction millis, ça va beaucoup me servir pour mes futurs projets :slight_smile:

J'ai incorporé la bibliothèque que j'avais trouvé pour les fonctions pause/play et ça a marché un certain temps...

J'ai beau retéléverser le code ça ne fonctionne pas, par contre si je téléverse un autre code utilisant l'encodeur ça fonctionne... J'ai l'impression que lorsque je téléverse mon code, ma carte se déconnecte automatiquement...

Vous auriez une idée d'où cela peut-il provenir?

Merci

postez votre code

sur un Leonardo, si vous utilisez le port série il faut rajouter unwhile (!Serial);dans le setup() après le Serial.begin().

Mais comme j'ai l'impression que vous l'utilisez en mode émulation de clavier, il ne faut pas utiliser Serial

#include <Encoder.h> // https://www.pjrc.com/teensy/td_libs_Encoder.html
Encoder encodeurRotatif(2, 3); // attaché sur pins 2 (Encodeur DT) et 3 (Encodeur CLK)

#include <HID-Project.h>



long positionEnCours;
unsigned long dernierTick = 0;
const unsigned long delaiMax = 500;

enum : byte {REPOS, TOURNE} etat = REPOS;

void etatInitial()
{
  encodeurRotatif.write(0); // valeur initiale du compteur
  positionEnCours  = 0L;
  etat = REPOS;
}

void setup()
{
  Serial.begin(115200);
 
  etatInitial();
}

void loop()
{
  // la librairie envoie 2 ticks par cran sur mon encodeur
  // je divise donc par 2 (décale à droite d'un bit) pour avoir le bon compte.
  // certains encodeurs envoient 4 ticks donc faudrait faire >> 2 dans ce cas,
  // d'autres qu'un seul tick, auquel cas il faut virer carrément le >> 1
  long nouvellePosition = encodeurRotatif.read() >> 1;

  switch (etat) {
    case REPOS:
      if (nouvellePosition > positionEnCours) { // on a commencé à tourner côté croissant
        Serial.println(nouvellePosition);
       Consumer.write(MEDIA_PLAY_PAUSE);
        etat = TOURNE;
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      break;
    case TOURNE:
      if (nouvellePosition > positionEnCours) { // on continue à tourner
        Serial.println(nouvellePosition);
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      if (millis() - dernierTick >= delaiMax) { // ça fait trop longtemps qu'on n'a pas tourné
      Consumer.write(MEDIA_PLAY_PAUSE);
        etatInitial();
      }
      break;
  }

//  if (boutonEncodeur) Serial.println(F("appui bouton"));
}

Voilà le code,
effectivement il est utilisé en émulation de clavier

essayez en virant l'usage de Serial par exemple en enlevantSerial.println(nouvellePosition);

J'ai essayé mais le problème doit vraiment venir de ma carte puisqu'elle se déconnecte même si je téléverse un sketch vierge...

Je vois sur mon gestionnaire des pilotes dès que je téléverse le pilote disparaît et ré-apparaît dans la foulée, ce qui empêche le téléversement et si j'appuie sur le bouton reset il se produit la même chose

"elle se déconnecte " --> que voulez vous dire par là?

si vous n'ouvrez pas de connexion Serial, la liaison USB n'est pas établie. C'est donc normal

je ne connais pas votre librairie "HID-Project.h".. assurez vous qu'elle soit de bonne qualité et que vous l'utilisez correctement

Vous avez essayé bêtement avec la librairie Keyboard standard? (ça n'envoie pas toutes les combinaisons de touches donc faudra peut être hacker un peu)

Je veux dire par là que la connexion usb entre mon ordi et la carte est interrompu pile au moment du téléversement

Cette librairie est issue d'un tutoriel prusa 3D

J'ai essayé avec la librairie standard mais effectivement comme la fonction pause/play n'est pas présente et que mes connaissances sont assez limitées, je me suis du coup tournée vers des librairies toutes faites...

ah et vous avez toujours un Vieux script qui tourne sur l'adruino et qui sature peut être la connexion et empêche l'upload.

essayez d'appuyer sur le reset de la carte Leonardo juste au moment où le code va se télécharger...

Alors effectivement j'ai reset la carte et je peux recommencer à téléverser

Par contre le code ne fonctionne toujours pas, j'ai essayé avec la librairie standard, je remet le code peut être que c'est moi qui m'y prend mal...

#include <Encoder.h> // https://www.pjrc.com/teensy/td_libs_Encoder.html
Encoder encodeurRotatif(2, 3); // attaché sur pins 2 (Encodeur DT) et 3 (Encodeur CLK)
#include <Keyboard.h>




long positionEnCours;
unsigned long dernierTick = 0;
const unsigned long delaiMax = 500;

enum : byte {REPOS, TOURNE} etat = REPOS;

void etatInitial()
{
  encodeurRotatif.write(0); // valeur initiale du compteur
  positionEnCours  = 0L;
  etat = REPOS;
}

void setup()
{
  Serial.begin(115200);
  while (!Serial);
 
  etatInitial();
}

void loop()
{
  // la librairie envoie 2 ticks par cran sur mon encodeur
  // je divise donc par 2 (décale à droite d'un bit) pour avoir le bon compte.
  // certains encodeurs envoient 4 ticks donc faudrait faire >> 2 dans ce cas,
  // d'autres qu'un seul tick, auquel cas il faut virer carrément le >> 1
  long nouvellePosition = encodeurRotatif.read() >> 1;

  switch (etat) {
    case REPOS:
      if (nouvellePosition > positionEnCours) { // on a commencé à tourner côté croissant
       Keyboard.press(' ');
       delay(50);
       Keyboard.release(' ');

        etat = TOURNE;
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      break;
    case TOURNE:
      if (nouvellePosition > positionEnCours) { // on continue à tourner
       // Serial.println(nouvellePosition);
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      if (millis() - dernierTick >= delaiMax) { // ça fait trop longtemps qu'on n'a pas tourné
    Keyboard.press(' ');
      delay(50);
       Keyboard.release(' ');
        etatInitial();
      }
      break;
  }

//  if (boutonEncodeur) Serial.println(F("appui bouton"));
}

essayez avec cela:

#include <Encoder.h> // https://www.pjrc.com/teensy/td_libs_Encoder.html
Encoder encodeurRotatif(2, 3); // attaché sur pins 2 (Encodeur DT) et 3 (Encodeur CLK)
#include <Keyboard.h>

long positionEnCours;
unsigned long dernierTick = 0;
const unsigned long delaiMax = 500;

enum : byte {REPOS, TOURNE} etat = REPOS;

void etatInitial()
{
  encodeurRotatif.write(0); // valeur initiale du compteur
  positionEnCours  = 0L;
  etat = REPOS;
}

void setup()
{
  Keyboard.begin();
  etatInitial();
}

void loop()
{
  // la librairie envoie 2 ticks par cran sur mon encodeur
  // je divise donc par 2 (décale à droite d'un bit) pour avoir le bon compte.
  // certains encodeurs envoient 4 ticks donc faudrait faire >> 2 dans ce cas,
  // d'autres qu'un seul tick, auquel cas il faut virer carrément le >> 1
  long nouvellePosition = encodeurRotatif.read() >> 1;

  switch (etat) {
    case REPOS:
      if (nouvellePosition > positionEnCours) { // on a commencé à tourner côté croissant
        Keyboard.press(' ');
        delay(50);
        Keyboard.releaseAll();
        etat = TOURNE;
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      break;
    case TOURNE:
      if (nouvellePosition > positionEnCours) { // on continue à tourner
        dernierTick = millis();
        positionEnCours = nouvellePosition;
      }
      if (millis() - dernierTick >= delaiMax) { // ça fait trop longtemps qu'on n'a pas tourné
        Keyboard.press(' ');
        delay(50);
        Keyboard.releaseAll();
        etatInitial();
      }
      break;
  }
}

il faut virer tout l'usage de Serial et penser à faire le Keyboard.begin();
(tapé ici donc pas testé)