torstai 7. tammikuuta 2021

Touch sensing for Arduino

I made a small christmas project (from which I might make a post later) in which I ended up using an Arduino Pro Micro (or rather a clone, Open-Smart Pro Micro). I also wanted to have two capacitive touch sensors to set brightness of a light, but the Atmega32U4 in that Arduino board does not have touch sensing circuitry build in.

I know that it is possible to still implement a capacitive touch sensing using the built-in I/O ports so I tested some methods and write about my experience here. I ended up using a method I haven't seen around so I will also explain that and link to the code.

Concept 1

The first concept is what is built-in (or at least easily installed) as a library in the Arduino IDE. The library is called CapacitiveSensor and is rather easy to use. One sensor needs two I/O pins and a large value resistor, in addition to the sensor itself (which can be a piece of aluminium foil, for example).

The basic connection is according to figure below. The left side shows circuitry internal to the microcontroller.

 

The touch sensor is connected to one I/O pin (the "drive" pin) via a resistor, which is typically mega ohm or tens of mega ohms. The sensor is also directly connected to another I/O pin (the "sense" pin).

The operation is depicted in following figure.

The drive pin on the top is configured as an output. The sense pin on the bottom is configured as an input, without pull-up resistor. With this circuit, the touch is sensed based on the time constant of the resistor and the capacitor formed by the touch pad and possibly the finger. Touching the sensor increases the capacitance to the circuit ground, which increases the time constant and that can be sensed. The operation of the circuit is as follows:

  • The drive pin is set to output a high level (red line)
  • The sense pin (blue line) is read until the pin status changes to indicate a high level in input
  • Timer is started (or current time is read)
  • The drive pin is set to output a low level (green line)
  • The sense pin (blue line) is read until the pin status changes to indicate a low level in input
  • Timer is stopped (or current time is read)
  • The time between the input state transitions is calculated

It is recommended to read the time from high-to-low and from low-to-high and calculate the average to increase the robustness of the circuit.

By monitoring the time between state transitions a change can be detected which indicates that the sensor is touched or touch is removed. At least in theory. In my tests, the sensitivity was very poor and it was difficult to detect touch robustly. Especially if any other electrical equipment was close to the sensor. I tried to add some grounded (to the circuit ground) plates near the sensor but that did not help at all.

Concept 2

This concept is very similar to the previous one but might allow getting rid of the external resistor and would use only one I/O pin per sensor. This could be achieved using the internal pull-up resistor of the microcontroller, according to figure below.

 Again, the internal parts are on the left. Rpu is the internal pull-up resistor, value of which is 20...50 kOhm in the Atmega32u40 (so quite small). The basic idea of this circuit is the same as previous one, but implementation would be a bit different.

  • Configure the I/O pin as output and drive it low to discharge the touch pad capacitance (green line)
  • After a (short) delay, start the timer
  • Configure the pin as input with pull-up resistor (red line)
  • Read the state of the input pin until it indicates a high level in input (blue line)
  • Stop the timer and calculate the time it took to charge the touch pad capacitance

I did  not try this, and the pull-up resistor value might be too low to make this circuit feasible (i.e. the time to charge the capacitance would be too short to measure accurately). It would be quite easy to test though, maybe some day...

Concept 3

The third concept I tested was something I came up with from reading how the real touch sensor chips work. I have not seen this concept used with Arduino, but I might be wrong. It uses only one pin per sensor and only needs the sensor connected to the pad. I didn't test the concept with single sensor, but at least with two it works well and probably is easy to expand to more sensors. One downside is that the sensors must be connected to analog input pins.

The concept looks like this:

The left side shows again the microcontroller internals, and this time I drew also the analog input sample and hold capacitor Csh, which is 14 pF in Atmega32U4 and the sampling resistor which is 1...100 kOhm (and is not important). I also drew the parasitic capacitance between the two touch sensors, value of which changes when a finger is brought close to the sensors.

The operation can be explained as follows when reading the upper touch sensor:

  • The lower (other) sensor pin is configured as output and driven low (green line)
  • The measurement pin (upper) is configured as output and driven high (red line). This charges the touch pad capacitance to the supply voltage level
  • The measurement pin (upper) is changed to analog input and read. This triggers the sample-and-hold circuit which charges the capacitor (blue line) and then reads it value with the ADC (orange line)
  • A change in the analog value read indicates whether the touch pad is touched or not (higher value means touch)

The key here is that the sample-and-hold capacitor is 14 pF and the touch sensor capacitance is typically in the same range, maybe tens of pF. When the touch pad is first charged to the supply voltage level and then the analog circuitry is connected, the charge in the touch pad capacitance is divided between the touch pad and the sample-and-hold capacitor. This means that the voltage is also divided in the ratio of the capacitances. Touching the sensor increases the capacitance of the sensor, which means the voltage that is sampled will be higher.

Driving the other sensor low also increases the sensitivity, as there will be capacitance between the sensor measured and the other sensor, especially when the sensor is touched. I tried driving the other sensor high also, but driving it low gives the best result.

I uploaded the code for this sensor stuff to GitHub here. Feel free to try it out and comment if there is something to improve.

Here is also a picture of the method in operation. In the figure below the green line is the raw ADC value (sensor is read 20 times with 1 ms in between to get rid of 50 Hz mains hum, and the values are summed together), blue is low-pass filtered signal and red is the threshold value where the low-pass filtered value is compared.

There are 4 touch events where the signal goes significantly higher than the base line. There is roughly 20 % increase in the reading when the sensor is touched and when it is not, and there was a thick (several millimeters) insulator between my finger and the sensor.

Conclusion

I ended up using this ADC based capacitive divider method which might be something not used before with Arduino. I got very good results with it so I want to share it to others to test and improve upon. Maybe it helps people to create even better projects :)