ltr308.tc¶
LTR-308ALS-01 Ambient Light Sensor Driver
// LTR-308ALS-01 Ambient Light Sensor Driver
// I2C address: 0x53, Part ID: 0xB1
// Range: 0.01 to 157,000 lux (20-bit ADC)
// Lux = (0.6 * ALS_DATA) / (Gain * Integration_Time)
// Scans both I2C buses, claims via Tasmota I2C system
// Reads every second, displays on web UI + JSON teleperiod
// Compile with -DUSE_CHART for 6h chart history
// ─── Register addresses ───
#define LTR308_ADDR 0x53
#define LTR308_MAIN_CTRL 0x00
#define LTR308_MEAS_RATE 0x04
#define LTR308_ALS_GAIN 0x05
#define LTR308_PART_ID 0x06
#define LTR308_MAIN_STATUS 0x07
#define LTR308_ALS_DATA_0 0x0D
#define LTR308_ALS_DATA_1 0x0E
#define LTR308_ALS_DATA_2 0x0F
// ─── MAIN_CTRL bits ───
#define LTR308_ALS_ENABLE 0x02
#define LTR308_SW_RESET 0x10
// ─── ALS_GAIN values ───
#define LTR308_GAIN_1X 0x00
#define LTR308_GAIN_3X 0x01
#define LTR308_GAIN_6X 0x02
#define LTR308_GAIN_9X 0x03
#define LTR308_GAIN_18X 0x04
// ─── ALS_MEAS_RATE: Resolution (bits 6:4) | Rate (bits 2:0) ───
#define LTR308_RES_20BIT 0x00
#define LTR308_RES_19BIT 0x10
#define LTR308_RES_18BIT 0x20
#define LTR308_RES_17BIT 0x30
#define LTR308_RES_16BIT 0x40
#define LTR308_RATE_25MS 0x00
#define LTR308_RATE_50MS 0x01
#define LTR308_RATE_100MS 0x02
#define LTR308_RATE_500MS 0x03
#define LTR308_RATE_1000MS 0x05
#define LTR308_RATE_2000MS 0x06
// ─── MAIN_STATUS bits ───
#define LTR308_DATA_STATUS 0x08
#define LTR308_PART_ID_VAL 0xB1
// ─── Sensor state ───
float lux;
int ltr_ok;
int ltr_addr;
int ltr_bus;
int ltr_gain; // gain register value (0-4)
int ltr_res; // resolution index (0-4)
char ltr_buf[4];
// ─── Gain multiplier table ───
// Index: 0=1x, 1=3x, 2=6x, 3=9x, 4=18x
float gain_tbl[5];
// ─── Integration time table (seconds) ───
// Index: 0=400ms(20bit), 1=200ms(19bit), 2=100ms(18bit), 3=50ms(17bit), 4=25ms(16bit)
float itime_tbl[5];
// ─── Chart history (6h at 1 sample/min = 360 points) ───
#ifdef USE_CHART
#define CHART_LEN 360
float hist_lux[CHART_LEN];
int hist_pos;
int hist_tick;
#endif
// ═══════════════════════════════════════════════════════════════════
// Initialize lookup tables
// ═══════════════════════════════════════════════════════════════════
void init_tables() {
gain_tbl[0] = 1.0;
gain_tbl[1] = 3.0;
gain_tbl[2] = 6.0;
gain_tbl[3] = 9.0;
gain_tbl[4] = 18.0;
itime_tbl[0] = 0.4;
itime_tbl[1] = 0.2;
itime_tbl[2] = 0.1;
itime_tbl[3] = 0.05;
itime_tbl[4] = 0.025;
}
// ═══════════════════════════════════════════════════════════════════
// Calculate lux from raw ALS data
// Lux = (0.6 * ALS_DATA) / (Gain * Integration_Time)
// ═══════════════════════════════════════════════════════════════════
float calc_lux(int raw) {
float numerator = 0.6 * (float)raw;
float denominator = gain_tbl[ltr_gain] * itime_tbl[ltr_res];
if (denominator == 0.0) return 0.0;
return numerator / denominator;
}
// ═══════════════════════════════════════════════════════════════════
// Scan I2C buses for LTR308
// ═══════════════════════════════════════════════════════════════════
int ltr_scan() {
int bus = 0;
while (bus < 2) {
if (i2cSetDevice(LTR308_ADDR, bus)) {
int id = i2cRead8(LTR308_ADDR, LTR308_PART_ID, bus);
if (id == LTR308_PART_ID_VAL) {
ltr_addr = LTR308_ADDR;
ltr_bus = bus;
i2cSetActiveFound(ltr_addr, "LTR308", ltr_bus);
return 1;
}
}
bus++;
}
return 0;
}
// ═══════════════════════════════════════════════════════════════════
// Configure sensor: reset, set gain/resolution/rate, enable
// ═══════════════════════════════════════════════════════════════════
int ltr_configure() {
// Software reset — ignore return (sensor NACKs during reset)
i2cWrite8(ltr_addr, LTR308_MAIN_CTRL, LTR308_SW_RESET, ltr_bus);
delay(100);
// Set measurement rate: 18-bit resolution (100ms), 100ms rate
if (!i2cWrite8(ltr_addr, LTR308_MEAS_RATE, LTR308_RES_18BIT | LTR308_RATE_100MS, ltr_bus)) return 0;
// Set gain: 3x
ltr_gain = LTR308_GAIN_3X;
ltr_res = 2; // index into itime_tbl for 18-bit/100ms
if (!i2cWrite8(ltr_addr, LTR308_ALS_GAIN, ltr_gain, ltr_bus)) return 0;
// Enable ALS
if (!i2cWrite8(ltr_addr, LTR308_MAIN_CTRL, LTR308_ALS_ENABLE, ltr_bus)) return 0;
return 1;
}
// ═══════════════════════════════════════════════════════════════════
// Read sensor — called every second
// ═══════════════════════════════════════════════════════════════════
void EverySecond() {
if (!ltr_addr) {
if (!ltr_scan()) {
ltr_ok = 0;
return;
}
if (!ltr_configure()) {
ltr_ok = 0;
ltr_addr = 0;
return;
}
}
// Check data ready
int status = i2cRead8(ltr_addr, LTR308_MAIN_STATUS, ltr_bus);
if (!(status & LTR308_DATA_STATUS)) return;
// Read 3 bytes of ALS data (0x0D, 0x0E, 0x0F)
if (!i2cRead(ltr_addr, LTR308_ALS_DATA_0, ltr_buf, 3, ltr_bus)) {
ltr_ok = 0;
ltr_addr = 0;
return;
}
int raw = (ltr_buf[0] & 0xFF) | ((ltr_buf[1] & 0xFF) << 8) | ((ltr_buf[2] & 0x0F) << 16);
lux = calc_lux(raw);
ltr_ok = 1;
#ifdef USE_CHART
// Update chart history every minute
hist_tick++;
if (hist_tick >= 60) {
hist_tick = 0;
hist_lux[hist_pos % CHART_LEN] = lux;
hist_pos++;
}
#endif
}
#ifdef USE_CHART
// ═══════════════════════════════════════════════════════════════════
// Web UI — chart on main page
// ═══════════════════════════════════════════════════════════════════
void WebPage() {
int n = hist_pos;
if (n > CHART_LEN) n = CHART_LEN;
if (n > 0) {
WebChart('l', "Illuminance", "lux", 0xf1c40f, hist_pos, CHART_LEN, hist_lux, 1, 0, 0, 0);
}
}
#endif
// ═══════════════════════════════════════════════════════════════════
// Web UI — sensor row
// ═══════════════════════════════════════════════════════════════════
void WebCall() {
char buf[48];
if (ltr_ok) {
sprintf(buf, "{s}LTR308 Illuminance{m}%.1f lux{e}", lux);
webSend(buf);
} else {
webSend("{s}LTR308{m}not found{e}");
}
}
// ═══════════════════════════════════════════════════════════════════
// JSON teleperiod — MQTT sensor payload
// ═══════════════════════════════════════════════════════════════════
void JsonCall() {
if (!ltr_ok) return;
char buf[64];
sprintf(buf, ",\"LTR308\":{\"Illuminance\":%.1f}", lux);
responseAppend(buf);
}
// ═══════════════════════════════════════════════════════════════════
// Cleanup on exit
// ═══════════════════════════════════════════════════════════════════
void OnExit() {
if (ltr_addr) {
// Put sensor to standby
i2cWrite8(ltr_addr, LTR308_MAIN_CTRL, 0x00, ltr_bus);
I2cResetActive(ltr_addr, ltr_bus);
ltr_addr = 0;
}
}
// ═══════════════════════════════════════════════════════════════════
// Main — initialize and scan
// ═══════════════════════════════════════════════════════════════════
int main() {
char buf[64];
ltr_ok = 0;
ltr_addr = 0;
#ifdef USE_CHART
hist_pos = 0;
hist_tick = 0;
#endif
init_tables();
// Clear any stale I2C claim from previous run
I2cResetActive(LTR308_ADDR, 0);
I2cResetActive(LTR308_ADDR, 1);
if (ltr_scan()) {
sprintf(buf, "LTR308 found at 0x%x on bus %d", ltr_addr, ltr_bus);
addLog(buf);
if (ltr_configure()) {
addLog("LTR308 configured: 3x gain, 18-bit, 100ms");
} else {
addLog("LTR308 configuration failed");
ltr_addr = 0;
}
} else {
addLog("LTR308 not found on any bus");
}
return 0;
}