Arduino: fill array with values from analogRead()
Asked Answered
G

1

2

How do I fill an array with values from analogRead() on an Arduino?

At a fixed interval of once per second, I want to read a value from analog input pin A0 via analogRead(A0), and store these values into an array.

Ideally, other code would also be able to run during the time between measurements.

Guarantee answered 5/3, 2015 at 21:0 Comment(1)
Have you tried looping?Nonagon
G
6

Let's say you want to read up to 100 values, do this:

1. Poor technique (uses blocking code with delay())

//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];
unsigned int i = 0;

void setup()
{
}

void loop()
{
  analogVals[i] = analogRead(A0);
  i++;
  if (i>=numReadings)
  {
    i=0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
  }
  delay(1000); //wait 1 sec
}

Note: you cannot make the number in brackets too large. Ex: analogVals[2000] will not work because it takes up too much RAM.

Note: the above type approach is also covered in Arduino's basic tutorials on the website. Please reference here: https://www.arduino.cc/reference/en/ --> click on "array" under "Data Types" --> at the bottom of this page it takes you to, click "How to Use Arrays example.

There are a lot of other really useful "Built-in Examples" here: https://docs.arduino.cc/built-in-examples/.

2. Alternate method (also a poor technique, since it uses blocking code with delay())

//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];

void setup()
{
}

void loop()
{
  //take numReadings # of readings and store into array
  for (unsigned int i=0; i<numReadings; i++)
  {
    analogVals[i] = analogRead(A0);
    delay(1000); //wait 1 sec
  }
}

3. Better technique (non-blocking method--no delay!)

//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];
unsigned int i = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  static uint32_t tStart = millis(); // ms; start time
  const uint32_t DESIRED_PERIOD = 1000; // ms
  uint32_t tNow = millis(); // ms; time now
  if (tNow - tStart >= DESIRED_PERIOD)
  {
    tStart += DESIRED_PERIOD; // update start time to ensure consistent and near-exact period

    Serial.println("taking sample");
    analogVals[i] = analogRead(A0);
    i++;
    if (i>=numReadings)
    {
      i = 0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
    }
  }
}

4. Professional-type approach (non-blocking method, avoids global variables by passing around pointers instead, uses C stdint types, and uses static variables to store local, persistent data)

// Function prototypes
// - specify default values here
bool takeAnalogReadings(uint16_t* p_numReadings = nullptr, uint16_t** p_analogVals = nullptr);

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

void loop()
{
  // This is one way to just take readings
  // takeAnalogReadings();

  // This is a way to both 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)
  {
    // Let's print them all out!
    Serial.print("numReadings = "); Serial.println(numReadings);
    Serial.print("analogVals = [");
    for (uint16_t i=0; i<numReadings; i++)
    {
      if (i!=0)
      {
        Serial.print(", ");
      }
       Serial.print(analogVals[i]);
    }
    Serial.println("]");
  }
}

// Function definitions:

//---------------------------------------------------------------------------------------------------------------------
// Take analog readings to fill up a buffer.
// Once the buffer is full, return true so that the caller can read out the data.
// Optionally pass in a pointer to get access to the internal buffer in order to read out the data from outside
// this function.
//---------------------------------------------------------------------------------------------------------------------
bool takeAnalogReadings(uint16_t* p_numReadings, uint16_t** p_analogVals)
{
  static const uint16_t NUM_READINGS = 10;
  static uint16_t i = 0; // index
  static uint16_t analogVals[NUM_READINGS];

  const uint32_t SAMPLE_PD = 1000; // ms; sample period (how often to take a new sample)
  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, so you don't try to save readings outside of the bounds of the array
    }
  }

  // Assign the user-passed-in pointers so that the user can retrieve the data if they so desire to do it this way
  if (p_numReadings != nullptr)
  {
    *p_numReadings = NUM_READINGS;
  }
  if (p_analogVals != nullptr)
  {
    *p_analogVals = analogVals;
  }

  return bufferIsFull;
}

Sample Serial Monitor output from the last code just above:

Begin  

taking sample num 1  
taking sample num 2  
taking sample num 3  
taking sample num 4  
taking sample num 5  
taking sample num 6  
taking sample num 7  
taking sample num 8  
taking sample num 9  
taking sample num 10  
numReadings = 10  
analogVals = [1023, 1023, 1023, 1023, 1023, 687, 0, 0, 0, 0]  
taking sample num 1  
taking sample num 2  
taking sample num 3  
taking sample num 4  
taking sample num 5  
taking sample num 6  
taking sample num 7  
taking sample num 8  
taking sample num 9  
taking sample num 10  
numReadings = 10  
analogVals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  

More reading/study:

  1. My thorough answer on How to do high-resolution, timestamp-based, non-blocking, single-threaded cooperative multi-tasking
  2. Arduino's very basic but extremely useful "Blink Without Delay" tutorial: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
  3. Other "Build-in Examples" from Arduino: https://docs.arduino.cc/built-in-examples/
Glider answered 5/3, 2015 at 21:31 Comment(3)
How can analogRead return either an int or an unsigned int?Johppah
According to the source code on line 38 here analogRead() returns an int. However, since the range is only 0-1023, it would make the most sense to store it into a uint16_t, which is the equivalent of unsigned int on ATmega328-based Arduinos. The compiler will automatically cast types as required to make them get stored into alternate types (ex: unsigned int <--> int), with loss of data only if you are outside the valid bounds of the new type. In this case it is safe.Glider
It is safe because analogRead returns 0-1023, and unsigned int can store values from 0 to 65,535 (see arduino.cc/reference/en/language/variables/data-types/…), while int can store values from -32,768 to 32,767 (see arduino.cc/reference/en/language/variables/data-types/int). As you can see, both can easily store values in the 0-1023 range.Glider

© 2022 - 2024 — McMap. All rights reserved.