Sound abspielen nach Signaleingang

Hallo zusammen

Meine Programmierung führt nicht das aus, was ich gerne möchte... Was soll passieren?

  1. Signale kommen auf D2 - D7 rein und werden ausgewertet.
  2. Aufgrund des Signals soll das entsprechende File vollständig abgespielt werden.
  3. Ein Signal kann mehrfach hintereinander reinkommen (Abstand ca. 0.5 Sekunden) und das File soll dann auch mehrfach abgespielt werden.

Vielen Dank für die Vorschläge, wie ich die Programmierung anpassen soll.

void loop() {
  static byte lastResult {0xFF};
  byte result = (PIND >> 2);
  // byte result = (~PIND >> 2) & 0x3F; // Invert if INPUT_PULLUP is used
  if (lastResult != result) {
    cout << "Dezimal: " << _WIDTH(result,2) << " Binär: ";
    printBits(cout,result);
    lastResult = result;
  }
  if (lastResult == 56) {
    myDFPlayer.play(1);
  }
    if (lastResult == 22) {
    myDFPlayer.play(3);
  }
}

Import necessary libraries
You should add a setup() function.
Use Serial.print() instead of cout

Maybe even read:
Read this before posting a programming question ... - #18 by nickgammon.._gaMTk1NDExNzgzMi4xNzUyMTY4Njcy*_ga_NEXN8H46L5*czE3NTIxNjg2NzIkbzEkZzAkdDE3NTIxNjg2NzIkajYwJGwwJGgzNjUxNDg5MTM.

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden.
Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Zeige bitte einmal den kompletten Code. Wenn es mehrfach abgespielt werden soll, dann musst du auch zählen wie oft das Signal ankommt. Auf der einen Seite hochzählen, und bei jedem abspielen wieder herunterzählen bis der counter auf Null ist.

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Streaming.h>
Print &cout {Serial};

SoftwareSerial mySoftwareSerial(10, 11);
DFRobotDFPlayerMini myDFPlayer;

constexpr byte DATA_PINS[] {2, 3, 4, 5, 6, 7};

template <typename T> void printBits(Print &cout, const T &value) {
  constexpr uint8_t maxBits = (sizeof(T) * 8);
  constexpr T mask = (static_cast<T>(0x01) << (maxBits - 1));

  uint8_t i = 0;
  while (i < maxBits) {
    cout << (((value << i++) & mask) ? 1 : 0);
    // Output a space after each nibble.
    if (!(i - ((i >> 2) << 2))) { cout << " "; }
  }
  cout << endl;
}

void setup() {
  mySoftwareSerial.begin(9600);
  Serial.begin(9600);
  for (auto &pin : DATA_PINS) {
    pinMode(pin, INPUT);
  }
  if (!myDFPlayer.begin(mySoftwareSerial)) {
    while (true) {
    delay(0);
    }
  }
myDFPlayer.volume(25);
myDFPlayer.play(1);
}

void loop() {
  static byte lastResult {0xFF};
  byte result = (PIND >> 2);
  // byte result = (~PIND >> 2) & 0x3F; // Invert if INPUT_PULLUP is used
  if (lastResult != result) {
    cout << "Dezimal: " << _WIDTH(result,2) << " Binär: ";
    printBits(cout,result);
    lastResult = result;
  }
  if (lastResult == 56) {
    myDFPlayer.play(1);
  }
    if (lastResult == 22) {
    myDFPlayer.play(3);
  }
}

Du könntest dir in einem Ringbuffer merken welche Pins gedrückt wurde und entsprechend dem Buffer die Ausgabe starten.
Du müsstest dir überlegen wie viele "Tastendrücke" du dir merken willst, weil so ein Buffer ist eben nur ein endlicher Speicher.

Und sehe ich das richtig, du reagierst eigentlich nur auf 2 Bitmuster und du spielst auch entsprechend nur zwei Tracks?

Wenn du uns dazu genauere Angaben machen kannst, kann dir hier vermutlich geholfen werden.

P.S.:
Wenn das alles im Zusammenhang mit deinem Projekt vor einem Jahr steht, dann wäre es zweckmäßig wenn du noch einmal kurz erklärst worum es genau geht damit alle wissen woran du bist

Und auch selber könntest du noch mal alle deine Threads vom letzten Jahr durchlesen - da steht ja eigentlich eh schon alles drinnen.

Die Idee mit Portzugriffen

statt mit digitalread zu arbeiten ist nett, wird es aber nicht leichter machen.

Du brauchst statemaschienen die nicht blockieren. Eine, die die Eingänge überwacht und den

füllt. Und dann eine die die Sounds abspielt.

ungetestet

// Forensketch DFPlayer mit Warteschlange
// https://forum.arduino.cc/t/sound-abspielen-nach-signaleingang/1394862/
/*
  Benutzt den BUSY-Pin des DFPlayer um festzustellen, ob kein Titel aktiv gespielt wird.
  Füllt ein Array entsprechend des ausgelöstem Pin (Zähler beginnt bei 0) mit der PlayID beginnend mit 1
*/

#include <Streaming.h>     // https://github.com/janelia-arduino/Streaming

#define DEBUG              // Wenn aktiviert, werden Zwischenwerte ausgegeben

#ifdef DEBUG
  #define DBG_PRINTLN(...) Serial << __VA_ARGS__ << endl
#else
  #define DBG_PRINTLN(...)
#endif

#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
SoftwareSerial mySoftwareSerial(10, 11);     // RX, TX DFPlayer
DFRobotDFPlayerMini myDFPlayer;

constexpr uint8_t busyPin {8};               // Abfragepin ob play aktiv
constexpr bool busy {LOW};                   // Wenn play, ist der Pin LOW aktiv

struct CHANNEL                               // was zusammengehört
{
  uint8_t inputPin;
  bool lastState;
  // uint32_t switchTime;                    // Wäre auslösezeit für debonuce
};

constexpr uint8_t channels {6};              // Anzahl aktiver Pins
CHANNEL channel[channels]
{
  {2, LOW,},
  {3, LOW,},
  {4, LOW,},
  {5, LOW,},
  {6, LOW,},
  {7, LOW,},
};

constexpr bool pinActive {HIGH};             // Auslöser für aktiv

constexpr uint8_t maxPlay {50};              // Anzahl Titel in der Playlist
uint8_t playList[maxPlay] = {0};
uint8_t playListIdx;                         // Index an dem der nächste Titel geschrieben wird

void fillPlaylist(const uint8_t playId)
{
  if (playListIdx < maxPlay - 1)             // Noch nicht Ende der Liste erreicht
  {
    DBG_PRINTLN(F("Index: ") << playListIdx << F(" gefüllt mit Titel: ") << playId);
    playList[playListIdx++] = playId;        // Füllen und Index erhöhen
  }
}

void readChannel()
{
  for (byte b = 0; b < channels; b++)                     // Zähle durch alle Pins
  {
    if (digitalRead(channel[b].inputPin == pinActive))    // Pin ist gesetzt
    {
      if (channel[b].lastState == !pinActive)             // war vorher nicht gesetzt
      {
        // Hier käme fürs debounce der Merker
        // channel[b].switchTime=millis();
        fillPlaylist(b + 1);                              // Playlist
        DBG_PRINTLN(F("PIN ") << b << F(" ausgelöst"));
      }
    }
    else if (channel[b].lastState == pinActive)           // Pin ist nicht gesetzt, war es aber vorher
    {
      // Normalerweise käme hier eine debounceabfrage
      // if (millis()-channel[b].switchTime>50)
      channel[b].lastState = !pinActive;                  // Merker löschen
    }
  }
}

void getPlayList()
{
  if (digitalRead(busyPin) != busy)                // Wenn nicht belegt
  {
    if (playList[0] != 0)                          // Wenn ein Titel vorgemerkt
    {
      DBG_PRINTLN(F("Spiele Titel ") << playList[0]);
      myDFPlayer.play(playList[0]);                // Spiele den Titel
      playList[0] = 0;                             // lösche Titel
    }

    if (playListIdx)                               // Wenn noch mehr Ttel in der Liste
    {
      for (byte b = 1; b < maxPlay; b++)           // Verschiebe Einträge
      {
        playList[b - 1] = playList[b];
      }

      playListIdx--;                               // Position für neuen Eintrag setzen
      playList[maxPlay - 1] = 0;                   // letzten Eintrag gesichert löschen
    }
  }
}

void setup()
{
  Serial.begin(115200);
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2650)) // https://github.com/arduino/Arduino/issues/10764
  delay(300);
#endif
  Serial << (F("\r\nStart...\r\n")) << endl;
  DBG_PRINTLN(__FILE__);
  DBG_PRINTLN( __TIMESTAMP__);

  for (uint8_t b = 0; b < channels; b++)
  {
    pinMode(channel[b].inputPin, INPUT);
  }

  pinMode(busyPin, INPUT);
}

void loop()

{
  readChannel();
  getPlayList();
}

Hallo noiasca

Du hast Recht. Ich habe das Projekt von vor einem Jahr wieder aufgenommen und möchte es nun finalisieren. Ich versuche die Tipps zwar umzusetzen und investiere viel Zeit, aber das mit der Programmierung und mir ist so eine Sache...

Es werden mehr als nur 2 Bitmuster (bis 32) sein. Dies war nur ein Test für mich. Mit dem Test bin ich auch nicht zufrieden, denn der Sound wird erst abgespielt, sobald das nächste Bitmuster rein kommt (also nicht sofort). Es sollen nur 4 bis max. 8 Bitmuster gespeichert werden. Mehr ist nicht notwendig bzw. darf verloren gehen.

Ziel:

  • Bitmuster lesen - das funktioniert
  • Sound gemäss Bitmuster vollständig abspielen
  • Falls neue Bitmuster gelesen werden, dann erst abspielen, sobald der vorherige Sound fertig ist

Kannst du mir beim Code helfen?

Dann funktioniert meiner nicht uneingeschränkt.
Die Leseroutine muss dann umgeschrieben werden.
Das füllen der Playlist und das abspielen kann so weiter verwendet werden.

da es zwischenzeitlich was von xy gibt, schlage ich vor du folgst seinem Vorschlag bzw. du gibst im Feedback was sein Sketch mit deiner Hardware genau macht/nicht macht.

Hallo zusammen

Vielen Dank für euren Support. Ich bin nun einen Schritt weiter.

Es wird der entsprechende Sound abgespielt und nicht mehr unterbrochen. Allerdings spielt er, wenn das Signal einmal rein kommt endlos den gleichen Sound ab. Macht es Sinn, wenn wir zuerst dieses Problem lösen und uns dann um die Bufferung kümmern? Hier der aktuelle Code

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Streaming.h>
Print &cout {Serial};

SoftwareSerial mySoftwareSerial(10, 11);
DFRobotDFPlayerMini myDFPlayer;

constexpr byte DATA_PINS[] {2, 3, 4, 5, 6, 7};

const int busyPin = 9; // Pin, an dem der BUSY-Pin des MP3-Players angeschlossen ist

template <typename T> void printBits(Print &cout, const T &value) {
  constexpr uint8_t maxBits = (sizeof(T) * 8);
  constexpr T mask = (static_cast<T>(0x01) << (maxBits - 1));

  uint8_t i = 0;
  while (i < maxBits) {
    cout << (((value << i++) & mask) ? 1 : 0);
    // Output a space after each nibble.
    if (!(i - ((i >> 2) << 2))) { cout << " "; }
  }
  cout << endl;
}

void setup() {
  mySoftwareSerial.begin(9600);
  pinMode(busyPin, INPUT);
  Serial.begin(9600);
  for (auto &pin : DATA_PINS) {
    pinMode(pin, INPUT);
  }
  if (!myDFPlayer.begin(mySoftwareSerial)) {
    while (true) {
    delay(0);
    }
  }
myDFPlayer.volume(25);
myDFPlayer.play(1);
}

void loop() {
  

  if (shouldPlayNewFile()) {
    if (!isPlaying()) {
      myDFPlayer.play(1); // Spielt die erste Datei ab
      Serial.println("Neues File wird abgespielt");
    } else {
      Serial.println("Player ist beschäftigt, kein neues File");
    }
  }
  delay(1000);
}

bool isPlaying() {
  // Überprüft, ob der Player beschäftigt ist
  return digitalRead(busyPin) == HIGH;
}

bool shouldPlayNewFile() {
static byte lastResult {0};
  byte result = (PIND >> 2);
  // byte result = (~PIND >> 2) & 0x3F; // Invert if INPUT_PULLUP is used
  if (lastResult != result) {
    cout << "Dezimal: " << _WIDTH(result,2) << " Binär: ";
    printBits(cout,result);
    lastResult = result;
  }
  if (lastResult == 56) {
    myDFPlayer.play(1);
  }
  if (lastResult == 60) {
    myDFPlayer.play(2);
  }
    if (lastResult == 22) {
    myDFPlayer.play(4);
  }
}

Was muss ich anpassen, damit der gleiche Sound nicht endlos abgespielt wird, sondern erst bei bei einem neuen Bitmuster? Kann das abgespielte Bitmuster z.B. auf 0 gesetzt werden?

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Streaming.h>
Print &cout {Serial};

SoftwareSerial mySoftwareSerial(10, 11);
DFRobotDFPlayerMini myDFPlayer;

constexpr byte DATA_PINS[] {2, 3, 4, 5, 6, 7};

constexpr uint8_t busyPin {9};       // Pin, an dem der BUSY-Pin des MP3-Players angeschlossen ist
constexpr bool busy {LOW};           // Wenn play, ist der Pin LOW aktiv

byte lastResult;

constexpr uint8_t maxTitels {50};
uint8_t playList[maxTitels] = {0};
uint8_t playListIdx;

template < typename T > void printBits(Print &cout, const T &value)
{
  constexpr uint8_t maxBits = (sizeof(T) * 8);
  constexpr T mask = (static_cast < T > (0x01) << (maxBits - 1));
  uint8_t i = 0;

  while (i < maxBits)
  {
    cout << (((value << i++) & mask) ? 1 : 0);

    // Output a space after each nibble.
    if (!(i - ((i >> 2) << 2)))
    {
      cout << " ";
    }
  }

  cout << endl;
}

void setup()
{
  mySoftwareSerial.begin(9600);
  pinMode(busyPin, INPUT);
  Serial.begin(9600);

  for (auto &pin : DATA_PINS)
  {
    pinMode(pin, INPUT);
  }

  if (!myDFPlayer.begin(mySoftwareSerial))
  {
    while (true)
    {
      delay(0);
    }
  }

  myDFPlayer.volume(25);
  myDFPlayer.play(1);
}

void playFile(const byte id)
{
  switch (id)                             // spiele ersten Titel aus der Liste
  {
    case 56: myDFPlayer.play(1); break;

    case 60: myDFPlayer.play(2); break;

    case 22: myDFPlayer.play(4); break;
  }

  if (id != 0)
  {
    Serial << F("Neues File wird abgespielt") << endl;
  }
}

void loop()
{
  getNewFile();                                      // Neue Daten?

  if (digitalRead(busyPin) != busy)                  // Es wird nicht abgespielt
  {
    playFile(playList[0]);                           // Player setzen
    playList[0] = 0;                                 // Eintrag löschen

    if (playListIdx)                                 // Wenn noch mehr Ttel in der Liste
    {
      for (byte b = 1; b < maxTitels; b++)           // Verschiebe Einträge
      { playList[b - 1] = playList[b]; }

      playListIdx--;                                 // Position für neuen Eintrag setzen
      playList[maxTitels - 1] = 0;                   // letzten Eintrag gesichert löschen
    }
  }
}

void getNewFile()
{
  if (playListIdx < maxTitels - 1)          // Nur wenn noch Platz in der Liste ist
  {
    byte result = (PIND >> 2);              // Hole Daten

    // byte result = (~PIND >> 2) & 0x3F; // Invert if INPUT_PULLUP is used
    if (lastResult != result)               // Daten sind neu
    {
      cout << "Dezimal: " << _WIDTH(result, 2) << " Binär: ";
      printBits(cout, result);
      lastResult = result;
      playList[playListIdx++] = result;     // Aufnehmen und Index erhöhen
    }
  }
}

Hast Du @noiasca zu verdanken.

Nachtrag: Jetzt mit Playlist und löschen damit Du kein Dauerlauf hast. Das dürfte deine Lösung sein.

Herzlichen Dank!!! Es sieht so aus, als ob es klappt! Ich muss den Code noch erweitern und dann ausgiebig testen. Nochmals ein tolles Dankeschön. So macht es Spass!!!

p.s. ich habe meinen letzten Post gelöscht, weil ich am Arduino umgesteckt habe und deshalb etwas nicht funktionierte. Mein Fehler.

Du könntest

ändern in:

      if (result != 0)
      { playList[playListIdx++] = result; }     // Aufnehmen und Index erhöhen

und in playFile()

ersetzen durch:

  Serial << F("Neues File ") << id << endl;

Damit werden nur tatsächlich abspielbare ID's aufgenommen.
Sonst kann es Dir passieren, dass auch 0x00 in der Warteschlange landet.
Macht nix, braucht nur unnütz Speicher.