Introducción

Cualquiera que haya visto un Theremín, aunque sea en youtube, ha tenido la oportunidad de ver en acción la sensibilidad de los sensores capacitivos.

Este instrumento, funciona a base de la variación de la capacidad producida entre las manos del ejecutante y las antenas del dispositivo. Una de ellas controla el tono del sonido (pitch) y la otra la amplitud (volumen).
Hoy en día usamos continuamente sensores capacitivos en las pantallas táctiles de nuestros celulares, en el cajero, etc.
En este post vamos a usar un Arduino UNO junto con la librería ADCTouch para controlar los colores de un led con el tacto, haciendo honor a Léon Theremin.

ADCTouch

ADCTouch es una librería que nos permite crear un sensor capacitivo sin ningún circuito externo, con solo un trozo de cable ya es suficiente. La mayoría de las librerías para sensado táctil (touch) requieren dos pins y una resistencia para adquirir mediciones precisas. Esta librería hace uso del cableado interno del chip AVR para obterner una resolución decente con solo un pin analógico.

¿Como funciona?

La librería es muy sencilla e ingeniosa, podemos ver y descargar el código acá.
Para adquirir una lectura, hace lo siguiente:

  1. Carga el pin en cuestion a 5v a través de la resistencia pullup interna (no directamente para prevenir cortocircuitos).
    pinMode(ADCChannel, INPUT_PULLUP);
    

    general

  2. Descarga el capacitor interno ~14pf (sample and hold del ADC), conectando el ADC a GND vía software. Dado que esto no se puede hacer con leguaje Arduino lo hace tocando los registros directamente:
    ADMUX |=   0b11111; //set the mux to GND
    ADCSRA |= (1<<ADSC); //start conversion
    while(!(ADCSRA & (1<<ADIF))); //wait for conversion to finish
    ADCSRA |= (1<<ADIF); //reset the flag
    

    GND

  3. Pone el pin en hi-z, alta impedancia (INPUT) y mide la tensión del capacitor interno con analogRead().
    pinMode(ADCChannel, INPUT);
    _value += analogRead(ADCChannel);
    

    sample&hold
    De esta manera, la carga se redistribuye entre el capacitor externo de valor desconocido y el interno.
    Si el pin tiene baja capacidad, la carga acumulada será pequeña como así también el voltaje medido con analogRead(); si la capacidad es igual a 14 pf, el voltaje debería ser de ~2,5v. Mayores capacidades resultarán en voltajes >2,5v.

  4. El proceso se repite n veces, dadas por el argumento samples en la funcion read, que por defecto es 100 veces, y luego se promedia:
    return _value / samples;
    

El chip y la placa de arduino aportan por si mismos una capacidad, que producen una lectura de alrededor de 390 y, con solo un cable exterior, puede subir hasta 500, por lo que se necesita una compensación de offset.

RGBTouch

RGBTouch es un sketch de ejemplo que hice para probar esta librería, con él vamos a controlar los colores de un led RGB a través de 3 cables y nuestras manos, podés descargar el mismo desde el repositorio RGBTouch.
El uso de la librería es muy sencillo, para hacer una lectura llamamos a la única función contenida en la clase ADCTouch: read, y ésta nos devuelve un valor. Por ejemplo, en el setup() lo hacemos una vez para calcular el offset:
refR = ADCTouch.read(A0,500);
en este caso pasamos un dos argumentos, el primero es el pin en el que deseamos realizar la lectura (que siempre será un pin analógico), y el segundo es la cantidad de samples; cuantos mas samples (muestras) mas precisa será la medición, pero también mas lenta será la misma. También podemos llamar a la función sin el segundo argumento y usar la cantidad por defecto (100), como lo hacemos en el loop():
int valueR = ADCTouch.read(A0);

En un principio no sabemos que valores nos entregará, esto va a depender de lo que conectemos al pin. Como regla general cuanto mayor sea la superficie del conductor que conectemos, mayor será la capacidad y por lo tanto mayor será la lectura. Por eso para hacer el debugging usamos el puerto serie. En mi caso encontré resultados interesantes:

En mi Funduino chino viejo y querido, el valor para un trozo de cable de par telefónico, aislado, de aproximadamente 10cm, una vez removido el offset, varía aproximadamente entre 0 y 200 cuando acerco la mano, y cuando apreto más, alrededor de 270. Estos valores resultan muy prácticos para el analogWrite();

Otra observación es que se produce interferencia entre los pines. Así, a medida que me acerco mas al canal rojo (A0), empiezan a aparecer valores en el canal verde (A1) y en menor medida en el canal azul (A2).

Con esta configuración entonces, solo resta asegurar que esos valores esten en el rango del analogWrite() (ni siquiera es necesario escalarlos) y pasarselos para encender el led con la intensidad correspondiente a cada color.

Resultados

GIF_nightlight

  • Teniendo en cuenta que estamos usando solo un trozo de cable, la sensibilidad es impresionante. Usando planchas de PCB o papel aluminio seguramente tendríamos valores mas altos de capacidad y mayor variación de la misma, con lo cual podríamos detectar la presencia del cuerpo humano bastante antes. De cualquier manera, encontré muy interesante el rango de detección con esta configuración: al hacer fuerza sobre los cables aumentan los valores.

  • El fenómeno de interferencia es muy limitado, sin embargo la contaminación de color azul con solo un valor en analogWrite de 2 sobre un color rojo ya es perceptible. Esto no es un problema ya que se podría eliminar facilmente por software.

  • Es importante tener en cuenta que el valor del offset se toma al inicio del programa una sola vez. Esto quiere decir que cada vez que encendamos o resetiemos nuestro arduino, tomará nuevos valores. Si nuestra mano o algún objeto se encuentra cerca al inicio, nuestro programa tomará ese valor como referencia. Al alejar la mano, el valor al restar el offset será negativo, y por más que acerquemos la mano nuevamente, ésta no encenderá el led.

Del código al osciloscopio

¿Que pasa si ponemos la punta del osciloscopio en uno de nuestros pins táctiles? ¿Seremos capaces de ver la carga y descarga?.
Probamos en el canal verde:

green channel

En esta captura podemos ver claramente la llamada a ADCTouch.read(), para el canal verde. Agregemos el canal azul:

green & blue channel

El que falta es el rojo, pero mi osciloscopio es muy económico, es lo que hay. Cada lectura, que requiere 100 sampleos, tarda aproximadamente 22 ms. Dado que la llamamos 3 veces (para cada canal), tardamos 66 ms en completar las mediciones, que es prácticamente todo lo que hace nuestro programa.

Y si reducimos la escala de tiempo podemos ver los sampleos individuales:

gChannelZoomIn

gChannelZooooomIn

Créditos

ADCTouch fue creada por Martin Pittermann @martin2250, basado en el trabajo de Tuomas Nylund @tuomasnylund.

Más

  • Tuomas Nylund original work: