Hello all, I have a sketch running an Uno that reads a voltage from analogs A0 and A1. The read is just a simple analogRead for each, running in the loop. I am then doing some other math on the value to convert it to the number I want.
I want performance to be as fast as possible. So, I added a simple check in the loop to check how fast my loop is running print it out every 500ms basically as loops per second.
With just one of the analogReads, I run around 4000 loops per second after my read and all the math. This is reasonable and I am fine with that. Then, when I add the second analogRead and all the math for it, it drops in half, around 2000 loops per second. Again, I am fine with that. I know it is a big performance hit but as long as I keep it at 1000 loops per second (1kHz) or faster, I am okay for this project.
All good so far, but here is where it turns real bad. I then want to do some simple if statements on the values I collected from analogRead and math. I was shocked to find that even just one if statement (which doesn't even evaluate to true so it should return right out of it) dropped me from 2000 loops per second to only 88!!! Why would that be? Is the if statement really that slow? Here is some example:
void loop() {
loopTimer++;
//Gather the analog value from A0 and convert to current
//Just this one running give me around 4000 loops per second
sensorValueSys1 = analogRead(sensePinSys1);
sensorValueSys1 = (sensorValueSys1 * vref) / 1023;
fCurrentSys1 = (sensorValueSys1 * 1000) / (10 * RS);
//Gather the analog value from A0 and convert to current
//Both this one and the one above gives around 2000 loops per second
sensorValueSys2 = analogRead(sensePinSys2);
sensorValueSys2 = (sensorValueSys2 * vref) / 1023;
fCurrentSys2 = (sensorValueSys2 * 1000) / (10 * RS);
//When I do this method call, I drop to under 100 loops per second.
evaluateCurrentValues(fCurrentSys1, fCurrentSys2);
//Check if it is time to report status
if((millis() - tStart) > logInterval)
{
Serial.print("RawCurrent1: ");
Serial.print(fCurrentSys1);
Serial.print(";");
Serial.print("RawCurrent2: ");
Serial.println(fCurrentSys2);
int loopsPerSecond = loopTimer * 2;
Serial.print("Loops per Second: ");
Serial.println(loopsPerSecond);
loopTimer = 0;
tStart = millis();
}
}
void evaluateCurrentValues(int current1, int current2)
{
if (current1 > errorConditionThreshold)
{
Serial.println("Error Condition Detected, Resetting Sensor");
resetSensor();
delay(2000);
Serial.println("Completed Sensor Reset");
return;
}
}
I should also add that I want to do even more if statements in that method call evaluateCurrentValues(). However, I backed out to just the one for now while troubleshooting this performance issue. I did note, that the biggest drop was just with the one which I provided. I had a second one that dropped me from 88 loops per second to 50. In other words, the biggest hit was on the first if statement. If I have additional statements, I continue to see performance drop which is still bad but not as drastically. An example of one other if statement that dropped me to 50 is:
Sorry about that, still learning the ropes. Here you go. Just compiled this on my Uno and verified the issue still exists with this code:
//Globals
const int RS = 15; //Shunt resistor value in ohms
const int vref = 5; //Analog voltage reference
float sensorValueSys1; //Raw analog reading
float sensorValueSys2; //Raw analog reading
float fCurrentSys1; //current value calculated
float fCurrentSys2; //current value calculated
int iCurrentSys1;
int iCurrentSys2;
//Pins
const int sensePinSys1 = 0; //analog input pin for sys1
const int sensePinSys2 = 1; //analog input pin for sys2
//Thresholds
int idleCurrent = 500; //This is the idle current with no wheel detected. Should be +/- 25 or so
int errorConditionThreshold = 600; //If current rises above this value, error condition detected.
//loop timing
int loopTimer = 0;
long tStart = millis();
int logInterval = 500;
void setup() {
Serial.begin(9600);
}
void loop() {
loopTimer++;
//Gather the analog value from A0 and convert to current
//Just this one running give me around 4000 loops per second
sensorValueSys1 = analogRead(sensePinSys1);
sensorValueSys1 = (sensorValueSys1 * vref) / 1023;
fCurrentSys1 = (sensorValueSys1 * 1000) / (10 * RS);
iCurrentSys1 = fCurrentSys1 * 100;
//Gather the analog value from A0 and convert to current
//Both this one and the one above gives around 2000 loops per second
sensorValueSys2 = analogRead(sensePinSys2);
sensorValueSys2 = (sensorValueSys2 * vref) / 1023;
fCurrentSys2 = (sensorValueSys2 * 1000) / (10 * RS);
iCurrentSys2 = fCurrentSys2 * 100;
//When I do this method call, I drop to under 100 loops per second.
evaluateCurrentValues(iCurrentSys1, iCurrentSys2);
//Check if it is time to report status
if((millis() - tStart) > logInterval)
{
Serial.print("RawCurrent1: ");
Serial.print(iCurrentSys1);
Serial.print(";");
Serial.print("RawCurrent2: ");
Serial.println(iCurrentSys2);
int loopsPerSecond = loopTimer * 2;
Serial.print("Loops per Second: ");
Serial.println(loopsPerSecond);
loopTimer = 0;
tStart = millis();
}
}
void evaluateCurrentValues(int current1, int current2)
{
if (current1 > errorConditionThreshold)
{
Serial.println("Error Condition Detected, Resetting Sensor");
delay(2000);
Serial.println("Completed Sensor Reset");
return;
}
if (current1 > (idleCurrent - 75) && current1 < (idleCurrent + 75))
{
Serial.println("Sys1 idle");
}
}
As you can see the loop counter from my serial monitor.
If it does, you're just filling up the serial buffer by writing to it faster than it can send it to the computer (9600 baud is ~960 bytes per second, and if the buffer fills up because youre writing faster than it can empty it, Serial.print calls will start blocking until there's room in the buffer.
Dr. Azzy, that fixed it...thank you! I was not aware the Serial.println could hurt me that bad. I knew there was a cost but didn't know it was that much. For the record, I only have it in there for testing purposes as I am getting the project going. That will eventually be replaced with logic instead.
Guess in the future when I want to use Serial.print for debugging, I should only log at intervals instead of in each loop?
I agree....for some reason I am in the habit of always selecting 9600. Are there any trade offs or risks with going with a higher baud?
Otherwise, I might want to do interval logging anyway cause to be honest, having serial monitor scroll that fast is not necessary in most applications.
It sure would be nice if you would not just state the problem but also give some guidance especially since most every example I look up of how to do timestamping with millis() says to use a long.
Oh, I see. We are nit picking because I didn't use unsigned long. Yes, it is nit picking because while it is true that I would never need a negative number with time so why not use unsigned, there is nothing wrong with just using a long other than the fact I will get half of the time before it rolls over (so instead of 50 days, I only get 25 days) at which point it will simply roll over to zero again. However, it works either way and where it rolls over is not noticeable.
Sorry to everybody for the huge mistake, I stand corrected. I will use unsigned long from now on.
mbedford:
...there is nothing wrong with just using a long other than the fact I will get half of the time before it rolls over (so instead of 50 days, I only get 25 days) at which point it will simply roll over to zero again. However, it works either way and where it rolls over is not noticeable.
If only that were true! But it's not. A long will fail when it rolls over.