Premessa
Qualche giorno fa, mentre stavo leggendo The Art of Electronics [1], ho trovato, fra le primissime pagine, lo schema di un semplice circuito per un termostato. Di per sé, non presentava nulla di particolare, ma ho deciso comunque di approfittare dell'occasione per prendere un po' la mano con JavaScript e creare una piccola applicazione interattiva che ne illustrasse il funzionamento. Il circuito di per sé è piuttosto semplice, così come l'applicazione che ho scritto, per cui lo scopo di quest'articolo è solo di raccogliere qualche appunto, sia sul termostato che sul codice che ho usato.
Il Circuito
Un termostato ha sostanzialmente la funzione di segnalare se la temperatura dell'ambiente circostante è al disopra o al di sotto di una certa soglia. Il circuito illustrato qui sotto compie questa funzione utilizzando principalmente due componenti (oltre ai vari resistori): un termistore ed un comparatore. Il termistore ha una resistenza che diminuisce con l'aumento della temperatura, mentre il comparatore produce un segnale alto (5V) o basso (0V) all'uscita, a seconda di quale dei due ingressi ha un voltaggio maggiore.
Le coppie di resistori R1-R2 ed R3-R4 formano dei partitori di tensione: in particolare, la tensione all'ingresso positivo del comparatore diminuisce con l'aumento della temperatura, per via dell'azione del termistore. Finché la tensione all'ingresso negativo del comparatore è più bassa di quella all'ingresso positivo, l'uscita del comparatore è a circa 5V e quindi non passa la corrente attraverso il diodo. Quando la situazione si inverte, invece, il comparatore emette un segnale basso e la corrente passa, facendo illuminare il diodo, che è un LED.
Per quanto riguarda R5, è semplicemente una resistenza che limita la corrente attraverso il diodo. Usando gli stessi dati della [1], possiamo supporre che il diodo richieda una tensione di 1.6V e che la corrente attraverso di esso non debba superare i 5mA. Il valore di R5 si può allora calcolare con una semplice divisione: \( R5 = \frac{5V-1.6V}{5mA}=680 \Omega \).
L'applicazione che ho creato qui sotto permette di cambiare la temperatura con il cursore bianco e controllare lo stato corrispondente del circuito. I valori di numerici forniti sono in realtà approssimativi, in quanto vengono calcolati supponendo che il termistore sia perfettamente lineare in tutto l'intervallo di temperature fra 0 e 50 °C.
Il Codice
Sebbene non abbia molto a che vedere con l'elettronica,
includo questa sezione sul codice dell'applicazione perché
penso sia utile (per me) scrivere qualche appunto sui
problemi che ho incontrato e come li ho risolti. Mi rendo
conto di essere alle prime armi con JavaScript e di non
aver creato nulla di speciale, ma, come dicono mille proverbi
popolari, in qualche modo bisogna pur cominciare. L'ultima
cosa da aggiungere, infine, è che la sorgente di questa
pagina si può consultare premendo CTRL+U
(o CMD+U
),
oppure sulla
mia pagina di GitHub. Il codice dell'applicazione
in JavaScript si trova qui.
Le principali funzionalità dell'applicazione sono le seguenti:
- Il cursore della temperatura.
- Il colore dell'indicatore numerico della temperatura.
- L'immagine contenuta in un elemento canvas.
- Il ridimensionamento dell'immagine insieme alla finestra.
- Il posizionamento fisso dei valori numerici e della luce del LED, anche se l'immagine viene ridimensionata.
Cursore della Temperatura
Il cursore della temperatura è piuttosto semplice e si può
ottenere utilizzando l'apposita etichetta di HTML
<input type="range">
ed ampliarlo un po' secondo
le proprie esigenze. Io ho usato il seguente codice:
<input type="range" id = "tempslide" min="0" max="100" step="1" onchange="updateTemp();updateCanvas();" oninput="updateTemp();updateCanvas();" value="52">L'unica cosa da notare qui è l'uso di
onchange
e
oninput
, che in teoria dovrebbero avere la stessa funzione.
Le ho incluse entrambe perché, a quanto pare, oninput
è compatibile con tutti i browser principali tranne Internet Explorer,
che invece richiede onchange
. Il risultato è comunque
lo stesso, ovvero di chiamare le due funzioni updateTemp()
e updateCanvas()
dal codice in JavaScript ogni volta
che il cursore cambia di posizione. Come suggerito dal nome, queste
due funzioni aggiornano l'indicatore della temperatura e lo stato
del canvas che contiene l'immagine.
Indicatore Numerico
Il colore dell'indicatore numerico, che cambia
gradualmente dal blu al rosso, mi ha dato vari problemi, per via
della conversione fra numeri esadecimali e stringhe. La funzione
che se ne occupa si chiama updateTemp()
ed è
riportata qui sotto.
function updateTemp() { getTemp(); var r = Math.round(tempSlide*255/100); var b = 255-r; var tempString = temp.toFixed(1) + " °C" var colorString = "#"+("0"+r.toString(16)).slice(-2)+"11"+("0"+b.toString(16)).slice(-2); tempPar.innerHTML ="<b>"+tempString.fontcolor(colorString)+"</b>"; }Le funzioni e variabili più semplici sono le seguenti.
getTemp()
: una funzione che aggiorna il valore ditemp
(la temperatura attuale).r
,b
: valori RGB di rosso e verde.tempString
: stringa accanto al cursore che indica la temperatura attuale.toFixed(1)
:temp
in una stringa con un numero fisso (1) di cifre decimali (per motivi estetici).
colorString
, che è la
stringa con il codice esadecimale per il colore di tempString
.
La funzione toString(16)
converte un numero decimale
in esadecimale sotto forma di stringa, ma non necessariamente in
un formato adatto per i colori: per esempio, il valore 10
viene convertito semplicemente in "A"
e non "0A"
,
per cui al posto di #0A11F3
si avrà #A11F3
,
che verrà interpretato come un altro colore.
Insomma, una volta
identificato il problema, ho trovato una soluzione su StackOverflow,
utilizzando ("0"+r.toString(16)).slice(-2)
.
v.slice(a,b)
crea un vettore con gli elementi
da v[a]
a v[b]
. Se a
viene omesso, è uguale a 0, mentre i numeri negativi
segnalano di cominciare dalla fine del vettore v
.
La funzione
slice(-2)
crea un vettore (una stringa, in questo
caso) di 2 elementi a partire dalla fine del vettore su cui
agisce, che è ("0"+r.toString(16))
. In questo modo,
i valori esadecimali hanno, all'occorrenza, uno 0 davanti e
sono quindi sempre di 2 cifre.
Immagine in Canvas
L'elemento canvas crea un ambiente grafico facilmente manipolabile
con JavaScript, che per quest'applicazione mi è risultato particolarmente
utile. Per inserirvi un'immagine, basta utilizzare la funzione
ctx.drawImage(immagine, x, y, ampiezza, altezza)
. Nel
mio caso, ho utilizzato il seguente codice, all'interno della
funzione updateCanvas()
. Per semplicità, riporto solo
le righe di codice rilevanti.
function updateCanvas() { var canvas = document.getElementById("myCanvas"); var img = document.getElementById("circuit"); var imgRatio = img.height/img.width; var articleWidth = document.getElementById("article").offsetWidth; var imgWidth = 0.9*articleWidth; canvas.width = articleWidth; canvas.height = imgWidth*imgRatio; ctx.drawImage(img, imgX, 0, imgWidth, imgWidth*imgRatio); // Altro codice non rilevante } // Altro codice non rilevante window.addEventListener('resize', updateCanvas);L'unica cosa da notare nel codice è come esso reagisce al ridimensionamento della finestra. Le variabili
imgWidth
e canvas.width
(le larghezze dell'immagine e del
canvas rispettivamente), sono entrambe definite in funzione
della larghezza dell'articolo, ovvero articleWidth
.
La larghezza dell'articolo è definita nel codice CSS come
70% della larghezza complessiva della pagina (o 90% per gli
schermi piccoli). Siccome questa è una definizione relativa,
in JavaScript ho dovuto usare .offsetWidth
piuttosto che .width
per definire
la variabile articleWidth
.
Lo stesso discorso vale per la variabile canvas.height
,
ovvero l'altezza del canvas, che è definita in funzione di
imgWidth
e una costante (imgRatio
). Il
ridimensionamento dell'immagine e del canvas è
reso possibile dall'ultima linea di codice, che utilizza la
funzione addEventListener(evento, funzione)
per
chiamare updateCanvas()
ogni volta
che viene ridimensionata la finestra (ovvero l'elemento window
).
Tutto questo codice compie la funzione di ridimensionare
l'immagine ed il canvas insieme alla finestra
principale, ma per mantenere costante la posizione degli elementi di testo
e della luce del LED (che vengono generati dal codice
JavaScript e non fanno parte dell'immagine), ho dovuto delle
coordinate relative che cambiano con le dimensioni dell'immagine.
Per ottenere ciò, ho creato una variabile cu
(canvas unit) all'interno della funzione updateCanvas()
,
come mostrato nel codice qui sotto.
function updateCanvas() { // Altro codice non rilevante cu = imgWidth/100; }Ogni volta che si effettua un ridimensionamento della finestra, si avrà quindi un nuovo valore di
cu
, che è sempre
uguale ad 1/100 della larghezza dell'immagine. Le coordinate
degli elementi di testo e della luce del LED, così come le loro dimensioni,
si possono allora definire in funzione di cu
: in questo
modo, la loro posizione sembrerà fissa indipendentemente dalle
dimensioni dell'immagine.