I'm trying to build a switch based on rpm but am having trouble with the reading.
The circuit already has a low pass filter but there is still the occasional bit of noise messing things up so I've started making a filter in code.
The problem is when the RPM starts from zero it only reads about half of what it should. But not always, sometimes it works as it should.
You can see this in the two serial prints.
I'm fairly certain it's something to do with the timing but I can't figure out what.
The rpm code works nicely until I try to implement the filter in the ISR filterTime = Interval * 0.75;
const byte rpm_in = 2;
volatile unsigned int rpm = 0; // Initial rpm value
volatile unsigned int cur_rpm = 0;
volatile unsigned int actual_rpm = 0;
unsigned int pulsesPerRev = 8;
unsigned long timeout = 0;
volatile unsigned long filterTime = 0;//Filters for rpm reading
unsigned long timeNow = 0;
volatile unsigned long Interval = 0;
unsigned long lastPulseTime = 0;
unsigned long secondPulseTime = 0;
volatile unsigned long secondInterval = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(rpm_in, INPUT); //Interrupt
attachInterrupt(digitalPinToInterrupt(rpm_in), rpm_interrupt, FALLING);
}
void loop() {
noInterrupts();
cur_rpm = rpm;
timeout = micros() - timeNow;
if (timeout > 400000)
{
cur_rpm = 0;
}
interrupts();
Serial.print("Current RPM = ");
Serial.println(cur_rpm);
Serial.print("Actual RPM = ");
Serial.println(actual_rpm);
}
void rpm_interrupt()
{
timeNow = micros();
Interval = timeNow - lastPulseTime;
if (Interval >= filterTime)
{
filterTime = Interval * 0.75;
rpm = 60000000UL / (Interval * pulsesPerRev);
lastPulseTime = timeNow;
}
secondInterval = timeNow - secondPulseTime;
actual_rpm = 60000000UL / (secondInterval * pulsesPerRev);
secondPulseTime = timeNow;
}
Perhaps it would help if you posted a schematic diagram of your circuit. A photo of a hand drawn schematic will suffice if you don’t have a schematic capture program.
Thanks for the replies, I'd like to figure out what the issue is with my code before I implement a solution from somewhere else otherwise I won't learn from my mistakes
What is your theory about how the "filter" should work? Sorry, but I can't make any sense of the code.
Standard low pass filter approaches that work well include averaging a few independent measurements, a moving average, or the exponential filter mentioned above, which is also the simplest to implement.
Interval is the time between pulses.
Whenever the time between pulses is > filterTime, the filterTime is recalculated to be 75% of that time period. So that on the next time through if noise creates a false interrupt before 75% of the previous period has elapsed the if() statement ignores it and waits for a interrupt that has been longer than 75% of the previous period.
Edit: I can't use an average as it would reduce the accuracy of the switch that is getting added later
This would be a very good place to start, have you got pullup or pulldown resistors in your RPM sensor?
Can you please post a link to specs/data of your RPM sensor?
Since your filtering approach was to smooth out the occasional bit of noise messing things up, I would assume that this noise would always occur near the first falling edge of the interval.
In this case, a debounce approach would be more appropriate as it applies an ignore period for the invalid transitions.
If the transitions occur randomly within the interval, then this indicates a hardware wiring, component or circuit issue or perhaps interference. In this case filtering would be band-aid improvement ... it would be much better to track down and resolve it.
If the rpm value jitters due to resolution or other inherent variables, then filtering could be a good solution, but I couldn't find anything indicating that this is the issue.
If you mean the one from post #4, it looks good to me. 1KHz input = 60,000 pulses / minute
With 8 pulses per revolution (as per your code), RPM = 60000 / 8 = 7500.
Yeah, I started with that and found the startup issue as described. Other than the possibility of noisy transitions (you can use a debounce method for this), you'll need to look at implementing RPM limits (min, max).
From here, Accessing multibyte global variables in a 8-bit system Using multibyte global variables in an 8-bit system requires careful attention because multibyte variables are read byte-by-byte. Care needs to be taken that the ISR does not occur and hence modify the variable when one or more bytes of the multi-byte variable have already been read but the read has not been completed. This would lead to data corruption.
So doing something like this when reading multi-byte variables is important:
I'd need to recalculate the debounce time at every interrupt I think?
As at a low rpm the debounce represents a small percentage of the time between interrupts say 25%. But for higher rpm the same debounce period might be 90% of the time between interrupts which would miss a reading if the motor accelerated in that time period
A single constant for the debounce interval is all that's needed.
Take the highest RPM you would ever expect or want the motor to run at, then multiply by 5.
For example, say this represents 120,000 RPM, then the debounce interval could be set to 500μs
Remember that with debounce, the very first transition is recognized and triggers the interrupt. All extra transitions (interrupts) within the debounce period are ignored. The debounce interval does not affect accuracy of your RPM measurement, its the first part of it and is included.