unstable readings from touchRead() esp32

hi all,
this is the first time im experimenting with the touch sensor on the esp32
im using the standard example library of Examples>Esp32>touch>touchread for my code

i didnt connect any wires to the esp32 and im still getting a lot of false positives.

T2 permanantly shows 0 reading.
T1 and T0 has a very unstable reading.

i plotted the reading in the serial plotter with delay(50);
attached images

looking in serial monitor it shows that the places where the graph dips the reading is often <10 which is definitely a false positive.

is this a known issue if so how to i overcome it or is my board faulty?

thanks in advance

How do you know you are reading a lot of false positives with nothing connected to the ESP32 GPIO? Have you programmed the GPIO pins of the ESP32 GPIO Matrix?

Can you post your code in code tags?

This is the standard example that comes with the ESP32 library, which i am using.

// ESP32 Touch Test
// Just test touch pin - Touch0 is T0 which is on GPIO 4.

void setup()
{
  Serial.begin(115200);
  delay(1000); // give me time to bring up serial monitor
  Serial.println("ESP32 Touch Test");
}

void loop()
{
  Serial.println(touchRead(T3));  // get value using T3
  delay(100);
}

i did not connect anything to the esp32 so the normal reading is 118 when im not touching the GPIO15 / T3 pin on the esp.

i say false positive because the reading goes down to <10 sometimes well below the threshold to consider it a touch input. if i use this in my code a touch will get registered even when i dont touch the pin.

i thought perhaps 50-100 ms delay was too short so i changed it back to delay(1000); still the same result.

Have you programmed the GPIO pins of the ESP32 GPIO Matrix?

no im using the stock GPIO definitions and im not advanced enough a user to know how to change them.

// ESP32 Touch Test
// Just test touch pin - Touch0 is T0 which is on GPIO 4.
int pin;
void setup()
{
  Serial.begin(115200);
  delay(1000); // give me time to bring up serial monitor
  Serial.println("ESP32 Touch Test");
}

void touchread()
{
int times = 0;
int wrong = 0;
int touchstat;
Serial.print("Testing ");
Serial.print(pin);
Serial.print(":   ");
while (times <= 1000) {

  touchstat = touchRead(pin);
  
  times = times+1;
  if (touchstat <= 80){
    Serial.print(" ");
    Serial.print(touchstat);
    wrong = wrong + 1;
     }
  delay(1000);
}

Serial.print(" ");
Serial.print("// Errors ");
Serial.println(wrong);
Serial.println();

}
void loop()
{
  pin = T0;
  touchread();
  pin = T3;
  touchread();
  pin = T4;
  touchread();
   pin = T5;
  touchread();
  pin = T6;
  touchread();
   pin = T7;
  touchread();
  pin = T8;
  touchread();
  pin = T9;
  touchread();
 
Serial.print("Test Concluded");



delay(6000000);


  
  
}

Result on serial print

ESP32 Touch Test
Testing 4: 34 15 29 12 71 10 68 31 5 56 14 18 // Errors 12

Testing 15: 1 59 28 57 71 34 41 79 75 43 // Errors 10

Testing 13: 45 45 38 0 65 34 72 56 21 51 // Errors 10

Testing 12: 75 20 50 23 27 20 3 68 57 4 26 26 39 80 71 // Errors 15

Testing 14: 70 4 52 77 5 11 0 14 23 59 35 22 30 66 36 12 73 // Errors 17

Testing 27: 28 55 58 55 38 44 64 64 76 49 44 17 // Errors 12

Testing 33: 66 42 74 67 78 34 65 51 23 57 4 15 11 45 55 73 11 41 65 // Errors 19

Testing 32: 72 47 17 53 63 3 45 25 40 61 // Errors 10

Test Concluded

Perhaps going to https://esp32.com/ might net you an answer.

the error rate was 1-2% . the issue is solved by getting an average of 100 readings.

i used the following code

void setup(){
Serial.begin(115200);
int touchpin = T3; //sensing touch on touchpin 3 equivalent of GPIO 15
}


void loop()
{



int touchstat = 0;
for(int i=0; i< 100; i++){ // used to get a hundred readings of the status of touch
  touchstat = touchstat + touchRead(touchpin); // adding the hundred readings within the for loop
  
}
 touchstat = touchstat / 100; // dividing by hundred to get average
  if (touchstat <= 60){           // i set 60 as threshold but it could be whatever number is appropriate
    Serial.print("Touch Detected ");
    Serial.println(touchstat);
    delay(2000);

}}

My fix:

/*
Antonio de Vincentiis
Tested on Heltec WiFi Kit32
*/
// TOUCH
const int TOUCH_NUM_READINGS = 3; // number of positive read for switch on
int touchReadIndex = 0;
int touchTotal1=0; 
int touchTotal2=0; 
const int TOUCH_PIN_1=T4;
const int TOUCH_PIN_2=T5;
//const int LED=25; // default on Heltec WiFi Kit32
const int TOUCH_THRESHOLD_1 = 35;
const int TOUCH_THRESHOLD_2 = 35;


void setup() {
  pinMode(TOUCH_PIN_1, INPUT);
  pinMode(TOUCH_PIN_2, INPUT);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
  delay(1000);
}

void touch_monitor(void){
    Serial.print("Touch values:  ");
    Serial.print(touchRead(TOUCH_PIN_1));
    Serial.print(" - ");
    Serial.print(touchRead(TOUCH_PIN_2));
    Serial.println();
    Serial.print("Touch events: ");
    Serial.print(touchTotal1);
    Serial.print(" - ");
    Serial.print(touchTotal2);
    Serial.println();  
}


void loop() {
  
    if(TOUCH_THRESHOLD_1 > touchRead(TOUCH_PIN_1)){ 
      touchTotal1++;
    }
    if(TOUCH_THRESHOLD_2 > touchRead(TOUCH_PIN_2)){ 
      touchTotal2++;
    }
    
    // uncomment for activate serial monitor
    touch_monitor();
    
    if(touchTotal1>=TOUCH_NUM_READINGS){
      digitalWrite(LED, HIGH);
    }
    if(touchTotal2>=TOUCH_NUM_READINGS){ 
      digitalWrite(LED, LOW);
    }
    
    // end read sequence to the beginning
    if (touchReadIndex >= TOUCH_NUM_READINGS) { 
      touchReadIndex = 0;
      touchTotal1=0;
      touchTotal2=0;
    }
    touchReadIndex++;

    delay(100);
}

touchSwitch.ino (1.52 KB)

Hi, guys!

I, too noticed the touchRead() function does not work as expected, I can confirm the unstable readings.

This is why I implemented a completely new and ansynchronously running library for the touch buttons.

Please have a look at the first draft of my ReactiveTouch touch buttons implementation for Arduino on the ESP32 platform. You need the three files put in the same directory or into the include path with the Arduino sketch:

The code hooks into the IIR filter output provided by the ESP-IDF framework and hast stable results plus auto-calibration.

This is an example .ino file:

#include "touch_buttons.hpp"

constexpr uint8_t led_pin = 2;

constexpr int touch_input_number = 4;
constexpr uint8_t threshold_percent = 90;


ReactiveTouch buttons{};


void toggle_led() {
 digitalWrite(led_pin, !digitalRead(led_pin));
}


void setup() {
 pinMode(led_pin, OUTPUT);
 buttons.configure_input(touch_input_number,
                         threshold_percent,
                         toggle_led);
 buttons.calibrate_thresholds();
 buttons.begin();
}

void loop() {
 // You can do anything here, the code above runs asynchronously.
}

I thought that doing a series of readings and then taking the median as the reading would work.

int runningMedian() {
const int sampleSize = 3;
int samples[sampleSize];
int temp;
for (int i = 0; i < sampleSize; i++) {
samples = touchRead(touchPin);

  • }*
  • for (int i = 0; i < sampleSize - 1; i++) {*
  • for (int j = 0; j < sampleSize - 1 - i; j++) {*
  • if (samples[j] < samples[j + 1]) {*
  • temp = samples[j];*
  • samples[j] = samples[j + 1];*
  • samples[j + 1] = samples[j];*
  • }*
  • }*
  • }*
  • return samples[sampleSize / 2 + 1];*
    }
    Now in loop() instead of
  • int val = touchRead(touchPin);*
    I just call
  • int val = runningMedian();*
    and then compare val with my threshold and act on the result.
    Good luck!
    ps. all you who think Bubble Sort is wasteful, you’re entitled to your own opinion. All I know is, I solved my problem and now I’m going to bed :slight_smile:

GordPayne:
I thought that doing a series of readings and then taking the median as the reading would work.

int runningMedian() {
const int sampleSize = 3;
int samples[sampleSize];
int temp;
for (int i = 0; i < sampleSize; i++) {
samples = touchRead(touchPin);

  • }*
  • for (int i = 0; i < sampleSize - 1; i++) {*
  • for (int j = 0; j < sampleSize - 1 - i; j++) {*
  • if (samples[j] < samples[j + 1]) {*
  • temp = samples[j];*
  • samples[j] = samples[j + 1];*
  • samples[j + 1] = samples[j];*
  • }*
  • }*
  • }*
  • return samples[sampleSize / 2 + 1];*
    }
    Now in loop() instead of
  • int val = touchRead(touchPin);*
    I just call
  • int val = runningMedian();*
    and then compare val with my threshold and act on the result.
    Good luck!
    ps. all you who think Bubble Sort is wasteful, you’re entitled to your own opinion. All I know is, I solved my problem and now I’m going to bed :slight_smile:
    [/quote]
    I know I’m a little late, but this may help new people reading this topic.
    doing this kind of multiple readings at first, I was happy thinking I solved my problem, but after some intense testing I discovered that by any reason the esp32, was reading only one value and repeating it on the for function, I don’t know why but, for example, I was doing 500 reading in a for with 5ms of delay with witch one, in theory, If I don’t maintain my finger on the pin for 2,5 seconds it won’t turn on, that turns out to be false, I touched it and all it was doing was delaying my touch for 2,5 seconds,
    I could like really fast touch the pin and after 2,5 seconds it was positive for the touch, because all readings after the first one, was the same, the for statement somehow was blocking new readings, so if any time some noise came through, the 500 readings was the noise. the code that truly solved my problem, stabilizing much more the reading was this one
    ```
    *void TouchRead()
    {
      static int positiveTouchCounter = 0;
      static int positiveTouchCounterLimit = 50;
      int touchReadValue = touchRead(TouchSensor);
      if (touchReadValue < TresholdValue())
      {
        positiveTouchCounter++;
        delay(10);
      }
      else
      {
        positiveTouchCounter = 0;
      }

if (positiveTouchCounter >= positiveTouchCounterLimit)
  {
    ShelfLightState();
    positiveTouchCounter = 0;
    delay(1000);
  }
}*

```
this code waits 0.6 seconds reading, before turn on/off anything and doing 100 readings witch every single one goes through loop function, and, the 100 readings have to be positive in a roll, if it reads 99 positives and 1 false, it will reset the counter. it works really great for me, and it only delays 6 ms if the reading is positive otherwise, it will not delay the program overall.

I beleive this might be the root cause of this issue:

1 Like

In my case, taking multiple readings, even rapidly, showed that the false low readings are not repeated in subsequent measurements.

Therefore the best solution was to take just 2 readings, compare, and then keep only the higher value.
Since you can keep looping quickly, a real touch easily lasts longer than 2 readings.

Resulting code is fast, no need for delays or dozens of readings (just 2), and the low values don't throw off results.

Interesting read about the ESP32 ADC, lorbi, but I didn't have to dive in that deep yet.