user71506: My question is: how can I use an ISR and store the data I get from my analog inputs into an array for easier data analysis.
More specifically, I want to collect my data using an ISR function (internal timer interrupt), then store the collected data into an array. This way I can analyze the data more easily.
I have posted the code below. I have posted code showing how I implemented my ISR without storing the data into an array. I was wondering I anyone could help me out with this (how to store that data into an array). Thank you.
const int A0_pin = PC0;
const int A1_pin = PC1;

const uint16_t t1_load = 0;
const uint16_t t1_comp = 3173;

float count = 0;
float num = 0;

void setup() {
 
  //Set A0 and A1 pins as inputs
  DDRC &= ~(1 << A0_pin);
  DDRC &= ~(1 << A1_pin);

  //Reset Timer1 Control Reg A
  TCCR1A = 0;

  //Set CTC mode
  TCCR1B &= ~(1 << WGM13);
  TCCR1B |= (1 << WGM12);

  //Set to prescaler of 256
  TCCR1B |= (1 << CS12);
  TCCR1B &= ~(1 << CS11);
  TCCR1B &= ~(1 << CS10);

  //Reset Timer1 and set compare value
  TCNT1 = t1_load;
  OCR1A = t1_comp;

  //Enable Timer1 compare interrupt
  TIMSK1 = (1 << OCIE1A);

  //Enable global interrupt
  sei();
  
  Serial.begin(9600);
  Serial.println("CLEARDATA"); //clears up any data left from previous projects
  Serial.println("LABEL,TIME,TIMER,AVG,GAUGE0, GUAGE1"); //always write LABEL, so excel knows the next things will be the names of the columns 
  Serial.println("RESETTIMER"); //resets timer to 0
}

void loop() {
}

ISR(TIMER1_COMPA_vect) {
  TCNT1 = t1_load;
  float Gauge0 = analogRead(A0_pin); 
  float Gauge1 = analogRead(A1_pin); //44
        
  //conversion of read data, to voltage range (0-100 percentVolts)
  float gauge0 = Gauge0 * (100.0 / 1023.0);
  float gauge1 = Gauge1 * (100.0 / 1023.0);
  float add = gauge0 + gauge1;
  float AVG = ((gauge0 + gauge1) / 2); 

  Serial.print("DATA,TIME,TIMER,"); 
  Serial.print(add); 
  Serial.print(",");
  Serial.print(gauge0);
  Serial.print(",");
  Serial.println(gauge1);
}

In the code, I want to store the gauge1 and the gauge0 values into an array essentially.

user31105: You can create two arrays, e.g.:
#define MAX_LENGTH 20

volatile float _gauge0[MAX_LENGTH];
volatile float _gauge1[MAX_LENGTH];

volatile int _filled = 0;

The _filled value shows that _gauge0 is filled from 0 upto (excluding) _filled.
So when adding two values you use:
_gauge0[_filled] = some value;
_gauge1[_filled] = some_other_value;
_filled++;

After processing, you reset _filled to 0 to refill both arrays again.
Because the Arduino has a limited amount of SRAM it's best to use a fixed array size (20 in this case). You have to make it volatile as it's used from within the ISR.
I'm not sure what you want to do with the data. If you want to gather like 20 (or a fixed amount of values), you can use the above.
If you need a continuous amount of memory and get the last x results, you can better use a so-called ring buffer (search for this, there are many articles about it).
Note that because of the limited amount of SRAM, it might be best not to store calculated values (like average and sum), unless you need the highest performance. This is a tradeoff which I cannot make as I don't know your requirements.
While processing, you use noInterrupts() before and interrupts after the processing of the data, otherwise interrupts will continue during processing. Make sure the handling of the processing takes as less time as possible as you might miss interrupts. Preferably only do the calculations and switch on interrupts before sending results via serial for example.

user69936: My DaquinOscope https://www.daqarta.com/dw_rroo.htm  uses the open-source DaqPort sketch https://www.daqarta.com/dw_rraa.htm to sample in batches, then transfer the data to the host in one high-speed blast.  It takes 1024 total samples, which in your case of 2 channels would be 512 per channel. It stores only the raw data, unscaled... the host is the place for that. The host processes and displays the results in real time. Since this uses a UNO with only 2K RAM, tricky array storage was required to fit in 1024 samples.  In 8-bit mode this is just a 1024-byte array, but for 10-bit it uses a supplemental array of 256 bytes.  On each sample the low byte is stored in the 1024-byte array, and the upper 2 bits are shifted into a 4-sample byte of the 256-byte array.  Feel free to download and use Daqarta for this... no charge for Arduino use. You may want to do this just to work out other issues even if you plan to later use your own host software.  Or you can use Daqarta macros to do whatever you ultimately want.

user7508: Storing the data into arrays is not an end in itself. The question is:
what do you want to do with these arrays? The way to proceed depends on
that purpose.
If you want the data acquisition (analogRead()) and the processing
(computation and printing) to proceed in parallel, then you will need
the arrays for buffering the data between those two processes. As
mentioned by Michel Keijzers in his answer, you will have to research
the concept of ring buffer.
There is a ring buffer implementation in the Arduino core you can use as
inspiration: when reading data from the Serial port, the incoming bytes
are received by an ISR and stored in a ring buffer. The
Serial.read() method can then be used to retrieve these bytes
and process them outside the interrupt context.
Another option is to alternate between an acquisition phase and a
processing phase. This can be an interesting approach if the acquisition
rate is too fast for the processing to be performed in real time. In
this case, you do not need a ring buffer. Instead, you have the ISR fill
the arrays and, when they are full, the main program can take on and
start the processing. For coordinating these jobs, you just need a
boolean to keep track of whether you are performing the acquisition
phase or the processing phase. Note that, since accessing a boolean is
an atomic operation, you do not need to disable interrupts for this.
Here is an example implementation of this strategy. The variable
sampling_done is used to coordinate the two processes:
const size_t sample_count = 256;

volatile int gauge0[sample_count];
volatile int gauge1[sample_count];
volatile bool sampling_done = false;

ISR(TIMER1_COMPA_vect) {
    if (sampling_done) return;  // nothing to do

    static size_t sample_idx = 0;
    gauge0[sample_idx] = analogRead(A0);
    gauge1[sample_idx] = analogRead(A1);
    if (++sample_idx >= sample_count) {
        sample_idx = 0;  // prepare for next round
        sampling_done = true;
    }
}

void process_samples(int Gauge0, int Gauge1) {
    // Whatever needs to be done with the samples:
    // computation, printing...
}

void loop() {
    if (sampling_done) {
        for (size_t i = 0; i < sample_count; i++) {
            process_samples(gauge0[i], gauge1[i]);
        }
        sampling_done = false;  // start next sampling round
    }
}

Now some random remarks:

There is no need to sei() in setup(): interrupts have already been
enabled by the Arduino core initialization.

Do not reset TCNT1 within the ISR: this is handled automatically by
the CTC mode. If you do this in software, you risk missing timer
ticks.

Do not store the readings in float variables: they take twice the
memory of an int and all operations on floats are incredibly slow
compared to the equivalent operations on ints.

The scale factor for converting the readings to percent scale is
100.0/1024: the maximum possible analog reading (1023) is one ADC
step below the reference voltage: see the datasheet.

You should remove unused variables as soon as they become unused
(count, num, AVG). Once you do that you should notice you do not
need floats at all.



71506: <p>My question is: how can I use an ISR and store the data I get from my analog inputs into an array for easier data analysis.</p>
<p>More specifically, I want to collect my data using an ISR function (internal timer interrupt), then store the collected data into an array. This way I can analyze the data more easily.</p>
<p>I have posted the code below. I have posted code showing how I implemented my ISR <em>without</em> storing the data into an array. I was wondering I anyone could help me out with this (how to store that data into an array). Thank you.</p>
<pre class="lang-cpp prettyprint-override"><code>const int A0_pin = PC0;
const int A1_pin = PC1;

const uint16_t t1_load = 0;
const uint16_t t1_comp = 3173;

float count = 0;
float num = 0;

void setup() {
 
  //Set A0 and A1 pins as inputs
  DDRC &amp;= ~(1 &lt;&lt; A0_pin);
  DDRC &amp;= ~(1 &lt;&lt; A1_pin);

  //Reset Timer1 Control Reg A
  TCCR1A = 0;

  //Set CTC mode
  TCCR1B &amp;= ~(1 &lt;&lt; WGM13);
  TCCR1B |= (1 &lt;&lt; WGM12);

  //Set to prescaler of 256
  TCCR1B |= (1 &lt;&lt; CS12);
  TCCR1B &amp;= ~(1 &lt;&lt; CS11);
  TCCR1B &amp;= ~(1 &lt;&lt; CS10);

  //Reset Timer1 and set compare value
  TCNT1 = t1_load;
  OCR1A = t1_comp;

  //Enable Timer1 compare interrupt
  TIMSK1 = (1 &lt;&lt; OCIE1A);

  //Enable global interrupt
  sei();
  
  Serial.begin(9600);
  Serial.println(&quot;CLEARDATA&quot;); //clears up any data left from previous projects
  Serial.println(&quot;LABEL,TIME,TIMER,AVG,GAUGE0, GUAGE1&quot;); //always write LABEL, so excel knows the next things will be the names of the columns 
  Serial.println(&quot;RESETTIMER&quot;); //resets timer to 0
}

void loop() {
}

ISR(TIMER1_COMPA_vect) {
  TCNT1 = t1_load;
  float Gauge0 = analogRead(A0_pin); 
  float Gauge1 = analogRead(A1_pin); //44
        
  //conversion of read data, to voltage range (0-100 percentVolts)
  float gauge0 = Gauge0 * (100.0 / 1023.0);
  float gauge1 = Gauge1 * (100.0 / 1023.0);
  float add = gauge0 + gauge1;
  float AVG = ((gauge0 + gauge1) / 2); 

  Serial.print(&quot;DATA,TIME,TIMER,&quot;); 
  Serial.print(add); 
  Serial.print(&quot;,&quot;);
  Serial.print(gauge0);
  Serial.print(&quot;,&quot;);
  Serial.println(gauge1);
}
</code></pre>
<p>In the code, I want to store the <code>gauge1</code> and the <code>gauge0</code> values into an array essentially.</p>
 31105: <p>You can create two arrays, e.g.:</p>
<pre><code>#define MAX_LENGTH 20

volatile float _gauge0[MAX_LENGTH];
volatile float _gauge1[MAX_LENGTH];

volatile int _filled = 0;
</code></pre>
<p>The _filled value shows that <code>_gauge0</code> is filled from 0 upto (excluding) <code>_filled</code>.</p>
<p>So when adding two values you use:</p>
<pre><code>_gauge0[_filled] = some value;
_gauge1[_filled] = some_other_value;
_filled++;
</code></pre>
<p>After processing, you reset <code>_filled</code> to 0 to refill both arrays again.</p>
<p>Because the Arduino has a limited amount of SRAM it's best to use a fixed array size (20 in this case). You have to make it <code>volatile</code> as it's used from within the ISR.</p>
<p>I'm not sure what you want to do with the data. If you want to gather like 20 (or a fixed amount of values), you can use the above.</p>
<p>If you need a continuous amount of memory and get the last x results, you can better use a so-called ring buffer (search for this, there are many articles about it).</p>
<p>Note that because of the limited amount of SRAM, it might be best not to store calculated values (like average and sum), unless you need the highest performance. This is a tradeoff which I cannot make as I don't know your requirements.</p>
<p>While processing, you use <code>noInterrupts()</code> before and <code>interrupts</code> after the processing of the data, otherwise interrupts will continue during processing. Make sure the handling of the processing takes as less time as possible as you might miss interrupts. Preferably only do the calculations and switch on interrupts before sending results via serial for example.</p>
 69936: <p>My DaquinOscope <a href="https://www.daqarta.com/dw_rroo.htm" rel="nofollow noreferrer">https://www.daqarta.com/dw_rroo.htm</a>  uses the open-source DaqPort sketch <a href="https://www.daqarta.com/dw_rraa.htm" rel="nofollow noreferrer">https://www.daqarta.com/dw_rraa.htm</a> to sample in batches, then transfer the data to the host in one high-speed blast.  It takes 1024 total samples, which in your case of 2 channels would be 512 per channel. It stores only the raw data, unscaled... the host is the place for that. The host processes and displays the results in real time. Since this uses a UNO with only 2K RAM, tricky array storage was required to fit in 1024 samples.  In 8-bit mode this is just a 1024-byte array, but for 10-bit it uses a supplemental array of 256 bytes.  On each sample the low byte is stored in the 1024-byte array, and the upper 2 bits are shifted into a 4-sample byte of the 256-byte array.  Feel free to download and use Daqarta for this... no charge for Arduino use. You may want to do this just to work out other issues even if you plan to later use your own host software.  Or you can use Daqarta macros to do whatever you ultimately want.</p>
 7508: <p>Storing the data into arrays is not an end in itself. The question is:
what do you want to do with these arrays? The way to proceed depends on
that purpose.</p>
<p>If you want the data acquisition (<code>analogRead()</code>) and the processing
(computation and printing) to proceed <em>in parallel</em>, then you will need
the arrays for buffering the data between those two processes. As
mentioned by Michel Keijzers in his answer, you will have to research
the concept of <a href="https://en.wikipedia.org/wiki/Circular_buffer" rel="nofollow noreferrer">ring buffer</a>.</p>
<p>There is a ring buffer implementation in the Arduino core you can use as
inspiration: when reading data from the Serial port, the incoming bytes
are <a href="https://github.com/arduino/ArduinoCore-avr/blob/1.8.3/cores/arduino/HardwareSerial_private.h#L101-L121" rel="nofollow noreferrer">received by an ISR</a> and stored in a ring buffer. <a href="https://github.com/arduino/ArduinoCore-avr/blob/1.8.3/cores/arduino/HardwareSerial.cpp#L180-L190" rel="nofollow noreferrer">The
<code>Serial.read()</code> method</a> can then be used to retrieve these bytes
and process them outside the interrupt context.</p>
<p>Another option is to alternate between an acquisition phase and a
processing phase. This can be an interesting approach if the acquisition
rate is too fast for the processing to be performed in real time. In
this case, you do not need a ring buffer. Instead, you have the ISR fill
the arrays and, when they are full, the main program can take on and
start the processing. For coordinating these jobs, you just need a
boolean to keep track of whether you are performing the acquisition
phase or the processing phase. Note that, since accessing a boolean is
an atomic operation, you do not need to disable interrupts for this.</p>
<p>Here is an example implementation of this strategy. The variable
<code>sampling_done</code> is used to coordinate the two processes:</p>
<pre class="lang-cpp prettyprint-override"><code>const size_t sample_count = 256;

volatile int gauge0[sample_count];
volatile int gauge1[sample_count];
volatile bool sampling_done = false;

ISR(TIMER1_COMPA_vect) {
    if (sampling_done) return;  // nothing to do

    static size_t sample_idx = 0;
    gauge0[sample_idx] = analogRead(A0);
    gauge1[sample_idx] = analogRead(A1);
    if (++sample_idx &gt;= sample_count) {
        sample_idx = 0;  // prepare for next round
        sampling_done = true;
    }
}

void process_samples(int Gauge0, int Gauge1) {
    // Whatever needs to be done with the samples:
    // computation, printing...
}

void loop() {
    if (sampling_done) {
        for (size_t i = 0; i &lt; sample_count; i++) {
            process_samples(gauge0[i], gauge1[i]);
        }
        sampling_done = false;  // start next sampling round
    }
}
</code></pre>
<p>Now some random remarks:</p>
<ul>
<li><p>There is no need to <code>sei()</code> in <code>setup()</code>: interrupts have already been
enabled by the Arduino core initialization.</p>
</li>
<li><p>Do not reset <code>TCNT1</code> within the ISR: this is handled automatically by
the CTC mode. If you do this in software, you risk missing timer
ticks.</p>
</li>
<li><p>Do not store the readings in <code>float</code> variables: they take twice the
memory of an <code>int</code> and all operations on floats are incredibly slow
compared to the equivalent operations on ints.</p>
</li>
<li><p>The scale factor for converting the readings to percent scale is
<code>100.0/1024</code>: the maximum possible analog reading (1023) is one ADC
step below the reference voltage: see the datasheet.</p>
</li>
<li><p>You should remove unused variables as soon as they become unused
(<code>count</code>, <code>num</code>, <code>AVG</code>). Once you do that you should notice you do not
need floats at all.</p>
</li>
</ul>
