Hilfe, ich finde den Fehler nicht!

Hallo, ich habe ein Metronom mit Vibrationsmotor für das Handgelenk gebaut. der Controller ist ein XIAO ESP32 C3. Es gibt 2 Modi: Modus 0 ist ESPNOW Empfang von einem anderen ESP und im Modus 1 generiert das Gerät selbst einen Tempopuls.
Nachdem es mit voreingestelltem Tempo (int bpm) lief, wollte ich nun die Tempoveränderung progarmmieren. Dabei hab ich wohl einen Fehler gemacht. Meine SerialMonitor-Ausgabe zeigt mir, dass das Tempo ohne das ich irgendeinen Touchsensor berühre auf den Mindestwert (minBPM) herunterrauscht. Der Ausdruck dürfte eigentlich nur einmal erfolgen, da er nur erfolgt, wenn der Wert geändert wurde: if( bpm != bpm_Old) ...

Die Ermittlung der anderen selbstgeschriebenen Funktionen (Voids) hat eigentlich funktioniert. Die Ermittlung von "statTouch" (bedeutet den Status der beiden Touchsensoren).

Ich weiß, es ist eine Zumutung solch laienhaften Code durchzuschauen. Aber "bpm" kann man ja mit der Suchfunktion "strg F" finden, denn nur im Zusammenhang mit "bpm" kann doch diese Integer verändert werden, oder nicht? Aber ich sehe da nichts, was automatisch, ohne Berührung von der Touchsensoren den Wert: "aktuell bpm: 30" verursacht.
Dann muss es ja woanders sein. Aber wo?
Also wenn mir da jemand weiterhelfen könnte, wäre das echt super. Oder auch eine Idee, was ich probieren / checken soll ...

Hier der Code:

const int Mot = D6;  // VibrMotor
int play;
const int stark = 255; const int mittel = 150; const int leicht = 75;  // Vibrationsstärke
const int tacet = 0;     // Vibrationsstärke 0 = no Vibration
bool statMot;
// debug
bool statMot_old;


int bpm = 80;
const int minBPM = 30;
const int maxBPM = 270;
// debug
int bpm_Old;

int microTime;
int microTLaenge; // gleichbedeutend mit TaktArt
int taktLaenge = 1;
const int maxTL = 8;
bool changeTL;

const int Led = D10;
bool statLed;
int Rpt = 3;
int T = 225;  // T = Time für Argument in Blitz()

const int Touch1 = D7, Touch2 = D8;
bool statTouch1, statTouch2; // in statTouchAll() ???
bool statTouch1_old, statTouch2_old;
int statTouch, statTouch_old;
long touch1Hold, touch2Hold;
const int touchWait = 1000; // = 1 Sekunde in ms
int IncrDecrTime; // kleiner zeitl Abstand bei TouchHold ( ==> gr Sprünge Incr/Decr) 
// wird nicht verwendet???
int touchDelay = 2000;  // in ms zur Entprellung und Two-Touchkombi

int sec = 1000;  // in ms
int inTerval;
long past, present;

float voltKorr = 0.18;  // Wert durch Testung ermittelt (bq)
int voltCounter;       // hier als Anzahl Pulse für Batterie-Voltage

int Modus;  // 0 = Reciever (default), 1 = Metronom, 2 = Charge, 3 wäre noch mögl. (noch nicht definiert)

// nur debug
int count;


void setup() {
  pinMode(Mot, OUTPUT);
  pinMode(Led, OUTPUT);
  pinMode(Touch1, INPUT);
  pinMode(Touch2, INPUT);
  //pinMode(A0, INPUT);  // für Voltage Messung -> braucht man nicht? -> Konflikt? wird so zu digitalEingang ???

  digitalWrite(Mot, LOW);  // evntl überflüssig ???

  //Blitz(3, 225);
  //delay(sec / 2); // zeitl. Abstand zur "Mod-ChoosingLed"

  //############# SERIAL ###################################
  Serial.begin(115200);
  delay(50);  // Zeit geben um Serial zum starten
  Serial.println("Setup");
  //########################################################
  

  //??????????? Voltage Messung ????????????????????????????????????  
  voltMess();  // voltage Messung wird ausgelöst
  showResult(Rpt, T);  // Ergebnis wird gezeigt
  //???????????????????????????????????????????????????????????
  //!!!!!!!!!!!! A L A R M  & DeepSleep !!!!!!!!!!!!!!!!!!!!!!!!!!
  if (voltCounter < 2) { // dauert 1sec showResult() + delay(sleep) = 128sec + 34,7min < 37 min
    doBattAlarm(Rpt, T); // am Ende von doBAttAlarm() nochmalige voltMess(), wenn nicht Volt erhöht ==> deepSleep() 
  } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  // wenn BattVolt OK dann einfach weiter

  /////////// Mod-Choice-LED //////////////////////////////////////
  digitalWrite(Led, HIGH);
  delay(sec * 2);  // jetzt Modus wählen (Touch 1 od 2 od no Touch)
  digitalWrite(Led, LOW);
  /////////////////////////////////////////////////////////////////

  statTouchAll();
  delay(sec / 2); // zeitl. Abstand zum Beginn showChooseResult

  // Modus ermitteln & anzeigen (3 od. 4 Modi mögl. statTouch 0 - 2, evntl. 3 od. 4)
  //////// Reciever - Modus: 3x Blink /////////////////////
  if (statTouch == 0) {
    Modus = 0;  // Reciever
    showResult(3, sec/4);
  }
  //////// internes Metronom - Modus: 5x Blink ////////////
  if (statTouch != 0) {
    Modus = 1;  // internes Metronom
    showResult(5, sec/4);
  }
/*  //////// Lademodus: 7x Blink ////// Lademod im RecieverMod includiert ///////////
  if (statTouch == 2) {
    Modus = 2;  // Chargemode
    for (int i = 0; i < 7; i++) {
      digitalWrite(Led, HIGH);
      delay(sec / 8);
      digitalWrite(Led, LOW);
      delay(sec / 2);
    }
  }
  */
  // Reset
  statTouch = 0;
  // Serial.print("Modus: "); Serial.println(Modus);
  // Serial.print("statTouch: "); Serial.println(statTouch);
  // Serial.println("Reset Touch done");
}


void statTouchAll() {
  statTouch1_old = statTouch1;
  statTouch2_old = statTouch2;
  statTouch_old = statTouch;
  statTouch1 = digitalRead(Touch1);
  statTouch2 = digitalRead(Touch2);

  if ((statTouch1 == 0) && (statTouch2 == 0)) { // nix berührt
    statTouch = 0;
  }

  /* ERLÄUTERUNG "statTouch" - Zustände (bei Berührung):
    nur Touch1 = 1, Touch1 + (danach) Touch2 = 3 (= ungerade Zahlen)
    nur Touch2 = 2, Touch2 + (danach) Touch1 = 4 (= gerade Zahlen)  */
  if (statTouch1 == 1) {  // Touch1 berührt
    if (statTouch1 != statTouch1_old) {
      Blitz(1, sec/4); // Feedback: "Touch1 wurde berührt"
    }
    if (statTouch2 != statTouch2_old)  {
      if (statTouch2 == 1) {  // zusätzlich Touch2 berührt
        statTouch = 3;
      }
    }
    else if (statTouch2 == 0) { // nur Touch1 berührt
      statTouch = 1;
    }
  } 

  if (statTouch2 == 1) {  // Touch2 berührt
    if (statTouch2 != statTouch2_old) {
      Blitz(1, sec/4); // Feedback: "Touch2 wurde berührt"
    }
    if (statTouch1 != statTouch1_old)  {
      if (statTouch1 == 1) {  // zusätzlich Touch1 berührt
        statTouch = 4;
      }
    }
    else if (statTouch1 == 0) { // nur Touch2 berührt
      statTouch = 2;
    }
  }
}


void voltMess() {
  uint32_t Vbatt = 0;
  for (int i = 0; i < 16; i++) {
    Vbatt = Vbatt + analogReadMilliVolts(A0);  // ADC with correction
  }
  float Vbattf = 2 * Vbatt / 16 / 1000.0;  // attenuation ratio 1/2, mV --> V
  float volBatt = Vbattf + voltKorr;

  // voltage counter change
  if (volBatt >= 4.1) {
    voltCounter = 5;
  }  // => voll aufgeladen
  if (volBatt >= 3.8 && volBatt < 4.1) {
    voltCounter = 4;
  }  // => fast voll
  if (volBatt >= 3.5 && volBatt < 3.8) {
    voltCounter = 3;
  }  // => mittelvoll
  if (volBatt >= 3.2 && volBatt < 3.5) {
    voltCounter = 2;
  }  // => hält noch ein wenig (wie lange?)
  if (volBatt < 3.2) {
    voltCounter = 1;
  }  // => dringend aufladen!

  Rpt = voltCounter;
 
}


void showResult(int Repeat, int Time) {
  /* Ergebniscode: Blink 5x = Batt voll, 4x = fast voll, 3x = halbleer, 2x = bald leer, 
      Blink 1x = DRINGEND AUFLADEN !!! */
  for (int i = 0; i < Repeat; i++) {
        delay(Time); // startet mit delay() wg zeitl Abstand zu vorherig LedON
        digitalWrite(Led, HIGH);
        delay(Time);
        digitalWrite(Led, LOW);
      } 
  delay(sec/2);
}


void doBattAlarm(int Repeat, int Time) {
  int sleep;
    for (int i = 0; i < 64; i++) { // delay(sleep) = 2080 sec /60 = 34,6667 min
      showResult(Repeat, Time);
      Blitz(2, sec/4);
      sleep = sleep++; 
      delay(sleep);
    }
    voltMess(); // vielleicht wurde inzwischen aufgeladen ...
  // >>>>>>>>>>>> go to SLEEP >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
  if (voltCounter < 2) {
    esp_deep_sleep_start();
  } // wird hoffentlich noch rechtzeitig geladen bevor Akku unter 3V ...
  else {
    sleep = 0;  // reset sleep
  };
}


void Blitz(int Repeat, int Time) { // Rpt = Anz. Wdh. Blitz; alarmTime = Pause zw Blitzen
  for (int y = 0; y < Repeat; y++) {
    //delay(Time); // delay() steht am Anfang für zeitl. Abstand zum vorherigen LedON
    digitalWrite(Led, HIGH);
    delay(25);  // immer nur ultrakurzer Blitz
    digitalWrite(Led, LOW);
    delay(Time); // steht am Ende für zeitl. Abstand zur nachfolgenden LedON
  }
}


void createBeat() {
  /* TaktArt (= taktLaenge)
  * TA 0 = alles 4tel (PWM: mittel); TA 1 = alles 4tel (Einsen - PWM: stark)
  * TA 2 = 2/4; TA 3 = 3/4; TA 4 = 4/4; TA 5 = 5/4; TA 6 = 6/8; 
  * TA 7 = 7/4 (223); TA 8 = 4/4 m offBeats;
  */
  if (microTime % 2 == 0) { //also bei microTime = 2; 4; 6; 8; 10; 12; 14; 16
    play = tacet;
  }
  if (taktLaenge < 6) {
    switch (microTime) {
      case 1: if (taktLaenge == 0) {play = mittel;}
              else play = stark; break;  // Beat 1
      case 3: play = mittel; break; // Beat 2
      case 5: play = mittel; break; // Beat 3;
      case 7: play = mittel; break; // Beat 4
      case 9: play = mittel; break; // Beat 5;  
      // case 11; 13; 15 nur im 6/8 & 7/4 & 4/4offBeats möglich
    }
  }
  if (taktLaenge == 6) {  // Betonung: 3|3
    switch (microTime) {
      case 1: play = stark; break;  // Beat 1
      case 3: play = leicht; break; // Beat 2
      case 5: play = leicht; break; // Beat 3
      case 7: play = stark; break; // Beat 4
      case 9: play = leicht; break; // Beat 5
      case 11: play = leicht; break; // Beat 6
    } // case 13; 15 nur im 7/4 & 4/4offBeats möglich
  }
  if (taktLaenge == 7) { // Betonung: 2|2|3
    switch (microTime) {
      case 1: play = stark; break;  // Beat 1
      case 3: play = mittel; break; // Beat 2
      case 5: play = stark; break; // Beat 3
      case 7: play = mittel; break; // Beat 4
      case 9: play = stark; break; // Beat 5
      case 11: play = mittel; break; // Beat 6
      case 13: play = mittel; break; // Beat 7
    } // case 15 nur im 4/4offBeats möglich
  }
  if (taktLaenge == 8) { //4/4 mit offBeats
    switch (microTime) {
      case 1: play = stark; break;  // Beat 1
      case 3: play = leicht; break; // offBeat
      case 5: play = mittel; break; // Beat 2
      case 7: play = leicht; break; // offBeat
      case 9: play = stark; break;  // Beat 3
      case 11: play = leicht; break; // offBeat
      case 13: play = mittel; break; // Beat 4
      case 15: play = leicht; break; // offBeat
    }
  }
}


void showTL() { 
  digitalWrite(Mot, LOW);  // evntl überflüssig ???
  statMot = false;  // überfrlüssig?
  for (int i = 0; i < taktLaenge; i++) { 
    Blitz(2, sec/8);
    delay(sec/4);
  }
  statMot = true; // überfrlüssig?
}


void loop() { 
    present = millis();
    statTouchAll();

    if (statTouch != 0) {  // bei Touch - Eingabe kein Motor
      statMot = false;
    }
    else statMot = true;
    
    //########### debug ################################
    if (statTouch != statTouch_old) {
      Serial.print("statTOUCH: "); Serial.println(statTouch);
    }
    if (bpm != bpm_Old) {
        Serial.print("aktuell BPM: "); Serial.println(bpm);
        bpm = bpm_Old;
    } 
    if (statMot != statMot_old) {
      Serial.print("statMot: "); Serial.println(statMot);
      statMot_old = statMot;
    } //################################################  

 
   if (Modus == 0) { ///////// RECIEVER (Baustelle) /////////////////////////////////////

    // hier kommt noch der Reciever-Block hin

    //??????????? Voltage Messung ????????????????????????????????????
    if (statTouch == 4) {  
      statMot = false;
      voltMess();  // voltage Messung wird ausgelöst
      showResult(Rpt, T);  // Ergebnis wir gezeigt
      //???????????????????????????????????????????????????????????
      //!!!!!!!!!!!! A L A R M  & DeepSleep !!!!!!!!!!!!!!!!!!!!!!!!!!
      if (voltCounter < 2) { // dauert 1sec showResult() + delay(sleep) = 128sec + 34,7min < 37 min
        doBattAlarm(Rpt, T);  
      } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    }  //???????????????????????????????????????????????????????????
    // wenn BattVolt OK dann einfach weiter
     statMot = true;

   }  /////////////////////// END RECIEVER /////////////////////////////////////////////

  
  if (Modus == 1) { ///////// internMETRONOM //////////////////////////////////////////
    
    microTLaenge = taktLaenge * 2;  // ungerade Beats sind Pulse, gerade Beats sind Pausen
    inTerval = 60000 / bpm / 2; // ungerade Beats sind Pulse, gerade Beats sind Pausen

    //!!!!!!!!! Erzeugung Microtiming & MotorPuls !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if (present >= past + inTerval) {
      microTime++;
      if (microTime > microTLaenge) { // microTLaenge: = taktLaenge * 2;
        microTime = 1;
      }
      if (statMot == true) {
        createBeat(); 
        analogWrite(Mot, play);  // play = Stärke PWM (stark, mittel od. leicht)
      }
      past = present;      
    }  //!!!!!!!!!!!!!END MicroTIMING & MotorPULS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    //!!!!!!!!!!!!!!! +/- changeTEMPO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if (statTouch_old == 0) {
      if (statTouch == 1) {
        bpm++; // = bpm wird höher (schneller)
        //debug #########################################
        Serial.println("bpm INcrease");
        touch1Hold = present;
      }
      if (statTouch == 2) {
        bpm--; // = bpm wird niedriger (langsamer)
        //debug #########################################
        Serial.println("bpm DEcrease");
        touch2Hold = present;
      }
    } 
    if (statTouch == 1) {
      if (present > touch1Hold + touchWait + IncrDecrTime) {
        bpm = bpm + 5;
        //debug #########################################
        Serial.println("bpm INcrease + 5");
        Blitz(1, 125);  // 1x kurzBlink - Anzeige für Hochzälen
        IncrDecrTime + 500; // nach 500 ms nächste +/- 5 IncreDecrement
      }
    }
    if (statTouch == 2) {
      if (present > touch2Hold + touchWait + IncrDecrTime) {
        bpm = bpm - 5;
        //debug #########################################
        Serial.println("bpm DEcrease + 5");
        Blitz(1, 125);  // 1x kurzBlink - Anzeige für Runterzälen
        IncrDecrTime + 500; // nach 500 ms nächste +/- 5 IncreDecrement
      }
    }
    if (bpm < minBPM) {
      bpm = minBPM;
    }
    if (bpm > maxBPM) {
      bpm = maxBPM;
    }
    // reset nach Loslassen Touch1 od Touch2
    if (statTouch == 0) {
      IncrDecrTime = 0;
    }
    
    //!!!!!!!!!!!!!! END changeTEMPO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    //!!!!!!!!!!!!!!! ++ takt LAENGE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if (statTouch != statTouch_old) { 
      if (statTouch == 3) { // Touch1 gehalten, Touch2 neu berührt
        taktLaenge++;
        changeTL = true;
        if (taktLaenge > maxTL) { // maxTL = 8 (in Header)
          taktLaenge = 0;  
        }        
      }
      // statTouch == 4 soll Tab-Tempo werden ==> Baustelle
      //++++++++++++++++++ Baustelle TAB - TEMPO ++++++++++++++++++++++++++
      //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
    } 
    if (statTouch != statTouch_old && changeTL == true) {
      if (statTouch == 0) {
        delay(sec/2); // Abstand zum Loslassen, um showTL() besser zu erkennen 
        showTL();
        changeTL = false;
      }
    } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    // //??????????? Voltage Messung ????????????????????????????????????
    // if (statTouch == 4) {  
    //   statMot = false;
    //   voltMess();  // voltage Messung wird ausgelöst
    //   showResult(Rpt, T);  // Ergebnis wir gezeigt
    //   //???????????????????????????????????????????????????????????
    //   //!!!!!!!!!!!! A L A R M  & DeepSleep !!!!!!!!!!!!!!!!!!!!!!!!!!
    //   if (voltCounter < 2) { // dauert 1sec showResult() + delay(sleep) = 128sec + 34,7min < 37 min
    //     doBattAlarm(Rpt, T);  
    //   } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // }  //???????????????????????????????????????????????????????????
    // // wenn BattVolt OK, dann einfach weiter
    // statMot = true;
  } //////////////////// END internMETRONOM ////////////////////////////////

}

Lass dir doch mal statTouch, statTouch_old und touch1Hold seriell ausgeben.

long past, present;

muss unsignel long sein.

unsigned long

Danke für den Tip. Ich verstehe aber nicht ganz warum, der Wert kann ja nicht negativ werden, da ich ihn ja mit "minBPM" begrenze. Habs geändert. Danke.

Ich habe jetzt aber den Übeltäter gefunden!
Es war im debug-Abschnitt am Anfang des Loop() die Zeile:

bpm = bpm_Old;

Das ist natürlich Unsinn. Es muss so rum sein:

bpm_Old = bpm;

Auch hier verstehe ich nicht, warum dieser Fehler den Wert: bpm 30 verursacht hat...
Nach einer Tasse Kaffee heut Morgen ist mir klar, dass der initialisierte Wert "bpm_Old" = 0 ist und im falschen Code sofort im 1. Loopdurchlauf zum aktuellen "bpm" wird, dieser wird weiter unten zurückgesetzt auf 30 bpm. Soweit, so simpel. Im gestrigen umnächteten Zustand war mir das leider nicht erkenntlich.

Das Auswertungsprozedere der Touchzustände hatten keine Schuld.
Wenn es etwas mit den Touchsensoren zu tun gehabt hätte, hätte mir

Serial.print("statTOUCH: "); Serial.println(statTouch);

das eigentlich zeigen müssen.

Die korrigierte Version funktioniert nun aber, wie gedacht. Prima. :smiley:

Beste Grüße,
bq.

Das ist Blödsinn. Bitte streichen...
Es geht ja um die Kontrolle und Verarbeitung der millis() - Werte. Wenn diese Werte bei Überlauf negativ würden, könnte das evntl zu Problemen führen.
@wwerner Hattest du das gemeint mit deinem Hinweis?

Genau. Einfach die Doku lesen und das benutzen was dort angegeben wurde:

Naja die Doku hatte ich natürlich mal gelesen. Aber das ist ne ganze Weile her. Das hatte ich nicht mehr auf dem Schirm (nur irgendwas mit long, weil man schnell sehr große Zahlen hat und Integer schnell überläuft). Es war schon gut, das konkret zu benennen. Danke nochmal. Nun weiß ichs wieder und hoffe, ich merk es mir. :slight_smile:
Obwohl es bei meinem Metronom wohl kaum zu einem Überlauf gekommen wäre und damit zu negativen Werten, denn das Gerät ist ja nur Minuten an, max 4h. Länger dauert eine Probe nicht und das nervt ja dann auch am Handgelenk.

Besten Gruß,
bq.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.