microsoft / devicescript

TypeScript for Tiny IoT Devices (ESP32, RP2040, ...)
https://microsoft.github.io/devicescript/
MIT License
3.24k stars 112 forks source link

DHT11/DHT22 #460

Open pelikhan opened 1 year ago

pelikhan commented 1 year ago

Device description

DH11 is a popular temperature/humidity sensor, I2c.

Datasheet, firmware, etc...

https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf

louisvangeldrop commented 1 year ago

Personally I am using the DHT22, since it is a little bit better in accuracy than the dht11. I have written for Moddable a driver for the DHT11/22 using the RMT-functionality. It is based on the Espruino-implementation. I was considering to do the same in devicescript. Hence my request for the setWatch-type of functionality.

pelikhan commented 1 year ago

Very interresting. Could you point us to the implementation of your driver?

louisvangeldrop commented 1 year ago

I have used the following class in Makecode/STS


class DHT11 {

    constructor(public pin: DigitalPin) {

    }

    public setTimeout(callBack: Function, time: number) {
        control.runInParallel(function () {
            // console.log("pause voor")
            pause(time);
            // console.log("pause na")
            callBack();
            // console.log("callback klaar")
        });
    };

    public read(
        cb: (data: { raw: number[]; rh: number; t: number; err: boolean }) => void,
        n = 10
    ) {
        let d: number[];
        let data: string = "";
        let ht = this;
        this.pin.digitalWrite(false);

        this.pin.setWatch(
            (t: any) => {
                d.push(t > 0.00005 * 1e6 ? 1 : 0);
                // console.log("CB Called");
            }
        );

        pause(20);
        this.pin.pin.setPull(PinPullMode.PullUp); // force pin state to output
        pause(50)

        /* this.setTimeout(() => {
                        this.pin.setPull(PinPullMode.PullUp)
                    }, 1); */
        // this.setTimeout(() => {
        this.pin.clearWatch(); // delete this.watch;
        let cks =
            this.decode(d.slice(2, 8)) +
            this.decode(d.slice(10, 8)) +
            this.decode(d.slice(18, 8)) +
            this.decode(d.slice(26, 8));
        if (cks && (cks & 0xff) == this.decode(d.slice(34, 8))) {
            cb({
                raw: d,
                rh: this.decode(d.slice(2, 8)),
                t: this.decode(d.slice(18, 8)),
                err: false
            });
        } else {
            if (n > 1) {
                // this.setTimeout(() => {
                pause(500)
                this.read(cb, --n);
                // }, 500);
            } else {
                cb({ raw: d, t: -1, rh: -1, err: cks > 0 });
            }
        }
        // }, 50);
    }

    public decode = (omega: number[]) => {
        let max = omega.length;
        let result = omega[max - 1] ? 1 : 0;
        let temp = 2;
        for (let i = max - 2; i > -1; i--) {
            result += omega[i] * temp;
            temp *= 2;
        }
        return result;
    };

}
louisvangeldrop commented 12 months ago
// https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf

import * as ds from "@devicescript/core"
import { pinMode, digitalWrite, subscribeDigital } from "@devicescript/gpio/src"
import { uptime } from "@devicescript/runtime/src"

type ARHT = { raw: string, absHumidity: number, humidity: number, celsius: number, fahrenheit: number }

export class dht {
    #dhtPin: ds.InputPin & ds.OutputPin & ds.AnalogInPin & ds.AnalogOutPin
    #lastTime: number
    #result: ARHT
    #raw = ''       // bit-string

    constructor(public dhtPin: ds.InputPin & ds.OutputPin, public dhtVCC?: ds.OutputPin) {
    }

    #handler = async (value: ds.DigitalValue) => {
        const currentTime = await uptime()
        const deltaTime = currentTime - this.#lastTime
        if (value === 0) {  // falling. Rising interval should be 50 microseconds
            this.#raw += 0 | <any>(deltaTime > 60)  // >70 => "1" 26-28 => 0
        }
        this.#lastTime = currentTime
    }

    async read(n = 10): Promise<boolean> {
        if (this.dhtVCC) {
            pinMode(this.dhtVCC, ds.GPIOMode.Output)
            digitalWrite(this.dhtVCC, 1)
        }

        pinMode(this.dhtPin, ds.GPIOMode.Output)
        digitalWrite(this.dhtPin, 0)
        await ds.sleep(20)  //?????maybe skip the first two interrupts???x
        const unSubscribe = subscribeDigital(this.#dhtPin, this.#handler)     //look for falling 
        pinMode(this.dhtPin, ds.GPIOMode.InputPullUp)
        await ds.delay(50)
        unSubscribe()
        if (this.dhtVCC) {
            pinMode(this.dhtVCC, ds.GPIOMode.Output)
            digitalWrite(this.dhtVCC, 0)
        }
        if (this.#raw.length > 39) {
            const cks =
                this.#parseInt(this.#raw.slice(0, 8), 2) +
                this.#parseInt(this.#raw.slice(8, 16), 2) +
                this.#parseInt(this.#raw.slice(16, 24), 2) +
                this.#parseInt(this.#raw.slice(24, 32), 2);
            const checkSum = this.#parseInt(this.#raw.slice(32, 40), 2)
            const ok = cks && ((cks & 0xFF) === checkSum)
            if (ok === false) {
                if (n > 1) {
                    await ds.delay(500)
                    return await this.read(--n)
                }
                else { return false }
            }
            else { return true }
        }
        return true
    }

    #parseInt = (string: string, radix: number = 2): number => {
        let base = 1
        let result = 0
        const strArr = string.split('')
        for (let i = strArr.length - 1; i >= 0; i--) {
            result += parseInt(strArr[i]) * radix ** (base - 1)
            base += 1
        }
        return result
    }

    #parseFloat = (number: number): number => {
        return Math.round(number * 100) / 100       // emulate toFixed(2)
    }

    dht11 = () => {
        this.#result.humidity = this.#parseInt(this.#raw.slice(0, 8), 2) + this.#parseInt(this.#raw.slice(8, 16), 2) * 0.1
        this.#result.celsius = this.#parseInt(this.#raw.slice(16, 24), 2) + this.#parseInt(this.#raw.slice(24, 32), 2) * 0.1
        this.#result.absHumidity = this.#parseFloat((this.#result.humidity * 0.42 * Math.exp(this.#result.celsius * 10 * 0.006235398) / 10))
        this.#result.fahrenheit = 32 + this.#result.celsius * 1.8
        return this.#result;
    }

    dht22 = () => {
        this.#result.humidity = this.#parseFloat((this.#parseInt(this.#raw.slice(0, 16), 2) * 0.1))
        this.#result.celsius = this.#parseFloat((this.#parseInt(this.#raw.slice(17, 32), 2) * 0.2 * (0.5 - parseInt(this.#raw[16]))))
        this.#result.absHumidity = this.#parseFloat((this.#result.humidity * 0.42 * Math.exp(this.#result.celsius * 10 * 0.006235398) / 10))
        this.#result.fahrenheit = 32 + this.#result.celsius * 1.8
        return this.#result;
    }
}
louisvangeldrop commented 11 months ago

The above DHT is just a sample, which will not work. With setWatch as a replacement for subscribeDigital+upTime I assume it will work.