analogRead data to array cannot end loop

Hello everyone. I’m working a project using a light sensor, Photodiode, to measure light photon signal. the output voltage from amplifier is an analog pulse signal(Voltage) has duration with various time (100-1000 millisecond) and I want to record data to the array.

The Arduino start to receive signal from the sensor by Pin A0 (analogRead(A0)>=5) and stop when analogRead(A0)<5 with a sampling rate 25 ms. I stored the data to arrays in NUM_READINGS = 40 (total time = 25ms * 40 = 1000 ms for one pulse)

If time period of signal is 1000 ms the output in serial monitor show :
taking sample num 1
taking sample num 2
taking sample num 3
taking sample num 4
.
.
.
taking sample num 40
numReadings = 40
analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 56, 56, 55, 55, 55, 55, 54, 55, 55, 55, 56, 56, 56, 55, 55, 55, 55, 55, 56, 55, 56, 55, 55, 55, 56, 55, 55, 55, 56, 55, 55, 35]

=====Hear is the problem: ==========
I has problem when the signal is lower than 1000 ms (let's say it's 200ms). The program will stop at 200
ms (NUM_READINGS = 8 ) and cannot run to the end of array. The output show. :

taking sample num 1
taking sample num 2
taking sample num 3
taking sample num 4
.
.
.
taking sample num 8

Can someone help me to resolve the problem to complete the array.

taking sample num 1
taking sample num 2
taking sample num 3
taking sample num 4
.
.
.
taking sample num 40
numReadings = 40
analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

bool takeAnalogReadings(uint16_t* p_numReadings = nullptr, uint16_t** p_analogVals = nullptr);

void setup()
{
  Serial.begin(115200);
  Serial.println("\nBegin\n");
}

void loop()
{

 if (analogRead(A0)>=5)
  {   
   //take readings and read out the values when the buffer is full
     uint16_t numReadings;
     uint16_t* analogVals;
     bool readingsDone = takeAnalogReadings(&numReadings, &analogVals);
  
      if (readingsDone)
       {
          Serial.print("numReadings = "); Serial.println(numReadings); //print the total number of readings
          Serial.print("analogVals = [");
          
          for (uint16_t i=0; i<numReadings; i++) //print data value to array
             {
               if (i!=0)
                 {
                    Serial.print(", ");
                  }
               Serial.print(analogVals[i]);
              }
          Serial.println("]");
        }
  }

}

bool takeAnalogReadings(uint16_t* p_numReadings, uint16_t** p_analogVals)
{
  static const uint16_t NUM_READINGS = 40;//total number of reading
  static uint16_t i = 0; // index
  static uint16_t analogVals[NUM_READINGS];
  const uint32_t SAMPLE_PD = 25; // ms; sampling period 
  static uint32_t tStart = millis(); // ms; start time
  bool bufferIsFull = false; // set to true each time NUM_READINGS have been taken

  // Only take a reading once per SAMPLE_PD
  uint32_t tNow = millis(); // ms; time now
  if (tNow - tStart >= SAMPLE_PD)
  {
    Serial.print("taking sample num "); Serial.println(i + 1);
    tStart += SAMPLE_PD; // reset start time to take next sample at exactly the correct pd
    analogVals[i] = analogRead(A0);
    i++;
    if (i >= NUM_READINGS)
    {
      bufferIsFull = true;
      i = 0; // reset to beginning of array
    }
  }

  if (p_numReadings != nullptr)
  {
    *p_numReadings = NUM_READINGS;
  }
  if (p_analogVals != nullptr)
  {
    *p_analogVals = analogVals;
  }

  return bufferIsFull;
}

You have to know the moment to start and the moment to stop recording. That very moment can be called an 'event'. Therefor you need to know the previous state, just as with the StateChangeDection sketch: https://www.arduino.cc/en/Tutorial/StateChangeDetection.

Instead of the 'lastButtonState', I will use a global 'bool' variable called "recording".

bool recording = false;   // true when busy with recording the pulse

...

int rawADC = analogRead( A0);
if( !recording && rawADC >= 5)
{
  // The sketch was not recording and the pulse is high enough to start recording.
  // Start recording.
  recording = true;
  ...
}
else if( recording && rawADC < 5)
{
  // The sketch was recording and the pulse dropped below 5.
  // Stop recording.
  recording = false;
  ...
}

You can test 'recording' if you need to add samples to the array. You can do that below that snippet of code or combine it with it.

No reason for that code to work differently with 200ms from 1000ms that I can see.
Stopping at 8 with a delay of 200ms is not stopping after 200ms. It’s stopping after 1600ms.

It’s not clear to me if the program stops dead, or only fills in the first 8 elements and the rest are zeroas per...
analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Are the zeros what you are getting, or what you are expecting?

If you are actually expecting to be reading zeros then this code...

if (analogRead(A0)>=5)

Will probably mean that you never read any value less than 5.
Sometimes you may read one or two, because the sample you take inside the function with analogRead is different from the one in the if statement. Depends how quickly your signal is changing.

yamadac:
I has problem when the signal is lower than 1000 ms (let's say it's 200ms). The program will stop at 200
ms (NUM_READINGS = 8 ) and cannot run to the end of array. The output show.

what do you mean "signal is lower than 1000ms"?

does the value of the signal you're sampling become < 5 after 200 ms?

Koepel:
You have to know the moment to start and the moment to stop recording. That very moment can be called an 'event'. Therefor you need to know the previous state, just as with the StateChangeDection sketch: https://www.arduino.cc/en/Tutorial/StateChangeDetection.

Instead of the 'lastButtonState', I will use a global 'bool' variable called "recording".

bool recording = false;   // true when busy with recording the pulse

...

int rawADC = analogRead( A0);
if( !recording && rawADC >= 5)
{
 // The sketch was not recording and the pulse is high enough to start recording.
 // Start recording.
 recording = true;
 ...
}
else if( recording && rawADC < 5)
{
 // The sketch was recording and the pulse dropped below 5.
 // Stop recording.
 recording = false;
 ...
}




You can test 'recording' if you need to add samples to the array. You can do that below that snippet of code or combine it with it.

Thank you for your help

I'm trying to use the code as you tell by using this code

bool recording;
int rawADC;

void setup()
{
   Serial.begin(115200);
   recording=false;
}

void loop()
{
   rawADC = analogRead(A0);
   
   if(rawADC>=5 && !recording )
   {
   Serial.println("Recording ");
   Serial.print("sensor = ");
   Serial.println(rawADC);
   recording = true;
   }
   else if(rawADC<5 && recording )
   {
   Serial.println("NOT Recording ");
   recording = false;
   }
}

but the code only record single data
the output show :
-> Recording sensor = 56 //when signal come
-> NOT Recording //stop after end signal

I want to measure all the voltage data during the signal come
am I on the right track?

pcbbc:
No reason for that code to work differently with 200ms from 1000ms that I can see.
Stopping at 8 with a delay of 200ms is not stopping after 200ms. It’s stopping after 1600ms.

It’s not clear to me if the program stops dead, or only fills in the first 8 elements and the rest are zeroas per...
analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Are the zeros what you are getting, or what you are expecting?

If you are actually expecting to be reading zeros then this code...

if (analogRead(A0)>=5)

Will probably mean that you never read any value less than 5.
Sometimes you may read one or two, because the sample you take inside the function with analogRead is different from the one in the if statement. Depends how quickly your signal is changing.

I want to measure all voltage data in signal duration

I set the sampling time every 25 ms so if the signal has 1000 ms duration.number of sampling = 40 and record to the array

analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 56, 56, 55, 55, 55, 55, 54, 55, 55, 55, 56, 56, 56, 55, 55, 55, 55, 55, 56, 55, 56, 55, 55, 55, 56, 55, 55, 55, 56, 55, 55, 35]

and if the signal has just 200 ms duration. number of sampling = 8 so the data in the array should be

analogVals = [15, 35, 55, 55, 55, 56, 55, 55]

but from my code after receiving 200 ms duration signal. the program stops dead, therefore, I try to record 200 ms duration signal as it was 1000 ms duration signal.
the data in the array should be

analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

thank you for replying. I hope you understand what I want to tell you.

if(rawADC>=5 && !recording)

Same as:

if(rawADC>=5 && !recording == true )

This is looking for ‘recording’ to be false.

You then make ‘recording’ = true, the next time around it won’t be evaluated.

gcjr:
what do you mean "signal is lower than 1000ms"?

does the value of the signal you're sampling become < 5 after 200 ms?

I mean when the signal duration shorter than 1000 ms. the program stops dead.

from this output array

analogVals = [15, 35, 55, 55, 55, 56, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

the zeros mean no data coming in the sensor

I want the output to show exactly when the data signal is coming
analogVals = [15, 35, 55, 55, 55, 56, 55, 55]

thank you for replying

Use CTRL T to format your code.
Attach your ‘complete’ sketch between code tags, use the </> icon in the posting menu.
[code]Paste your sketch here[/code]

You were provided with the code to detect that earlier...

 else if(rawADC<5 && recording )
   {
   Serial.println("NOT Recording ");
   recording = false;
   }

Just print out the readings you have got so far.

Your function takeAnalogReadings sets the loop() variable analogVals to point to a stack-based local variable in takeAnalogReadings. Once you return from takeAnalogReadings, that local variable will be over-written by the stack used by any interrupts (like the millis timer), or function calls (like the calls to Serial.print in loop()).

NEVER return a pointer to a local variable, as local variables can and will be over-written once the function that defined them has returned.

Regards,
Ray L.

larryd:
if(rawADC>=5 && !recording)

Same as:

if(rawADC>=5 && !recording == true )

This is looking for ‘recording’ to be false.

You then make ‘recording’ = true, the next time around it won’t be evaluated.

so I delete

recording = true;

it's work but how can I get into

else if (rawADC<5 && recording )

RayLivingston:
NEVER return a pointer to a local variable, as local variables can and will be over-written once the function that defined them has returned.

It’s good general advice, for sure...

..but in the OPs code the local variable he is passing back is a static, so it’s on the heap and not the stack. So it works. I certainly wouldn’t claim it’s the best code though. :wink:

OP: “get into”?
You get into that code when the sensor value is less than 5 AND recording == true

If you’ve deleted everywhere that sets recording = true, then you’ll never “get in” that code.
So obviously you need that assignment somewhere. Why did you delete it?

Also, please post your entire code if you make changes, not snippets or descriptions. We can’t see what you’ve changed otherwise.

People who are helping are allowed to post snippets. We do that to avoid writing your entire program for you and force you to actually think about the code you are writing.

thanks for your advice.

First. I collect the data by this code

const int analogInPin = A0;
float sensorValue = 0;

void setup()
{
  Serial.begin(115200);
}
void loop()
{
  sensorValue = analogRead(analogInPin);

  if (sensorValue >= 5)
  {
    Serial.print("sensor = ");
    Serial.println(sensorValue, 3);

    if (sensorValue < 5)
    {
      Serial.print("sensor = 0.000 ");
    }
  }
  delay(25);
}

Now, I'm currently focusing on putting these readings to the array.

bool takeAnalogReadings(uint16_t* p_numReadings = nullptr, uint16_t** p_analogVals = nullptr);

void setup()
{
  Serial.begin(115200);
  Serial.println("Begin");
}

void loop()
{
  if (analogRead(A0) >= 5)
  {
    //take readings and read out the values when the buffer is full
    uint16_t numReadings;
    uint16_t* analogVals;
    bool readingsDone = takeAnalogReadings(&numReadings, &analogVals);

    if (readingsDone)
    {
      Serial.print("numReadings = "); Serial.println(numReadings); //print the total number of readings
      Serial.print("analogVals = [");

      for (uint16_t i = 0; i < numReadings; i++) //print data value to array
      {
        if (i != 0)
        {
          Serial.print(", ");
        }
        Serial.print(analogVals[i]);
      }
      Serial.println("]");
    }
  }

}

bool takeAnalogReadings(uint16_t* p_numReadings, uint16_t** p_analogVals)
{
  static const uint16_t NUM_READINGS = 40;//total number of reading
  static uint16_t i = 0; // index
  static uint16_t analogVals[NUM_READINGS];
  const uint32_t SAMPLE_PD = 25; // ms; sampling period
  static uint32_t tStart = millis(); // ms; start time
  bool bufferIsFull = false; // set to true each time NUM_READINGS have been taken

  // Only take a reading once per SAMPLE_PD
  uint32_t tNow = millis(); // ms; time now
  if (tNow - tStart >= SAMPLE_PD)
  {
    Serial.print("taking sample num "); Serial.println(i + 1);
    tStart += SAMPLE_PD; // reset start time to take next sample at exactly the correct pd
    analogVals[i] = analogRead(A0);
    i++;
    if (i >= NUM_READINGS)
    {
      bufferIsFull = true;
      i = 0; // reset to beginning of array
    }
  }

  if (p_numReadings != nullptr)
  {
    *p_numReadings = NUM_READINGS;
  }
  if (p_analogVals != nullptr)
  {
    *p_analogVals = analogVals;
  }

  return bufferIsFull;
}

but the program stops dead when the data signal duration is shorter than 1000ms

thank you all for your advice.

I finally found the solution.