Problème prog Arduino, capteur de son Iduino 1485297

Bonjour,

Je viens vers vous aujourd'hui car je ne parvient pas à comprendre pourquoi mon programme ne fonctionne pas.

Materiel :

  • Arduino UNO R3
  • Iduino 1485297

Contexte :

l'idée derrière ce projet est de mesurer un niveau de son (digital) a l'aide du capteur Iduino et d'allumer une LED en conséquence. Cependant afin d'éviter les déclenchements intempestifs je dois valider le fait que le son est bien présent sur une durée minimum ( échantillonnage chaque seconde avec validation si la somme des échantillons est supérieure ou égale à 3/5).

Programme :

J'ai écrit le programme ci-dessous qui manifestement ne fonctionne pas (les échantillons restent à 1 même si l'entrée digitale est sur 0)

int echantillon1 = 0;

int echantillon2 = 0;

int echantillon3 = 0;

int echantillon4 = 0;

int echantillon5 = 0;

void setup()
{
  pinMode(7, INPUT);
  pinMode(12, OUTPUT);
}

void loop()
{
  echantillon1 = 0;
  echantillon2 = 0;
  echantillon3 = 0;
  echantillon4 = 0;
  echantillon5 = 0;
  if (digitalRead(7) == 1) {
    echantillon1 = 1;
  }
  delay(1000); // Wait for 1000 millisecond(s)
  if (digitalRead(7) == 1) {
    echantillon2 = 1;
  }
  delay(1000); // Wait for 1000 millisecond(s)
  if (digitalRead(7) == 1) {
    echantillon3 = 1;
  }
  delay(1000); // Wait for 1000 millisecond(s)
  if (digitalRead(7) == 1) {
    echantillon4 = 1;
  }
  delay(1000); // Wait for 1000 millisecond(s)
  if (digitalRead(7) == 1) {
    echantillon5 = 1;
  }
  delay(1000); // Wait for 1000 millisecond(s)
  if (echantillon1 + (echantillon2 + (echantillon3 + (echantillon4 + echantillon5))) >= 3) {
    digitalWrite(12, HIGH);
    delay(500); // Wait for 500 millisecond(s)
    digitalWrite(12, LOW);
    delay(180000); // Wait for 180000 millisecond(s)
  }
}

je suis preneur de toutes idées qui pourrais me faire avancer :wink:.

le programme fait quelque chose, mais peut-être pas ce que vous voulez.
Décrivez exactement ce que vous entendez par "ne fonctionne pas"
(avec tous ces délais je ne suis pas surpris que ce ne soit pas simple à tester)

Décrivez aussi comment tout est branché et alimenté

(lire les recommandations listées dans "Les bonnes pratiques du Forum Francophone” pour voir quelles informations fournir)

Citation
le programme fait quelque chose, mais peut-être pas ce que vous voulez.
Décrivez exactement ce que vous entendez par "ne fonctionne pas"
Citation

Bonsoir,

Veuillez excuser le manque de précision de mon dernier post.

En ce qui concerne le câblage, ci dessous un schéma.

Pour l’objectif fonctionnel du système, le but est d’allumer une led dans une pièce quand un enfant pleure dans la chambre. Cependant la led ne doit pas se déclencher de manière intempestive

Pour la programmation, ci-dessous un schéma type grafcet représentant mon idée.

Schéma de câblage :

schéma type grafcet :

Notes :
À l’étape 7 on envoie HIGH puis LOW car la LED n’est pas directement commandée par l’Arduino mais par un actionneur KNX. L’Arduino commande un relais qui ferme un contact sec. Les 500 ms de pause entre ses deux actions permettent de s’assurer que ce relais claque bien. Enfin la pause de 3 minutes à la fin permet de laisser un laps de temps suffisamment important pour que les pleures de l’enfant ne fasse pas claquer pas le relais en continu.

Bonsoir

Pour information ce tutoriel 'Progammation Automate Fini' (structure voisine du Grafcet)
https://forum.arduino.cc/t/programmation-automate-fini-machine-a-etat/452532

pas de résistance de limitation de courant pour votre LED ?

EDIT: ah je viens de voir que c'était juste une représentation

concrètement donc qu'est-ce qui ne fonctionne pas ?

Que se passe-t-il dans le cas où les SI sont faux ?

1 Like

Bonjour,

Citation
Que se passe-t-il dans le cas où les SI sont faux ?
Citation

Dans le cas où les SI sont faux le programme suit son cours, j'entendent par là que mon entrée étant digital, si elle n'est pas sur HIGH elle est sur LOW et inversement.

Citation
concrètement donc qu'est-ce qui ne fonctionne pas ?
Citation

Concrètement ce qui ne fonctionne pas c'est toute la partie prise d'échantillons, (cf schéma type grafcet étapes 1 à 6). Les échantillons passent tout a 1 et restent à cet état indépendamment de la valeur de mon entrée digitale.

Pour rappel l'objectif de cette partie et de vérifier la véracité d'une mesure de son (digital pin 7) J'estime donc qu'au-delà de X mesures (input 7 = 1) sur 5 secondes on valide l'allumage de la LED.

J'utilise actuellement une méthode "approximative", à savoir, prendre 5 échantillons avec 1s de pause entre chaque.

Après réflexion il serait préférable de prendre des mesures pendant 5 secondes ( je pense à la fonction millis() ) puis d'actionner la sortie si X% des mesures sont à HIGH. Cependant pour pouvoir calibrer ce X% je dois connaitre le temps de cycle de l'Arduino (ou le nombre de mesures prises au total).

Selon vous cette seconde méthode est-elle réalisable ?
Si oui connaissez-vous la méthode pour la mettre en place ?

PS: vous trouverez ci-dessous un screen du comportement de mon capteur cela vous aidera peut-être à comprendre pourquoi je dois vérifier ma valeur HIGH.

Oui prendre un échantillon toutes les secondes ne sert à rien... vous pouvez tomber dans un creux ou un pic temporaire. il faut écouter en permanence.

sur votre dessin on ne voit pas la valeur temps donc c'est difficilement exploitable.
quel était le code ? est-ce le résultat de la sortie analogique ou numérique (influencée par le potentiomètre de la carte)

Le code était le suivant :

void setup()
{
  pinMode(7, INPUT);
  Serial.begin(9600);
}

void loop()
{
  Serial.print("Digitalin: ");
  Serial.println( digitalRead(7) );
}

Il s'agit donc du résultat de l'entrée digital (numérique, Pin 7). Ce signal est le résultat de ma voix enregistré par le capteur et non d'un "forçage" de la sensibilité du capteur via le potentiomètre.

Suite à votre dernier message j'ai écrit le code ci-dessous, j'y ai utilisé la fonction while() qui me permet d'incrémenter ma variable donnée pendant 5 secondes ( temps régis par la fonction millis() ) cependant la fonction while() semble être bloquante, malgré le fait que je compte le temps à l'intérieur même de cette variable.

info dans les //

int tempoDepart = 0;//Variable pour initialiser la tempo
int tempoActive = 0;//Variable qui definit l'état de la tempo 1=active 0=inactive
int Nbdonnee = 0;//Variable permettant de stocker le nombre de donnees enregisters 
int donnee = 0;//Variable permettant de stocker le nombre de donnees sur HIGH
int TotalLOW = 0;//Variable resultante due au calcule suivant: Nbdonee - donee
int TotalHIGH = 0;//Variable resultante due au calcule suivant: Nbdonne - TotalLOW elle permet de verifier si le seuil de declanchement de la led est dépasser ou non

void setup()
{
  pinMode(7, INPUT);
  pinMode(12, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  if ( digitalRead(7) == HIGH ){//Si capteur = HIGH alors
  tempoActive = 1;//On met la variable tempoActive sur 1 
        while (tempoActive == 1) {//On cree une boucle de mesure qui s'arrette quand la variable tempoActive = 0
        tempoDepart = millis();//On initialise la tempo
        Nbdonnee = (Nbdonnee + 1);//On compte le nombre de donnee
         if (digitalRead(7) == HIGH) {//Si capteur = HIGH alors
         donnee = (donnee + 1);//On incremente le compteur de donnee HIGH
         }
         if (( millis() - tempoDepart ) >= 5000) //Si on compte depuis 5 secondes alors
         {    
         tempoActive = 0; //On met la variable tempoActive sur 0 pour arreter la boucle
         } 
         }
         TotalLOW = (Nbdonnee - donnee);//On calcule le nombre total de donnee sur LOW
         TotalHIGH = (Nbdonnee - TotalLOW);//On calcule le nombre total de donnee sur HIGH
         }      
  if (TotalHIGH >= 3) {//Si le total de donnee sur HIGH est superieur ou egal a X alors
    digitalWrite(12, HIGH);//On allume la led
    delay(500); //On attend 500 millisecondes
    digitalWrite(12, LOW);
    delay(180000); // On attend 3 minutes
    }
    Serial.print("tempoDepart: ");
    Serial.println( millis() - tempoDepart );
    delay(500);

    Serial.print("tempoActive: ");
    Serial.println( tempoActive );
    delay(500);

    Serial.print("Nbdonnee: ");
    Serial.println( Nbdonnee );
    delay(500);

    Serial.print("donnee: ");
    Serial.println( donnee );
    delay(500);

    Serial.print("TotalLOW: ");
    Serial.println( TotalLOW );
    delay(500);

    Serial.print("TotalHIGH: ");
    Serial.println( TotalHIGH );
    delay(500);

    Serial.print("Digitalin: ");
    Serial.println( digitalRead(7) );
    delay(500);

    Serial.print("Digitalout: ");
    Serial.println( digitalRead(12) );
    delay(500);
}

Cela est il normal ou est-ce moi qui fait une erreur ?

le potentiomètre définit un niveau au dessus duquel il donne un 1. donc il a un rôle.
Si vous preniez la sortie analogique vous verriez mieux les variations et pourrez traiter le seuil dans votre code (éventuellement plus dynamiquement).

en supposant que votre capteur soit stable et en ayant réglé le potentiomètre à la valeur de déclenchement attendue, pour mesurer le temps il suffirait de faire

const byte pinCapteur = 7;

void setup() {
  Serial.begin(115200); Serial.println();
  pinMode(pinCapteur, INPUT);
}

void loop() {
  while (digitalRead(pinCapteur) == LOW);   // on attend le passage à HIGH
  unsigned long t0 = millis();              // on note le moment du passage à HIGH
  while (digitalRead(pinCapteur) == HIGH);  // on attend le retour à LOW
  if ( millis() - t0 >= 5000) {
    Serial.println(F("HIGH pour plus de 5 secondes"));
  } else {
    Serial.println(F("HIGH pour moins de 5 secondes"));
  }
}

Je ne suis pas certain que l'on puisse compter sur un son assez fort pendant 5 secondes sans interruption.
Si le programme ne fait que ça, il y a pas mal de mémoire libre.
Il serait peut-être intéressant de faire un tampon de 1K octets par exemple que l'on nourrirait toutes les 5ms et de faire une moyenne tournante. On peut aussi bien utiliser la sortie logique que la sortie analogique du capteur de son. Le traitement serait sans doute plus simple avec le signal logique puisqu'il y a déjà une forme de filtrage par le seuil appliqué sur la carte du capteur.

Tout dépend sans doute du réglage du potentiomètre

Le code ci dessus devrait le montrer

A mon avis il faut passer par La sortie analogique pour avoir un meilleur contrôle du seuil (par exemple le régler dans le setup en écoutant le bruit de fond quand c’est calme)

Le buffer tournant est sans doute le plus précis - en stockant des bits à 1 ou 0 pour représenter l’état nécessitera 8x moins de mémoire si besoin au prix d’une gymnastique sur les bits/index (division et modulo 8 pour trouver l’octet et le bit donc pas super difficile puisque puissance de 2 ➜ on prend un uint16_t et on a dans 3 bits de poids faible la position dans l’octet et les 13 bits de poids fort décalés de 3 bits à droite donneront l’octet à cibler dans le buffer)

On peut régler le potentiomètre mais pas le bébé.
Le pleur n'est pas nécessairement continu pendant 5s même si globalement il est plus long.

Faudrait voir le domaine d’application. Niveau de bruit dans une salle de classe ou un concert ou pleurs de bébé effectivement. L’approche par buffer est plus générique et règlera tous les cas.

Bonjour,

effectivement il serait plus confortable de pouvoir régler le seuil directement dans le programme. Cependant l'approche par la sortie analogique du capteur me semble plus complexe étant donnée que le signal est du type "fréquentiel" ou sinusoïdal.(voir exemple ci-dessous)

Code utilisé pour la mesure du screen ci-dessus :

void setup()
{
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop()
{
  Serial.print("Analogin: ");
  Serial.println( analogRead(A0) );
}

c'est le même signal que celui qui sort sur la pin D0 si vous faites une comparaison. Ce n'est pas la "fréquence" (ça ne veut pas dire grand chose il y aurait un mélange de sons à différentes fréquences - cf FFT) du son capté.

const int seuil = 300;
void setup()
{
  pinMode(A0, INPUT); // inutile, un analogRead se fait automatiquement en INPUT
  Serial.begin(9600); // vaut mieux aller plus vite 115200 par exemple
}

void loop()
{
  Serial.print("Analogin: "); // ce serait bien de nommer A0 dans une constante
  Serial.println( analogRead(A0) > seuil ? 1 : 0 ); // imprime 1 si on est > seuil sinon 0
}

L'avantage c'est que vous avez la main sur le seuil dans le code plutôt que d'avoir à jouer avec le potentiomètre sur la carte.

Si on utilise le signal analogique, le "plus simple" serait de supprimer la composante continu et de travailler avec les valeurs absolues.

une proposition de code qui maintient les données dans un buffer

const byte pinCapteur = A0;
const uint32_t dureeEcoute = 5000; // en ms
const int seuil = 300;
const uint16_t tailleBuffer = 125;

uint8_t echantillons[tailleBuffer]; // initialisé à 0 par le compilateur car variable globale

const uint32_t nombreEchantillons = tailleBuffer * 8;
const uint32_t periodeEchantillonage = dureeEcoute / nombreEchantillons;

uint16_t numeroEchantillon = 0;
uint16_t sommeEchantillons = 0;

// **** acquisition ****
//   rajoute un échantillon dans le buffer si c'est le moment.
//   retourne vrai si l'échantillon a été rajouté
//   met à jour la variable sommeEchantillons qui est le nombre de fois où l'échantillon était > seuil sur la durée d'écoute (après initialisation)
//**********************

bool acquisition() {
  static uint32_t derniereAcquisition = -periodeEchantillonage; // valeur initale pour que le premier échantillon soit pris immédiatement en compte
  if (millis() - derniereAcquisition >= periodeEchantillonage) {
    uint16_t indexOctet = numeroEchantillon >> 3;   //
    uint8_t  indexBit = numeroEchantillon & 0b111;  // 3 bits de poids faible (de 0 à 7)
    sommeEchantillons -= bitRead(echantillons[indexOctet], indexBit);
    if (analogRead(pinCapteur) > seuil) {
      sommeEchantillons++;
      bitSet(echantillons[indexOctet], indexBit);
    } else {
      bitClear(echantillons[indexOctet], indexBit);
    }
    if (++numeroEchantillon >= nombreEchantillons) numeroEchantillon = 0;
    derniereAcquisition = millis();
    return true;
  }
  return false;
}

void setup() {
  Serial.begin(115200);
  Serial.print(F("\nPréparation .")); // on remplit le buffer
  while (true) {
    if (acquisition()) {
      if ((numeroEchantillon % 100) == 0) Serial.write('.');
      if (numeroEchantillon == 0) break; // on remplit le buffer
    }
  }
  Serial.print(F("\nSystème Prêt.\nSon Fort à ")); Serial.print((100ul * sommeEchantillons) / nombreEchantillons);
  Serial.println(F("%."));
}

void loop() {
  if (acquisition()) {
    Serial.print(F("Son Fort à ")); Serial.print((100ul * sommeEchantillons) / nombreEchantillons);
    Serial.println(F("%."));
  }
}