29 Agosto 2017

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 tuo browser non è compatibile con l'elemento "canvas".

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:

  1. Il cursore della temperatura.
  2. Il colore dell'indicatore numerico della temperatura.
  3. L'immagine contenuta in un elemento canvas.
  4. Il ridimensionamento dell'immagine insieme alla finestra.
  5. 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. Infine, la parte più problematica è stata 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.