
Sensore di luminosità
Inizio questa lezione con un’informazione che va a correggere quanto scritto sul sito Arduino in riferimento al Grove Light Sensor in cui erroneamente viene indicato il dispositivo come fotoresistenza, in realtà si tratta di un fototransistor e ciò, soprattutto per i neofiti può creare qualche problema di comprensione, soprattutto perché lo sketch presentato non fornisce esattamente quanto indicato.
Fotoresistenza o fototransistor?
- Versione 1.0 del Grove-Light Sensor – usava una classica CdS LDR GL5528, cioè una vera fotoresistenza.
- Versione 1.1 / 1.2 (quella montata sullo Shield dell’Arduino Sensor Kit) – per ragioni RoHS (il cadmio delle CdS è vietato) Seeed ha sostituito l’LDR con un LS06-S: si tratta di un sensore a fototransistor lineare (tecnicamente una fotodiodo-transistor) che “mima” la vecchia fotoresistenza ma è più rapido e lineare.
Il sito di Arduino non ha aggiornato la terminologia e continua a chiamarlo “photo-resistor”.
Ora come possiamo fare per non creare problemi a chi inizia?
Innanzi tutto se volete utilizzare una fotoresistenza vi rimando alle mie slide: Alfabeto di Arduino – Lezione 3, ma attenzione in questo caso dovrete usare una breadboard e realizzare un circuito con un resistore da 10Kohm e una fotoresistenza, in questo modo usando lo sketch presente sul sito Arduino o quelli indicati nelle mie slide tutto funzionerà ed avrete valori che variano tra 0 e circa 900, coll’esempio sul sito Arduino avrete un valore massimo più basso.
Dal punto di vista teorico cosa succede (usando una fotoresistenza):
La fotoresistenza (o LDR, Light Dependent Resistor) per rilevare l’intensità della luce:
- la resistenza della fotoresistenza diminuisce quando l’intensità luminosa aumenta;
- la resistenza della fotoresistenza aumenta quando l’intensità luminosa diminuisce.
L’ADC dell’Arduino la converte in un numero intero da 0 (buio) a 1023 (molta luce) quindi la lettura avviene tramite l’istruzione analogRead() per questo kit collegheremo direttamente il modulo al pin A3 e quindi nel codice scriveremo: analogRead(A3).
8 | analogReference(INTERNAL); |
14 | int luce_grezza = analogRead(sensore_luce); |
15 | int luce = map(luce_grezza, 0, 1023, 0, 100); |
17 | Serial.print( "Livello di luce: " ); |
Le cose sono simili con il Grove-Light Sensor sull’Arduino Sensor Kit, ma avrete, come dicevo, valori massimi più bassi, che raggiungo circa i 750 con la torcia dello smartphone direttamente puntata sul fototransistor. Dal punto di vista funzionale nulla cambia ma è importante aver ben presente che siamo lavorando con componenti elettronici diversi che hanno comportamenti simili.
Con lo sketch precedente otterremo sulla serial monitor questi valori:

Il fatto che il valore massimo si fermi attorno a 750 è in realtà perfettamente coerente con l’elettronica del modulo.
Quindi per ora, per chi inizia potete far finta che il componente sull’Arduino Sensor Kit è una fotoresistenza e se desiderate potete fermarvi a questo punto.

Qualche approfondimento in più sul Grove-Light Sensor
Fototransistor LS06-S
Sotto luce intensa scorre una corrente dell’ordine di qualche decina di µA, che non bastano a generare direttamente tensioni alte. L’amplificatore operazionale LM358 presente sul modulo (con resistenza di feedback di circa 47 kΩ), converte quella corrente in tensione. Pur essendo alimentato fra 0 V e 5 V, riesce a portare l’uscita solo fino a circa 5 V – 1,2 V ≈ 3,8 V.
Ora come ottenere un range “0-1023” (o quasi) con il Grove-Light Sensor – due strategie
Strategia 1
Cambiare il riferimento ADC analogReference(INTERNAL);
in questo modo il riferimento interno a 1,1 V lo stesso segnale da 0-2 V “riempie” tutta la scala (tocca il fondo scala già a ≈ 1,1 V), per contro però si perde in linearità oltre 1,1 V (valori restano saturi a 1023).
Strategia 2
Riscalare via software val_norm = raw * (1023.0 / 750.0); in questo modo non si altera l’hardware, però per contro rimane un range” reale di 0-750 sull’ADC.
Un sensore di questo tipo trova impiego in svariati ambiti: dal rilevamento di presenze al tracciamento della posizione del sole, fino alla gestione automatica dell’illuminazione.
Un esempio comune è l’uso del sensore di luce nei telefoni cellulari: quando la luce ambientale è intensa, il sensore fa aumentare la luminosità dello schermo per migliorarne la leggibilità; in condizioni di scarsa illuminazione, la luminosità viene ridotta, con conseguente risparmio energetico.
ATTENZIONE
Il valore fornito dal sensore di luminosità riflette soltanto l’andamento approssimato dell’intensità della luce e NON corrisponde ai lumen.
Gli esempi che seguono useranno analogReference(INTERNAL); ma spieghiamo esattamente a cosa serve.
analogReference(INTERNAL); è un’istruzione della libreria Arduino API che imposta quale tensione di riferimento (VREF) userà il convertitore analogico-digitale (ADC) per trasformare un segnale analogico in un numero da 0 a 1023.
L’ADC di Arduino misura una tensione di ingresso (VIN) e la confronta con un valore fisso, VREF:

- se VIN = 0 V ⇒ conteggio = 0
- se VIN = VREF ⇒ conteggio = 1023
Scegliere VREF più bassa aumenta la sensibilità (più millivolt per “step”), ma riduce il range massimo misurabile.
Cosa fa INTERNAL
Su un Arduino UNO la tensione di riferimento passa da 5 V (DEFAULT) a ~1,1 V.
Risultato:
- Risoluzione: 1,1 V / 1023 ≈ 1 mV per step (invece di ~4,9 mV).
- Range utile: tutto ciò che supera 1,1 V viene saturato a 1023.
è buona norma inserire analogReference(INTERNAL) nel setup() in questo modo:
2 | analogReference(INTERNAL); |
Ma a cosa serve delay(3);?
Lo scopo è dare il tempo al riferimento di tensione interno (la “band-gap” da ≈ 1,1 V presente nei microcontrollori AVR) di accendersi e stabilizzarsi prima di eseguire la prima analogRead(), nello specifico:
- Avvio del generatore band-gap
Quando si passa dal valore di DEFAULT
(Vcc, 5 V) a INTERNAL
l’hardware dell’ADC collega un piccolo condensatore al generatore band-gap. Occorrono alcuni microsecondi perché il generatore si attivi e qualche altro millisecondo perché il condensatore si carichi alla tensione esatta.
- Stabilità delle letture
Se effettuate subito una lettura analogica, il riferimento non è ancora arrivato al valore finale: il primo campione risulterebbe non corretto. Dopo 2-5 ms (dato tipico dei datasheet AVR) la tensione di riferimento è già entro pochi millivolt dal valore nominale e le conversioni diventano affidabili.
- Sicurezza extra
Usare 3 ms è un compromesso: abbastanza lungo per coprire la maggior parte dei casi, ma così breve da essere impercettibile nel ciclo di esecuzione.
Per approfondire questo aspetto vi rimando al datasheet dell’ATmega328P.
- A pag. 37 e 43 del datasheet viene specificato che la band-gap interna richiede un breve tempo di avvio quando viene accesa; per questo, dopo analogReference(INTERNAL); si inserisce tipicamente un delay() di pochi millisecondi.
- Le specifiche numeriche della band-gap – tensione nominale e altre caratteristiche di reset di sistema – sono riportate nella Tabella 28-4 (pag. 262).
Esempio con riferimento interno
6 | analogReference(INTERNAL); |
12 | int raw = analogRead(A3); |

Esempio: stampa il valore in mV
2 | analogReference(INTERNAL); |
8 | int raw = analogRead(A3); |
9 | float vin = raw * (1.1 / 1023.0); |
10 | Serial.println(vin, 3); |

Con questo sketch potete leggere variazioni di pochi millivolt – perfetto per sensori la cui uscita non supera mai ~0,8–1 V.
Esempio: realizziamo una lampada automatica, l’intensità luminosa del LED sull’Arduino Sensor Kit aumenta al diminuire della luminosità esterna.
6 | const byte LDR_PIN = A3; |
10 | analogReference(INTERNAL); |
12 | pinMode(LED_PIN, OUTPUT); |
16 | int raw = analogRead(LDR_PIN); |
20 | raw = constrain(raw, 0, 800); |
21 | int pwm = map(raw, 800, 0, 0, 255); |
22 | analogWrite(LED_PIN, pwm); |
Seguire il link per maggiori informazioni sull’uso dell’istruzione constrain().
Esempio: LED che lampeggia ad una frequenza proporzionale alla luce rilevata dal sensore di luce
8 | const byte LDR_PIN = A3; |
12 | const unsigned int PERIOD_MIN = 150; |
13 | const unsigned int PERIOD_MAX = 2000; |
16 | analogReference(INTERNAL); |
18 | pinMode(LED_PIN, OUTPUT); |
22 | int raw = analogRead(LDR_PIN); |
26 | raw = constrain(raw, 50, 750); |
27 | unsigned int period = map(raw, 750, 50, PERIOD_MIN, PERIOD_MAX); |
30 | digitalWrite(LED_PIN, HIGH); |
32 | digitalWrite(LED_PIN, LOW); |