Averaging values from an array using multiple arrays?

I’m trying to help stabilize some pot readings in Pure Data by averaging the data in the arduino first. I’m multiplexing 16 pots so I need to pick out values from the array at specific indexes and then average them. I used the smoothing code and pointed it to read from my array. It works but I need to do this 15 more times. Surely I can’t copy this code 15 times, I know there are ways to simplify the code but I’m new to programming so I could use suggestions.

#define CONTROL0 2
#define CONTROL1 3
#define CONTROL2 4
#define CONTROL3 5

byte muxarray0[33];

const int numReadings = 1000;

int readings[numReadings];      // the readings from the analog input
int Index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

void setup() {
  
// initialize all the readings to 0: 
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0;      
  
  Serial.begin(9600);
}

void loop() {
  
  muxarray0[0] = 0xc0; // denote beginning of data
  byte k = 1;
  for (int i = 0; i < 16; i++) {
    digitalWrite(CONTROL0, (i&15)>>3);
    digitalWrite(CONTROL1, (i&7)>>2);
    digitalWrite(CONTROL2, (i&3)>>1);
    digitalWrite(CONTROL3, (i&1));
    unsigned int mod = analogRead(0);
    muxarray0[k++] = mod & 0x007f;
    muxarray0[k++] = (mod >> 7) & 0x0007; /* this way you send two values per potentiometer
    so you can get the whole 0-1023 range instead of just 0-255 which is what byte takes.
    I used to do it like this analogRead(0) / 4.
*/
  }
  
   // subtract the last reading:
  total= total - readings[Index];         
  // read from the sensor:  
  readings[Index] = muxarray0[1]; 
  // add the reading to the total:
  total= total + readings[Index];       
  // advance to the next position in the array:  
  Index = Index + 1;                    

  // if we're at the end of the array...
  if (Index >= numReadings)              
    // ...wrap around to the beginning: 
    Index = 0;                           

  // calculate the average:
  average = total / numReadings;         
  // send it to the computer as ASCII digits
  Serial.println(average);   
  delay(1);        // delay in between reads for stability  
  
}

The output of the array is bitshifted so that I can get a 0-1024 value in Pure Data. I think I’ll need to redo the muxarray to just hold 16 values and then do the bit shifting on the value from the average array.

Any ideas how to make this work for all 16 pots? Thanks

So, if I have this right muxarray0[X..X+1] contains the instantaneous reading of one of your 16 potentiometers. And what you want to do is to average out the successive readings of a given pot. Something like that?

To compute a proper moving average, you would need to keep N successive values of each pot. Another smoothing function is a simple average = 1/n * reading + (n-1)/n * average. So If I have:

int reading;
int average;

for (;;) {
    reading = analogRead(A0);
    average = reading / 10 + average - (average / 10);
}

Over successive readings, "average" will converge on a smoothed out moving average of "reading". You could use this approach to update the reading values in your muxarray0[].

The muxarray0[33] contains the instantaneous readings of all 16 pots. It's bit shifted to 8bit so there are two values for each pot, but the values for every pot are in this array. I will change this to just hold 16 10bit numbers.

What I want to do is average the readings of each of the 16 pots over a certain number of readings so that they are smoother in Pure Data. Since each pot is read and then the array moves to the next pot, if I used your code it would average the readings of all the pots together?

Here’s what I have so far. It works to read only one pot, and only to 508.

#define CONTROL0 2
#define CONTROL1 3
#define CONTROL2 4
#define CONTROL3 5

int muxarray0[16];

const int numReadings = 16;
const int numArrays = 16;
const int numTotal = 16;
const int numAverage = 16;

int readings[numArrays][numReadings];      // the readings from the analog input
int Index= 0;                  // the index of the current reading
int total[numTotal];                 // the running total
int average[numAverage];               // the average

void setup() {

  Serial.begin(9600);
}

void loop() {

  for (int i = 0; i < 16; i++) {
    digitalWrite(CONTROL0, (i&15)>>3);
    digitalWrite(CONTROL1, (i&7)>>2);
    digitalWrite(CONTROL2, (i&3)>>1);
    digitalWrite(CONTROL3, (i&1));
    muxarray0[i++] = analogRead(0);
  }
  
  for (int i = 0; i < 16; i++) {
   // subtract the last reading:
  total[i]= total[i] - readings[i][Index];         
  // read from the sensor:  
  readings[i][Index] = muxarray0[i]; 
  // add the reading to the total:
  total[i]= total[i] + readings[i][Index];       
  // advance to the next position in the array:  
  Index = Index + 1;                    

  // if we're at the end of the array...
  if (Index >= numReadings)              
    // ...wrap around to the beginning: 
    Index = 0;                           

  // calculate the average:
  average[i++] = total[i++] / numReadings;         
  // send it to the computer as ASCII digits
 
  }
Serial.println(average[0]);
Serial.println(average[1]);
Serial.println(average[2]);
Serial.println(average[3]);
Serial.println(average[4]);
Serial.println(average[5]);
Serial.println(average[6]);
Serial.println(average[7]);
Serial.println(average[8]);
Serial.println(average[9]);
Serial.println(average[10]);
Serial.println(average[11]);
Serial.println(average[12]);
Serial.println(average[13]);
Serial.println(average[14]);
Serial.println(average[15]);
    delay(2);        // delay in between reads for stability   
}

Is this the right idea?

I have not looked at your code in detail but do you really want to increment the variable being used by a for loop inside that loop ?

  for (int i = 0; i < 16; i++) 
  {
//more code here
    // calculate the average:
    average[i++] = total[i++] / numReadings;         
  }

It also seems to me that you could use 2 nested for loops to process the 2 dim array and you could certainly use a for loop to output the averages.

Okay I got it working with 16 pots. It seems I didn’t have the control pins initialized in the setup so that made the multiplexer only read one pot. Also, the number of reads for the average function seems like it needs to be x*(arraysize) - 1 in order to get the maximum value of 1024 from the pots. So for my arrays of 16, the number of reads needs to be 15, 31, 47, 63, etc. Even still I only get a maximum of 1017 from my pots.

#define CONTROL0 2
#define CONTROL1 3
#define CONTROL2 4
#define CONTROL3 5

int muxarray0[16];

const int numReadings = 63;
const int numArrays = 16;
const int numTotal = 16;
const int numAverage = 16;

int readings[numArrays][numReadings];      // the readings from the analog input
int Index= 0;                  // the index of the current reading
int total[numTotal];                 // the running total
int average[numAverage];               // the average

void setup() {
  pinMode(CONTROL0, OUTPUT);
  pinMode(CONTROL1, OUTPUT);
  pinMode(CONTROL2, OUTPUT);
  pinMode(CONTROL3, OUTPUT);
  Serial.begin(9600);
}

void loop() {
Serial.write(0xc0);

  for (int i = 0; i < 16; i++) {
    digitalWrite(CONTROL0, (i&15)>>3);
    digitalWrite(CONTROL1, (i&7)>>2);
    digitalWrite(CONTROL2, (i&3)>>1);
    digitalWrite(CONTROL3, (i&1));
    muxarray0[i] = analogRead(0);
  
  for (int i = 0; i < 16; i++) {
   // subtract the last reading:
  total[i]= total[i] - readings[i][Index];         
  // read from the sensor:  
  readings[i][Index] = muxarray0[i]; 
  // add the reading to the total:
  total[i]= total[i] + readings[i][Index];       
  // advance to the next position in the array:  
  Index = Index + 1;                    

  // if we're at the end of the array...
  if (Index >= numReadings)              
    // ...wrap around to the beginning: 
    Index = 0;                           

  // calculate the average:
  average[i] = total[i] / numReadings;         
  // send it to the computer as ASCII digits
  }
  Serial.write(average[i]);
  }
}

The Serial.write(192) gives me a value to start sorting from in Pure Data. I hope to get the average bitshifted so that the pot values never go above 127, and falsely trip the sorting selection.

Now… How can I bitshift the average to make it 8 bit? In my first post you can see how the multiplexed array was shifted. In other sketches using single pots connected to the arduino I did it like this:
* *int lowerBits = potState & 0x007F; //bitmask and bitshift to make 7bit 0-127 and 3bit 0-7 int upperBits = (potState >> 7) & 0x0007;* *

Where potState was the analogRead.
Thanks for any help!

So for my arrays of 16, the number of reads needs to be 15, 31, 47, 63, etc. Even still I only get a maximum of 1017 from my pots.

One issue is that the division to get the average is an integer division. If the true average were 1017.625, the integer division will yield 1017. To get a rounding effect, add an extra numReadings/2 to the sum before dividing.

  // calculate the rounded average:
  average[i] = (total[i] + numReadings/2) / numReadings;

The bit shifting to extract the two subfields is going to work the same way as you had before.

int lowerBits = average[i] & 0x007F; //bitmask and bitshift to make 7bit 0-127 and 3bit 0-7
int upperBits = (average[i] >> 7) & 0x0007;

Oh, and you're going to be running out of memory...

michaelallen: int muxarray0[16];                        // 16 x 2 => 32 bytes const int numReadings = 63; const int numArrays = 16; const int numTotal = 16; const int numAverage = 16; int readings[numArrays][numReadings];      //  16 x 63 x 2 => 2,016 bytes int total[numTotal];                             // 16 x 2 => 32 bytes int average[numAverage];                    // 16 x 2 ==> 32 bytes

You've got over 2K tied up in these tables. An Uno only has 2K. I would recommend reducing the size of readings[][].

Oh, and since you are trying to sum up 63 readings that can be values up to 1023, the total can be up to 63 x 1,023 => 64,449. This exceeds the range of int total[] which can only go to 32,767. Making total[] unsigned would let you squeak in there, but I would recommend dropping numReadings to ~30 or less anyhow.

Okay thanks so much, you're right the bitshifting does work the same. I tried it earlier and must have done something wrong because it gave me an error about using an int[int] or something. I got it working now though. Works perfectly!

Also I'm using a Teensy 3.0 and it says uploading that sketch used. Binary sketch size: 11,048 bytes (of a 131,072 byte maximum) Estimated memory use: 7,676 bytes (of a 16,384 byte maximum)

So I think it's good for memory. Thanks for your help, I really appreciate it!

I’m getting some bleed over between pots. I noticed it in PureData, and I can see it in the Arduino serial monitor as well. Each pot slightly adjusts the values of the pot printed next. If I get rid of the bit shifting and just print the 10bit values, they don’t bleed at all. Am I bitshifting incorrectly? or do I need to put a slight delay somehow between printing the bitshifted value and then reading the next pot?

Here’s the full code right now.

#define CONTROL0 2
#define CONTROL1 3
#define CONTROL2 4
#define CONTROL3 5

int muxarray0[16];

const int numReadings = 16;
const int numArrays = 16;
const int numTotal = 16;
const int numAverage = 16;

int readings[numArrays][numReadings];      // the readings from the analog input
int Index= 0;                  // the index of the current reading
int total[numTotal];                 // the running total
int average[numAverage];               // the average

void setup() {
  pinMode(CONTROL0, OUTPUT);
  pinMode(CONTROL1, OUTPUT);
  pinMode(CONTROL2, OUTPUT);
  pinMode(CONTROL3, OUTPUT);
 
  Serial.begin(9600);
  
}

void loop() {
 
  for (int i = 0; i < 16; i++) {
    digitalWrite(CONTROL0, (i&15)>>3);
    digitalWrite(CONTROL1, (i&7)>>2);
    digitalWrite(CONTROL2, (i&3)>>1);
    digitalWrite(CONTROL3, (i&1));
    muxarray0[i] = analogRead(0);
  
  for (int i = 0; i < 16; i++) {
   // subtract the last reading:
  total[i]= total[i] - readings[i][Index];         
  // read from the sensor:  
  readings[i][Index] = muxarray0[i]; 
  // add the reading to the total:
  total[i]= total[i] + readings[i][Index];       
  // advance to the next position in the array:  
  Index = Index + 1;                    

  // if we're at the end of the array...
  if (Index >= numReadings)              
    // ...wrap around to the beginning: 
    Index = 0;                           

  // calculate the average:
  average[i] = total[i] / numReadings;         
  // send it to the computer as ASCII digits
 }
int lowerBits = average[i] & 0x007F;
int upperBits = (average[i] >> 7) & 0x0007;
Serial.println(lowerBits);
Serial.println(upperBits);
}

delay(20);  
}

I've tried changing the values of how things are bitshifted, and the results are the same. I tried writing each bitshifted value, upper and lower from my code above, into separate average arrays and then printing those arrays. I can see that when a pot is adjusted, it's corresponding value in the average array is changed and so is the value on either side. This seems like when bit shifting, the bits that are moved are moved into the next spot in the array? Is this possible, and how can I get around this? Is there a better way to bit shift values from an array?

Why do you have two nested loops that both use ‘i’ as the iterator? This cannot be what you really wanted.

michaelallen:
Here’s the full code right now.

  for (int i = 0; i < 16; i++) {

for (int i = 0; i < 16; i++) {
}
}

I was using j for the second loop, but changed it at some point during experimenting.

I have tried changing it back to j, and also using just one 'for' loop. In both cases I still get overwriting data between pots

I rethought my strategy. Rather than writing pots to an array and then setting up all those arrays to average, I just averaged the value before writing to the array to begin with. There is still a little bit of interaction, but it seems like only at certain settings between pots. And the change in value is only 3 or 4 out of 1023, while before it was a few hundred. And this way, I can take a ton of reads without taking up a lot of memory.

#define CONTROL0 2
#define CONTROL1 3
#define CONTROL2 4
#define CONTROL3 5

byte muxarray0[33];

const int numReadings = 100;

int readings[numReadings];      // the readings from the analog input
int Index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

// constants won't change. They're used here to 
// set pin numbers:
const int ledPin =  13;

const int Sw1 = 12;    
const int Sw2 = 21;
const int Sw3 = 11;
const int Sw4 = 22;
const int Sw5 = 10;
const int Sw6 = 23;
const int Sw7 = 9;
const int Sw8 = 8;
const int Sw9 = 7;
const int Sw10 = 6;
const int Sw11 = 15;

const int pot17 = 16;
const int pot18 = 17;


void setup() {
  pinMode(CONTROL0, OUTPUT);
  pinMode(CONTROL1, OUTPUT);
  pinMode(CONTROL2, OUTPUT);
  pinMode(CONTROL3, OUTPUT);
  
   // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT); 
  
  // initialize the pushbutton pin as an input:
  pinMode(Sw1, INPUT);  
  pinMode(Sw2, INPUT);
  pinMode(Sw3, INPUT);
  pinMode(Sw4, INPUT);
  pinMode(Sw5, INPUT);
  pinMode(Sw6, INPUT);
  pinMode(Sw7, INPUT); 
  pinMode(Sw8, INPUT);
  pinMode(Sw9, INPUT);
  pinMode(Sw10, INPUT);
  pinMode(Sw11, INPUT);
  
  pinMode(16, INPUT);
  pinMode(17, INPUT);
  Serial.begin(9600);
  
}

void loop() {
  muxarray0[0] = 0xc0; // denote beginning of data
  byte k=1;
  for (int i = 0; i < 16; i++) {
    digitalWrite(CONTROL0, (i&15)>>3);
    digitalWrite(CONTROL1, (i&7)>>2);
    digitalWrite(CONTROL2, (i&3)>>1);
    digitalWrite(CONTROL3, (i&1));
for (Index = 0; Index < 100; Index++){
// subtract the last reading:
  total= total - readings[Index];         
  // read from the sensor:  
  readings[Index] = analogRead(0); 
  // add the reading to the total:
  total= total + readings[Index];       
                        
  // calculate the average:
  average = total / numReadings;         
  // send it to the computer as ASCII digits
}
  muxarray0[k++] = average & 0x007f;
  muxarray0[k++] = (average >> 7) & 0x0007;
}
Serial.write(muxarray0, 33);

int Sw1State = digitalRead(Sw1);
int Sw2State = digitalRead(Sw2);
int Sw3State = digitalRead(Sw3);
int Sw4State = digitalRead(Sw4);
int Sw5State = digitalRead(Sw5);
int Sw6State = digitalRead(Sw6);  
int Sw7State = digitalRead(Sw7);
int Sw8State = digitalRead(Sw8);
int Sw9State = digitalRead(Sw9);
int Sw10State = digitalRead(Sw10);
int Sw11State = digitalRead(Sw11);

int pot17State = analogRead(pot17);
int pot18State = analogRead(pot18);

int lowerBits17 = pot17State & 0x007F; //bitmask and bitshift to make 7bit 0-127 and 3bit 0-7
int upperBits17 = (pot17State >> 7) & 0x0007;
int lowerBits18 = pot18State & 0x007F; //bitmask and bitshift to make 7bit 0-127 and 3bit 0-7
int upperBits18 = (pot18State >> 7) & 0x0007;

Serial.write(lowerBits17);
Serial.write(upperBits17);
Serial.write(lowerBits18);
Serial.write(upperBits18);
Serial.write(Sw1State);
Serial.write(Sw2State);
Serial.write(Sw3State);
Serial.write(Sw4State);
Serial.write(Sw5State);
Serial.write(Sw6State);
Serial.write(Sw7State);
Serial.write(Sw8State);
Serial.write(Sw9State);
Serial.write(Sw10State);
Serial.write(Sw11State);
 // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (Sw1State == HIGH) {     
    // turn LED on:    
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW); 
  }
delay(20);  
}

I got the impression that you wanted a moving average of the readings of each POT, but what you have here is simply the average taken over 100 readings on each iteration.

michaelallen:

for (Index = 0; Index < 100; Index++){

// subtract the last reading:
  total= total - readings[Index];         
  // read from the sensor: 
  readings[Index] = analogRead(0);
  // add the reading to the total:
  total= total + readings[Index];       
                       
  // calculate the average:
  average = total / numReadings;         
  // send it to the computer as ASCII digits
}
  muxarray0[k++] = average & 0x007f;
  muxarray0[k++] = (average >> 7) & 0x0007;

From the way the code looks now, I see no reason to retain the readings in an array. You could just do this:

   long total = numReadings / 2;
   for (Index = 0; Index < numReadings; Index++)
      total += analogRead(0);

   average = total / numReadings;

   muxarray0[k++] = average & 0x007f;
   muxarray0[k++] = (average >> 7) & 0x0007;

This will take the 100 (numReadings) readings and average them, and spit out the average. No need to save them in an array unless you need a moving average.

oh interesting. Really, I don't want a ton of data going into pure data since I'll be running this on a raspberry pi and it just cant handle all the data. So just a simple average will be fine. I'll try your code and see if I get less interaction.