I'm trying to detect how often a particular sensor value occurs. My issue occurs with the value of i which gets really high then really low. I need help to determine why i behaves the way it does.
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long i = 0;
int p = 0;
unsigned long peak = 0;// this is the initial peak value
unsigned long startTime = 0;
unsigned long endTime = 0;
unsigned long sensor;
startTime = millis();
while(millis() - startTime<=5000){ //this while loop runs for 2 seconds
if(peak <= analogRead(A0)){ //this compares the initial peak value to the values from the sensor
peak = analogRead(A0); // if the value from the sensor is bigger the the current peak value, replace
the current peak value witht he value from the sensor and continue
comparing
}
}
endTime = millis();
Serial.print("Peak: ");
Serial.println(peak);
while(millis() - endTime <= 5000){ //this while loop also runs for 2 seconds
sensor = analogRead(A0);
if(sensor == peak){// compare the values from the sensor to the established peak value
i++;// everytime the sensor value is equal to the determined peak, increment i
}
}
Wrong. i remains at exactly 0 and it does so because the only time that variable is referenced in that code is when it is created and assigned the Initial value.
If you updated your code then please make sure your question relates to the code you present.
JaBa:
Wrong. i remains at exactly 0 and it does so because the only time that variable is referenced in that code is when it is created and assigned the Initial value.
If you updated your code then please make sure your question relates to the code you present.
i is being incremented in the while loop:
while (millis() - endTime <= 5000) {
sensor = analogRead(A0);
if (sensor == peak) {
i++;
}
}
Serial.println(i);
The reason i is sometimes very large, and sometimes very small, is that the code in the while loop runs repeated for 5 seconds, and is limited mainly by the time it takes to do an analog read, which on a lot of the arduino boards is around 100nS, or 10,000 times per second. If the value read by analogRead stays exactly the same, i will be very large, if the value changes at all, i doesn't get incremented and is very small.
I changed your code a little, because analogRead returns a byte, not an unsigned long:
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long count = 0;
byte peak = 0; // this is the initial peak value
unsigned long startTime = 0;
unsigned long endTime = 0;
startTime = millis();
/* if the value from the sensor is bigger than the current peak value, replace
the current peak value with the value from the sensor and continue comparing */
while (millis() - startTime <= 5000ul) {
byte sensor = analogRead(A0);
if (peak <= sensor) peak = sensor;
}
Serial.print("Peak: ");
Serial.println(peak);
endTime = millis();
/* compare the values from the sensor to the established peak value
everytime the sensor value is equal to the determined peak, increment cunt */
while (millis() - endTime <= 5000ul) if (analogRead(A0) == peak) count++;
Serial.println(count);
}
To take into account the noise, you can define a threshold value and compare the difference between the sensor and the peak with this threshold:
while (millis() - endTime <= 5000ul) if (abs(analogRead(A0) - peak) <= threshold) count++;
To account for the reading time, you need to wait
while (millis() - endTime <= 5000ul) {
if (abs(analogRead(A0) - peak) <= threshold) {
count++;
delayMicroseconds(1);
}
}
If you really want to wait 100ns, then you need to use no-ops instead of delayMicroseconds
lesept:
I just read what David said before: 100 ns. If it's longer than that, just change the delay accordingly.
And OOPS! You're right, analogRead can go up to 1023!!
So both sensor and peak variables should be declared as int or unsigned int
Oops, I meant to say 100uS, clearly 100nS wouldn't give a maximum of 10,000 reads per second.
As for the original problem, detecting the peak reading within a 5 second period, then counting how many times that peak occurs during the subsequent 5 second period, is going to give wildly varying results if you get a noise spike or anything else that causes the input to go slightly higher for just a single reading.
I would like to record the time, each time an event occurs. For example, every time analogRead is equal to a particular value, store the time when this event occured. I would like to do this for any number of events.
unsigned long time[10];
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long i = 0;
unsigned long peak = 0;// this is the initial peak value
unsigned long startTime = 0;
unsigned long sensor;
startTime = millis();
while(millis() - startTime<=2000){ //this while loop runs for 2 seconds
if(peak <= analogRead(A0)){ //this compares the initial peak value to the values from the sensor
peak = analogRead(A0); // if the value from the sensor is bigger the the current peak value, replace
the current peak value witht he value from the sensor and continue
comparing
}
}
sensor = analogRead(A0);
for(i = 0; i<10; i++){
if(sensor >= peak){// compare the values from the sensor to the established peak value
time[i++] = millis();// store the time every moment this condition was true
}
}
}
Why are you incrementing the value of i inside the for loop ?
Fix that but bear in mind that you have all of the code in loop() so the process will repeat rapidly. So at any one time you have no idea whether the values in the time array are all from the same run of 10 readings. If this is a concern then you should have a flag indicating that a set of readings have been done and prevent overwriting a previously save value.
Bear in mind too that because of the speed at which the Arduino runs the 10 readings will be taken in a period of a few microseconds so there may not be a variation in the millis() value during that time.
I would like to record the time, each time an event occurs. For example, every time analogRead is equal to a particular value, store the time when this event occured. I would like to do this for any number of events.
Could you explain what you are trying to accomplish? Your description doesn't match your code, the code is taking the peak reading over a two second period, then taking a single reading, and only storing the time if that single reading equals or exceeds the peak value (and storing that same time ten times in a ten element array), without storing anything to indicate what the actual value was.
Storing the time that analogRead is equal to a particular value is quite easy, just define an array of 1024 elements, one for each possible value of analogRead, then read the input and store the time in the appropriate array element that corresponds to the value read from analogRead..
const byte analogPin = A0;
unsigned long analogTimes[1024];
int analogInput;
void setup() {
Serial.begin(9600);
for (int i = 0; i < 1024; i++) { // initialize the array elements to 0
analogTimes[i] = 0UL;
}
}
void loop() {
analogInput = analogRead(analogPin); //read the input
analogTimes[analogInput] = millis(); //record when input occurred
Serial.print("input value: ");
Serial.print(analogInput);
Serial.print(" time: ");
Serial.println(analogTimes[analogInput]);
}
The only problem is that this will take 4096 bytes of storage, which is half the available memory in an arduino mega, and trying to do this with two inputs will be impossible because the two arrays alone will fill all the available memory, leaving no space for any other variables.
I'm trying to implement a heart rate monitor. I need to determine a peak initially, detect values that are equal to or higher than the peak ( do this 10 times), and determine what those those times are.
For example, if the peak turns out to be 700, I want analogRead to keep reading the sensor values and record the time if analogRead encounters a value that is >= 700, and I want analogRead to do this 10 times per loop.
Your code is working to get a peak value over the 2 second period.
In the second part, I would probably use a WHILE loop, initialize i to 0, and loop while i < 10.
Inside the loop, read the sensor, if it equals or exceeds the peak value, then store the time in the array and increment i.
Note that you will have no way to determine how long this is going to take, since it is dependent on having the reading equal or exceed the peak. If the heart rate decreases immediately after the peak value is determined, the loop might never complete.
lesept:
So I think you need to put the first part of your code, ie determine the peak value, in the setup, and the test, ie 10 readings, in the loop.
The issue with doing what you're saying is, I get stuck with a particular peak value for as long as I don't reset the arduino. And depending on the stability of the heart beat, that initial peak may have been a fluke and new peak will need to be found.
This is a very different requirement to anything you mentioned in your original post (which, by the way, you have still not corrected by adding code tags).
I suggest a new algorithm which maintains a running average of the readings, and a running Mean Absolute Deviation (MAD) from that average. Then, peaks can be detected that exceed the average plus 150~200% of the MAD.