ARDUINO... TOUCHE'


torna alla Home Page


GUARDARE E TOCCARE

Qualche tempo fa comprai per qualche euro uno schermo "touch" da 5 pollici che misi nello scrigno delle cose "magari trovo un problema a questa soluzione", intravedendo un Arduino all'orizzonte, sul suo destriero bianco (una breadboard).

Dopo avere navigato quà e là, ho appurato che il mio touch screen a tecnologia resistiva (ce se sono altre più complesse) è composto da una piastra superiore trasparente, ricoperta nella parte inferiore di materiale resistivo, ai cui lati destro e sinistro sono applicati due elettrodi. Un'altra piastra realizzata allo stesso modo, ma ruotata di 90° chiude la parte inferiore, mentre in mezzo si trovano dei microdistanziatori che tengono le piastre separate elettricamente.

Nel momento in cui si tocca la faccia superiore, la pressione la fa flettere quel tanto che basta da mettere in contatto le due facce dal lato resistivo, creando in tal modo un doppio partitore di tensione.

La misura si effettua in due passaggi, applicando dapprima una differenza di potenziale nota ai capi Y (ovvero Top e Bottom), e misurando la tensione su un contatto del lato X (diciamo Right). A questo punto si ripete l'operazione invertendo i contatti: si applica la stessa differenza di potenziale ai capi X (Left e Right), e si va a misurare la tensione sul lato Top. Il risultato è ovviamente una coppia di tensioni proporzionali alle coordinate orizzontali e verticali del punto toccato.

Esistono una quantità di integrati (cosiddetti driver dedicati) che fanno questa operazione in modo rapidissimo, svincolando il resto dei circuiti da questa misura speciale, ma è proprio quello che voglio fare, piegando Arduino ai miei voleri.

Per la realizzazione ho deciso di usare le uscite digitali 2,3,4,5 e gli ingressi 3 e 4, adottando  il seguente schema (perdonate l'inglesismo, mi sento internescional):

Wiring Logic
Touchpad Arduino Phase 1:
X axis reading
Phase 2:
Y axis reading
Left Dig 2 mode OUTPUT
value: LOW
mode INPUT
(high impedance)
Top An 3 Read X axis value
(last operation)*
(not used)
Dig 5 mode INPUT
(high impedance)
mode OUTPUT
value: HIGH
Right An 4 (not used) Read Y axis value
(last operation)*
Dig 4 mode OUTPUT
value: HIGH
mode INPUT
(High impedance)
Bottom Dig 3 mode INPUT
(high impedance)
mode OUTPUT
value: LOW

Per poter effettuare lo scambio dei lati di lettura, ho dovuto mettere alcuni pin DIGITAL in contatto con un paio di pin INPUT, (DIGITAL 5 e ANALOGIC 3, DIGITAL 4 e ANALOGIC 4) e per evitare influenze durante la lettura, ho impostato le uscite digitali in modo "INPUT" per poterle lasciare flottanti dal punto di vista elettrico.

*Nota relativa alla logica: la lettura del singolo asse va effettuata per ultima (An3 nella fase 1 e An4 nella fase 2), dopo avere impostato prima tutte le uscite ai valori indicati in ogni fase, altrimenti la misura non avrà senso.

Qui sotto potete vedere l'esempio pratico e il relativo codice: nulla di eccezionale, il tocco sul pannello viene interpretato dall'Arduino usando la logica sopra esposta, direi che con una manciata di righe di programma il risultato è onorevole, forse troppo sobrio.

/*
* TOUCH.PDE - Arduino sketch
* Read from a touch panel the touch position
* return it on serial port
*
* Hardware used:
* Resistive touch panel, 4 wire
* Arduino Diecimila
*
* April, 5th 2009
* by Marco Nicolato
*
*/


// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Touch panel wiring
// Connect to Arduino these wires (used to drive power)
#define Lo 2 // LEFT to digital output 2
#define Bo 3 // BOTTOM to digital output 3
#define Ro 4 // RIGHT to digital output 4
#define To 5 // TOP to Digital output 5

// Connect to Arduino these wires (used to read the touch position)
#define Ti 3 // TOP also to analog input 3
#define Ri 4 // RIGHT also to analog input 4

#define LED 13 // LED event
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// current touched
int touchX = 0;
int touchY = 0;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
}

void loop()
{
if (touched())
{
Serial.print("X=");
Serial.print(touchX);
Serial.print(" Y=");
Serial.println(touchY);

digitalWrite(LED, HIGH);
delay(50);
digitalWrite(LED, LOW);
}

}

// return TRUE if touched, and coords in touchX and touchY
boolean touched()
{
boolean touch = false;

// Horizontal routine - set L to gnd and R to Vcc
// set L = ground
pinMode(Lo, OUTPUT);
digitalWrite(Lo, LOW);
// set R = Vcc
pinMode(Ro, OUTPUT);
digitalWrite(Ro, HIGH);
// T e B high impedance (input mode)
pinMode(To, INPUT);
pinMode(Bo, INPUT);
// wait a bit, then read from Top
delay(10);
touchX = analogRead(Ti);

// Vertical routine - set T to gnd and B to Vcc
// Set B = Vcc
pinMode(Bo, OUTPUT);
digitalWrite(Bo, LOW);
// set T = gnd
pinMode(To, OUTPUT);
digitalWrite(To, HIGH);
// R e L high impedance (input mode)
pinMode(Ro, INPUT);
pinMode(Lo, INPUT);
// wait a bit, then read from Right
delay(10);
touchY = analogRead(Ri);

// Only if coords are below 1000
if(touchX < 1000 and touchY < 1000)
touch = true;

return touch;
}


TOUCH ME MORE... !

Si sa che l'appetito vien mangiando, per cui ho fatto l'ingordo e mi sono domandato: ma con una matrice di led "sotto" e il touchpad "sopra", perchè non accendere il LED nella posizione sotto il touch ? Per quanto riguarda la matrice, avevo già realizzato con Arduino un driver per questo tipo di componente, quindi non manca che mescolare accuratamente le due sciocchezzuole.

Per poter usare la matrice led, tramite l'integrato 7221 della Maxim, occorre includere la libreria creata da Eberhard Fahle, che si trova all'indirizzo http://www.wayoda.org/arduino/ledcontrol/index.html e usare il seguente cablaggio tra Arduino e l'integrato 7221:

Maxim 7221 Arduino
Data (1) Dig 8
Latch (12) Dig 9
Clock (13) Dig 10

Secondo la tradizione, quando si lavora con questi dispositivi occorre eseguire una calibratura iniziale per determinare dove è posizionata la matrice di led rispetto al touch panel. Nel video infatti si accendono in sequenza i led dei due angoli estremi, e la lettura dei relativi punti fornisce la zona dove accettare i successivi touch, per poterli associare al led sottostante con una semplice proporzione.

/*
* TOUCHDISP.PDE
* Read from a touch panel the touch position
* and show it on a 8x8 matrix display.
* Every led can be swapped ON or OFF,
* if doubletouched, the led goes to FLASH
*
* Hardware used:
* Resistive touch panel, 4 wire
* MAX 7219/7221
* LED matrix 8x8
* Arduino Diecimila
*
* March, 28th 2009
* by Marco Nicolato
*
*/

// Use library LedControl - Copyright (c) 2007 Eberhard Fahle
// Download it from:
// http://www.wayoda.org/arduino/ledcontrol/index.html
#include "LedControl.h"

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Touch panel wiring

// Connect to Arduino these wires (used to drive power)
#define Lo 2 // LEFT to digital output 2
#define Bo 3 // BOTTOM to digital output 3
#define Ro 4 // RIGHT to digital output 4
#define To 5 // TOP to Digital output 5

// Connect to Arduino these wires (used to read the touch position)
#define Ti 3 // TOP also to analog input 3
#define Ri 4 // RIGHT also to analog input 4

#define LED 13 // Touch LED
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// MAX7219/7221 wiring
// Pin 8 is connected to the DATA IN-pin of the first MAX7221
// Pin 9 is connected to the LOAD(/CS)-pin of the first MAX7221
// Pin 10 is connected to the CLK-pin of the first MAX7221
#define DATA 8
#define LATCH 9
#define CLOCK 10

LedControl dsp=LedControl(DATA,CLOCK,LATCH,1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Working area coords (Top-Left and Bottom-Right)
int tlX;
int tlY;
int brX;
int brY;

// Absolute width and height working area
int wX;
int wY;

// Used to store analog coords
int touchX = 0; // current
int touchY = 0;

// Current matrix LED coords
int ledX;
int ledY;

// Local matrix
int matrix[8][8];
#define ON 1
#define OFF 0
#define FLASH 2
unsigned long flashTime;
boolean flashing = false;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


void setup()
{
// Initialize display
dsp.setScanLimit(1, 4);
dsp.setIntensity(0, 10);
dsp.shutdown(0,false);
flashTime=millis();

pinMode(LED, OUTPUT);

// reset all matrix
for(int c = 0; c <= 8; c++)
for(int r = 0; r <= 8; r++)
matrix[c][r] = OFF;

// Initialize - ask for TOP-LEFT...
dsp.setLed(0,7,7,true); // top left led = on
while(not touched()); // wait until touched
tlX = touchX; // store top-left coords
tlY = touchY;
dsp.setLed(0,7,7,false); // top left led = off
while(touched()); // wait for untouch

// ...now ask for BOTTOM-RIGHT
dsp.setLed(0,0,0,true); // bottom-righ = on
while(not touched()); // wait until touched
brX = touchX; // store bottom-right coords
brY = touchY;
dsp.setLed(0,0,0,false); // bottom-right led = off
while(touched()); // wait for untouched

// working area width and height
wX = brX - tlX;
wY = tlY - brY;
}

void loop()
{
if (touched() & (touchX > tlX and touchX < brX and touchY < tlY and touchY > brY)) // only when touched inside active area
{
// store first LED coords
int firstledX = ledX;
int firstledY = ledY;
int v = (matrix[ledX][ledY]);

// swap value: on->off or off->on
boolean led = (v == OFF);
if (led)
matrix[ledX][ledY]=ON;
else
matrix[ledX][ledY]=OFF;

// wait for untouched
while(touched());

// wait for a second touch ...
unsigned long wait;
wait = millis() + 200;
while (millis() < wait)
{if (touched() & (touchX > tlX and touchX < brX and touchY < tlY and touchY > brY))
if (ledX == firstledX and ledY == firstledY)
matrix[ledX][ledY] = FLASH; // yes, flash it
}
}

// display current led matrix
display();

}

// return 1 if touched, and coords in touchX and touchY
boolean touched()
{
boolean touch = false;

// Horizontal routine - set L to gnd and R to Vcc
// set L = ground
pinMode(Lo, OUTPUT);
digitalWrite(Lo, LOW);
// set R = Vcc
pinMode(Ro, OUTPUT);
digitalWrite(Ro, HIGH);
// T e B hight impedance (input mode)
pinMode(To, INPUT);
pinMode(Bo, INPUT);
// wait a bit, then read from Top
delay(10);
touchX = analogRead(Ti);

// Vertical routine - set T to gnd and B to Vcc
// Set B = Vcc
pinMode(Bo, OUTPUT);
digitalWrite(Bo, LOW);
// set T = gnd
pinMode(To, OUTPUT);
digitalWrite(To, HIGH);
// R e L hight impedance (input mode)
pinMode(Ro, INPUT);
pinMode(Lo, INPUT);
// wait a bit, then read from Right
delay(10);
touchY = analogRead(Ri);

// Only if coords are below 1000
if(touchX < 1000 and touchY < 1000)
{
// calculate the led to show
ledX = 8 * (touchX - tlX) / wX;
ledY = 8 * (touchY - brY) / wY;

// Touch LED on for a bit
digitalWrite(LED, HIGH);
delay(20);
digitalWrite(LED, LOW);

touch = true;
}
return touch;
}


// show all matrix display, flashing leds with special FLASH value
void display()
{
boolean led;
// every 300ms swap status of flashing leds
if ((flashTime + 300) < millis())
{flashing = (not flashing); // swap ON and OFF
flashTime = millis();
}

for(int r = 0; r <= 7; r++)
{
for(int c = 0; c <= 7; c++)
{
int v = matrix[r][c];
led = (v == ON);
if (v == FLASH)
led = flashing;

dsp.setLed(0, c, 7-r, led);
}
}
}

VEDO DOPPIO

Per dare un'aspetto un pò più dinamico ho fatto in modo che il doppio touch facesse lampeggiare il led, e direi che a questo punto, seppure semplice e grezzo, il progettino è funzionale e completo. A cosa possa servire, poi me lo direte voi che leggete, io ci ho ricavato qualche ora di divertimento e sano hacking grazie alla flessibilità di Arduino (e alla pazienza della moglie).

CONCLUDENDO...

Non che debba fare pubblicità ad Arduino, ma poter fare interagire il computer e il mondo fisico con questa schedina milleusi è una cosa che fino a qualche anno fa era riservata ai guru dell'elettronica, mentre ora pure io (che so a malapena come si accende un led, o quasi) riesco a fare.
Leggero' con piacere i vostri commenti, se vorrete scrivermi al seguente indirizzo: mnicolato@hotmail.com

Aggiornato il 13 aprile 2009