rpm and PID values from DC motor (help required)

I appreciate you being open that this is a school project (many hide this fact behind the word URGENT !! :wink:

Some remarks wrt your code:

at 12000 rpm = 500 rps the counter volatile byte rpmcount;
can overflow quite fast as it can only count to 255 == ~0.25 seconds as ==> //Each rotation, this interrupt function is run twice, so take that into consideration for ).

If that is enough OK, otherwise you might increase it to unsigned long and just use the delta with the previous value.

Serial.begin(9600); ==> Serial.begin(115200); // use the highest baudrate to minimize communicating time

   // read the potentiometer:
   int sensorValue = analogRead(A0);
   // map the sensor value to a range from 0 - 255:
   int outputValue = map(sensorValue, 0, 1023, 0, 255);
   // use that to control the transistor:

   // read the potentiometer, convert it to 0 - 255:
   potValue = analogRead(potPin) / 4;

two different ways to map unto 0..255 (the latter is faster & prefered )

delay(1000);
this will block all except interrupts so it will definitely overflow ...

AND you will not compensate the RPM for one whole long second which was your goal
==> have a look at the "blink with out delay" example in the tutorial section to let your code work without using delay's will improve the realtime behaviour

   rpm = 30*1000/(millis() - timeold)*rpmcount;
   timeold = millis();
   rpmcount = 0;

make that something like (to work with delta rpmcount) The value of millis() could have incremented...

   nointerrupts();
   unsigned long temp = rpmcount;
   unsigned long t = millis();   
   interrupts();
   
   rpm = (temp - rpmold) * 30 * 1000 / (t - timeold);  
   timeold = t;
   rpmold = temp;

finally don't do detach and reattach IRQ as it is not needed to, doing the math with a delta counter is easier (you allready did it for time!)

Succes,