DFPlayer ... probleme de lecture

oui c'est une façon de faire

l'approche de @jpbbricole a le mérite d'attendre juste le temps qu'il faut (mais il faut que la pin busy ait eu le temps de s'activer)

sans parler de l'animation des LEDs, est-ce que ceci fait ce que vous voulez d'un point de vue gestion des sons:

#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
//---------------------------------------------------
SoftwareSerial dfSerial(10, 11);
DFRobotDFPlayerMini player;

enum : byte {NORMAL, BATAILLE} etat = NORMAL;
const byte pinBouton = 2;
const byte pinDfBusy = 3;

void attenteFin() {
  while (digitalRead(pinDfBusy) == LOW)   // LOW => une lecture est en cours
    yield();                              // on attend
}

void jouer(int numero) {
  player.play(numero);
  dfSerial.flush();                       // on s'assure que la dernière commande est bien partie
  delay(5);                               // on donne un peu de temps au DFPlayer d'analyser la commande
}

void stop() {
  player.stop();
  dfSerial.flush();                       // on s'assure que la dernière commande est bien partie
  delay(5);                               // on donne un peu de temps au DFPlayer d'analyser la commande
}

void setup() {
  pinMode(pinBouton, INPUT_PULLUP);
  pinMode(pinDfBusy, INPUT_PULLUP);
  Serial.begin(115200);
  dfSerial.begin(9600);
  player.begin(dfSerial);

  jouer(1);
  attenteFin();
}

void loop() {
  // si aucune lecture n'est en cours, on relance
  if (digitalRead(pinDfBusy) == HIGH)
    jouer((etat == NORMAL) ? 20 : random(2, 18));

  switch (etat) {
    case NORMAL:
      // on regarde si le bouton vient d'être appuyé
      if (digitalRead(pinBouton) == LOW) {
        delay(15); // anti rebond du pauvre
        stop();
        etat = BATAILLE;
      }
      break;

    case BATAILLE:
      // on regarde si le bouton a été relâché
      if (digitalRead(pinBouton) == HIGH) {
        delay(15); // anti rebond du pauvre
        stop();
        etat = NORMAL;
      }
      break;
  }
}

Je viens de tester votre code .. mais pour qu'il fonctionne j'ai dû augmenter le delais du temps d'analyse (j'ai mis 100ms)

Mais comme avec la proposition de jpbbricole .. le bouton-poussoir ne fonctionne plus

100ms c'est énorme... pendant ce temps là la loop ne tourne pas et donc on ne peut pas tester le bouton.

quelle est la durée d'un son ?
avez vous pu qualifier que la pin 3 (pinDfBusy dans mon code) reflète bien l'état de la lecture ?

Ca fonctionne qu'à condition de porter le temps d'analyse du DFplayer à 40ms
Je suis satisfait mais Il manque encore un rien dans le fonctionnement :

  • avec mon sketch, une impulsion sur le BP provoque la lecture complète du morceau ... une pression prolongée conduit à un blanc
  • avec votre sketch, une pression prolongée conduit à une lecture permanente de tous les sons mais lors d'une simple pression, le son s'interrompt dès le relachement du bouton

Mon objectif .. pression prolongée = lecture continue ... pression courte = lecture complète du seul morceau

Il est temps de vous expliquer le but de ce sketch:
c'est pour mon fils : une épée starwars

  • a la mise sous tension = son 1 (allumage) d'environ 1 seconde puis de façon permanente son 20 (ronronnement)
  • pression permanente ou non sur un bouton = sons 2à17 toujours différents de bruit de chocs entre épéés tous inférieurs à 1sec
  • et bien entendu des leds pour accompagner tout cela

Je dois encore y ajouter un son correspondant à l'extinction de l'épée

Ok trouvé
En supprimant STOP dans "case Bataille" .. j'obtiens ce que je souhaitais

J'ai du mal à comprendre la syntaxe de cette ligne .. surtout le point d'interrogation

jouer((etat == NORMAL) ? 20 : random(2, 18));

Bonjour jo_6466

est l'équivalent de ça:

	if (etat == NORMAL)
	{
		jouer(20);
	} 
	else
	{
		jouer(random(2, 18));
	}

La condition peut être passée comme paramètre dans l'appel d'une fonction comme jouer(x) par exemple ou pour le passage d'une valeur à une variable.

Cordialement
jpbbricole

comme aime souvent le dire @J-M-L , ce projet ce prête particulièrement à la mis en place d'une machine à état, surtout que tu à déjà la machine dans la loop, avec quel que état en plus tu te simplifierais la vie dans setup.
Enfin il le dit mieux que moi :slight_smile:

c'est un IF ternaire.
si la condition entre parenthése est vrai, cela renvoit 20.
sinon un chiffre aléatoire(2, 18)

tu peux donc affecter une variable avec le retour du IF ternaire.

Merci .. C'est très clair ainsi pour un novice comme moi

Autre question : dans une fonction comme random (1,18) , il arrive souvent que deux valeurs identiques soient sorties l'une derrière l'autre

Existe-t-il un moyen d'éviter cela?

Non, c'est la définition du hasard.
si tu ne veux pas deux valeur identique, il faut utiliser une variable "valeurPred" et refaire un tirage aléatoire tant que c'est différent.

Bonjour jo_6466

Oui, au moyen d'une fonction qui te retourne ce numéro aléatoire tout en contrôlant que le nouveau numéro tiré est différent du précédent:


int mp3Hasard()
{
	static int mp3Precedent;
	int mp3Hasard = random(2, 18);
	
	// Tant que le nouveau numéro est le même que le précédent, on retire
	while(mp3Hasard == mp3Precedent)     
	{
		mp3Hasard = random(2, 18);
	}
	
	mp3Precedent = mp3Hasard;
	return mp3Hasard;
}

que tu appelles par jouer(mp3Hasard());

Cordialement
jpbbricole

pourquoi t'embêter avec un appel de fonction dans ce cas là, même si le optimisateur à de forte chance de supprimer l'appel?

Il ne semble pas forcément vouloir l'appeler plusieurs fois ?

J'ai finallement écrit ceci et ça fonctionne très bien

if (digitalRead(pinDfBusy) == HIGH)
    if (etat == NORMAL) {
      jouer(20);
    } else {
      while (mp3Hasard == mp3Precedent) {
        mp3Hasard = random(2, 18);
      }
      mp3Precedent = mp3Hasard;
      jouer(mp3Hasard);
    }
  delay(40);

Je pense que mon projet est maintenant terminé
Je vous remercie tous de m'avoir apporté par-ci par-là les réponses qu'il me fallait

Arghhh par tout a fait finalement
Pourquoi après avoir lu le son 17 de démarrage, au 1er appui sur le BP, je le réentend à nouveau pour ensuite passer au random pour les autres appuis

Voici le skech actuel


#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
//---------------------------------------------------
SoftwareSerial dfSerial(10, 11);
DFRobotDFPlayerMini player;

enum : byte { NORMAL,
              BATAILLE } etat = NORMAL;
const byte pinBouton = 2;
const byte pinDfBusy = 3;
int mp3Hasard;
int mp3Precedent;

void attenteFin() {
  while (digitalRead(pinDfBusy) == LOW) ;       // LOW => une lecture est en cours
yield();  // on attend
}
void jouer(int numero) {
  player.play(numero);
  //  dfSerial.flush();                       // on s'assure que la dernière
  //commande est bien partie
  delay(40);  // on donne un peu de temps au DFPlayer d'analyser la commande
}

void setup() {
  pinMode(pinBouton, INPUT_PULLUP);
  pinMode(pinDfBusy, INPUT_PULLUP);
  Serial.begin(115200);
  dfSerial.begin(9600);
  player.begin(dfSerial);

  jouer(17);
  attenteFin();
}

void loop() {  // si aucune lecture n'est en cours, on relance

  if (digitalRead(pinDfBusy) == HIGH)
    if (etat == NORMAL) {
      jouer(20);
    } else {
      while (mp3Hasard == mp3Precedent) {
        mp3Hasard = random(2, 18);
      }
      mp3Precedent = mp3Hasard;
      jouer(mp3Hasard);
    }
  delay(40);

  switch (etat) {
    case NORMAL:  // on regarde si le bouton vient d'être appuyé
      if (digitalRead(pinBouton) == LOW) {
        delay(15);      // anti rebond du pauvre
        player.stop();  // arrêt lecture son 20 (ronronnement)
        delay(40);      // on donne du temps au DFPlayer d'analyser la commande
        etat = BATAILLE;
      }
      break;

    case BATAILLE:  // on regarde si le bouton a été relâché
      if (digitalRead(pinBouton) == HIGH) {
        delay(15);  // anti rebond du pauvre
        etat = NORMAL;
      }
      break;
  }
}

Bonjour terwal

A ce moment on ne le sait pas et comme tout programme est susceptible d'évoluer, j'ai proposé une fonction, ce qui est quand même plus pratique et, surtout, ça "ne mange pas de pain"
Dès qu'une suite d'opérations un tant soit peu complexe est effectuée plus d'une fois, la création d'une fonction est nécessaire.

Cordialement
jpbbricole

Si ça mange du pain, enfin des miettes, un call et suivant la fonction une lisibilité moindre(mais c'est très relatif).
Du coup si justement on ne le sait pas encore, il n'y a pas de raison particulière de passer par une fonction :frowning:

Il ne reste plus qu’à rajouter les LEDs en faisant une petite machine à états pour le timing afin de ne pas bloquer la loop

un call oui (et encore, le compilateur est libre de faire de l'inlining) mais généralement la lisibilité est augmentée si le nom de la fonction est bien choisie