Schleife zurücksetzen nach Event (PIR Sensor + NANO + DFPlayer)

Hallo ZSM.

Folgendes Zinnario: PIR Sensor soll Bewegung detektieren (geht). Im PIR kann man das Ausgangssignal zw 1&10s einstellen(meschanisch). Im Endeffekt soll ein an den Nano angeschlossener DFPlayer zb Musik spielen (gleiches Song) Nachdem Bewegung detektiert wurde (geht). Nun was noch nicht geht:

  • Die Musik soll 15 sek nachdem keine Bew. detektiert wurde ausgehen. (gibt einen Befehl in DFplayer (myDFPlayer.pause();).

  • Dann soll der Gleiche song bei erneuter bewegung Neustarten (myDFPlayer.play(1):wink:

Ich hatte an eine Schleife gedacht die 15 s lang, einmal pro sek auf Bewegungsstatus=High checkt, wenn 15 mal LOW int Stop = 1

Wenn in dieser Zeit (15s) eine Bew. erkannt wird, dann startet die Schleife wieder mit 0 (bis 15s).

Wenn 15s keine Bewegung, dann dann Musik Stop + neue Variable int Stop = 1; und (myDFPlayer.pause();

Wenn gestopppt war Und neue Beweg. erkannt wurde:
if Stop == 1 && Bewegungsstatus == HIGH { myDFPlayer.play(1); Stop = 0;}

vil. hat ja jemand eine Idee wie man dies in Code fasst, mfg

unsigned long previousMillis = 0; 
unsigned long interval = 1000;   


int bewegungsstatus=0;
int Bewegungerkannt = 0;
int PIR_SENSOR = 3;
int PirLED = 5;
int i = 0;

void setup() {
Serial.begin(115200);
pinMode(PIR_SENSOR, INPUT);
pinMode(PirLED, OUTPUT);
 
}
 
void loop() {
  

bewegungsstatus=digitalRead(PIR_SENSOR); //Hier wird der Pin7 ausgelesen (Befehl:digitalRead).

if (millis() - previousMillis > interval) {                           //Die Miliis Funktion schreibt 1 mal pro sek!
    previousMillis = millis();   // aktuelle Zeit abspeichern

//-------------- hier bin ich unsicher
Serial.println(i);

}

//if (bewegungsstatus == HIGH)        // TEST mit LED ------- Oder so // int s = digitalRead(PirPin);// digitalWrite(PirLED, s);   
//{                                       
//   digitalWrite(PirLED, HIGH);
//}
//else
//  {
//  digitalWrite(PirLED, LOW);
//  }

}

Dann sieh dir das Beispiel "BlinkWithoutDelay" in der IDE an, damit kannst du deine 15 Sek. Schleife aufbauen, ohne den weiteren Ablauf zu behindern.

Hi

Ich sehe zumindest Ansätze einer State-Maschine.
Was Du willst:

  • in jedem loop()
    --Durchlauf PIR auslesen
    --- bei Bewegung 'lastseen' auf millis(); setzen
    --- prüfen, ob wir den Song schon spielen (Flag)
    --- wenn wir den Song noch nicht spielen, den Song starten und das Flag setzen
    --prüfen, ob das Flag gesetzt ist
    --- prüfen, ob zwischen millis() und lastseen unsere 15 Sekunden vorbei sind
    --- wenn Ja, den Song stoppen, Flag löschen

Du brauchst auf gar keinen Fall eine Schleife, eine Status-Variable reicht völlig - Diese kann auch das aktive Flag beinhalten, muß aber nicht.

Habe ich was übersehen?

MfG

Thx Postmaster& Sytems,

bei Bewegung 'lastseen' auf millis(); setzen heist bei mir previousMillis?

mit flags setzen habe ich noch keine erfahrung, habe das immer mit varablenwert ändern getan,

"wenn wir den Song noch nicht spielen, den Song starten und das Flag setzen" heist Stop = 0?

Hi

Flag heißt Flagge und ist eigentlich nur ein Merker - im µC selber sind Flags einzelne Bits, bei Dir kann ein Flag auch ein Byte oder ein weiterer Status sein.
Du erstellst ein Flag 'musik_ist_an' und setzt Das auf 1, wenn Du die Musik startest.
Als Typ reicht hier boolean (true/false bzw HIGH/LOW bzw 1/0) oder auch Byte (0...255) - Beides belegt hier den gleichen Speicher (1 Byte).
Verschiedene Status hast Du auch
0- Musik ist aus, wenn 'bewegung_erkannt' 1 ist, merken wir uns die aktuelle Zeit (lastseen), löschen 'bewegung_erkannt', Status -> 1
1- Musik starten -> Status 2
2- Wir warten auf das Ende der Wartezeit, wenn hier 'bewegung_erkannt' gesetzt ist, merken wir uns die aktuelle Zeit (lastseen) und resetten das Flag. Wenn die Wartezeit vorrüber ist, Status -> 3
3- Musik stoppen, Stautus -> 0

Unabhängig vom Status prüfen wir auf Bewegung und setzen ein Flag 'bewegung_erkannt'

Hier brauchen wir 'musik_an' gar nicht, daß ist bereits in den verschiedenen Status (ist ebenfalls die Mehrzahl von Status) enthalten.

In der loop() prüfen wir bei jedem Durchgang also den PIR und setzen das Flag.
Je nach Status prüfen wir, ob das Flag gesetzt ist (setzen lastseen und löschen das Flag), starten die Musik oder stoppen Diese wieder.
Alles sind immer nur kurze Arbeiten, die Hauptaufgabe des Arduino ist's, im Kreis zu rennen und den PIR abzufragen - der Rest ist eher 'schnarch lahm' - zumindest für den Arduino.

MfG

PS: Ja, Dein previousMillis ist mein lastseen, Beides sollte uint32_t sein (wie millis() ).
Was Du dem dfplayer sendest, ist dieser State-Maschine egal, bei Status 1 muß der Befehl für den Start stehen, bei Status 3 für den Stop.
Wenn Du hier z.B. bei erneuter Erkennung was Anderes machen willst - füge einen Status hinzu und mache was Eigenes - Du musst nur eine Überprüfung haben, wann zu einem anderen Status gesprungen werden soll, sonnst hängt sich die State-Maschine hier auf.

Thx, habe mich etwas in statemachine belesen. Habe versucht, dies in Code zu schreiben. Schöner wäre es warscheinlich mit Switch Case + einzelnen Funktionsaufrufen ().

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;

uint32_t lastseen = 0; 
uint32_t interval = 1000;   


bool bewegungsstatus=0;
bool Bewegungerkannt = 0; // = 1 heist bewegung
int PIR_SENSOR = 3;
int PirLED = 5;
int i = 0;

void setup() {
Serial.begin(115200);
pinMode(PIR_SENSOR, INPUT);
pinMode(PirLED, OUTPUT);
 
}
 
void loop() {
  

bewegungsstatus=digitalRead(PIR_SENSOR); //Hier wird der Pin7 ausgelesen (Befehl:digitalRead).

if (millis() - lastseen > interval) {                           //Die Miliis Funktion schreibt 1 mal pro sek!
    lastseen = millis();   // aktuelle Zeit abspeichern
}
//Serial.println(i);

//-------------- NEW CODE 

//Keine Musik + Bewegung erkannt
if (myDFPlayer.readState() == LOW && bewegungsstatus == HIGH){  //State 0
  myDFPlayer.play(1);
}


if (myDFPlayer.readState() == HIGH && Bewegungerkannt == 0){ //State 1
  myDFPlayer.pause();

}

if (millis > 15000){                          // Nach 15s  Bewegungerkannt = 0; & zurückgesetzt
  Bewegungerkannt = 0;
  lastseen = 0; 
  }


//if (bewegungsstatus == HIGH)        // TEST mit LED ------- Oder so // int s = digitalRead(PirPin);// digitalWrite(PirLED, s);   
//{                                       
//   digitalWrite(PirLED, HIGH);
//}
//else
//  {
//  digitalWrite(PirLED, LOW);
//  }

}

Hallo, zsm. Es funktioniert leider noch nicht so. vil. könnte ein C-Pro nochmal über mein Code schauen, mfg

R2k88:
Thx, habe mich etwas in statemachine belesen. Habe versucht, dies in Code zu schreiben. Schöner wäre es warscheinlich mit Switch Case + einzelnen Funktionsaufrufen ().
.....

Und warum machst du das nicht mit Switch/Case und den Funktionen ?

if (millis > 15000)

Das ist leider formal möglich, aber ganz sicher nicht, was du willst.
Der Compiler kann dieses if ersatzlos wegoptimieren !
Bei eingeschalteten Warnungen meckert er über den Vergleich zwischen Adressen und int.
warning: ISO C++ forbids comparison between pointer and integer [ -fpermissive ]

//Die Miliis Funktion schreibt 1 mal pro sek!

Kommentar und Code verstehe ich nicht.
Willst du, dass ein Teil deines Codes einmal pro Sekunde ausgeführt wird ?

const uint16_t interval = 1000;
uint32_t lastseen;
...
void loop() {
    if (millis() - lastseen > interval) {          
      lastseen = millis();   // aktuelle Zeit abspeichern
      SekundenFunktion();  // Funktion aufrufen
   }
...
}

void SekundenFunktion() {
  // Diese Funktion kommt einmal jede Sekunde dran.

}

Hallo, ja, das war noch das Konzept, dass einmal pro sek geguckt werden sollte ob die "bewegungsflag" gesetzt wurde. Siehe letzter POST.

HotSystems:
Und warum machst du das nicht mit Switch/Case und den Funktionen ?

Weil ich das mit Switch/Case und den Funktionen noch nicht programmiertechnisch umsetzen kann.

habe weiter an dem Code gearbeitet, nur leider wird bewegungsstatus auch nach Taster betätigung, VOR der 15 s, von alleine wieder auf LOW gesetzt... Sollte eigentlich HIGH bleiben... (Beim: Neuer Versuch) -- Taster wird später mit PIR sensor ersetzt

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int taster = 4;
int PIR_SENSOR = 8;
int PirLED = 5;

bool pinStatus1 = HIGH;
bool bewegungsstatus ;
int spielt;
int tasterstatus;
int i = 0; 

uint32_t lastseen = 0;
uint32_t interval = 1000;  

void setup()
{
pinMode(taster, INPUT_PULLUP);
pinMode(PIR_SENSOR, INPUT);
pinMode(PirLED, OUTPUT);
  
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  
  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... Hello R2k"));
  
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));
  
  myDFPlayer.volume(10);  //Set volume value. From 0 to 30

 Serial.println(myDFPlayer.readState());
}

void loop()
{

//---------------------------------------------- Hier versuche ich  bewegungsstatus = LOW zu setzen, wenn nicht innerhalb der 15s auf 0 gesetzt wird
//if (millis() - lastseen > interval) {
//  if ( digitalRead(taster) == LOW){
//  lastseen = 0;
//   bool bewegungsstatus = HIGH;
//   digitalWrite(PirLED, LOW);//
//   Serial.println("if");
//  }
//  else{
//    lastseen = millis();   // aktuelle Zeit abspeichern
//    bool bewegungsstatus = LOW; //Die 15 sekunden wurden überschritten
//    digitalWrite(PirLED, HIGH);//
//    Serial.println("else");
//  }
//}
//------------------------------------------------Neuer Versuch: jede sek hochzählen bis 15, dann ist bewegungsstatus = LOW (wenn !nicht! vorher zurückgesetzt)
if(digitalRead(taster) == LOW)
  {i=0; bool bewegungsstatus = HIGH; 
  lastseen = millis();
  Serial.println(i); Serial.println(bewegungsstatus); 
  digitalWrite(PirLED, LOW);} 

if (millis() - lastseen > interval) {
  ++i;
  lastseen = millis();
  Serial.println(i);
  Serial.println(bewegungsstatus);
  if(i>14)
  {i=0; bool bewegungsstatus = LOW; 
  lastseen = millis();
  Serial.println("über 15s");
  digitalWrite(PirLED, HIGH); }
}


//--------------------------------------------------------------------------------- Taster wird zum Schalter, auch zum Testen

//bewegungsstatus=digitalRead(taster);

  if (digitalRead(taster) == LOW) { //Teste mit LED Tasterstatus
     if (pinStatus1 == HIGH){ 
   //  digitalWrite(PirLED, HIGH);//
     pinStatus1 = LOW;
     }
     else {
     pinStatus1 = HIGH;
 //    digitalWrite(PirLED, LOW);//
     }
    digitalWrite(tasterstatus, pinStatus1);
    delay(50);
   }
//------------------------------------------------------------------------------------------------

if (digitalRead(taster) == LOW && myDFPlayer.readState() == 512)              // readState() == 512 heißt keine "Music" -> Music startet
{    
 // digitalWrite(PirLED, HIGH);//Programmabschnitt des IF-Befehls öffnen.
  myDFPlayer.play(1);  //Play the first mp3
  int spielt = 1;
  delay(500);
  Serial.println(myDFPlayer.readState());
  //delay(301800);
}

if (bool bewegungsstatus = LOW && myDFPlayer.readState() == 513)              //Verarbeitung: Wenn der bewegungsstatus ist (Das Spannungssignal ist hoch)
{    
 // digitalWrite(PirLED, LOW);//Programmabschnitt des IF-Befehls öffnen.
 myDFPlayer.pause();  //Play the first mp3
 int spielt = 0;
  delay(500);
  Serial.println(myDFPlayer.readState());
  //delay(301800);
  
}

//if (digitalRead(taster) == LOW && myDFPlayer.readState() != 512 && spielt == 0)              //Verarbeitung: Wenn der bewegungsstatus ist (Das Spannungssignal ist hoch)
//{    
// // digitalWrite(PirLED, HIGH);//Programmabschnitt des IF-Befehls öffnen.
//  myDFPlayer.play(1);  //Play the first mp3
//  delay(500);
//  Serial.println(myDFPlayer.readState());
//  //delay(301800);
//  
//}

  
  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}