need guidance: 6' x 6' arduino based multitouch surface

Hey guys, so for my last semester studying Art X at the University of GA I tackled the task of making an interactive projection screen for my performance work. I succeeded in implementing a fairly accurate single-touch version of it back in December:

http://www.amanamun.com/blog/?p=24

When you press a place where a row and a column cross, the voltage of the column (determined by the resistor sitting at the top connecting it to the 5v power source) is fed to the analog input the row runs into. For some reason, however, when I press in a given position multiple analog inputs receive values. I've tried adding in delays between the different analogRead() calls and even tried the:

analogRead(row);
delay(10);
rowVal = analogRead(row);
delay(10);

trick but it doesn't seem to solve the problem. Originally my best guess was that my pulldown resistors ( originally 150 ohms ) we're the wrong value and that the voltage was getting to the other inputs via the ground wire, but I pulled off the pulldown resistors and still was able to replicate how a single voltage fed to one analog input would affect all the other analog inputs. I read somewhere on this board that the analog inputs are actually a single multiplexed input, are there any multiplexing tricks I can use to prevent this issue? What are my options to isolate the inputs so I can make this solid and multitouch for each row?

I'm also considering attaching all the rows straight to ground and using the onboard pullup resistors. Is this a bad idea? Can the excess voltage leak to the other input rows via the groundwire like I was concerned about?

I understand that while the scale is large this is a very basic circuit, and there may some very fundamental things I still don't understand about electrical engineering. I would really appreciate some direction asap.

Here's the most recent version of it's arduino code:

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include "OSCClass.h"

byte mac[]      = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte localIP[]  = { 169, 254, 229, 100 };
byte remoteIP[] = { 169, 254, 229, 56 };

unsigned int localPort = 7040;      // local port to listen on
unsigned int remotePort = 7050; // holds received packet's originating port

char *topAddress = "/arduino";
char *subAddress[2] = { "/analog", "/digital" };
  
OSCMessage recMessage;
OSCMessage sendMessage;
  
OSCClass osc(&recMessage);

long quiltState[128];
long runningAvgs[16];

void setup() {
  
  // start the Ethernet and UDP:
  Ethernet.begin(mac, localIP);
  osc.begin(localPort);
  
  // for debugging
  Serial.begin(19200);
  
  enableInputs();
  pullUp();
  
  // clear osc buffer
  osc.flush();
  
  // configure outgoing osc
  sendMessage.setIp(remoteIP);
  sendMessage.setPort(remotePort);
  sendMessage.setTopAddress(topAddress);
  sendMessage.setSubAddress(subAddress[0]);
  
  for( int i = 0; i < 128; i++) {
     quiltState[i] = 0;
  }
  
  for( int i = 0; i < 16; i++) {
     runningAvgs[i] = 0;
  }

}

void loop() {
  
  for( int i = 0; i < 16; i++) {
    
    
     //analogRead(i);
     //delay(10);
     
     quiltState[i*8] = quiltState[i*7];
     quiltState[i*7] = quiltState[i*6];
     quiltState[i*6] = quiltState[i*5];
     quiltState[i*5] = quiltState[i*4]; 
     quiltState[i*4] = quiltState[i*3];
     quiltState[i*3] = quiltState[i*2];
     quiltState[i*2] = quiltState[i];
     
     quiltState[i] = long(analogRead(i));
     
     runningAvgs[i] = (quiltState[i] + quiltState[i*2] + quiltState[i*3] + quiltState[i*4] + quiltState[i*5] + quiltState[i*6] + quiltState[i*7] + quiltState[i*4] ) / 8;
     
     //delay(10);
     
     //long row = long(i);
     //sendMessage.setArgs( "ii", &row, &quiltState[i] );
     //osc.sendOsc(&sendMessage);
     
     /**
     Serial.print(i);
     Serial.print(": ");
     Serial.print(quiltState[i]);
     Serial.print(" ");
     **/
     
  }
  
  //Serial.println("");
  sendMessage.setArgs( "iiiiiiiiiiiiiiii", &runningAvgs[0], &runningAvgs[1], &runningAvgs[2], &runningAvgs[3], &runningAvgs[4], &runningAvgs[5], &runningAvgs[6], &runningAvgs[7], &runningAvgs[8], &runningAvgs[9], &runningAvgs[10], &runningAvgs[11], &runningAvgs[12], &runningAvgs[13], &runningAvgs[14], &runningAvgs[15]);
  osc.sendOsc(&sendMessage);
  //delay(10);
}

void enableInputs() {
  
  pinMode(A0, INPUT);  
  pinMode(A1, INPUT);  
  pinMode(A2, INPUT);  
  pinMode(A3, INPUT);  
  pinMode(A4, INPUT);  
  pinMode(A5, INPUT);  
  pinMode(A6, INPUT);  
  pinMode(A7, INPUT);  
  pinMode(A8, INPUT);  
  pinMode(A9, INPUT);  
  pinMode(A10, INPUT);  
  pinMode(A11, INPUT);  
  pinMode(A12, INPUT);  
  pinMode(A13, INPUT);  
  pinMode(A14, INPUT);  
  pinMode(A15, INPUT);  
  
}

void pullUp() {
  
  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);
  digitalWrite(A3, HIGH);
  digitalWrite(A4, HIGH);
  digitalWrite(A5, HIGH);
  digitalWrite(A6, HIGH);
  digitalWrite(A7, HIGH);
  digitalWrite(A8, HIGH);
  digitalWrite(A9, HIGH);
  digitalWrite(A10, HIGH);
  digitalWrite(A11, HIGH);
  digitalWrite(A12, HIGH);
  digitalWrite(A13, HIGH);
  digitalWrite(A14, HIGH);
  digitalWrite(A15, HIGH);
  
}

Hey KE7GKP, thanks for the information.

I had come to the same conclusion as you about using a multiplexed arrangement as opposed to my current design. I already have a bunch of 4067 multiplexors to implement the next version of the quilt with, however, I still need to figure out how to make the current quilt as solid as it can possibly be thanks to some deadline issues.

Should I be using different value pulldown resistors for the different rows? I've tried 150 ohms and 10k ohm resistors so far and the 150 ohms ones seemed to work better. I should clarify that the issue isn't with a given column feeding a given row a range of values that makes it difficult to determine the specific column, but somehow instead a column feeding a given row will cause multiple rows to receive some of the electricity which the column is delivering only to that row. I've heard of dropping a capacitor across the power supplies of multiplexors to solve noise issues similar to mine. Considering that the analog inputs on the arduino mega are actually multiplexed, would doing this to the arduino be an effective approach?

Regardless, I'll be sure to test the resistance variation created by the row-column contacts as you suggested.
Thanks.

bump.

seriously guys, no one can tell me why I'm receiving analog values on rows other than the one i'm feeding v+ from a column? only one row is getting power but multiple rows read input. I can determine the column without too much effort, it seems absurd that I can't figure out which analog input on the arduino is the one receiving the voltage...

great feedback. i've done my best to cut the aforementioned unknowns down to the resistor values and the variability of the switch matrix performance, but these two still prove to be enough to slow progress to a crawl.

i cleared up my deadline issue, so now i'm finishing the planning for mux implementation. the video a posted before proves that for single-touch the resistor ladder approach works, though noise issues make it slow and difficult to keep functional. my redesign will be using multiplexed pressure sensitive soft buttons. I'll share diagrams and code on here for the interested when that pans out.

thanks again for the help.

You seem to have plenty of IO pins available - why not give the keypad library a try and treat this like a big 16 x 16 kepyad?

I did not come to the same conclusion from watching that video. It seemed to me that there were a large percentage of "hits" where the computer was WAY OFF in its determination of the location of the hit. It looked to me like they were also having problems with accuracy and reliability.

This is a fair assumption. The last press happens to particularly validate that statement. For the record, however, initially after creating the the quilt I was able to determine the location of the press 100% of the time by only looking at the row with the highest input voltage (since all showed input voltage with presses) and combining that with a lookup table for the column voltages. What would happen, however, is that the position would often read a number of incorrect values for a few seconds after the initial press before it settled on the correct value (using a running average). Then when I released the "button" it would read some random voltage values on various inputs. Using an envelope (don't read values before this much time or number of readings, ignore values outside a threshold of the average after that point and consider the change a note-off event) I was able to make it work with single-touch, but the delay between press and response made it pretty worthless for my purposes.

You seem to have plenty of IO pins available - why not give the keypad library a try and treat this like a big 16 x 16 kepyad?

This is might be a good way for me to make temporary use of the current quilt until I have the new one done. Would I run a different digital output each of the different columns? How would this work? Like KE7GKP said, over time my attempts to solve these problems and add multitouch became more and more difficult because of the resistive ladder approach. Hopefully others attempting similar projects can learn from my struggle; use a multiplexed approach instead.

My redesign attempts to continue to use the analog inputs by sequentially reading multiplexed rows of pressure sensing soft buttons (using velostat). Hopefully this will be much more reliable over time despite the use of variable resistance.

However, I WAS pretty impressed by whatever they were using to generate the graphics that were being fed to the projector. VERY nice!

I appreciate that. It was just a modified version of the softbody processing example by Ira Greenberg. I'd be happy to post the code if anyone would find it useful.

Using the keypad library - I think it sends Highs thru the columns, then reads the rows to see if any changed state. Knowing which column was high it can narrow to a single pin.