Zum Inhalt

ltr308.tc

LTR-308ALS-01 Ambient Light Sensor Driver

Source on GitHub

// 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;
}