Mesure de l’éclairement lumineux

Notions de base

L’éclairement lumineux est la grandeur définie par la photométrie correspondant à la sensation humaine de l’éclairement. Plus un objet qui n’est pas totalement noir et qui ne produit pas de lumière par lui-même est éclairé, plus il est visible distinctement. La photométrie définit cette grandeur rigoureusement, afin de pouvoir la calculer, connaissant l’intensité lumineuse des sources de lumière, leur distance et leur direction.

L’éclairement lumineux se différencie de l’éclairement énergétique par l’application d’une pondération par longueurs d’onde qui correspond à la sensibilité de la vision humaine. La Commission internationale de l’éclairage a défini les tables d’efficacité lumineuse spectrale qui représentent la sensibilité de l’« observateur de référence ». Les appareils de mesure doivent avoir une sensibilité spectrale similaire.

En photométrie, l’éclairement lumineux correspond à un flux lumineux reçu par unité de surface. Son unité dans le système international d’unités est le lux : 1 lux (lx) correspond à un flux lumineux de 1 lumen (lm) couvrant uniformément 1 mètre carré (m^2) : 1 lx = 1 lm /m^2.

Remarque : Compte tenu des caractéristiques de la vision photopique chez les êtres humains, il n’y a pas de facteur de conversion unique entre le lux (lumen par mètre carré) et le watt par mètre carré, mais des facteurs de conversion différents pour chaque longueur d’onde. Par exemple, pour une couleur vert-jaune, par exemple, un éclairement énergétique de 1 W⋅m-2 correspond à 683 lux, tandis que pour du rouge ou du bleu, il s’agit de moins de 50 lux. Il est donc impossible de faire une conversion sans connaitre la répartition spectrale de la lumière.

Le lux sert de cadre normatif pour définir, dans les législations française et européenne, les niveaux minimums requis pour l’éclairage public et l’éclairage des lieux de travail.

Situation Éclairement moyen
Nuit pleine lune 0,5 lux
Nuit bien éclairée 20 à 70 lux
Ciel couvert 500 à 25 000 lux
Plein soleil 50 000 à 100 000 lux

L’appareil de mesure de l’éclairement lumineux est le luxmètre.

Terminologie

Lux
Le lux est une unité de mesure de l’éclairement lumineux (symbole : lx). Il caractérise le flux lumineux reçu par unité de surface. Un lux est l’éclairement d’une surface qui reçoit, d’une manière uniformément répartie, un flux lumineux d’un lumen par mètre carré.
Lumen
Le lumen (du latin, lumière) est l’unité dérivée du système international du flux lumineux. Son symbole est lm.
Candela
La candela ou candéla (symbole cd, du mot latin qui signifie « chandelle ») est l’une des sept unités de base du système international. Elle sert à mesurer l’intensité lumineuse ou éclat perçu par l’œil humain d’une source lumineuse.
Éclairement énergétique
L’éclairement énergétique ou irradiance est un terme radiométrique qui quantifie la puissance d’un rayonnement électromagnétique frappant par unité de surface. Dans le système international d’unités, elle s’exprime en watts par mètre carré (W/m² ou W⋅m-2).

Capteurs

Une cellule photoélectrique (dite aussi cellule photovoltaïque ou photorésistance) est un dispositif composé d’un capteur photosensible, dont les propriétés électriques (tension, résistance, …) varient en fonction de l’intensité du rayonnement lumineux capté.

Une photorésistance est un composant électronique dont la résistivité varie en fonction de la quantité de lumière incidente. On peut également la nommer LDR (Light-Dependent Resistor) pour résistance photo-dépendante.

Une photodiode est un composant semi-conducteur ayant la capacité de détecter un rayonnement du domaine optique et de le transformer en signal électrique.

On utilisera le capteur BH1750. C’est un capteur numérique de la lumière ambiante avec une interface I2C. Il est possible de détecter une large gamme à haute résolution (1-65535 lx).

Le principe d’une mesure est décrit dans la ficure ci-dessous :

Les différents code de commande sont :

Les différents mode de résolution :

Datasheet : bh1750fvi-e.pdf

Autres : tsl2561

Objectifs

Être capable de lire la valeur de l’éclairement lumineux en lux.

Préparation

Il vous faudra tout d’abord lire le document sur le bus I2C.

Mise en oeurvre

Code source : test-mo-bh1750.zip

La déclaration de la classe BusI2C :

#ifndef BUSI2C_H
#define BUSI2C_H

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define MIN_I2C_BUS 0
#define MAX_I2C_BUS 7

#define MAX_WRITE_LEN 511

class BusI2C
{
   private:      
      int i2c_bus;// = 1 (default on RPI B)
      int i2c_fd;
      int current_slave;
      unsigned char txBuff[MAX_WRITE_LEN + 1];
      
   public:
      BusI2C(int i2c_bus=1);
      int open();
      void close();
      int select_slave(unsigned char slave_addr);      
      void set_i2c_bus(int bus);
      int write(unsigned char slave_addr, unsigned char reg_addr, int length, unsigned char const *data);
      int read(unsigned char slave_addr, unsigned char reg_addr, int length, unsigned char *data);
      int read(unsigned char slave_addr, unsigned char length, unsigned char *data);
      int delay_ms(unsigned long num_ms);
      int get_ms(unsigned long *count);
};

#endif 

La définition de la classe BusI2C :

#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include "busi2c.h"

BusI2C::BusI2C(int i2c_bus/*=1*/):i2c_bus(i2c_bus),i2c_fd(0),current_slave(0)
{
}

int BusI2C::open()
{
    char buff[32];

    if (!i2c_fd) 
    {
        sprintf(buff, "/dev/i2c-%d", i2c_bus);

#ifdef I2C_DEBUG
        printf("\t\t\topen() : %s\n", buff);
#endif

        i2c_fd = ::open(buff, O_RDWR);

        if (i2c_fd < 0) 
        {
            perror("open(i2c_bus)");
            i2c_fd = 0;
            return -1;
        }
    }

    return 0;
}

void BusI2C::close()
{
    if (i2c_fd) 
    {
        ::close(i2c_fd);
        i2c_fd = 0;
        current_slave = 0;
        #ifdef I2C_DEBUG
        printf("\t\t\tclose()\n");
        #endif
    }
}

int BusI2C::select_slave(unsigned char slave_addr)
{
    if (current_slave == slave_addr)
        return 0;

    if (this->open())
        return -1;

#ifdef I2C_DEBUG
    printf("\t\tselect_slave(%02X)\n", slave_addr);
#endif

    if (::ioctl(i2c_fd, I2C_SLAVE, slave_addr) < 0) 
    {
        perror("ioctl(I2C_SLAVE)");
        return -1;
    }

    current_slave = slave_addr;

    return 0;
}

void BusI2C::set_i2c_bus(int bus)
{
    if (i2c_fd)
        this->close();

    i2c_bus = bus;
}

int BusI2C::write(unsigned char slave_addr, unsigned char reg_addr, int length, unsigned char const *data)
{
    int result, i;

    if (length > MAX_WRITE_LEN)
    {
        printf("Max write length exceeded in write()\n");
        return -1;
    }

#ifdef I2C_DEBUG
    printf("\twrite(%02X, %02X, %u, [", slave_addr, reg_addr, length);

    if (length == 0) 
    {
        printf(" NULL ] )\n");
    }
    else 
    {
        for (i = 0; i < length; i++)
            printf(" %02X", data[i]); 

        printf(" ] )\n");
    }
#endif

    if (select_slave(slave_addr))
        return -1;
   
    if (length == 0) 
    {
        result = ::write(i2c_fd, &reg_addr, 1);
        #ifdef I2C_DEBUG
        printf("\tresult : %d\n", result);
        #endif
        if (result < 0) 
        {
            perror("write:1");
            return result;
        }
        else if (result != 1) 
        {
            printf("Write fail:1 Tried 1 Wrote 0\n");
            return -1;
        }
    }
    else 
    {
        txBuff[0] = reg_addr;

        for (i = 0; i < length; i++)
            txBuff[i+1] = data[i];

        result = ::write(i2c_fd, txBuff, length + 1);

        if (result < 0) 
        {
            perror("write:2");
            return result;
        }
        else if (result < (int)length) 
        {
            printf("Write fail:2 Tried %u Wrote %d\n", length, result); 
            return -1;
        }
    }

    return 0;
}

int BusI2C::read(unsigned char slave_addr, unsigned char reg_addr, int length, unsigned char *data)
{
    int tries, result, total;

#ifdef I2C_DEBUG
    int i;

    printf("\tread(%02X, %02X, %u, ...)\n", slave_addr, reg_addr, length);
#endif

    if (this->write(slave_addr, reg_addr, 0, NULL))
        return -1;

    total = 0;
    tries = 0;

    while (total < length && tries < 5) 
    {
        result = ::read(i2c_fd, data + total, length - total);

        if (result < 0) 
        {
            perror("read");
            break;
        }

        total += result;

        if (total == length)
            break;

        tries++;        
        delay_ms(10);
    }

    if (total < length)
        return -1;

#ifdef I2C_DEBUG
    printf("\tLeaving read(), read %d bytes: ", total);

    for (i = 0; i < total; i++)
        printf("%02X ", data[i]); 

    printf("\n");
#endif

    return 0;
}

int BusI2C::read(unsigned char slave_addr, unsigned char length, unsigned char *data)
{
    int tries, result, total;

#ifdef I2C_DEBUG
    int i;

    printf("\tread(%02X, %u, ...)\n", slave_addr, length);
#endif  

    if (select_slave(slave_addr))
        return -1;

    total = 0;
    tries = 0;

    while (total < length && tries < 5) 
    {
        result = ::read(i2c_fd, data + total, length - total);

        if (result < 0) 
        {
            perror("read");
            break;
        }

        total += result;

        if (total == length)
            break;

        tries++;        
        delay_ms(10);
    }

    if (total < length)
        return -1;

#ifdef I2C_DEBUG
    printf("\tLeaving read(), read %d bytes: ", total);

    for (i = 0; i < total; i++)
        printf("%02X ", data[i]); 

    printf("\n");
#endif

    return 0;
}

int BusI2C::delay_ms(unsigned long num_ms)
{
    struct timespec ts;

    ts.tv_sec = num_ms / 1000;
    ts.tv_nsec = (num_ms % 1000) * 1000000;

    return nanosleep(&ts, NULL);
}

int BusI2C::get_ms(unsigned long *count)
{
    struct timeval t;

    if (!count)
        return -1;

    if (gettimeofday(&t, NULL) < 0) 
    {
        perror("gettimeofday");
        return -1;
    }

    *count = (t.tv_sec * 1000) + (t.tv_usec / 1000);    

    return 0;
}

La déclaration de la classe BH1750 :

#ifndef BH1750_H
#define BH1750_H

//#define BH1750_DEBUG

#define BH1750_I2CADDR_L (0x23)
#define BH1750_I2CADDR_H (0x5C)

// No active state
#define BH1750_POWER_DOWN 0x00

// Wating for measurment command
#define BH1750_POWER_ON 0x01

// Reset data register value - not accepted in POWER_DOWN mode
#define BH1750_RESET 0x07

// Start measurement at 1lx resolution. Measurement time is approx 120ms.
#define BH1750_CONTINUOUS_HIGH_RES_MODE  0x10

// Start measurement at 0.5lx resolution. Measurement time is approx 120ms.
#define BH1750_CONTINUOUS_HIGH_RES_MODE_2  0x11

// Start measurement at 4lx resolution. Measurement time is approx 16ms.
#define BH1750_CONTINUOUS_LOW_RES_MODE  0x13

// Start measurement at 1lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_HIGH_RES_MODE  0x20

// Start measurement at 0.5lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_HIGH_RES_MODE_2  0x21

// Start measurement at 1lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_LOW_RES_MODE  0x23

class BusI2C;

class BH1750 
{
   public:
      BH1750(uint8_t adresse = BH1750_I2CADDR_L);
      void begin(uint8_t mode = BH1750_CONTINUOUS_HIGH_RES_MODE);
      void configure(uint8_t mode);
      float readLightLevel(void);

   private:
      uint8_t _adresse;      
      BusI2C *busi2c;
      
      void write8(uint8_t data);
      void enable(void);
      void disable(void);
      void reset(void);
};

#endif

La définition de la classe BH1750 :

#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <stdint.h>
#include <cstring>

#include "busi2c.h"
#include "BH1750.h"

BH1750::BH1750(uint8_t adresse) 
{
   _adresse = adresse;
   
   busi2c = new BusI2C;
   busi2c->set_i2c_bus(1);
   #ifdef BH1750_DEBUG   
   printf("adresse : 0x%02X\n", _adresse);
   #endif
}

void BH1750::begin(uint8_t mode) 
{
   //disable();
   configure(mode);  
}

void BH1750::configure(uint8_t mode) 
{
    //reset();
    //enable();
    switch (mode) 
    {
        case BH1750_CONTINUOUS_HIGH_RES_MODE:
        case BH1750_CONTINUOUS_HIGH_RES_MODE_2:
        case BH1750_CONTINUOUS_LOW_RES_MODE:
        case BH1750_ONE_TIME_HIGH_RES_MODE:
        case BH1750_ONE_TIME_HIGH_RES_MODE_2:
        case BH1750_ONE_TIME_LOW_RES_MODE:
            // apply a valid mode change            
            busi2c->write(_adresse, mode, 0, NULL);
            #ifdef BH1750_DEBUG
            printf("0x%02X <- 0x%02X\n", _adresse, mode);
            #endif
            busi2c->delay_ms(180);
            break;
        default:
            // Invalid measurement mode
            #ifdef BH1750_DEBUG
            printf("Invalid measurement mode !\n");
            #endif
            break;
    }
}

float BH1750::readLightLevel(void) 
{   
   uint16_t level = 0;
   float lux = 0.;

   unsigned char datas[2];
   if (busi2c->read(_adresse, 2, datas))
      return 0.;
   #ifdef BH1750_DEBUG   
   printf("0x%02X -> 0x%02X 0x%02X\n", _adresse, datas[0], datas[1]);   
   #endif
   level = datas[0] << 8;   
   level |= datas[1];   

   lux = (float)level/1.2; // convert to lux

   #ifdef BH1750_DEBUG
   printf("Luminosite : %.2f lux (%d : 0x%04X)\n", lux, level, level);
   #endif

   return lux;
}

/*********************************************************************/

void BH1750::enable(void)
{
   busi2c->write(_adresse, BH1750_POWER_ON, 0, NULL);
   #ifdef BH1750_DEBUG
   printf("0x%02X <- 0x%02X\n", _adresse, BH1750_POWER_ON);
   #endif
}

void BH1750::disable(void)
{
   busi2c->write(_adresse, BH1750_POWER_DOWN, 0, NULL);
   #ifdef BH1750_DEBUG
   printf("0x%02X <- 0x%02X\n", _adresse, BH1750_POWER_DOWN);
   #endif
}

void BH1750::reset(void)
{
   enable();
   busi2c->write(_adresse, BH1750_RESET, 0, NULL);
   printf("0x%02X <- 0x%02X\n", _adresse, BH1750_RESET);
}

Un programme de test de la classe BH1750 :

#include <cstdio>
#include <cstdlib>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "BH1750.h"

int main()
{
   BH1750 bh1750(BH1750_I2CADDR_L); // cf. doc
   float luminosite = 0.;
   
   bh1750.begin();
   while (true)
   {      
      luminosite = bh1750.readLightLevel();
      printf("Luminosite : %.2f lux\n", luminosite);
      sleep(2);        
   }
     
   return EXIT_SUCCESS;
} 

Code source : test-mo-bh1750.zip

Retour au sommaire