Switchable LED Stroboscope (for turntable calibration)?

I want a LED stroboscope for turntable speed adjusting but don't know where to start with the code.
How do i code the frequencies?
I want to make it switchable between 50/60/120/150/300 Hz.

And it seems like there is more to it than just the frequency.
From an analog 120Hz Circuit i did read (i try to translate it from German):
The monovibrator is important. 120Hz has a periodic time of 8.33msec and from that the LED is just 135µsec on. That makes a duty cycle of ~1:62.
That means short bright flashes give high contrast and high sharpness.
Without the monovibrator the duty cycle is 1:1 and contrast and sharpness are relatively bad.

Hi,

You need to start by working out the on and off times in microseconds for each frequency. Then you can use the micros() function to time them. Note that micros() is only accurate to 4us. But that should get you to within 0.05% even at 300Hz. That is assuming your Arduino's crystal/resonator is accurate!

You will also need to put a large current through your led(s) because the on times will be very short. It is usually ok to exceed the max continuous current of an led by at least a factor of 2 if the pulses are short. The Arduino's outputs can source or sink 40mA for short periods, so you may not need a transistor.

I think I would recommend high brightness green leds. They give the brightest light for the current you can give them, as perceived by the human eye. Avoid white leds because they are in reality ultra violet leds inside a glowing phosphor, so they do not switch on and off quite as quickly. But perhaps that does not really matter at these frequencies, I'm not sure.

I guess 4us is more than good enough because normally people used 120V/230V light bulbs (50/60Hz) and power from the grid is not that stable?

Any hints how to do the calculation?
I found a calculator for Hz to period: Frequency formula period time frequency cycle per second hertz Hz amplitude duration periodic time period to angular frequency formulary wavelength acoustic equation relationship wavelength Hz millisecond ms calculation calculate calculator t=1/f Hz hertz to ms T to f worksheet - sengpielaudio Sengpiel Berlin

This is very basic school maths!

For 300Hz, the period in us is 1000000/300 = 3333us (ignoring fractions).

For a 1:62 on time (I'm not sure why they were so particular about that) the on time would be 3333/62 =54us

That means the LED needs to be off for 3279µsec and on for 54µsec?

I guess it does not need to be 1:62. It's just to have something high and not 1:1.
The analogue circuit is from 2002 and done with CD4013, CD4060 and 3,93216MHz quartz
and i guess it's just something that was the easiest way.

From the physically side i don't really understand it.
Why is it that 1:1 looks the same to the eyes like 1:62?

MrGlasspoole:
That means the LED needs to be off for 3279µsec and on for 54µsec?

Yes!

MrGlasspoole:
I guess it does not need to be 1:62. It's just to have something high and not 1:1.
The analogue circuit is from 2002 and done with CD4013, CD4060 and 3,93216MHz quartz
and i guess it's just something that was the easiest way.

In 1972 that was the easiest way. In 2002 it was already "old school" or "retro"!

MrGlasspoole:
From the physically side i don't really understand it.
Why is it that 1:1 looks the same to the eyes like 1:62?

It doesn't. 1:1 would mean the led was continuously on, so not strobing at all, and useless for your turntable.
Having a short on time means that there is less chance of visible motion blur. But it also means less light intensity. You have to balance the two.

By 1:1 i guess he means on and off time is the same?
Here is the circuit with 300Hz: http://www.krishu.de/wp-content/uploads/stroboblitzer_schema.gif

The designer of the original 120Hz circuit wrote:
The 4060 is the Oscillator.
The 4013 has 2 D-Flops, both are used. The first D-Flop is as divider connected through 2. The second D-Flop is the monovibrator for the short on time of the LED (135µsec).

Ok my calculation is:
50Hz period in µsec is 1000000/50 = 20000 µsec
60Hz period in µsec is 1000000/60 = 16666 µsec
120Hz period in µsec is 1000000/120 = 8333 µsec
150Hz period in µsec is 1000000/150 = 6666 µsec
300Hz period in µsec is 1000000/300 = 3333 µsec

50Hz on 20000/62 = 322 µsec
50Hz off 20000-322 = 19678 µsec
60Hz on 16666/62 = 268 µsec
60Hz off 16666-268 = 16398 µsec
120Hz on 8333/62 = 134 µsec
120Hz off 8333-134 = 8199 µsec
150Hz on 6666/62 = 107 µsec
150Hz off 6666-107 = 6559 µsec
300Hz on 3333/62 = 54 µsec
300Hz off 3333-54 = 3279 µsec

I understand about the 1:1 thing now. Not the way I am used to thinking about it, but it makes sense. I think my idea about motion blur explains why a short flash is better.

It also sounds like the design uses a fixed 135us flash for any frequency. You can do the same in the Arduino sketch.

Keep it simple to begin with. Just one led, a 220R or 330R series resistor, and just one frequency. Use this tutorial as a starting point, but use micros() instead of millis().

Ok the code:

/**************************************************************
* Calculation                                                 *
***************************************************************

50Hz period in µsec is 1000000/50 = 20000 µsec
60Hz period in µsec is 1000000/60 = 16666 µsec
120Hz period in µsec is 1000000/120 = 8333 µsec
150Hz period in µsec is 1000000/150 = 6666 µsec
300Hz period in µsec is 1000000/300 = 3333 µsec

50Hz on 20000/62 = 322 µsec
50Hz off 20000-322 = 19678 µsec
60Hz on 16666/62 = 268 µsec
60Hz off 16666-268 = 16398 µsec
120Hz on 8333/62 = 134 µsec
120Hz off 8333-134 = 8199 µsec
150Hz on 6666/62 = 107 µsec
150Hz off 6666-107 = 6559 µsec
300Hz on 3333/62 = 54 µsec
300Hz off 3333-54 = 3279 µsec

**************************************************************/
// On and Off Times (as int, max=32secs)
const unsigned int onTime50 = 322;
const unsigned int offTime50 = 19678;
const unsigned int onTime60 = 268;
const unsigned int offTime60 = 16398;
const unsigned int onTime120 = 134;
const unsigned int offTime120 = 8199;
const unsigned int onTime150 = 107;
const unsigned int offTime150 = 6559;
const unsigned int onTime300 = 54;
const unsigned int offTime300 = 3279;

/**************************************************************
* MISC SETUP                                                  *
**************************************************************/
int interval50 = onTime50;        // Interval is how long we wait
int interval60 = onTime60;        // Interval is how long we wait
int interval120 = onTime120;      // Interval is how long we wait
int interval150 = onTime150;      // Interval is how long we wait
int interval300 = onTime300;      // Interval is how long we wait
unsigned long previousMicros = 0; // Tracks the last time event fired
boolean LEDstate = true;          // Used to track if LED should be on or off

// Define unused pins
byte pin[] = {10, 11, 12, 13};                   // Array of unused digital pins
byte pinCount = sizeof(pin) / sizeof(pin[0]);    // Count unused digital pins
byte pinA[] = {A0, A1, A2, A3, A4, A5, A6, A7};  // Array of unused analog pins
byte pinACount = sizeof(pinA) / sizeof(pinA[0]); // Count unused analog pins

// Define button pins
byte pinU[] = {3, 4, 5, 6, 7};                   // Array of used digital pins (buttons)
byte pinUCount = sizeof(pinU) / sizeof(pinU[0]); // Count used digital pins

// Usual Setup Stuff
void setup() {

  for (byte i = 0; i < pinCount; i++) {
    pinMode(pin[i], OUTPUT);    // Set unused digital pins as output
    digitalWrite(pin[i], LOW);  // Set unused digital pins state to low 
  }

  for (byte i = 0; i < pinACount; i++) {
    pinMode(pinA[i], OUTPUT);    // Set unused analog pins as output
    digitalWrite(pinA[i], LOW);  // Set unused analog pins state to low 
  }

  for (byte i = 0; i < pinUCount; i++) {
    pinMode(pinU[i], INPUT);      // Set button pins as an input
    digitalWrite(pinU[i], HIGH);  // Enable internal pull-up resistor
  }

  pinMode(2, OUTPUT);    // Set LED pin as output

}

void Hz50() {
  // Set Pin 2 to state of LEDstate each time through the loop
  // If LEDstate hasn't changed, neither will the pin
  digitalWrite(2, LEDstate);

  // Grab snapshot of current time, this keeps all timing
  // consistent, regardless of how much code is inside the next if-statemen
  unsigned long currentMicros = micros();

  if ((unsigned long)(currentMicros - previousMicros) >= interval50) {
    if (LEDstate) {                 // Change wait interval, based on current LED state
      interval50 = offTime50;       // LED is currently on, set time to stay off
    } else {
      interval50 = onTime50;        // LED is currently off, set time to stay on
    }
    LEDstate = !(LEDstate);         // Toggle the LED's state
    previousMicros = currentMicros; // Save the current time to compare "later"
  }
}

void Hz60() {
  // Set Pin 2 to state of LEDstate each time through the loop
  // If LEDstate hasn't changed, neither will the pin
  digitalWrite(2, LEDstate);

  // Grab snapshot of current time, this keeps all timing
  // consistent, regardless of how much code is inside the next if-statemen
  unsigned long currentMicros = micros();

  if ((unsigned long)(currentMicros - previousMicros) >= interval60) {
    if (LEDstate) {                 // Change wait interval, based on current LED state
      interval60 = offTime60;       // LED is currently on, set time to stay off
    } else {
      interval60 = onTime60;        // LED is currently off, set time to stay on
    }
    LEDstate = !(LEDstate);         // Toggle the LED's state
    previousMicros = currentMicros; // Save the current time to compare "later"
  }
}

void Hz120() {
  // Set Pin 2 to state of LEDstate each time through the loop
  // If LEDstate hasn't changed, neither will the pin
  digitalWrite(2, LEDstate);

  // Grab snapshot of current time, this keeps all timing
  // consistent, regardless of how much code is inside the next if-statemen
  unsigned long currentMicros = micros();

  if ((unsigned long)(currentMicros - previousMicros) >= interval120) {
    if (LEDstate) {                 // Change wait interval, based on current LED state
      interval120 = offTime120;     // LED is currently on, set time to stay off
    } else {
      interval120 = onTime120;      // LED is currently off, set time to stay on
    }
    LEDstate = !(LEDstate);         // Toggle the LED's state
    previousMicros = currentMicros; // Save the current time to compare "later"
  }
}

void Hz150() {
  // Set Pin 2 to state of LEDstate each time through the loop
  // If LEDstate hasn't changed, neither will the pin
  digitalWrite(2, LEDstate);

  // Grab snapshot of current time, this keeps all timing
  // consistent, regardless of how much code is inside the next if-statemen
  unsigned long currentMicros = micros();

  if ((unsigned long)(currentMicros - previousMicros) >= interval150) {
    if (LEDstate) {                 // Change wait interval, based on current LED state
      interval150 = offTime150;     // LED is currently on, set time to stay off
    } else {
      interval150 = onTime150;      // LED is currently off, set time to stay on
    }
    LEDstate = !(LEDstate);         // Toggle the LED's state
    previousMicros = currentMicros; // Save the current time to compare "later"
  }
}

void Hz300() {
  // Set Pin 12 to state of LEDstate each time through the loop
  // If LEDstate hasn't changed, neither will the pin
  digitalWrite(2, LEDstate);

  // Grab snapshot of current time, this keeps all timing
  // consistent, regardless of how much code is inside the next if-statemen
  unsigned long currentMicros = micros();

  if ((unsigned long)(currentMicros - previousMicros) >= interval300) {
    if (LEDstate) {                 // Change wait interval, based on current LED state
      interval300 = offTime300;     // LED is currently on, set time to stay off
    } else {
      interval300 = onTime300;      // LED is currently off, set time to stay on
    }
    LEDstate = !(LEDstate);         // Toggle the LED's state
    previousMicros = currentMicros; // Save the current time to compare "later"
  }
}

void loop() {

  int button50 = digitalRead(3);  //read the button value
  int button60 = digitalRead(4);  //read the button value
  int button120 = digitalRead(5); //read the button value
  int button150 = digitalRead(6); //read the button value
  int button300 = digitalRead(7); //read the button value
  
  if(button50 == LOW) {           // If button on pin 2 is low
    Hz50();                       // run 50Hz code
  } else if (button60 == LOW) {
    Hz60();                       // run 60Hz code
  } else if (button120 == LOW) {
    Hz120();                      // run 120Hz code
  } else if (button150 == LOW) {
    Hz150();                      // run 150Hz code
  } else if (button300 == LOW) {
    Hz300();                      // run 300Hz code
  } else {
      digitalWrite(2, LOW);       // LED off
  }

}

Somebody out there who can measure it to confirm its right?

For fun i want to switch the color and want to use this LED:

So i calculated:
red 47ohm = 62.77 mA
blue 22ohm = 63.64 mA
green 22ohm = 63.64 mA

I always have problems reading the hFE graphs so i guess 1k is ok for a BC337-40 base resistor?
BC337 fast enough?

From a quick test with self printed 50Hz and 300Hz strobe discs and if the turntable is not totally off it looks ok.

But the RGB LED seems like is not a good idea.
I found some 16400 MCD green ones in one of my boxes:
Angle: 20°
MCD typ.: 16400 mcd
MCD max.: 20000 mcd
mA test.: 20 mA
mA typ.: 30 mA
V typ.: 3,5 V
V max.: 4,0 V

Have it running with 23.5 ohm (~65mA) and in daylight it's not very bright...

Do you have a data sheet for the green led? It may have a higher max pulse current. The data sheet for your rgb led gave a max pulse current of 100mA for 0.1ms pulses, versus the 35mA continuous current. Some I have seen in the past can take almost 5x the continuous current for 0.1ms pulses. If so, you can reduce your series resistor even further, but it would be important to fix the on time to 100us, instead of on times that are different for each frequency.

Your code is 10 times longer than the sketch from the tutorial I gave you the link to! It is very repetitive and I don't understand the purpose of some parts of it. Would you like me to suggest some changes? There is one change I want to suggest that will make the timing more accurate.

I had to wrote the seller because i have this LEDs since 8 years and he did send me the data sheet (attachment).

As code base i used this one:

LT-425.pdf (288 KB)

Those leds can tolerate 100mA for 10ms with a 1:10 duty cycle. Unfortunately, pulses that long might cause motion blur. But you could try lengthening the pulses to see how it looks. Also you could try using multiple leds. The bc337 is good for 800mA so you could wire up to 8 leds to it (each with their own series resistor).

I really have problems bringing the milliseconds and duty cycle in conjunction.
So with the settings i have now you would not reduce the series resistor?

What was the change you where talking about to make it more accurate?

MrGlasspoole:
Have it running with 23.5 ohm (~65mA) and in daylight it's not very bright...

You could push that up to 100mA by lowering the series resistor. And connect 3 or 4 leds in parallel, each with their own resistor, sinking the current from all 4 with the bc337.

I have incorporated the technique to make the timing more accurate into your code, and shortened it a little also.

#define BUTTON_COUNT 5
#define LED 2

unsigned long previousMicros = 0; // Tracks the last time event fired

// Define button pins
const byte pinU[BUTTON_COUNT] = {3, 4, 5, 6, 7};                   // Array of used digital pins (buttons)
const unsigned long interval[BUTTON_COUNT] = {1000000 / 50, 1000000 / 60, 1000000 / 120, 1000000 / 150, 1000000 / 300};

// Usual Setup Stuff
void setup() {

  for (byte i = 0; i < BUTTON_COUNT; i++) {
    pinMode(pinU[i], INPUT_PULLUP);      // Set button pins as an input and Enable internal pull-up resistor
  }
  pinMode(LED, OUTPUT);    // Set LED pin as output

}

void loop() {

  for (byte i = 0; i < BUTTON_COUNT; i++) { // check each button in turn
    if (digitalRead(pinU[i]) == LOW) { // is the button pressed ?
      if (micros() - previousMicros >= interval[i]) { // is it time to flash the led ?
        digitalWrite(LED, HIGH); // flash the led
        delayMicroseconds(100);
        digitalWrite(LED, LOW);
        previousMicros += interval[i]; // accurately calculate the time for the next flash
        if (micros() - previousMicros >= interval[i]) { // check we have not fallen behind while no button was pressed
          previousMicros = micros(); // we fell behind. catch up, exact timing does not matter.
        }
      }
    }
  }

}

So the duty cycle is now different on every frequency and is set by the delay.
And instead of defining every period in µsec the Arduino does the calculation.

Will test it later on the turntable i repaired. Somebody my parents know from the ice cream shop
gave it to them because he had no use for him and he was broken.
This thing weights 35lb: Kenwood KD 650 "The Rock" - YouTube

MrGlasspoole:
the Arduino does the calculation.

Almost certainly it doesn't. The calculation will get done by the compiler on the pc and the result gets uploaded with the sketch. At worst, the Arduino does the calculation once before the sketch starts running.

MrGlasspoole:
gave it to them because he had no use for him and he was broken.

He was broken? Poor man! Or it was broken? If you can restore it, it might be valuable. I don't know about your country, but in the UK vinyl records are increasingly back in fashion!

Something like:

const unsigned long interval[BUTTON_COUNT] = {1000000 / 50, 1000000 / 60, 1000000 / 120, 1000000 / 150, 1000000 / 300};

is calculated by the compiler? First time i read the compiler does something like that :astonished:

him: the turntable - is right?
he: the turntable - is wrong?

I belief my english is ok but its not error free. In german "it" would be something like "the baby".

This guy is the opposite from poor. He drives every year another Ferrari. He invents spray nozzles for car painting robots and Mercedes and BMW buy them.
My mother wanted a turntable again so she can listen to her records again. I guess they where talking about that and then this guy said he has one she can have.
I already restored the turntable and now need to make sure the speeds are right. Thats the reason for the stroboscope.
Back then it was an expensive turntable and today they sell from 100$ up to 400$.

Yes, compilers are quite clever. The compiler that the Arduino IDE uses, for example, is clever. It will remove whole sections of your code, if it can figure out what the result must be, at compile time.

Ah, English must be a difficult language to learn, I am glad I did it when I was too young to remember!

A turntable is an "it", not a "he", a "him", a "she" or a "her". A turntable has no genitals, it cannot marry another turntable and make baby turntables. In that way, at least, English is simple! Although sometimes something which is an "it" can be referred to as a "she" if it is old or large or people have an emotional attachment to it, for example an old ship or an old car.

Babies are always "he" or "she", but sometimes people might refer to a baby as "it" if they do not know the sex!

But usually English is confusing. For example when I said "Poor man", I meant that he was unfortunate, I did not mean that he had no money!

So does our code work OK still?

Ok had time now and it works :slight_smile:

Only thing that is not so nice are the rings and shadows you have when you shine down a LED on a white paper.
I wonder if something like those holders make it better:
LED holder
LED holder with lense

And when it comes to languages most people say German is difficult.
For "the" we have 3 words: der, die, das

The man = der Mann
The woman = die Frau
The baby = das Baby

And then we have a lot of words that you capitalize in the middle of a sentence.
For example every word where you can but "der", "die" or "das" in front will be capitalized.