Misurare l’intensità luminosa col sensore TSL2591

Il sensore TSL2591 permette di misurare l’intensità luminosa di un ambiente. Ha una buona capacità di campionamento ed in particolare permette con un solo sensore di rilevare sia lo spettro IR sia quello visibile. Il range di misurazione va da 188 Lux sino ad arrivare ad 88000 Lux (unità di misura Lux).
Nella figura seguente potete osservare il diagramma del circuito:

TSL2591 Diagramma di funzionamento

La conversione dei due canali viene eseguita contemporaneamente. Al termine del ciclo di conversione i dati vengono memorizzati nei relativi registri (CH0 Data e CH1 Data). Dopo la memorizzazione dei dati viene automaticamente eseguita la misurazione successiva.

Per questo tutorial ho acquistato questo articolo.

Il TSL2591 possiede anche due livelli di soglia programmabili (Upper Limit e Lower Limit) che generano un interrupt se il valore della misurazione dell’intensità di luce eccede questi limiti, una funzionalità importante per ottimizzare il campionamento in base alle condizioni di luminosità dell’ambiente.

Il collegamento del sensore con un microcontrollore avviene utilizzando il bus I2C, l’indirizzo del dispositivo nel bus è 0x29 e non può essere modificato via software, come accade con analoghi dispositivi.

L’interazione microcontrollore sensore avviene scrivendo e leggendo i registri del dispositivo.

Nonostante esista una libreria che semplifica l’interfacciamento, credo che sia interessante capire come accedere ai registri del TSL2591 utilizzando le semplici istruzioni della libreria Wire (bus i2C), questo per ottimizzare il codice in termini di dimensione e di prestazioni.

Sul datasheet sono presenti tutte le informazioni relative al TSL2591, tramite questo ‘manuale’ possiamo capire quali registri dobbiamo leggere e scrivere. La tabella seguente elenca tutti i registri del TSL2591, il loro indirizzo e le informazioni sulla loro funzione:

TSL2591 accedere ai Registri

Il primo registro che dobbiamo conoscere e il COMMAND, più che un registro è il primo dato che inviamo al TSL2591 suil bus I2C, che permette successivamente di accedere ad un registro specifico (indirizzi da 0x00 a 0x17), nel dettaglio il COMMAND è così composto:

TSL2591 Command register

Il bit 7 deve essere impostato alto per poter rendere valido il commando. I bit 6 e 5 servono per definire l’uso dei bit da 0 a 4. Se i bit 6 e 5 vengono settati rispettivamente uno basso e uno alto, intendiamo accedere ad un indirizzo, se invece i bit 6 e 5 vengono impostati rispettivamente uno alto e uno basso, intendiamo specificare una funzione (maggiori dettagli sul datasheet del TSL2591).

Passiamo subito passare alla pratica per capire come funziona questo meccanismo. Andremmo a leggere il registro 0x12 che contiene l’identificativo del dispositivo, settato di default al valore 0x50.

Formiamo, usando la notazione binaria, il valore che deve avere il COMMAND. Il bit 7, abbiamo detto, deve essere messo a 1, i bit 6 e 5 devono essere 0 e 1 (poichè intendiamo scrivere nei bit da 0 a 4 un indirizzo e non abilitare una funzione) e i bit da 0 a 4 conterranno il valore del registro che voglio leggere o scrivere, in questo caso 0x12:

TSL2591 Setting command register

Se questo è corretto dal registro 0x12 dovremmo leggere il valore 0x50:

//inserisco la libreria I2C
#include <Wire.h>

void setup()
{
  //Attivo la seriale
  Serial.begin(9600);
  
  //inizializzo la porta I2C
  Wire.begin();

  //inizio la trasmissione specificando
  //l'indirizzo del TSL2591
  Wire.beginTransmission(0x29);
  //invio sul bus il valore del registro COMMAND
  Wire.write(0b10110010);
  //termino la trasmissione
  Wire.endTransmission();
  //richiedo al dispositivo il valore del registro
  //precedentemente selezionato
  Wire.requestFrom(0x29, 1);
  //leggo il valore del registro
  byte c = Wire.read();
  //invio al serial monitor il dato letto
  Serial.print("0b"); Serial.print(c ,BIN); Serial.print(" 0x"); Serial.print(c ,HEX);
}

void loop() {
  delay(50);

}

Aprendo il Serial Monitor otterremo il valore aspettato:

TSL2591 ID register result

Questo è il meccanismo che permette di accedere ai registri del TLS2591. Ora andremo a recuperare i valori dei registri interessati alla memorizzazione dei canali ADC CH0 e CH1, relativi alla spettro visibile e alla spettro IR.
Prima di eseguire la lettura dai registri 0x14, 0x15, 0x16 e 0x17 (Che contengono appunto il valore della conversione ADC) è necessario configurare alcuni registri del dispositivo come l’ENABLE (0x00) e il CONFIG (0x01).

Il registro ENABLE (0X00) è così composto:

TSL2591 Enable register

Permette di ‘accendere’ il dispositivo e di avviare l’ALS Data Register per memorizzare i dati provenienti dagli ADC. I bit 4, 6 e 7 servono per settare le funzioni di interrupt e power down. Per il momento non useremo questi bit.
Quindi i bit 0 e 1 vengono impostati a 1 mentre gli altri bit impostati a 0.
Il registro ENABLE avrà questo valore: 0b00000011.

Un altro registro da configurare, prima di eseguire la lettura dai registri 0x14-0x17 e il CONTROL (0x01):

TSL2591 Control register

Questo registro ci permette di selezionare tramite i bit 4 e 5 il guadagno dell’integratore dei due canali. Il valore del AGAIN permette di variare la sensibilità del sensore in base alle condizioni di luce, se andiamo a misurare ambienti molto luminosi useremmo un basso guadagno, viceversa in ambienti bui ne potremmo aumenteremmo il guadagno.
Stesso discorso vale per il tempo di acquisizione, integration time ATIM, per ambienti luminosi potremmo impostare un basso tempo di integrazione (100 ms) mentre per ambienti poco luminosi un alto valore di integrazione (600ms).
Il bit 7 va impostato a zero, altrimenti otterremo il reset del dispositivo.
Impostiamo SRESET a uno (bit 7), AGAIN a 01 (medium Gain Mode) e ATIME a 100ms. Il registro avrà quindi questo valore 0b00010000.

Ora non ci resta che leggere i 4 byte dei registri 0x14-0x17, per recuperare i dati relativi alla conversione dello spettro visibile e IR:

//carico libreria i2c
#include <Wire.h>

void setup() {
  //init seriale
  Serial.begin(9600);

  //inizializzo la porta I2C della Arduino
  Wire.begin();
  delay(100);
 
  Wire.beginTransmission(0x29);
  Wire.write(0b10110010);//10110010
  Wire.endTransmission();
  Wire.requestFrom(0x29, 1);
  byte c = Wire.read();
  Serial.print("0b"); Serial.print(c ,BIN); Serial.print(" 0x"); Serial.print(c ,HEX);
 
  //attiva il tsl2195
  Wire.beginTransmission(0x29);
  //attiva seleziono il registro 0x00 (enable)
  Wire.write(0b10100000);
  //attiva
  Wire.write(0b00000011);
  Wire.endTransmission();
 
  //configuro il sensore gain e timing
  Wire.beginTransmission(0x29);
  //seleziono il registro 0x01 (Control)
  Wire.write(0b10100001);
  //imposto reset a 0 gain a 01 e timing a 000
  Wire.write(0b00010000);
  Wire.endTransmission();

  Wire.beginTransmission(0x29);
  //seleziono il registro 0x14
  Wire.write(0b10110100);
  Wire.endTransmission();

}

void loop() {

  //attendo almeno 100ms tra una lettura e l'altra
  delay(500);

  //leggo i dati dal registro 0x14 0x15 0x16 e 0x17
  Wire.requestFrom(0x29, 4);
  byte ByteLowVIS = Wire.read();
  byte ByteHighVIS = Wire.read();
  byte ByteLowIR = Wire.read();
  byte ByteHighIR = Wire.read();

  int valore = 0;

  //unisco i due byte del registro
  //0x14 con il registro 0x15
  valore = ByteHighVIS;
  valore <<= 8;
  valore |= ByteLowVIS;
  //invio il dato al serial monitor
  //luce visibile (CH0)
  Serial.print("LUCE VISIBILE ");
  Serial.println(valore);

  valore = 0;

  //unisco i due byte del registro
  //0x16 con il registro 0x17
  valore = ByteHighIR;
  valore <<= 8;
  valore |= ByteLowIR;
  //invio il dato al serial monitor
  //luce IR (CH1)
  Serial.print("LUCE IR ");
  Serial.println(valore);
}

Caricando il codice otterremo  l’acquisizione dei dati:

TSL2591 CH0 e CH1 data register acquisition

I risultati ottenuti sono valori grezzi che vengono recuperati direttamente dai registri di conversione. Tramite questi dati possiamo calcolare il valore della luminosità espresso in Lux oppure considerarli come semplici variazioni numeriche.