sht31_th.tc¶
SHT31 Temp & Humidity — tc2plugin PoC test variant (T+H only)
// SHT31 Temp & Humidity — tc2plugin PoC test variant (T+H only)
// Trimmed copy of sht31.tc: dewpoint/abshumi REMOVED because they use
// log()/exp() (jt-routed transcendentals). On real HW the plugin's
// jt[] binding for logf/expf wild-jumps (MMU entry fault @0x428663cc,
// inside tc_sht_calc_dewpoint) — a separate known issue. Soft-float
// (+ - * / and (float) casts) is proven working live. Canonical
// sht31.tc is left intact.
#define SHT_ADDR1 0x44
#define SHT_ADDR2 0x45
float sht_temp = 0.0;
float sht_humi = 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;
}
// Scan both buses and addresses using Tasmota's I2C claiming
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_ok = 1;
}
void sht_web_label(int idx) {
char vt[32];
LGetString(idx, sht_lbl);
sprintf(vt, "{s}SHT31 %s{m}", sht_lbl);
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);
} 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);
}
// Called before VM stops — release I2C address so driver can restart
void OnExit() {
if (sht_addr) {
I2cResetActive(sht_addr, sht_bus);
sht_addr = 0;
}
}
// Init contract (tc2plugin -> pFUNC_INIT): return > 0 on success,
// 0 on failure. The translator wires this return value to the
// plugin `initialized` flag at every return path. Returning 0 when
// no sensor is found lets the loader retry init later instead of
// marking a missing sensor as up.
int main() {
sht_ok = 0;
sht_addr = 0;
if (sht_scan()) {
addLog("SHT3X found at 0x%x on bus %d", sht_addr, sht_bus);
return 1;
}
addLog("SHT3X not found on any bus");
return 0;
}