sht31.tc¶
SHT31 Temperature & Humidity Sensor Driver
// 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 °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 °C{e}", sht_dewp);
webSend(buf);
sht_web_label(20);
sprintf(buf, "%.1f g/m³{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;
}