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.
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
Être capable de lire la valeur de l’éclairement lumineux en lux.
Il vous faudra tout d’abord lire le document sur le bus I2C.
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, ®_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