I have trouble understanding why I am not able to extract the data in real time using this code. I am not sure what is happening. I tried looking up the web, but I didn't find anyone who had this problem. Cattledog and jremington helped me to set up the sensor and everything with the code. Go to this link to see what we have done. here
So, coming to the problem
#define pii 3.1415926535
byte countsPerRevolution = 113;
float rpm_exponentialavg = 0, sincoeffavg = 0, coscoeffavg = 0, thirdOrder, freq;
float rpm, Inst_speed, sinval, cosval, sincomp, coscomp;
volatile unsigned long countTeeth = 0;
volatile boolean finishCount = false;
volatile long period = 0;
long copy_period;
volatile unsigned long time_last;
volatile unsigned long interruptCount;
unsigned long copy_interruptCount;
void setup()
{
Serial.begin(115200);
Serial.println("start...");
attachInterrupt(digitalPinToInterrupt(3), pulsePeriod, RISING);//interrupt on pin3
}
void loop()
{
while (1)//remove overhead of loop()
{
if (finishCount == true)
{
finishCount = false;//reset flag
detachInterrupt(0);
// disable interrupts, make protected copy of time values
copy_period = period;
copy_interruptCount = interruptCount;
attachInterrupt(0, pulsePeriod, RISING);
freq = 1000000.0 / copy_period;
rpm = (freq * 60) / 113; // to calculate the rpm of the flywheel
rpm_exponentialavg = rpm_exponentialavg * 0.99115044 + rpm * 0.00884956; // running average of the rpm being calculated
Inst_speed = rpm_exponentialavg - rpm; // to calculate the instantaneous speed of the flywheel for every tooth
sinval = sin(2 * pii * (float)countTeeth / 113.0); // to calculate the sine of angle between the teeth in radians.
cosval = cos(2 * pii * (float)countTeeth / 113.0); // to calculate the cosine of angle between the teeth in radians.
countTeeth++; // increment the counter for every tooth passing.
sincomp = sinval * Inst_speed; // to calculate the sine component to perform fourier transformation
coscomp = cosval * Inst_speed; // to calculate the cosine component to perform FT.
// to calculate the running averages of sine and cosine, we get
sincoeffavg = sincoeffavg * 0.99115044 + sincomp * 0.00884956;
coscoeffavg = coscoeffavg * 0.99115044 + coscomp * 0.00884956;
thirdOrder = 2 * sqrt(sincoeffavg * sincoeffavg + coscoeffavg * coscoeffavg) * (2 * pii / 60) * rpm_exponentialavg * 6 * pii / 60;
//debug prints
Serial.println(copy_period);
Serial.print(";");
Serial.print(copy_interruptCount);
}
}
}
void pulsePeriod()//simplify isr
{
period = (micros() - time_last);
time_last = micros();
finishCount = true;
interruptCount++;
}
When I print the interrupt count and pulse period. This is what I see.
If you see from the results it is clear that I am not able to extract data of each pulse periods. There is some difference between each interrupt count. I want to understand why this is happening. I anticipated that I will be able to extract each data in real time. That's not the case. Assuming my frequency is varying between 2200 to 3500hz. Arduino should be capable of doing it.
Thanks in advance for your help.
I really want some suggestions from you guys on this.
Suggestions recieved so far:
Cattledog suggested me to buy new microcontroller with high clock speed.
Your ISR just needs to record the value of micros() and the fact that the ISR has occurred. Then the code in loop() can calculate the number of µsecs between pulses. Whether it is necessary to convert that to RPM (or anything else) depends on the application. But get the simple µsec per pulse reading working first.
This may get you started. It compiles but I have not tested it.
byte pulsePin = 2;
unsigned long prevPrintMillis;
unsigned long printIntervalMillis = 1000;
unsigned long prevPulseMicros;
unsigned long newPulseMicros;
volatile unsigned long latestPulseMicros;
volatile boolean newPulse = false;
unsigned long microsBetweenPulses;
void setup() {
Serial.begin(9600);
Serial.println("Starting my program");
pinMode(pulsePin, INPUT_PULLUP);
attachInterrupt(0, pulseDetect, RISING);
}
void loop() {
if (newPulse == true) {
prevPulseMicros = newPulseMicros;
noInterrupts();
newPulseMicros = latestPulseMicros;
newPulse = false;
interrupts();
microsBetweenPulses = newPulseMicros - prevPulseMicros;
}
if (millis() - prevPrintMillis >= printIntervalMillis) {
prevPrintMillis += printIntervalMillis;
Serial.print("Micros Between Pulses ");
Serial.println(microsBetweenPulses);
}
}
void pulseDetect() {
latestPulseMicros = micros();
newPulse = true;
}
Robin2:
Your ISR just needs to record the value of micros() and the fact that the ISR has occurred. Then the code in loop() can calculate the number of µsecs between pulses. Whether it is necessary to convert that to RPM (or anything else) depends on the application. But get the simple µsec per pulse reading working first.
My problem is not interrupt not being fast enough or the precision of the measured period of the pulse not good enough.
As you can see from the above post that interrupt service does gets incremented but it's just data is coming so fast, So I am not able to pull all the data from the buffer to do some calculations.
Btw, I used array to store the incoming data, and then I used that data to do calculation in excel and I was able to get all the periods of continuous signal.
I appreciate for your help in providing the code. But, you are calculating for a fixed gate time of 1000ms. In my case I want to calculate each period of continuous signal, because I will be controlling clutch shifting in real time. The main problem in my application is frequency modulating that's why I need to measure period of each pulse rather than fixed gate time.
This is my hypothesis. I think arduino uses FIFO buffer. Since, the data coming in is so fast. That data overides the buffer or the data changes its position in buffer.
I hope you understand what I am saying. I am not sure if my hypothesis is correct? what do you think? If you think you have a better hypothesis I would really like to listen.
AWOL:
My first instinct would be to ditch the floating point calculations
I ditched all the floating point calculations. It didn't improve much. My interrupt is increments every 2 counts for each measurement.
Note: I didn't miss any pulses FYI, I just have some problem with extracting the data. However, If I missed the pulses the increment counter wouldn't increment in the first place.
This is my hypothesis. I think arduino uses FIFO buffer. Since, the data coming in is so fast. That data overides the buffer or the data changes its position in buffer.
There is no buffer. The pulse intervals are being calculated and they are not buffered or stored. You can not transfer the time data from the ISR, process it, and print it out in the time before the next pulse triggers the interrupt.
My bad I was tinkering with the code. I just need a small clarification on noInterrupts() & detachInterupt() do they have similar purpose? Sorry, if I am dumb I am from mechanical backgorund lately starting to understand how arduino works.
This is the code without floating point calculations.
#define pii 3.1415926535
//volatile byte count = 0;
//byte numCount = 1; //number of pulse intervals to measure
byte countsPerRevolution = 113;
long freq;
long rpm;
volatile unsigned long countTeeth = 0;
volatile boolean finishCount = false;
volatile unsigned long period = 0;
unsigned long copy_period;
volatile unsigned long time_last;
volatile unsigned long interruptCount;
unsigned long copy_interruptCount;
void setup()
{
Serial.begin(115200);
Serial.println("start...");
attachInterrupt(digitalPinToInterrupt(3), pulsePeriod, FALLING);//interrupt on pin3
}
void loop()
{
while (1)//remove overhead of loop()
{
if (finishCount == true)
{
finishCount = false;//reset flag
// disable interrupts, make protected copy of time values
noInterrupts();
copy_period = period;
copy_interruptCount = interruptCount;
interrupts();
freq = 1000000 / period;
rpm = (freq * 60) / 113; // to calculate the rpm of the flywheel
//debug prints
Serial.print(rpm);
Serial.print(" ");
Serial.println(copy_interruptCount);
}
}
}
void pulsePeriod()//simplify isr
{
period = (micros() - time_last);
time_last = micros();
finishPulse = true;
interruptCount++;
I made some changes to the code and removed some lines. Which is not primary importance.
allanhurst:
Even at 115200 baud each character you print takes abut 80uS.
Could that be eating too much time?
I've used the pulseIn() function for a similar job without problems.
Allan
I used pulseIn() which didn't give me better readings. My readings were off by + or - 50hz.
avr_fred:
You do not want to detach and then re-attach a pin change interrupt. Attach it and leave it, there is absolutely no reason to detach.
Please post your current code without floating point. Also, what is the maximum rpm of the flywheel?
--> I changed the detach interrupt according to your suggestion. The maximum rpm of the fly wheel can be 1600RPM. Fly wheel has 113 teeth, so (1600/60) * 113 which gives teeth frequency of 3000 hz.
I really appreciate for providing solutions. But, at this moment I want to know why this is happening so I can do some literature research, because right know, I dont know where I should look into. A hint can be helpful.
meme:
My problem is not interrupt not being fast enough or the precision of the measured period of the pulse not good enough.
You have not provided some basic data ...
How fast is the wheel rotating?
How many pulses do you get per revolution?
If the interval between pulses is short you could have the ISR count to (say) 8 or 16 and only take the time then so that the interval is the interval between 8 (or 16) pulses. I suggest 8 or 16 because it makes the maths easy for the Arduino.
If the interval between pulses is short you could have the ISR count to (say) 8 or 16 and only take the time then so that the interval is the interval between 8 (or 16) pulses. I suggest 8 or 16 because it makes the maths easy for the Arduino.
...R
The flywheel can rotate between 1200RPM to 1600 RPM, it has 113 teeth on it. So, I get 113 pulses per rev, if you convert it into frequency which is about 2260 hz to 3000 Hz.
Since, my signal has frequency modulation --> the time period of each and every pulses(113) changes for each revolution. I wish it was simple. So, it is critical for me to get the data of all pulses. Assuming arduino clock speed of 16Mhz, it shouldn't be hard. I think we are missing something --> can't wait to find out what it is.
P.S
Dammit my professor doesn't even give me time to figure out the problem regarding this. He wants a resonable explanation. Since, I am from mechanical background, Whatever you guys suggest me I'll do it. And, I will try to understand it.
At 3000 pulses per second there will be about 330 microsecs between pulses which is not enough time to do any complex calculations.
The whole purpose of a flywheel is to eliminate fluctations in the rotational speed. I suspect it would be perfectly adequate to measure the time after every 16 pulses - about 5000 µsecs. That is still about 7 times per revolution. (Indeed measuring the time after every 32 pulses would probably be sufficient). I have a small DC motor (in a 00 Gauge model train) and I can control the speed at 12,000 RPM within a few percent with a single pulse per revolution using code like I posted earlier.
So my suggestion is to use the ISR to count pulses and every 16 pulses record the value of micros() and set the variable newPulseTime to true.
AllanHurst nailed it for you. That 330 usec/pulse is not long enough to process your serial prints. The latest code you're using will print ~10 characters which will take more than 860 usec to print.
I'd suggest that to prove it you get rid of the printing and store the data in an array. Collect some readings (a hundred?), detach the interrupt and then print the results. Initially, just store the period in the array and avoid any calculations. I suspect that once the the slow serial stuff is gone there will be enough time for your integer math operations, but I'd leave it out initially to verify you can keep up.
wildbill:
Collect some readings (a hundred?), detach the interrupt and then print the results.
That is another way of doing things.
But it depends on what the OP wants to do with the data. I have been assuming he wants to control the speed and the printing is just for debugging purposes.
Robin2:
At 3000 pulses per second there will be about 330 microsecs between pulses which is not enough time to do any complex calculations.
...R
That really makes sense to me . Okay, I will try to increase the interval to 16 or 32 pulses as you suggested and I will see how it goes.
Basically, you guys are saying that in real time it's hard to keep up.
wildbill:
AllanHurst nailed it for you. That 330 usec/pulse is not long enough to process your serial prints. The latest code you're using will print ~10 characters which will take more than 860 usec to print.
I'd suggest that to prove it you get rid of the printing and store the data in an array. Collect some readings (a hundred?), detach the interrupt and then print the results. Initially, just store the period in the array and avoid any calculations. I suspect that once the the slow serial stuff is gone there will be enough time for your integer math operations, but I'd leave it out initially to verify you can keep up.
This makes complete sense to me. Thank you for wonderful explanation --> @allanhurst and @wildbill. So, serial print is the culprit here. Even cattle dog suggested me that without serial print it should be okay.
Suggestion Bucket list:
Buy arduino due which has 84mhz clock speed, there might be chance. --> do you think this might work? If my professor is being naive and selfish that he wants each period.
Instead of doing it for every pulse do it for 16 or 32 pulses. --> if he is not being selfish.
Thanks guys.
But one more clarification what happens if I increase the baud rate does it effect?
If you can send the data in raw format instead of human readable form you can send the data much faster.
Also in your counting you may (will) get jitter from other interrupts - millis and Serial related at least. If it turns out to be a problem you may use Input Capture feature of Timer/Counter 1. I am not sure if there is some ready to use library for this but it is not so difficult with Datasheet's aid.
The only reason to use the serial printing is to confirm that you are not missing any interrupts and that your calculations make sense.
I would benchmark your desired calculations (remove floats if you can) to see if they, and the response to them(shifting?), can be done in the 300us you have.
You can confirm this only printing the difference in interrupt count, which should only be '1' if you zero the count each time it is read. Set the serial baud rate to 250000 in monitor and sketch for fastest output.
Once you gain confidence in the code, you can implement without the serial output, and perhaps add some error checking on the interrupt count.
I also think that tightening up the ISR with only one call to micros() and calculating period in loop as Robin2 suggests is good practice.
#define pii 3.1415926535
byte countsPerRevolution = 113;
float rpm_exponentialavg = 0, sincoeffavg = 0, coscoeffavg = 0, thirdOrder, freq;
float rpm, Inst_speed, sinval, cosval, sincomp, coscomp;
volatile unsigned long countTeeth = 0;
volatile boolean finishCount = false;
//volatile long period = 0;
unsigned long period;
//long copy_period;
//volatile unsigned long time_last;
unsigned long time_last;
volatile unsigned long interruptTime;
unsigned long copy_interruptTime;
volatile unsigned long interruptCount;
unsigned long copy_interruptCount;
void setup()
{
Serial.begin(250000);
Serial.println("start...");
attachInterrupt(digitalPinToInterrupt(3), pulsePeriod, RISING);//interrupt on pin3
}
void loop()
{
while (1)//remove overhead of loop()
{
if (finishCount == true)
{
finishCount = false;//reset flag
//detachInterrupt(0);
// disable interrupts, make protected copy of time values
noInterrupts();
//copy_period = period;
copy_interruptTime = interruptTime;
copy_interruptCount = interruptCount;
interruptCount = 0;
//attachInterrupt(0, pulsePeriod, RISING);
interrupts();
period = (copy_interruptTime - time_last);
time_last = copyInterruptTime;
//freq = 1000000.0 / copy_period;
freq = 1000000.0 / period;
rpm = (freq * 60) / 113; // to calculate the rpm of the flywheel
rpm_exponentialavg = rpm_exponentialavg * 0.99115044 + rpm * 0.00884956; // running average of the rpm being calculated
Inst_speed = rpm_exponentialavg - rpm; // to calculate the instantaneous speed of the flywheel for every tooth
sinval = sin(2 * pii * (float)countTeeth / 113.0); // to calculate the sine of angle between the teeth in radians.
cosval = cos(2 * pii * (float)countTeeth / 113.0); // to calculate the cosine of angle between the teeth in radians.
countTeeth++; // increment the counter for every tooth passing.
sincomp = sinval * Inst_speed; // to calculate the sine component to perform fourier transformation
coscomp = cosval * Inst_speed; // to calculate the cosine component to perform FT.
// to calculate the running averages of sine and cosine, we get
sincoeffavg = sincoeffavg * 0.99115044 + sincomp * 0.00884956;
coscoeffavg = coscoeffavg * 0.99115044 + coscomp * 0.00884956;
thirdOrder = 2 * sqrt(sincoeffavg * sincoeffavg + coscoeffavg * coscoeffavg) * (2 * pii / 60) * rpm_exponentialavg * 6 * pii / 60;
//debug prints
//Serial.println(copy_period);
// Serial.print(";");
Serial.println(copy_interruptCount);
}
}
}
void pulsePeriod()//simplify isr
{
interruptTime = micros();
// period = (interruptTime - time_last);
// time_last = interruptTime;
finishCount = true;
interruptCount++;
}
Buy arduino due which has 84mhz clock speed, there might be chance.. --> do you think this might work?
As you have never given a description of your project how can anyone answer that. There have been several suggestions that would each be valid in different circumstances.
Robin2:
As you have never given a description of your project how can anyone answer that. There have been several suggestions that would each be valid in different circumstances.
...R
Well, clutch shifting(automatic) in real time with arduino.
This is just basic description. What is your definition of project description, what details you need from me regarding this.
Having done some research, I found out that. I need to optimize my code without floating point calculations or divisions, and also I read in few posts that sin() takes about 180 ~ 240 usecs. I am not sure if this is right? Really sin() consumes a lot of time. Because, I don't think there is any problem in serial print when I tested, without sin() and cos() functions in main loop(). My interrupt count was always 1 @ cattledog.
Note: all the benchmark results were done on 16Mhz. I gathered this information from the old threads.