Turntable Digital Tachometer

Would you mind to share your code you finally used? With Code tags, of course :wink:

1 Like

Great!! Does this happen to be code sample I gave....?:relaxed:

At least two problems remain with your ISR. The shared variable is not declared 'volatile'. You have not implemented the critical section required in loop(), to ensure that an interrupt won't fire when you're reading it.

Please study and learn techniques before feeding them to beginners.

I am only trying to help - its not as if "feeding" this is going to result in a catastrophe. Of course others are welcome to post corrected versions of my code or better still their own code.

2 Likes

You're not in the least concerned that you don't know how to write an ISR? If I were alerted to such a thing, I would not be waiting for other people to produce examples. I told you what the problems were, exactly. Do you not think, it would be a good idea to research them? Correcting your own flawed code would be a perfect learning exercise.

I like this guide:
https://www.gammon.com.au/interrupts

1 Like

182

Yes! :* :* :*

//Thanks  rohitbd
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define INTERRUPT_PIN 2 //TO DO: Give the correct pin number
#define windowIntervalSec 2 //2 seconds window - increase this if the no. of markers is less for better accuracy
#define INTERRUPT_MODE FALLING //Set this to one of FALLING, RISING, CHANGE, LOW
#define NUM_MARKERS 182 //TO DO: Check this as per your setup

unsigned int numPulses = 0;
unsigned int lastMillis = 0;
unsigned int curMillis = 0;
float revPerMin  = 0.0f;
void displayResults() {
  //TO DO: Display 'revPerMin' or use it for something...
}
void interruptRoutine() {
  numPulses++;
}
void setup() {
  //  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), interruptRoutine, INTERRUPT_MODE);
  numPulses = 0;
  curMillis = lastMillis = millis();
  revPerMin  = 0.0f;
}
void loop() {
  curMillis = millis();
  if (curMillis >= lastMillis + (windowIntervalSec  * 1000)) {
    //Interval passed, time to calculate
    revPerMin = 60 * ((float)numPulses / (float)windowIntervalSec  / 182.0f);
    //Reset everything for a fresh calculation...
    lastMillis = curMillis;
    numPulses = 0;

    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
      Serial.println(F("SSD1306 allocation failed"));
      for (;;);
    }
    //************** RPM ***************************
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(5, 5); // Horizontal, Vertical
    display.println("RPM:");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(52, 5); // Horizontal, Vertical
    display.println(revPerMin);
    //************** FAKE PITCH % ******************
    display.setTextSize(1, 2);
    display.setTextColor(WHITE);
    display.setCursor(5, 25); // Horizontal, Vertical
    display.println("PITCH");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(45, 25); // Horizontal, Vertical
    display.println("+8.0%");
    display.display();
  }
  displayResults();
}

numPulses cannot be read in a single cycle, so is vulnerable to being corrupted when read or written by non-interrupt context .

It should be read and copied with interrupts disabled.

Please remember to use code tags when posting code.

1 Like

Thank you so much.
I bought the official book
and I found only scattered examples.
It is difficult to find a complete guide
starting with A and ending with Z.
Youtube tutorials instead are plans of kids who
think they are geniuses: /

In my view, Nick Gammon's tutorial leaves no stone unturned.

2 Likes

In addition to what @TheMemberFormerlyKnownAsAWOL wrote in #30:

Rewrite as

if ( (curMillis - lastMillis)  >=  (windowIntervalSec  * 1000) )

to manage overflow of millis() time (even though you need to run your turntable more than 42 days continously to do this).
Additionally there will be jitter in your measurements caused by the time to run the display. That is, your conditions is more like

if ( (curMillis - lastMillis)  ==  (windowIntervalSec  * 1000 + JitterCausedByTimeConsumedToControlMyDisplay) )

So, I would assume your measurement shows that the turntable is running faster than it should (provided it is not running too slow ... anyway, you get a positive offset in your measurement)

Thank you with all my heart :slight_smile:

//by rohitbd 2
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define INTERRUPT_PIN 2 //TO DO: Give the correct pin number
#define windowIntervalSec 2 //2 seconds window - increase this if the no. of markers is less for better accuracy
#define INTERRUPT_MODE FALLING //Set this to one of FALLING, RISING, CHANGE, LOW
#define NUM_MARKERS 182 //TO DO: Check this as per your setup

unsigned int numPulses = 0;
unsigned int lastMillis = 0;
unsigned int curMillis = 0;
float revPerMin  = 0.0f;
void displayResults() {
  //TO DO: Display 'revPerMin' or use it for something...
}
void interruptRoutine() {
  numPulses++;
}
void setup() {
  //  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), interruptRoutine, INTERRUPT_MODE);
  numPulses = 0;
  curMillis = lastMillis = millis();
  revPerMin  = 0.0f;
}
void loop() {
  curMillis = millis();
//  if (curMillis >= lastMillis + (windowIntervalSec  * 1000)) 
    if ( (curMillis - lastMillis)  >=  (windowIntervalSec  * 1000) ) // thanks @ dsebastian
{
    //Interval passed, time to calculate
    revPerMin = 60 * ((float)numPulses / (float)windowIntervalSec  / 182.0f);
    //Reset everything for a fresh calculation...
    lastMillis = curMillis;
    numPulses = 0;

    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
      Serial.println(F("SSD1306 allocation failed"));
      for (;;);
    }
    //************** RPM ***************************
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(5, 5); // Horizontal, Vertical
    display.println("RPM:");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(52, 5); // Horizontal, Vertical
    display.println(revPerMin);
    //************** FAKE PITCH % ******************
    display.setTextSize(1, 2);
    display.setTextColor(WHITE);
    display.setCursor(5, 25); // Horizontal, Vertical
    display.println("PITCH");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(45, 25); // Horizontal, Vertical
    display.println("+8.0%");
    display.display();
  }
  displayResults();
}

What are you running this on?

I don't know yet, I still have to study it.

Is there any good soul who suggests me how to make it real
the PITCH as a percentage -8.0 and +8.0%
and remove the third digit of RPM?
So I'll be able to hide that slight RPM instability;)

Last question:
In a single Arduino, is it possible to add a frequency meter to the same display?
Or does it take two Arduinos with another display?
work in progress..

//by rohitbd 2
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define INTERRUPT_PIN 2 //TO DO: Give the correct pin number
#define windowIntervalSec 2 //2 seconds window - increase this if the no. of markers is less for better accuracy
#define INTERRUPT_MODE FALLING //Set this to one of FALLING, RISING, CHANGE, LOW
#define NUM_MARKERS 182 //TO DO: Check this as per your setup

unsigned int numPulses = 0;
unsigned int lastMillis = 0;
unsigned int curMillis = 0;
float revPerMin  = 0.0f;
void displayResults() {
  //TO DO: Display 'revPerMin' or use it for something...
}
void interruptRoutine() {
  numPulses++;
}
void setup() {
  //  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), interruptRoutine, INTERRUPT_MODE);
  numPulses = 0;
  curMillis = lastMillis = millis();
  revPerMin  = 0.0f;
}
void loop() {
  curMillis = millis();
//  if (curMillis >= lastMillis + (windowIntervalSec  * 1000)) 
    if ( (curMillis - lastMillis)  >=  (windowIntervalSec  * 1000) ) // thanks @ dsebastian
{
    //Interval passed, time to calculate
    revPerMin = 60 * ((float)numPulses / (float)windowIntervalSec  / 182.0f);
    //Reset everything for a fresh calculation...
    lastMillis = curMillis;
    numPulses = 0;

    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
      Serial.println(F("SSD1306 allocation failed"));
      for (;;);
    }
    //************** RPM ***************************
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(5, 5); // Horizontal, Vertical
    display.println("RPM:");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(52, 5); // Horizontal, Vertical
    display.println(revPerMin);
    //************** FAKE PITCH % ******************
    display.setTextSize(1, 2);
    display.setTextColor(WHITE);
    display.setCursor(5, 25); // Horizontal, Vertical
    display.println("PITCH");
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(45, 25); // Horizontal, Vertical
    display.println("+8.0%");
    display.display();
  }
  displayResults();
}

Since millis() returns an unsigned long
all associated variables ought to be too.
The display doesn't necessarily have to be shoehorned into time between strobe pulses. You can grab many samples (counts), display that (and repeat). The immediate increments could be displayed along with an averaged overall figure.
A float can be configured into a string with dtostrf().

"Floating point math is also much slower than integer math in performing calculations, so should be avoided if, for example, a loop has to run at top speed for a critical timing function. Programmers often go to some lengths to convert floating point calculations to integer math to increase speed."

What are you displaying when the turntable strobe indicates a "lock" anyway? For this to be accurate it has to be good vs. a calibrated (reliable) source

    display.println(revPerMin, 1);

will print the RPM with only one trailing decimal.

1 Like

Why not? Can you see that it's possible to print almost anything you want to the display? You should because you already did.

1 Like

Dear runaway_pancake,
thank you for the valuable advice.
Unfortunately I have difficulty starting the Arduino study
because the tutorials on the WEB are all fragmented.
I also bought the official book and it is also fragmented.
Also I have to deal with the English language.

For now, unfortunately I can only "copy and paste" and often
I can't even figure out where to "paste".

"A float can be configured into a string with dtostrf ()."
for me it is hieroglyph, I don't understand it.
In practice, I can only "paste" pre-written strings.
Of course, a faster display would look great.

Locked Strobe:
The strobe, according to the MANUAL SERVICE run at 262.08 kHz (I have not yet checked if it is out of frequency)
When the POINTS / REFLECTORS (182) are "LOCKED" it indicates that
the platter runs perfectly at 33 or 45 RPM.
I hope to put a frequency meter inside this sketch, as well as make the PITCH +/- 8.0% REAL
I restored my DJ turntables from the 90s and
I would like to digitize them as much as possible :slight_smile:

Thank you very much

Locked Strobe:
The strobe, according to the MANUAL SERVICE run at 262.08 kHz (I have not yet checked if it is out of frequency)
When the POINTS / REFLECTORS (182) are "LOCKED" it indicates that
the platter runs perfectly at 33 or 45 RPM.

That frequency that you are quoting is derived from a 4.19328 MHz crystal controlled oscillator which is divided by 16.

This frequency gets reduced even more to give a final frequency of 100.1 Hz (33.3 rpm) or 135.1 Hz (45 rpm) at the LEDs.

I wouldn't bother making a frequency counter just to measure the 262.08 kHz, because there is no provision to adjust it.

I think you would be better off concentrating on getting the pitch indicator working,
Now that you have the speed, it is just a mathematical problem to calculate the percentage variation from 33.3 or 45.