Page 1 of 1

Tap Tempo Metronome drifts (ESP32 C3 supermini)

Posted: Sun Mar 23, 2025 10:56 pm
by campidelli
Hi everyone, I have a question: I have created this project (https://github.com/campidelli/tap-tempo-metronome) to create a tap tempo metronome pedal (there is a demo video in the GitHub repository).

It works, sort of. The problem is that the tempo drifts a little bit after a while. For example, if I put another tap tempo metronome side by side, they start syncing but after some seconds I start hearing the difference. What could it be?

This is the code, in case you didn't check the Github repo:

Code: Select all

#include "Arduino.h"
#include "ArduinoTapTempo.h"
#include "ESP32_C3_TimerInterrupt.h"

#define LED_PIN              8
#define TAP_TEMPO_SWITCH_PIN 5
#define TIMER1_INTERVAL_MS   20
#define PWM_AUDIO_OUTPUT_PIN 4
#define PWM_CHANNEL          0
#define PWM_FREQ             4000 // Frequency in Hz
#define PWM_RESOLUTION       8    // 8-bit resolution (0-255)

ESP32Timer ITimer1(1);    // Timer to check the button state every TIMER1_INTERVAL_MS
ArduinoTapTempo tapTempo; // Used to calculate the BPM

unsigned long lastClickTime = 0;     // Last time a click sound was played
volatile bool buttonPressed = false; // Flag to indicate if the button is pressed

bool IRAM_ATTR TimerHandler1(void * timerNo) {
  if (digitalRead(TAP_TEMPO_SWITCH_PIN) == LOW && !buttonPressed) {
    buttonPressed = true;
  }
  if (digitalRead(TAP_TEMPO_SWITCH_PIN) == HIGH) {
    buttonPressed = false;
  }
  tapTempo.update(buttonPressed);
  return true;
}

void setup() {
  Serial.begin(9600);

  pinMode(TAP_TEMPO_SWITCH_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);

  // Set up PWM on the audio output pin
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PWM_AUDIO_OUTPUT_PIN, PWM_CHANNEL);

  // Attach timer interrupt to check the button state every TIMER1_INTERVAL_MS
  if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)) {
    Serial.println("Timer interrupt started.");
  } else {
    Serial.println("Failed to start timer interrupt.");
  }
}

void loop() {
  float bpm = tapTempo.getBPM();
  unsigned long interval = 60000 / bpm; // Interval in milliseconds for each beat

  // Click sound and LED blink if it's time for a beat
  if (bpm > 0 && millis() - lastClickTime >= interval) {
    Serial.print(F("Current BPM: "));
    Serial.println(bpm);

    digitalWrite(LED_PIN, LOW);

    ledcWrite(PWM_CHANNEL, 200); // Set a moderate duty cycle
    delay(20);
    ledcWrite(PWM_CHANNEL, 0);   // Stop sound

    digitalWrite(LED_PIN, HIGH);

    lastClickTime = millis(); // Update the last click time
  }
}
Cheers!

Re: Tap Tempo Metronome drifts (ESP32 C3 supermini)

Posted: Mon Mar 24, 2025 1:39 am
by Sprite
Hard to say; how are you syncing the two metronomes? Issue is that even a slight difference in measurements between the two (either because of your input method or because of rounding errors or whatever) tend to accumulate over the long term.

Re: Tap Tempo Metronome drifts (ESP32 C3 supermini)

Posted: Mon Mar 24, 2025 3:03 am
by boarchuz

Code: Select all

  if (bpm > 0 && millis() - lastClickTime >= interval) {
    ...
    delay(20);
    ...
    lastClickTime = millis(); // Update the last click time
  }
}
Every click, you're growing further out of sync by however long it takes to process the click.

Taking a quick look at the lib, I guess you're supposed to use onBeat to take advantage of the internal timing:
https://github.com/dxinteractive/Arduin ... pp#L51-L54 (Somebody loves his floats!?)
Which looks like it expects you to be calling update() in the loop. It also notes that this specific function is "not accurate enough for timing in music" but I don't see how that makes sense given that there's no higher resolution timing anywhere else.