File is a plain txt file, I couldn't upload as an .ino file.
No snickering or snide comments about my coding is allowed, I'll start to cry. I'd be happy to have a better programmer clean up this code, hint hint...
And no whining about my using two pots to select cutter diameter and material, it's cheap, simple to code, and means each dial only has one function, a defining characteristic of good HMI design. I could have changed it to a rotary encoder, but Arduino nanos only have two interrupt pins, so it would need a menu system and multiple steps to select inputs, so why bother?
My mill has a 16-hole disc on the quill, just change the RPM Array formula to match the number of pulses available per revolution. And any IR sensor will work, I first set it up with a reflective sensor and a single chunk of white tape on the quill.
My mill tops out a 2000 RPM, so my <cal rpm> function displays ">2000" for any calculated rpm values above 2000, no point in displaying something my mill can't do. YMMV
There is no code restrictions on the actual tachometer display, I've successfully tested this at 5,000 RPM with good results.
/*
Modified from:
Author: Chris @ PyroElectro.com
Date: 8/26/2012
Description:
This project is meant to capture interrupt counts from an IR breakbeam circuit
and display them as an RPM number on a 16x2 LCD module.
Full Project Details:
http://www.pyroelectro.com/tutorials/tachometer_rpm_arduino/
components
Arduino Nano
Nano screw terminal board
20x4 LCD with I2C interface
Two 10K pots
LM393 slot-type iR speed sensor module
This speed sensor has an optical sensor that allows you to measure revolution per minute (RPM).
x2robotics.ca
USB power adapter
case, etc.
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// SDA pin A4
// SCL pin A5
volatile float time = 0;
volatile float time_last = 0;
volatile int rpm_array[7] = {0, 0, 0, 0, 0, 0, 0};
void setup()
{
//Digital Pin 2 Set As An Interrupt
attachInterrupt(0, fan_interrupt, FALLING);
// set up the LCD's number of columns and rows:
lcd.begin();
// Print the main header to the LCD.
lcd.setCursor(0, 0);
lcd.print("Current RPM: ");
}
void loop()
{
int rpm = 0;
while (1) {
//Slow Down The LCD Display Updates
delay(500);
// fill array of cutter diameters
float CutterSixteens[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 40, 48, 64}; // cutter diamters in sixteenths
char* CutterNames[] = {"1/16\" ", "1/8\" ", "3/16\" ", "1/4\" ", "5/16\" ", "3/8\" ", "7/16\" ", "1/2\" ", "9/16\" ", "5/8\" ", "11/16\"", "3/4\" ", "13/16\"", "7/8\" ", "15/16\"", "1\" ", "1-1/4\"", "1-1/2\"", "2\" ", "2-1/2\"", "3\" ", "4\" "}; // cutter descriptions in text
int HSSsfm[] = {300, 150, 100, 80, 50, 90, 80, 40, 50, 60, 40, 175, 500}; // book values for SFM for materials
// Set cutter diameter
int CutterDial = analogRead(A2); // 10K pot wiper to analog Pin 2
int Cutter = map(CutterDial, 0, 1023, 0, 21); // maps pot to 1 of 12 cutter diamters
char* MatNames[] = {"ALUMINUM ", "BRASS ", "COPPER ", "SOFT IRON ", "HARD IRON ", "MILD STEEL ", "CAST STEEL ", "ALLOY STEEL", "TOOL STEEL ", "S-STEEL 303", "S-STEEL 316", "PLASTICS ", "WOOD "};
// Set material
int MatList = analogRead(A1); // 10K pot wiper to analog Pin 1
int Material = map(MatList, 0, 1023, 0, 13); // maps pot to 1 of 14 material types
float SFM = (((CutterSixteens[Cutter] / 16) * rpm) / 3.82);
int smoothedSFM = SFM * .2; // divided by five to get to multiples of five
int displaySFM = smoothedSFM * 5; // restores by 5 to get back to rounded x 5 sfm
// cutter rpm = sfm x 3.82 / cutter diameter in inches
// sfm = rpm x cutter diameter in inches / 3.82
// cutter diamters from array is in sixteenths
int HSBookRPM = ((HSSsfm[Material]) * 3.82 / ((CutterSixteens[Cutter]) / 16));
int calmeddisplayRPM = rpm /5; // divided by five to get to multiples of five
int displayrpm = calmeddisplayRPM * 5; // restores by 5 to get back to rounded x 5 rpm
//Clear The Bottom Row
lcd.setCursor(13, 0);
lcd.print(" ");
//Update The Rpm Count
lcd.setCursor(13, 0);
lcd.print(displayrpm);
lcd.setCursor(0, 2); // prints out cutter size on line 3
lcd.print(CutterNames[Cutter]);
lcd.setCursor(7,2);
lcd.print("cal rpm "); //prints out calculated rpm and restricts to 2000 max
if (HSBookRPM > 2000){
lcd.print(">2000");
}
else {
lcd.print(HSBookRPM);
lcd.print(" ");
}
lcd.setCursor(0, 1);
lcd.print("Current SFM: ");
lcd.setCursor(13, 1);
if (displaySFM <5){
lcd.print("<5");
}
else {
lcd.print(displaySFM);
lcd.print(" ");
}
lcd.setCursor(0, 3); // prints material name on line 4
lcd.print(MatNames[Material]);
lcd.print(" "); //kills trailing zero
lcd.setCursor(12,3); // prints material sfm on line 4
lcd.print("sfm ");
lcd.print(HSSsfm[Material]);
lcd.print(" ");
//Update The RPM
if (time > 0)
{
//5 Sample Moving Average To Smooth Out The Data
rpm_array[0] = rpm_array[1];
rpm_array[1] = rpm_array[2];
rpm_array[2] = rpm_array[3];
rpm_array[3] = rpm_array[4];
rpm_array[4] = rpm_array[5];
rpm_array[5] = rpm_array[6];
rpm_array[6] = 60 * (1000000 / (time * 16)); //(time x pulses per revolution) - mill quill has 16 perforated holes around circumference
//Last 5 Average RPM Counts Eqauls....
rpm = (rpm_array[0] + rpm_array[1] + rpm_array[2] + rpm_array[3] + rpm_array[4] + rpm_array[5]+ rpm_array[6])/ 7;
}
}
}
void fan_interrupt()
{
time = (micros() - time_last);
time_last = micros();
}