Zum Inhalt

sht31_publisher.tc

sht31_publisher.tc — SHT31 sensor that publishes to UDP globals

Source on GitHub

// =====================================================================
// sht31_publisher.tc — SHT31 sensor that publishes to UDP globals
//
// Variant of `examples/sht31.tc` for the multi-slot pattern: instead
// of populating SensorJSON via JsonCall (which forces every consumer
// to call `sensorGet()` → `MqttShowSensor()` → fan-out to all slots'
// JsonCall), this script publishes the room values to two
// `global float`s. Other slots — local or remote — just declare the
// same names and receive updates via UDP loopback / multicast.
//
// Why a separate script: the upstream `sht31.tc` is the canonical I2C-
// claim demo (i2cSetActiveFound, CRC-8, JsonCall + WebCall). Keeping
// it intact preserves the teaching value. This file is the "pub-to-
// globals" pattern, suitable for devices where:
//   - Tasmota's USE_SHT3X is NOT compiled in
//   - You want multiple slots reading the value cheaply
//   - You want cross-device sharing automatic via UDP
//
// Differences from sht31.tc:
//   - Adds `global float btemp; global float bhumi;` (UDP-shared)
//   - EverySecond writes those globals on each successful read
//   - Drops JsonCall (SensorJSON path is not used by consumers)
//   - Drops WebCall (no display rows; consumers render their own)
//   - delay(30) in EverySecond replaced with a 2-tick state machine
//     (issue measurement on tick N, read result on tick N+1) so we
//     never hold vm_mutex through the SHT31 conversion
//
// Naming: btemp/bhumi match the existing house-network convention
// (see core2_energy.tc, epaper42_v2.tc). Tweak both ends if you use
// different names elsewhere.
// =====================================================================

#define SHT_ADDR1  0x44
#define SHT_ADDR2  0x45

// ── House-network UDP globals (auto-broadcast on every change) ──
global float btemp = 20.0;
global float bhumi = 50.0;

// ── Internal state ──
char sht_data[6];
int  sht_addr = 0;
int  sht_bus  = 0;
int  sht_ok   = 0;

// 0 = idle (next tick: issue measurement)
// 1 = waiting (next tick: read result)
int  sht_state = 0;

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

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;
        }
        sht_state = 0;  // start fresh after scan
    }

    if (sht_state == 0) {
        // Issue: clock-stretching, high-repeatability measurement command.
        // Result is ready ~15 ms later — we read it on the NEXT tick.
        if (!i2cWrite8(sht_addr, 0x2C, 0x06, sht_bus)) {
            sht_ok    = 0;
            sht_addr  = 0;
            sht_state = 0;
            return;
        }
        sht_state = 1;
        return;
    }

    // sht_state == 1: read the result issued one tick ago.
    sht_state = 0;
    if (!i2cRead0(sht_addr, sht_data, 6, sht_bus)) {
        sht_ok   = 0;
        sht_addr = 0;
        return;
    }

    if (sht_crc8(0, 2) != sht_data[2]) { sht_ok = 0; return; }
    if (sht_crc8(3, 2) != sht_data[5]) { sht_ok = 0; return; }

    int raw_t = (sht_data[0] << 8) | sht_data[1];
    int raw_h = (sht_data[3] << 8) | sht_data[4];
    float t = -45.0 + 175.0 * (float)raw_t / 65535.0;
    float h = 100.0 * (float)raw_h / 65535.0;

    // Publish to UDP-shared globals. Same-device readers (other TinyC
    // slots that declare `global float btemp`) receive updates via the
    // local loopback path; off-device readers receive via UDP multicast
    // on the next ~2 s broadcast cycle.
    btemp = t;
    bhumi = h;
    sht_ok = 1;
}

void OnExit() {
    if (sht_addr) {
        I2cResetActive(sht_addr, sht_bus);
        sht_addr = 0;
    }
}

int main() {
    sht_ok    = 0;
    sht_addr  = 0;
    sht_state = 0;

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