Reading tap tempo and writing out tempo on off beat

Hello all, first post so hopefully i'm in the right place.

I am new to coding, i understand the basics but may have bitten off more than i can chew.
I want to use arduino (eventually an ATtiny85) to read the tempo coming from a tap tempo circuit, then apply a delay to that tempo so that i can output the same tempo but on the off beat. I hope that makes sense.

At the moment i am using some code from another forum, which is itself a tap tempo. I thought this would be the easiest route. Instead of reading a footswitch though i am reading a squarewave output. The code works at slow speeds but when the tempo reaches perhaps 4 pulses per second it starts going out of time and i can't work out why. I have an LED hooked up to so i can see the tempo. Is there something in this code which is causing the output to not exactly follow the input?
I actually don't need the button state display on pin 13 but don't want to delete it in case i screw something else up.

I can worry about getting the output on the off-beat later, for now i just want the output to follow the input precisely.

Thank you!

void setup()
{
  pinMode( 12, INPUT_PULLUP );   /*   /* tap button - press it to set the tempo */

  pinMode( 13, OUTPUT );  /* button state display - not necessary */

  for ( int i = 2 ; i < 12 ; i++ ) {
    pinMode( i, OUTPUT );  /* tempo display light - shows the current tempo */
  }
}


int lastTapState = LOW;  /* the last tap button state */
unsigned long currentTimer[2] = { 500, 500 };  /* array of most recent tap counts */
unsigned long timeoutTime = 0;  /* this is when the timer will trigger next */

unsigned long indicatorTimeout; /* for our fancy "blink" tempo indicator */

void loop()
{
  /* read the button on pin 12, and only pay attention to the
     HIGH-LOW transition so that we only register when the
     button is first pressed down */
  int tapState = digitalRead( 12 );
  if ( tapState == LOW && tapState != lastTapState )
  {
    tap(); /* we got a HIGH-LOW transition, call our tap() function */
  }
  lastTapState = tapState; /* keep track of the state */

  /* check for timer timeout */
  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + 30;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }

  /* display the button state on LED 2 */
  digitalWrite( 13, tapState );

  /* display the tap blink on LED 13 */
  for ( int i = 2 ; i < 12 ; i++ ) {
    if ( millis() < indicatorTimeout ) {
      digitalWrite( i, HIGH );
    } else {
      digitalWrite( i, LOW );
    }
  }
}

unsigned long lastTap = 0; /* when the last tap happened */
void tap()
{
  /* we keep two of these around to average together later */
  currentTimer[1] = currentTimer[0];
  currentTimer[0] = millis() - lastTap;
  lastTap = millis();
  timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */
}

void rescheduleTimer()
{
  /* set the timer to go off again when the time reaches the
     timeout.  The timeout is all of the "currentTimer" values averaged
     together, then added onto the current time.  When that time has been
     reached, the next tick will happen...
  */
  timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
}

What do you have connected to pins 2-11? Are those the LEDs you are using to see the tempo? You are writing them HIGH/LOW based on on the 'indicatorTimeout' value which appears to just be the pin 13 blinking, not the 'timeoutTime' which is based on your tap input.

Also, this is code is horribly written with respect to the use of millis(). You can never calculate future times [e.g. millis() + 30] and then check to see if the current value of milis() is less than that future time. It will bite you since millis() rolls over to 0 every ~49 days. This may seem like a long time and not be relevant to your current project, but it is a bad habit to be avoided.

The proper way to deal with time it to measured elapsed time by recording a start time and then computing the elapsed time (current - start) to see if enough time has elapsed to do whatever the next task is.

+1 Karma for using code tags on your first post!

Thank you for the reply :slight_smile:

Yes pins 2-11 were for LEDs, the person who wrote the code was using loads of LEDs as tempo indicators for some reason. I'm just using one for now.

I haven't yet got my head around millis() so am unsure how i should re-write the code. Do you think this is what is causing the timing issues?

I think it has to do with your indicatorTimeout use.

But I am not sure what you are exactly trying to do. You record a tap and then average the last two tap times to set your timeoutTime but make indicatorTimeout 30 milliseconds beyond that. Why?

You can never calculate future times [e.g. millis() + 30] and then check to see if the current value of milis() is less than that future time. It will bite you since millis() rolls over to 0 every ~49 days. This may seem like a long time

This is because it is a long time. Have you ever made a project that has been powered up for 49 days? And after that there is a chance it could stop working, but only a chance.

But the big problem here is:-

patrick398:
the person who wrote the code

The OP has not written it and so has no idea what it is doing, along with us.

This comes of the belief that you find code, you don't you write it.

Thanks for the replies.

Grumpy_Mike:
But the big problem here is:-The OP has not written it and so has no idea what it is doing, along with us.

This comes of the belief that you find code, you don't you write it.

Yes a fair point, i did not write this code. I am trying to learn how to code, and i thought what i wanted to achieve was relatively straight forward. When i realised it was going to be trickier than i suspected i thought editing some existing code might be a good way to learn some new concepts, but i've just confused myself...and everyone else i expect.

blh64:
I think it has to do with your indicatorTimeout use.

But I am not sure what you are exactly trying to do...

Let's forget about the previous code i posted for now.

In my head what i want to do actually seems quite straight forward. There is an existing tap tempo circuit controlling another circuit. I want to read the output of this tap tempo (which is putting out roughly 4v pulses). The arduino input pin will read the existing tap tempo output, when it is HIGH, it will output an arduino pin HIGH, when the input is LOW it will output that pin LOW.

I need to calculate the tempo of the incoming pulses so i guess i would use pulseIn to measure the 'high time' of the input, and pulseIn to measure the 'low time of the input'. Adding them together would give me the period.

I then need to apply a delay somehow to the output, so that it falls exactly on the off beat. I guess the length of this delay would be equal to 'high time' or 'low time' since that's half of the period.

Anyway, here is a sketch i threw together quickly. For now i have ignored the delay aspect. It's probably completely wrong but maybe it's a better starting point and hopefully you can understand what i'm trying to do.

int input = 12;
int output = 11;
unsigned long highTime;
unsigned long lowTime;
unsigned long period;
unsigned long time;


void setup() {
  pinMode (input, INPUT_PULLUP);
  pinMode (output, OUTPUT);

}

void loop() {

  time = millis();
  highTime = pulseIn (input, HIGH); // read time input is high
  lowTime = pulseIn (input, LOW); // read time input is low
  period = (highTime + lowTime); // period is equal to high time plus low time

  digitalRead (input); // read input pin
  if (input == HIGH) { // if input pin is high write output pin high
    digitalWrite (output, HIGH);

  }

  digitalWrite (output, LOW); // write output pin low


}

Thanks again

Patrick

I've just been trying to use a simple toggle kind of set up to see if i can get my output to follow my input. This works with a button, but it won't follow the square wave i'm feeding in. Could this be because the square wave is a pwm output?

int ledState = 0; // when program starts led is off
int ledPin = 8; // LED on pin 8
int inputPin = 12; // input on pin 12
int inputCurrent; // new input state
int inputLast = 1; // old input state


void setup() {
  Serial.begin (9600);
  pinMode (ledPin, OUTPUT);
  pinMode (inputPin, INPUT);
}

void loop() {
  inputCurrent = digitalRead (inputPin); // say that inputCurrent is equal to what is read on input pin

  if (inputLast == 1 && inputCurrent == 0) { // if switch goes from high to low it has been preessed so do what's in { }
    if (ledState == 0) { // if led state is 0 (off) we want to turn it on

      digitalWrite (ledPin, HIGH); // turn led on
      ledState = 1; // keeping track of led state. It has been turned on so declare it's value as 1 (on)
    }

    else { // if previous statement is false i.e led is 1 (on) we want to turn it off

      digitalWrite (ledPin, LOW); // turn off led
      ledState = 0; // keep track of led state. It has been turned off
    }
  }

  inputLast = inputCurrent;

}

How to you have this wired up? Your original code configured the input pin as INPUT_PULLUP but this code does not. Do you have an external pullup resistor in the circuit?

What is the frequency of this PWM frequency of this square wave you are trying to follow?

If you are (initially) not worried about the delay, all you code needs to do is read the input and write that value to the output

void loop {
  digitalWrite(ledPin, digitalRead(inputPin));
}

but it won't follow the square wave i'm feeding in. Could this be because the square wave is a pwm output?

No, it should follow a PWM signal just as easily as it follows a button, so there must be something wrong with the wiring or common ground connection between your system and what is producing the PWM.

I think i may have got it working. A friend pointed out that a square wave which is half a length out of phase is the same as it being 180 degrees out of phase, so when my input is high i just need to send the output low and vice versa. No need for timings or delays. That's a relief.

Seems to be playing nice with the pwm output now as well, and i can't detect any timing issues.
I'm interested if you foresee any issues i might run into with this embarrassingly simple code...

int input = 7;
int output = 8;
int inputState;



void setup() {
  pinMode (input, INPUT_PULLUP);
  pinMode (input, OUTPUT);
  Serial.begin (9600);

 
}

void loop() {

  inputState = digitalRead (input);
  if (inputState == LOW) {
    digitalWrite (output, HIGH);
  }

  if (inputState == HIGH) {
    digitalWrite (output, LOW);
    
  }

}

Well its a bit long winded. A shorter code was in reply #7. But that outputs the input signal. To output the inverse of this use

 void loop {
  digitalWrite(ledPin, !digitalRead(inputPin));
}

Thank you, that is very snappy indeed. The ! means 'not' or 'opposite' or something?

also the code i posted above has an error:

void setup() {
  pinMode (input, INPUT_PULLUP);
  pinMode (input, OUTPUT);

Here's what i have now:

int input = 7;
int output = 8;
int inputState;



void setup() {
  pinMode (input, INPUT_PULLUP);
  pinMode (output, OUTPUT);
  Serial.begin (9600);

 
}

void loop() {

  digitalWrite(output, !digitalRead(input));
}

Thank you for the help!

The ! means 'not' or 'opposite' or something?

Yes.
The logical inverse to be correct but it is much the same thing.