dhtxx-read-raw

xod/common-hardware/dhtxx-read-raw

Utility. Reads raw data bytes from a DHTxx thermometer with one-wire interface. Possible errors: — Checksum failure — Read timeout — Initialization error — Invalid port
dhtxx-read-raw
@/dhtxx-read-raw
Deprecated: Use nodes from `xod-dev/dht` library instead
Utility. Reads raw data bytes from a DHTxx thermometer with one-wire interface. Possible errors: — Checksum failure — Read timeout — Initialization error — Invalid port
PORTport
Board port number the thermometer is connected to.
UPDpulse
Triggers an update, i.e. reading values again.
dhtxx-read-raw
D1
D0
D2
D3
DONE
PORT
UPD
DONEpulse
Fires on reading complete
D3number
3-rd byte (temperature low) of the reading
D2number
2-nd byte (temperature high) of the reading
D0number
0-th byte (humidity high) of the reading
D1number
1-st byte (humidity low) of the reading

C++ implementation

struct State {
    bool reading;
};

{{ GENERATED_CODE }}

enum DhtStatus
{
    DHT_OK = 0,
    DHT_START_FAILED_1 = 1,
    DHT_START_FAILED_2 = 2,
    DHT_READ_TIMEOUT = 3,
    DHT_CHECKSUM_FAILURE = 4,
};

unsigned long pulseInLength(uint8_t pin, bool state, unsigned long timeout) {
    unsigned long startMicros = micros();
    while (digitalRead(pin) == state) {
        if (micros() - startMicros > timeout)
            return 0;
    }
    return micros() - startMicros;
}

bool readByte(uint8_t port, uint8_t* out) {
    // Collect 8 bits from datastream, return them interpreted
    // as a byte. I.e. if 0000.0101 is sent, return decimal 5.

    unsigned long pulseLength = 0;
    uint8_t result = 0;
    for (uint8_t i = 8; i--; ) {
        // We enter this during the first start bit (low for 50uS) of the byte

        if (pulseInLength(port, LOW, 70) == 0)
            return false;

        // Dataline will now stay high for 27 or 70 uS, depending on
        // whether a 0 or a 1 is being sent, respectively.

        pulseLength = pulseInLength(port, HIGH, 80);

        if (pulseLength == 0)
            return false;

        if (pulseLength > 45)
            result |= 1 << i; // set subsequent bit
    }

    *out = result;
    return true;
}

DhtStatus readValues(uint8_t port, uint8_t* outData) {
    // Stop reading request
    digitalWrite(port, HIGH);

    // DHT datasheet says host should keep line high 20-40us, then watch for
    // sensor taking line low.  That low should last 80us. Acknowledges "start
    // read and report" command.
    delayMicroseconds(30);

    // Change Arduino pin to an input, to watch for the 80us low explained a
    // moment ago.
    pinMode(port, INPUT_PULLUP);

    if (pulseInLength(port, LOW, 90) == 0)
        return DHT_START_FAILED_1;

    // After 80us low, the line should be taken high for 80us by the sensor.
    // The low following that high is the start of the first bit of the forty
    // to come. The method readByte() expects to be called with the system
    // already into this low.
    if (pulseInLength(port, HIGH, 90) == 0)
        return DHT_START_FAILED_2;

    // now ready for data reception... pick up the 5 bytes coming from
    // the sensor
    for (uint8_t i = 0; i < 5; i++)
        if (!readByte(port, outData + i))
            return DHT_READ_TIMEOUT;

    // Restore pin to output duties
    pinMode(port, OUTPUT);
    digitalWrite(port, HIGH);

    // See if data received consistent with checksum received
    uint8_t checkSum = outData[0] + outData[1] + outData[2] + outData[3];
    if (outData[4] != checkSum)
        return DHT_CHECKSUM_FAILURE;

    return DHT_OK;
}

void enterIdleState(uint8_t port) {
    // Restore pin to output duties
    pinMode(port, OUTPUT);
    digitalWrite(port, HIGH);
}

void evaluate(Context ctx) {
    static_assert(isValidDigitalPort(constant_input_PORT), "must be a valid digital port");

    State* state = getState(ctx);
    uint8_t port = (uint8_t)getValue<input_PORT>(ctx);

    if (state->reading) {
        uint8_t data[5];
        auto status = readValues(port, data);

        if (status == DHT_OK) {
            emitValue<output_D0>(ctx, data[0]);
            emitValue<output_D1>(ctx, data[1]);
            emitValue<output_D2>(ctx, data[2]);
            emitValue<output_D3>(ctx, data[3]);
            emitValue<output_DONE>(ctx, 1);
        } else {
            raiseError(ctx);
        }

        enterIdleState(port);
        state->reading = false;
    } else if (isInputDirty<input_UPD>(ctx)) {
        // initiate request for data
        pinMode(port, OUTPUT);
        digitalWrite(port, LOW);
        // for request we should keep the line low for 18+ ms
        setTimeout(ctx, 18);
        state->reading = true;
    } else {
        enterIdleState(port);
    }
}