So I am currently trying to get the RPM from a crankshaft sensor in a vehicle. It uses a 5v hall sensor which produces a square wave output, the hall sensor is pointed at a metal wheel with 36 evenly spaced teeth, but one of the teeth is ground down/cut off/missing. (known as a missing tooth wheel, 36-1) To get a pretty signal for bench testing, I'm using Ardustim on another Arduino to generate the signal.
When the wheel spins, The missing tooth section is used by the original ECU to calculate what degree of rotation the engine is currently in, in order to accurately trigger ignition/fuel injection events. I don't actually care about doing any of this, I just want a way of converting the pulses to a revolutions per minute so I can log the data to pc/sd card etc.
My current method was taken from some code posted years ago on this forum, we define a 'Multiplier' number which uses the following calculation: Microseconds in a minute / the number of pulses in one full revolution. So 60,000,000 / 36 = 1666666.66666666667
It then uses a rising tooth trigger ISR to calculate the time between the start of one tooth to the next, and then divide the multiplier by delta Time = RPM.
This works well until the missing tooth comes around, the delta T is doubled and RPM is halved (double duration between teeth). Ideally i would like to be able to write the code to deal with the missing tooth properly, maybe by counting 35 teeth and then skipping a count? Easy way out is just by deleting the rows of data afterwards, but that's a cheat. I thought about maybe doing an if statement in the ISR, and checking if the deltaT = 2 * the last deltaT, and then resetting the tooth counter to 0 for counting to 35 teeth? But I wasn't too sure if that would make the ISR too long... I can do the basics for Arduino but never really played with this stuff too much haha!
Any help is much appreciated!! Even if it's just a pointer to somewhere that may help me out haha!
Sample serial output:
ToothDeltaT,1256.00
RPM,1326.96
ToothDeltaT,1256.00
RPM,1335.47
ToothDeltaT,1256.00
RPM,1335.47
ToothDeltaT,2512.00
RPM,663.48
ToothDeltaT,1248.00
RPM,1326.96
ToothDeltaT,1252.00
RPM,1322.75
ToothDeltaT,1256.00
RPM,1326.96
#define ClockPin 2 // Must be pin 2 or 3
// Multiplier = (60 seconds * 1000000 microseconds) microseconds in a minute / the number of potential pulses in 1 full revolution)
// My engine has a 36-1 toothed wheel, so 60,000,000 / 36 = 1,666,666.666666667
#define Multiplier 1666666.66666666667 // don't forget a decimal place to make this number a floating point number
volatile long count = 0;
volatile long EncoderCounter = 0;
volatile float SpeedInRPM = 0;
volatile float ToothGap = 0;
void onPin2CHANGECallBackFunction(){
static uint32_t lTime; // Saved Last Time of Last Pulse
uint32_t cTime; // Current Time
cTime = micros(); // Store the time for RPM Calculations
int32_t dTime; // Delt in time
// calculate the DeltaT between pulses
dTime = cTime - lTime;
lTime = cTime;
SpeedInRPM = Multiplier / dTime; // Calculate the RPM Switch DeltaT to either positive or negative to represent Forward or reverse RPM
ToothGap = dTime;
}
void setup() {
Serial.begin(115200); //115200
// put your setup code here, to run once:
pinMode(ClockPin, INPUT);
attachInterrupt(digitalPinToInterrupt(ClockPin),onPin2CHANGECallBackFunction,RISING);
}
void loop() {
// long Counter;
float Speed;
float toothDeltaT;
noInterrupts ();
// Because when the interrupt occurs the EncoderCounter and SpeedInRPM could be interrupted while they
// are being used we need to say hold for a split second while we copy these values down. This doesn't keep the
// interrupt from occurring it just slightly delays it while we maneuver values.
// if we don't do this we could be interrupted in the middle of copying a value and the result get a corrupted value.
Speed = SpeedInRPM;
toothDeltaT = ToothGap;
interrupts ();
Serial.print("RPM,");
Serial.println(Speed);
Serial.print("ToothDeltaT,");
Serial.println(ToothGap);
}