Zum Inhalt

sht31.tc

SHT31 Temperature & Humidity Sensor Driver

Source on GitHub

// SHT31 Temperature & Humidity Sensor Driver
// Scans both I2C buses (0 and 1) and both addresses (0x44, 0x45)
// Uses i2cSetDevice/i2cSetActiveFound to properly claim the address
// Demonstrates: I2C bus scan, CRC-8, WebCall, JsonCall

#define SHT_ADDR1  0x44
#define SHT_ADDR2  0x45

float sht_temp = 0.0;
float sht_humi = 0.0;
float sht_dewp = 0.0;
float sht_absh = 0.0;
int sht_ok = 0;
int sht_addr = 0;
int sht_bus = 0;
char sht_lbl[32];

// Shared buffer for I2C data
char sht_data[6];

// CRC-8 for SHT31 (polynomial 0x31)
int sht_crc8(int start, int len) {
    int crc = 0xFF;
    int i = 0;
    while (i < len) {
        crc = crc ^ sht_data[start + i];
        int bit = 0;
        while (bit < 8) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ 0x31;
            } else {
                crc = crc << 1;
            }
            crc = crc & 0xFF;
            bit++;
        }
        i++;
    }
    return crc;
}

// Dewpoint (Magnus formula), returns °C
float sht_calc_dewpoint(float t, float h) {
    if (h <= 0.0) return 0.0;
    float gamma = (17.271 * t) / (237.7 + t) + log(h / 100.0);
    return (237.7 * gamma) / (17.271 - gamma);
}

// Absolute humidity in g/m³
float sht_calc_abshumi(float t, float h) {
    float ah = 6.112 * exp((17.67 * t) / (t + 243.5)) * h * 2.1674;
    return ah / (273.15 + t);
}

// Scan both buses and addresses using Tasmota's I2C claiming
// i2cSetDevice checks: not already claimed AND device responds
int sht_scan() {
    int bus = 0;
    while (bus < 2) {
        if (i2cSetDevice(SHT_ADDR1, bus)) {
            sht_addr = SHT_ADDR1;
            sht_bus = bus;
            i2cSetActiveFound(sht_addr, "SHT3X", sht_bus);
            return 1;
        }
        if (i2cSetDevice(SHT_ADDR2, bus)) {
            sht_addr = SHT_ADDR2;
            sht_bus = bus;
            i2cSetActiveFound(sht_addr, "SHT3X", sht_bus);
            return 1;
        }
        bus++;
    }
    return 0;
}

void EverySecond() {
    if (!sht_addr) {
        if (!sht_scan()) {
            sht_ok = 0;
            return;
        }
    }

    // Send measurement command: clock stretching, high repeatability (0x2C06)
    if (!i2cWrite8(sht_addr, 0x2C, 0x06, sht_bus)) {
        sht_ok = 0;
        sht_addr = 0;  // force rescan next time
        return;
    }

    // SHT31 needs ~15ms for high repeatability measurement
    delay(30);

    // Read 6 bytes: [temp_msb, temp_lsb, temp_crc, humi_msb, humi_lsb, humi_crc]
    if (!i2cRead0(sht_addr, sht_data, 6, sht_bus)) {
        sht_ok = 0;
        sht_addr = 0;
        return;
    }

    // Verify CRC for temperature (bytes 0-1, CRC in byte 2)
    if (sht_crc8(0, 2) != sht_data[2]) {
        sht_ok = 0;
        return;
    }

    // Verify CRC for humidity (bytes 3-4, CRC in byte 5)
    if (sht_crc8(3, 2) != sht_data[5]) {
        sht_ok = 0;
        return;
    }

    // Temperature: -45 + 175 * raw / 65535
    int raw_t = (sht_data[0] << 8) | sht_data[1];
    sht_temp = -45.0 + 175.0 * (float)raw_t / 65535.0;

    // Humidity: 100 * raw / 65535
    int raw_h = (sht_data[3] << 8) | sht_data[4];
    sht_humi = 100.0 * (float)raw_h / 65535.0;

    sht_dewp = sht_calc_dewpoint(sht_temp, sht_humi);
    sht_absh = sht_calc_abshumi(sht_temp, sht_humi);

    sht_ok = 1;
}

void sht_web_label(int idx) {
    char vt[32];
    LGetString(idx, sht_lbl);
    strcpy(vt, "{s}SHT31 ");
    strcat(vt, sht_lbl);
    strcat(vt, "{m}");
    webSend(vt);
}

void WebCall() {
    char buf[32];
    if (sht_ok) {
        sht_web_label(0);
        sprintf(buf, "%.1f &deg;C{e}", sht_temp);
        webSend(buf);
        sht_web_label(1);
        sprintf(buf, "%.1f %{e}", sht_humi);
        webSend(buf);
        sht_web_label(3);
        sprintf(buf, "%.1f &deg;C{e}", sht_dewp);
        webSend(buf);
        sht_web_label(20);
        sprintf(buf, "%.1f g/m&sup3;{e}", sht_absh);
        webSend(buf);
    } else {
        webSend("{s}SHT31{m}not found{e}");
    }
}

void JsonCall() {
    if (!sht_ok) return;
    char buf[96];
    sprintf(buf, ",\"SHT3X\":{\"Temperature\":%.1f", sht_temp);
    responseAppend(buf);
    sprintf(buf, ",\"Humidity\":%.1f", sht_humi);
    responseAppend(buf);
    sprintf(buf, ",\"DewPoint\":%.1f", sht_dewp);
    responseAppend(buf);
    sprintf(buf, ",\"AbsHumidity\":%.1f}", sht_absh);
    responseAppend(buf);
}

// Called before VM stops — release I2C address so driver can restart
void OnExit() {
    if (sht_addr) {
        I2cResetActive(sht_addr, sht_bus);
        sht_addr = 0;
    }
}

int main() {
    char buf[48];
    sht_ok = 0;
    sht_addr = 0;

    if (sht_scan()) {
        sprintf(buf, "SHT3X found at 0x%x on bus %d", sht_addr, sht_bus);
        addLog(buf);
    } else {
        addLog("SHT3X not found on any bus");
    }
    return 0;
}