Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
daniel23
Published © LGPL

Opto-isolated tachometer

Measure contactless the spindle speed with an optical reflection sensor

AdvancedFull instructions provided1,925
Opto-isolated tachometer

Things used in this project

Story

Read more

Custom parts and enclosures

PCB as .bmp files at 600DPI

PCB Copper and silkscreen for top and bottom sides in a .zip file( .bmp are at 600DPI )

Gerber files bottom (solder side)

Gerber files TOP (Top side)

Tachometer_Bill Of Material

Schematics

tachometer_v1_0_XlnAHobfrf.pdf

Code

Tachometer with 1 pulse per revolution

Arduino
iI that doesn't fit your needs, you can customize the number of pulses per turn (#define nbrPulsePerTurn), timeout (const long timeOut = 1000000 * 2;) or something else that meets your needs. The software seems sufficiently well documented to me for you to intervene in it
//         *****************************************************
//        *   Tachometer with optoisolated reflective sensor  *
//        *    Daniel Engel                    10/08/2022     *
//        *****************************************************
//
/*
   Many thanks to Oliver Kraus for his U8glib library
      https://github.com/olikraus/U8glib_Arduino
      
   and jonnieZG  for "Ewma library" (Exponentially Weighted Moving Average)
      https://github.com/jonnieZG/EWMA
*/

#include <Arduino.h>
#include "U8glib.h"     // 
#include <Ewma.h>       // 
//Ewma rpmFilter(0.1);  // Less smoothing - faster to detect changes, but more prone to noise
Ewma rpmFilter(0.01);   // More smoothing - less prone to noise, but slower to detect changes

#define nbrPulsePerTurn 1           // Number of pulse per turn (can be 1, 2, 4, 5, 10...)
const long timeOut = 1000000 * 2;   // 2 seconds => allows to measure speeds higher than 30rpm
//                                     if 1 pulse per turn (or 8RPM if 4 pulse per turn...)

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// SSD1306-I2C constructor call
//   If you owned an other OLED display you may change this line. See the examples at:
//   https://github.com/olikraus/U8glib_Arduino/tree/master/examples/

volatile long time = 0;
volatile long time_last = 0;
volatile bool intOccured = false;
int8_t nbrLoop = 3;                 // first measures are wrong. Result is ok when nbrLoop == 0
int rpm = 0;
const long displayIntervallDelay = 1000000 * 1; // intervall of display refresh
long displayIntervall_last = 0;

void setup(void) {
  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);       // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
    u8g.setHiColorByRGB(255, 255, 255);
  }
  uint8_t contrast;
  // contrast = u8g.setContrast(255);         // highest brightness ...
  contrast = u8g.setContrast(0);              // lowest brightness ...
//  Serial.begin (115200);
  pinMode(13, OUTPUT);        // LED_BUILTIN
  pinMode(2, INPUT);          // Pin 2 As Interrupt (rising edge)
  attachInterrupt(0, rpm_interrupt, RISING);
  time_last = micros();       // initialize 'time_last' for first timeout
  delay(100);
}

void loop(void) {

  static bool ledState = 0;
  int rpmAveraged = 0;
  long timeOutTime = micros();
  //
  if ((timeOutTime - time_last) >=  timeOut) {
    nbrLoop = 3;
    rpm = 0;
  }
  if (intOccured) {
    digitalWrite(LED_BUILTIN, ledState);  // sensor switching indication
    ledState = !ledState;
    nbrLoop--;
    if (nbrLoop < 0) {
      nbrLoop = 0;
    }
    intOccured = false;
  }
  if ((time > 0) && (nbrLoop == 0)) {
    rpm = ((60000000 / nbrPulsePerTurn) / (time));
    rpmAveraged = rpmFilter.filter(rpm);
  }

  //********************************************************
  //  Refreshes display at a given intervall to avoid fast blinking
  //
  long displayIntervall = micros();
  if ((displayIntervall - displayIntervall_last) >=  displayIntervallDelay) {
    //   noInterrupts();
    displayIntervall_last = displayIntervall;
    affiche(rpmAveraged);
    //   interrupts();
  }
}

//********************************************************
//    OLED display routine for RPM
//
void affiche(int x) {
  u8g.firstPage();
  do {
    //u8g.setFont(u8g_font_fur20);
    //u8g.setFont(u8g_font_gdb25n);
    //u8g.setFont(u8g_font_profont29);
    u8g.setFont(u8g_font_helvB24);
    char buf[5];
    sprintf (buf, " %4d", x);
    u8g.drawStr(0, 32, buf);
    u8g.setFont(u8g_font_fub11);
    u8g.drawStr( 90, 32, "RPM");
  } while (u8g.nextPage());
}




//********************************************************
//    Interrupt routine
//
void rpm_interrupt() {
  time = (micros() - time_last);
  time_last = micros();
  intOccured = true;
}

Credits

daniel23
8 projects • 10 followers

Comments