 double Kp = 0.02;//0.06;
 double Ki = 0.05;//0.05;
 double Kd = 0.0005;//0.0005;
-//double OS = 18;
-//double BrkPt = 22;
-//double X = 0.0;
 double PIDOut = 0.0;
-double Max = 254.0;
-double Min = 0.0;
+double Max = 254.0; //max allowed PWM value
+double Min = 1.0; //min allowed PWM value
 int SampleTime = 60;// measured in milliseconds [FWIW: 10,000 RPM measured over 10 revolutions = 60 ms]
 int MstrSampleTime = SampleTime;
 

// ================================================================

// SpindleTachInterrupt:  called every time an event occurs

void SpindleTachInterrupt(void)
{
   int ThisRPM = 0;
   double pulseperiod = 0;
//   if (Startup>0) // PID Lock out counter
//   {
//     LoopCounter = 1;
//     Startup -= 1;
//     return;
//   }
   if (eventCounter == 0)
     {
      Start = micros();//gettimeofday(&Start, NULL);
     }
   if (eventCounter == 10)
     {
      Stop = micros();//gettimeofday(&Stop, NULL);
     }
   eventCounter +=1;//eventCounter++;
///////////////////////////////////////////////////////////////////////////////
    if (eventCounter < 11)// Let's see if there's enough samples to calc the current RPM
     {
      if (eventCounter < 3)
       {
        return; //No, there isn't enough samples
       }
      else
       {
        // yes there is, but does it seem like a reasonable value 
        Stop = micros(); //gettimeofday(&CalcStrt, NULL);
        //pulseperiod = ((Stop.tv_sec-Start.tv_sec)*1000000ULL+(Stop.tv_usec-Start.tv_usec));
        pulseperiod = (Stop-Start);
        ThisRPM  = (60000000*(eventCounter-1))/pulseperiod;
        if (ThisRPM <1200 || ThisRPM > 4200)
         {
          return; // its too slow, or its going fast enough that we need to go for a full ten  
                  // samples before deciding what to do with it
         }
       }
     }
   if (eventCounter >= 11 || ThisRPM >0 )// There's enough samples to calc the current RPM
     {
      if (eventCounter >= 11)
       {
        pulseperiod = (Stop-Start);
        calcrpm = 600000000/pulseperiod;// calculate rpm based on having measured the period of 10 revolutions
       }
       else
       {
        calcrpm = ThisRPM;
       }

      SpeedChange =  calcrpm -  LastRPM;
      //LastRPM = curspeed;
      LastRPM = calcrpm; 
      eventCounter = 0;
      LoopCounter = 1;
      //return; // temp stop for testing just the Tachometer portion of the code
     
      if (pulseperiod<0 )//|| pulseperiod > 600000
       {
         sprintf (buf, "Pulse Period Err");
         //Serial1.println(buf); 
        return; // disregard this result; it appears to be bogus
       }
      
      SampleTime = MstrSampleTime; 
      SetTunings(Kp, Ki, Kd);
      SpndlPWM = CalcMtrPID(calcrpm);//Max+CalcMtrPID(calcrpm);
      analogWrite(PWMpin, SpndlPWM);
      dutycycle = (int)((SpndlPWM*100)/(Max));//(int)((SpndlPWM*100)/(2*Max));// convert PWM signal to a precentage
     }

   }
// ================================================================
// ===               END INTERRUPT DETECTION ROUTINE            ===
// ================================================================

This Program when loaded on an Arduino/Leonardo is a Tach based speed controller.
It was originally written to operate a 400W/48VDC CNC spindle motor through
a simple electronic controller, readily found on E-Bay
This program assumes that the +PWM lead of the controller is connected to
pin D13 of the Leonardo (or Pin D11 on the UNO), and the collector output of the speed sensor 
[OPB704 is a direct pin-for-pin replacement for the once popular,
but now discontinued, QRB1114; the Parallax Inc 550-27401 or FairChild qrd1114
might also maybe adapted to work] is connected to pin D3. 
Note this collector is also tied +5Volts through a 20K pull-up resistor.
It Furhter assumes that a Sansmart 16x2 LCD/KeyPad sheild has been installed.
This display & keypad provides a simple user interface, where the Up/Down buttons
can be used to ramp the Set Speed from 1400 to 10,000 RPM, in steps of 200 RPM, 
while the Left/Right Buttons will select the display mode.

Note: The LCDKeypadR1 Library, contained in this file set, 
is a derivative of the files found here:

http://sainsmart.com/zen/documents/20-011-901/keypad_lcd.zip

