zPatch / zPatch.github.io

19 stars 3 forks source link

Instruction / code for "Multi-Sensor Synergies" #1

Open oveddan opened 6 years ago

oveddan commented 6 years ago

In the research paper and in pictures it talks about different version of the z-patch with multi-sensors:

zPatch configurations with more than two electrodes are also possible. In fact, combinations allow for increasing the spatial resolution beyond their sum: Figure 6a and 12c shows a layout in which four electrodes allow us to infer pressure from nine locations, based on common activation.

Flexibility with pin-configurations allows us to minimize complexity of such sensors. For example, the four electrodes shown in Figure 6a, can be pulled low sequentially and the voltage can be measured by a shared electrode at the bottom. In the depicted setup (also seen in Figure 12c),

What does it mean by "four electrodes...can be pulled low sequentially" ?

It would be great if there was some sort of schematic or instructions on how to set this up, and some sample code.

fkeel commented 6 years ago

True, those sensors are not documented. Apologies and thanks for pointing it out. I'll try to eventually get around to it.

Quick answer to your question: The sensor you speak of can be seen functioning at ~0:35 in this video: https://youtu.be/88QgGQLsvpo

The physical setup is a large conductive square at the bottom, a piezo resistive element in the centre, and four conductive pads on top, arranged like the windows logo. Like this: schematic_fusion

I just threw together example code. Its not tested and I probably forgot some detail, but it should give you the basic idea:

int sensorValues[4];

void setup() {

  //set top four pads as output and pull them high
  for (int i = 2, i++ i < 6) {
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);
  }

}

void loop() {

  //sequentially pull one of the top pads low
  //read from bottom pad
  for (int i = 2, i++ i < 6) {
    digitalWrite(i, LOW);
    sensorValues[i - 2] = analogRead(A0);
    digitalWrite(i, HIGH);
  }
  /*
    the sensorValues array now stores the pressure value
    as measured between the bottom pad and each top pad.
    you can now also do the capactivie proximity sensing
    but you only get a single value from it
  */
}
oveddan commented 6 years ago

Thanks for your quick response, this is super helpful! I will try to make one and document it :)

fkeel commented 6 years ago

Please do :-)

The sensor in the paper has the odd shape to increase the area where overlap occurs, we get the 3*3 touch positions by checking if multiple pads are compressed simultaneously. Kind of similar to pressure sensor matrices. You might want to dig around here: https://github.com/eTextile/Matrix

oveddan commented 6 years ago

@fkeel I'm not entirely clear how the capacitive proximity sensing works, as it's a bit challenging to understand how the guts of the capactiveRead function works.

Assuming I just copy and paste the code for that function, what pins would I be passing to it as arguments? Would it be the one for the bottom piece and any of the top pieces?

fkeel commented 6 years ago

@oveddan

EDIT: Yes, exactly as you say. EDIT2: Tweaked code for readability

Sorry, its been a while. You could do: capacitiveRead(A0, 2, 10)

I wrote the capacitiveRead function to accept two pins as I wanted the whole thing symmetrical, so that it doesn't matter which way they are plugged into the Arduino. With the 2 by 2 setup that no longer really makes sense.

You could also try this, it should be a tad faster and easier to understand (you'd call it like this: simpleCapacitiveRead(A0, 10) ):

int simpleCapacitiveRead(measuringPin, repetitions) { //reads the capacitance from a single pin
  int capacitanceA = 0; //capacitance measured at single reading
  int capacitance = 0; //overall capacitance measured

  for (int i = 0; i < repetitions; i++) { //read multple times, because the signal is messy

// this part charges an internal capacitor connected to the ADC
// its discharge speed depends on the capacitance of the pin
// this allows us to infer touch/proximity

    pinMode(measuringPin, INPUT_PULLUP); 
    ADMUX |=   0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(measuringPin, INPUT);
    capacitanceA = analogRead(measuringPin);

    capacitance = capacitance + capacitanceA; //get the sum of all readings (I could have also just done average)

  }
  return capacitance;
}

again, the above code is not tested.

fkeel commented 6 years ago

(I tweaked a brainfart above and change variable names to make more sense, just FYI)

oveddan commented 6 years ago

Awesome, I've just made the sensor, looking forward to trying the above. Let you know if I run into any issues! img_7440 img_7444 img_7448

oveddan commented 6 years ago

@fkeel ok i've made some good progress, I've gotten it mostly working, except simpleCapactiveRead doesn't work, unless I comment out some code. here is my code now:

const int BOTTOM_SENSOR = A0;
const int TOP_SENSORS[] = {A1, A2, A3, A4};
const int numSensors = 4;
float k = 0.2; // this adjusts the low-pass filter: 
               // 0 == no signal
               // 0.001 == very aggressive (slow but steady) 
               // 0.999 == not aggressive at all (fast but noisy)
               // 1 == no filter

int sensorValues[4];
int baseline;
int prevCapValues;

void setup() {

  Serial.begin(9600);
  delay(50);
  for (int i = 0; i< 4; i++) {
    pushRead(TOP_SENSORS[i], 3);  
  }
  //baseline = simpleCapacitiveRead(A0, 10); //set the baseline for capacitive readings
  baseline = capacitiveRead(10); //set the baseline for capacitive readings
  delay(50);

}

void loop() {

 // int rawCapacitance = simpleCapacitiveRead(A0, 10);
  int rawCapacitance = capacitiveRead(10);
  rawCapacitance = (rawCapacitance - baseline); //baseline capacitance value 
  int capValues = prevCapValues + (k * (rawCapacitance - prevCapValues)); //filter capacitive value
  prevCapValues = capValues; //for filtering

  Serial.print(capValues);
  Serial.print(" ");
  //sequentially pull one of the top pads low
  //read from bottom pad
  for (int i = 0; i< numSensors; i++) {
    sensorValues[i] = pushRead(TOP_SENSORS[i], 3);  
  }
  for(int i = 0; i < numSensors; i++) {
    if (i != numSensors - 1) {
      Serial.print(sensorValues[i]); 
      Serial.print(" ");
    } else
       Serial.println(sensorValues[i]); 
  }
  /*
    the sensorValues array now stores the pressure value
    as measured between the bottom pad and each top pad.
    you can now also do the capactivie proximity sensing
    but you only get a single value from it
  */

  delay(5); // (so as not to completely flood the serial port)
}

int pushRead(int pin, int numSamples) {
  int resistance;
  pinMode(pin, OUTPUT);

  for (int i = 0; i < numSamples; i++) {
    digitalWrite(pin, LOW);
    pinMode(BOTTOM_SENSOR, INPUT_PULLUP);
    resistance += analogRead(BOTTOM_SENSOR);
    digitalWrite(pin, HIGH);
  }
  return resistance / numSamples;
}

int capacitiveRead(int number) {
  int pinA = A0;
  int pinB = A1;
  int capacitanceA = 0;
  int capacitanceB = 0;
  int capacitance = 0;

  for (int i = 0; i < number; i++) {
    pinMode(pinA, INPUT);
    pinMode(pinB, INPUT_PULLUP);
    ADMUX |=  0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinB, INPUT);
    capacitanceB = analogRead(pinB);

    pinMode(pinB, INPUT);
    pinMode(pinA, INPUT_PULLUP);
    ADMUX |=   0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinA, INPUT);
    capacitanceA = analogRead(pinA);

    capacitance = capacitance + capacitanceA + capacitanceB;

  }
  return capacitance;
}

I could not get simpleCapactiveRead to ever return proper values...so I stuck with the original capactiveRead function.

As the code above stands, the sensorValues array properly gets updated and has decreased resistance when one of the corresponding pads are pressed. However, the capactive read function returns inconsistant, randomish values that don't change much. If I comment out the code that calls pushRead, capacativeRead works as expected, increasing in value when hovering over the sensor. I have a feeling that pushRead is leaving the pins in an invalid state.

Any idea what I'm doing wrong?

oveddan commented 6 years ago

Nevermind...the above doesnt seem to work anymore :(

oveddan commented 6 years ago

@fkeel disregard the above, but I'm onto what the issue is. If I have the below code:

const int BOTTOM_SENSOR = A0;
const int TOP_SENSORS[] = {A1, A2, A3, A4};
const int numSensors = 4;
float k = 0.2; // this adjusts the low-pass filter: 
               // 0 == no signal
               // 0.001 == very aggressive (slow but steady) 
               // 0.999 == not aggressive at all (fast but noisy)
               // 1 == no filter

int sensorValues[4];
int baseline;
int prevCapValues;

void setup() {

  Serial.begin(9600);
  delay(50);
    //set top four pads as output and pull them high
  for (int i = 0; i< 4; i++) {
    pinMode(TOP_SENSORS[i], OUTPUT);
    digitalWrite(TOP_SENSORS[i], HIGH);
  }
  baseline = capacitiveRead(10); //set the baseline for capacitive readings
  delay(50);
}

void loop() {
  int rawCapacitance = capacitiveRead(10);
  rawCapacitance = (rawCapacitance - baseline); //baseline capacitance value 
  int capValues = prevCapValues + (k * (rawCapacitance - prevCapValues)); //filter capacitive value
  prevCapValues = capValues; //for filtering

  Serial.println(capValues);

  delay(5); // (so as not to completely flood the serial port)
}

int capacitiveRead(int number) {
  int pinA = A0;
  int pinB = A1;
  int capacitanceA = 0;
  int capacitanceB = 0;
  int capacitance = 0;

  for (int i = 0; i < number; i++) {
    pinMode(pinA, INPUT);
    pinMode(pinB, INPUT_PULLUP);
    ADMUX |=  0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinB, INPUT);
    capacitanceB = analogRead(pinB);

    pinMode(pinB, INPUT);
    pinMode(pinA, INPUT_PULLUP);
    ADMUX |=   0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinA, INPUT);
    capacitanceA = analogRead(pinA);

    capacitance = capacitance + capacitanceA + capacitanceB;

  }
  return capacitance;
}

The capacitive reading always has a nearly constant value, regardless of the hover.

If I comment out the lines:

    pinMode(TOP_SENSORS[i], OUTPUT);
    digitalWrite(TOP_SENSORS[i], HIGH);

Then this solves the issue and it works as expected. What state should the pins be in before checking the capacitance?

oveddan commented 6 years ago

Alright I seemed to figure it out! I had to set the pinMode back to INPUT, so for example in the setup:

 for (int i = 0; i< 4; i++) {
    pinMode(TOP_SENSORS[i], OUTPUT);
    digitalWrite(TOP_SENSORS[i], HIGH);
    pinMode(TOP_SENSORS[i], INPUT);
  }

This somehow fixed everything. My final code looks like:

const int BOTTOM_SENSOR = A0;
const int TOP_SENSORS[] = {A1, A2, A3, A4};
const int numSensors = 4;
float k = 0.2; // this adjusts the low-pass filter: 
               // 0 == no signal
               // 0.001 == very aggressive (slow but steady) 
               // 0.999 == not aggressive at all (fast but noisy)
               // 1 == no filter

int sensorValues[4];
int baseline;
int prevCapValues;

void setup() {

  Serial.begin(9600);
  delay(50);
    //set top four pads as output and pull them high
  for (int i = 0; i< 4; i++) {
    pinMode(TOP_SENSORS[i], OUTPUT);
    digitalWrite(TOP_SENSORS[i], HIGH);
    pinMode(TOP_SENSORS[i], INPUT);
  }
  baseline = capacitiveRead(10); //set the baseline for capacitive readings
  delay(50);
}

void loop() {

  // sequentially read the push strength from each pad
  for (int i = 0; i< numSensors; i++) {
    int sensorValue = pushRead(TOP_SENSORS[i], 3);  
    Serial.print(sensorValue); 
    Serial.print(" ");
  }

  // get single capacitance value which represents the hover effect
  int rawCapacitance = capacitiveRead(10);
  rawCapacitance = (rawCapacitance - baseline); //baseline capacitance value 
  int capValues = prevCapValues + (k * (rawCapacitance - prevCapValues)); //filter capacitive value
  prevCapValues = capValues; //for filtering

  Serial.println(capValues);

  delay(5); // (so as not to completely flood the serial port)
}

int pushRead(int pin, int numSamples) {
  int resistance;
  pinMode(pin, OUTPUT);

  for (int i = 0; i < numSamples; i++) {
    digitalWrite(pin, LOW);
    pinMode(BOTTOM_SENSOR, INPUT_PULLUP);
    resistance += analogRead(BOTTOM_SENSOR);
    digitalWrite(pin, HIGH);
  }
  pinMode(pin, INPUT);
  return resistance / numSamples;
}

int capacitiveRead(int number) {
  int pinA = A0;
  int pinB = A1;
  int capacitanceA = 0;
  int capacitanceB = 0;
  int capacitance = 0;

  for (int i = 0; i < number; i++) {
    pinMode(pinA, INPUT);
    pinMode(pinB, INPUT_PULLUP);
    ADMUX |=  0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinB, INPUT);
    capacitanceB = analogRead(pinB);

    pinMode(pinB, INPUT);
    pinMode(pinA, INPUT_PULLUP);
    ADMUX |=   0b11111;
    ADCSRA |= (1 << ADSC); //start conversion
    while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
    ADCSRA |= (1 << ADIF); //reset the flag
    pinMode(pinA, INPUT);
    capacitanceA = analogRead(pinA);

    capacitance = capacitance + capacitanceA + capacitanceB;

  }
  return capacitance;
}

When I have more time will try to figure out how to get the simpleCapacitiveRead function working

fkeel commented 6 years ago

What is the 'capacitiveRead' function you're using, is it what you posted before? Is it giving you a hover-measure? Why do you not want to use it? Just glancing at your code makes me think everything looks good.

(I don't have anything to test at the moment).

On Tue, Oct 9, 2018 at 2:55 AM Dan Oved notifications@github.com wrote:

Alright I seemed to figure it out! I had to set the pinMode back to INPUT, so for example in the setup:

for (int i = 0; i< 4; i++) { pinMode(TOP_SENSORS[i], OUTPUT); digitalWrite(TOP_SENSORS[i], HIGH); pinMode(TOP_SENSORS[i], INPUT); }

This somehow fixed everything. My final code looks like:

const int BOTTOM_SENSOR = A0;const int TOP_SENSORS[] = {A1, A2, A3, A4};const int numSensors = 4;float k = 0.2; // this adjusts the low-pass filter: // 0 == no signal // 0.001 == very aggressive (slow but steady) // 0.999 == not aggressive at all (fast but noisy) // 1 == no filter int sensorValues[4];int baseline;int prevCapValues; void setup() {

Serial.begin(9600); delay(50); //set top four pads as output and pull them high for (int i = 0; i< 4; i++) { pinMode(TOP_SENSORS[i], OUTPUT); digitalWrite(TOP_SENSORS[i], HIGH); pinMode(TOP_SENSORS[i], INPUT); } baseline = capacitiveRead(10); //set the baseline for capacitive readings delay(50); // // set top four pads as output and pull them high // for (int i = 0; i < 4; i++) { // pinMode(TOP_SENSORS[i], OUTPUT); // digitalWrite(TOP_SENSORS[i], HIGH); // } } void loop() {

// sequentially read the push strength from each pad for (int i = 0; i< numSensors; i++) { int sensorValue = pushRead(TOP_SENSORS[i], 3); Serial.print(sensorValue); Serial.print(" "); }

// get single capacitance value which represents the hover effect int rawCapacitance = capacitiveRead(10); rawCapacitance = (rawCapacitance - baseline); //baseline capacitance value int capValues = prevCapValues + (k * (rawCapacitance - prevCapValues)); //filter capacitive value prevCapValues = capValues; //for filtering

Serial.println(capValues);

delay(5); // (so as not to completely flood the serial port) } int pushRead(int pin, int numSamples) { int resistance; pinMode(pin, OUTPUT);

for (int i = 0; i < numSamples; i++) { digitalWrite(pin, LOW); pinMode(BOTTOM_SENSOR, INPUT_PULLUP); resistance += analogRead(BOTTOM_SENSOR); digitalWrite(pin, HIGH); } pinMode(pin, INPUT); return resistance / numSamples; } int capacitiveRead(int number) { int pinA = A0; int pinB = A1; int capacitanceA = 0; int capacitanceB = 0; int capacitance = 0;

for (int i = 0; i < number; i++) { pinMode(pinA, INPUT); pinMode(pinB, INPUT_PULLUP); ADMUX |= 0b11111; ADCSRA |= (1 << ADSC); //start conversion while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish ADCSRA |= (1 << ADIF); //reset the flag pinMode(pinB, INPUT); capacitanceB = analogRead(pinB);

pinMode(pinB, INPUT);
pinMode(pinA, INPUT_PULLUP);
ADMUX |=   0b11111;
ADCSRA |= (1 << ADSC); //start conversion
while (!(ADCSRA & (1 << ADIF))); //wait for conversion to finish
ADCSRA |= (1 << ADIF); //reset the flag
pinMode(pinA, INPUT);
capacitanceA = analogRead(pinA);

capacitance = capacitance + capacitanceA + capacitanceB;

} return capacitance; }

When I have more time will try to figure out how to get the simpleCapacitiveRead function working

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/zPatch/zPatch.github.io/issues/1#issuecomment-428024547, or mute the thread https://github.com/notifications/unsubscribe-auth/ADsW3p8O0shbLjR3WzSAjiNU1hO_IcIkks5ui_PogaJpZM4XMBtq .

-- Paul Strohmeier - PhD candidate, University of Copenhagen

       [group] body-ui.eu
       [web] paulstrohmeier.info
       [mail] paul.strohmeier@gmail.com