Logging Temperature, Humidity, and Dew Point with Electric Imp

The Electric Imp is an amazing device for IoT projects and its integration with Ubidots makes it very simple to create graphs and SMS/Email alerts in a few minutes. This Electric Imp tutorial we’ll teach you how to use the Electric Imp breakout board with a DHT11 sensor to measure temperature and relative humidity, and then compute the Dew Point using Ubidots’ math engine.

The Dewpoint can be used as an index for human perception of comfort in relation to the weather; a dew point between 13-16°C (55-59°F) makes you feel comfortable, but a Dew point above 21°C (70°F) will make you feel too warmbecause the air would have too much moisture and your body couldn’t cool down:

Dew Point Human perception
24-26°C / 75-80°F Extremely uncomfortable, oppressive
21-24°C / 0-74°F Very Humid, quite uncomfortable
18-21°C / 65-69°F Somewhat uncomfortable for most people at upper edge
16-18°C / 60-64°F OK for most, but all perceive the humidity at upper edge
13-16°C / 55-59°F Comfortable
10-12°C / 50-54°F Very comfortable
Under 10°C / 50-54°F Very comfortable
Under 10°C / Under 50°F A bit dry
Under 10°C / Under 50°F Luckily, there’s a linear function to compute Dewpoint based on temperature and relative humidity.

Materials

  • An Imp

  • An Electric Imp Breakout Board

  • Three female to female wires

  • 10k resistor

  • DHT11 humidity and temperature sensor

Wiring

Here’s how the DHT11 sensor is wired to your Electric Imp board:

Code

Time to code! when working with Electric imp, you’ll need two sections of code: one for the device and another one for the agent. Here’s the device code (a bit long since it has the class to handle the DHT11):

const SPICLK = 937.5;
// Class to read the DHT11 temperature/humidity sensor
// These sensors us a proprietary one-wire protocol. The imp
// emulates this protocol with SPI. 
// To use:
//  - tie MOSI to MISO with a 10k resistor
//  - tie MISO to the data line on the sensor
class DHT11 {
    static STARTTIME_LOW     = 0.001000;    // 1 ms low time for start
    static STARTTIME_HIGH    = 0.000020;  // 20 us min high time for start
    static STARTTIME_SENSOR  = 0.000080;  // 80 us low / 80 us high "ACK" from sensor on START
    static MARKTIME          = 0.000050;  // 50 us low pulse between 0 or 1 marks
    static ZERO              = 0.000026; // 26 us high for "0"
    static ONE               = 0.000075;  // 70 us high for "1"
    
    spi                 = null;
    clkspeed            = null;
    bittime             = null;
    bytetime            = null;
    start_low_bits      = null;
    start_low_bytes     = null;
    start_high_bits     = null;
    start_high_bytes    = null;
    start_ack_bits      = null;
    start_ack_bytes     = null;
    mark_bits           = null;
    mark_bytes          = null;
    zero_bits           = null;
    zero_bytes          = null;
    one_bits            = null;
    one_bytes           = null;
    
    // class constructor
    // Input: 
    //      _spi: a pre-configured SPI peripheral (e.g. spi257)
    //      _clkspeed: the speed the SPI has been configured to run at
    // Return: (None)
    constructor(_spi, _clkspeed) {
        this.spi = _spi;
        this.clkspeed = _clkspeed;
    
        bittime     = 1.0 / (clkspeed * 1000);
        bytetime    = 8.0 * bittime;
        
        start_low_bits      = STARTTIME_LOW / bittime;
        start_low_bytes     = (start_low_bits / 8);
        start_high_bits     = STARTTIME_HIGH / bittime;
        start_high_bytes    = (start_high_bits / 8);
        start_ack_bits      = STARTTIME_SENSOR / bittime;
        start_ack_bytes     = (start_ack_bits / 8);
        mark_bits           = MARKTIME / bittime;
        mark_bytes          = (mark_bits / 8);
        zero_bits           = ZERO / bittime;
        zero_bytes          = (zero_bits / 8);
        one_bits            = ONE / bittime;
        one_bytes           = (one_bits / 8);
    }
    
    // helper function
    // given a long blob, find times between transitions and parse to 
    // temp and humidity values. Assumes 40-bit return value (16 humidity / 16 temp / 8 checksum)
    // Input: 
    //      hexblob (blob of arbitrary length)
    // Return: 
    //      table containing:
    //          "rh": relative humidity (float)
    //          "temp": temperature in celsius (float)
    //      if read fails, rh and temp will return 0
    function parse(hexblob) {
        local laststate     = 0;
        local lastbitidx    = 0;
        
        local gotack        = false;
        local rawidx        = 0;
        local result        = blob(5); // 2-byte humidity, 2-byte temp, 1-byte checksum
    
        local humid         = 0;
        local temp          = 0;
        
        // iterate through each bit of each byte of the returned signal
        for (local byte = 0; byte < hexblob.len(); byte++) { for (local bit = 7; bit >= 0; bit--) {
                
                local thisbit = (hexblob[byte] & (0x01 << bit)) ? 1:0;
                
                if (thisbit != laststate) {
                    if (thisbit) {
                        // low-to-high transition; watch to see how long it is high
                        laststate = 1;
                        lastbitidx = (8 * byte) + (7 - bit);
                    } else {
                        // high-to-low transition;
                        laststate = 0;
                        local idx = (8 * byte) + (7 - bit);
                        local hightime = (idx - lastbitidx) * bittime;
                        
                        // we now have one valid bit of info. Figure out what symbol it is.
                        local resultbyte = (rawidx / 8);
                        local resultbit =  7 - (rawidx % 8);
                        //server.log(format("bit %d of byte %d",resultbit, resultbyte));
                        if (hightime < ZERO) {
                            // this is a zero
                            if (gotack) {
                                // don't record any data before the ACK is seen
                                result[resultbyte] = result[resultbyte] & ~(0x01 << resultbit);
                                rawidx++;
                            }
                        } else if (hightime < ONE) {
                            // this is a one
                            if (gotack) {
                                result[resultbyte] = result[resultbyte] | (0x01 << resultbit);
                                rawidx++;
                            }
                        } else {
                            // this is a START ACK
                            gotack = true;
                        }
                    }
                }
            }
        }
        
        //server.log(format("parsed: 0x %02x%02x %02x%02x %02x",result[0],result[1],result[2],result[3],result[4]));
        humid = (result[0] * 1.0) + (result[1] / 1000.0);
        if (result[2] & 0x80) {
            // negative temperature
            result[2] = ((~result[2]) + 1) & 0xff;
        }
        temp = (result[2] * 1.0) + (result[3] / 1000.0);
        if (((result[0] + result[1] + result[2] + result[3]) & 0xff) != result[4]) {
            return {"rh":0,"temp":0};
        } else {
            return {"rh":humid,"temp":temp};
        }
    }
    
    // read the sensor
    // Input: (none)
    // Return:
    //      table containing:
    //          "rh": relative humidity (float)
    //          "temp": temperature in celsius (float)
    //      if read fails, rh and temp will return 0
    function read() {
        local bloblen = start_low_bytes + start_high_bytes + (40 * (mark_bytes + one_bytes));
        local startblob = blob(bloblen);
        for (local i = 0; i < start_low_bytes; i++) {
            startblob.writen(0x00,'b');
        }
        for (local j = start_low_bytes; j < bloblen; j++) {
            startblob.writen(0xff,'b');
        }
        
        //server.log(format("Sending %d bytes", startblob.len()));
        local result = spi.writeread(startblob);
        return parse(result);
    }
}
rele <- hardware.pin9;
spi  <- hardware.spi257;

while(1){
clkspeed <- spi.configure(MSB_FIRST, SPICLK);
dht11 <- DHT11(spi, clkspeed);
data <- dht11.read();
server.log(format("Relative Humidity: %0.1f",data.rh)+" %");
server.log(format("Temperature: %0.1f C",data.temp));
agent.send("temp",data.temp);
imp.sleep(1);
}

Thanks to Thomas Byrne for providing the class to handle the DHT11 sensor in Github.

As you can see, the Agent code is really simple, just don’t forget to put your own auth token and Variable ID in the HTTP call:

device.on("temp", function(value) {
    server.log("Trying to post to Ubi the value:");
    server.log(value);
    local headers = { "Content-Type": "application/json", "X-Auth-Token": "NBbF3PWPxWc2IaO40aXOKnhIu8tOv92rYN3ibiEc7Jh6GV3KZUUCHtXuNz7Y" }; // Replace the token with yours
    local url = "http://things.ubidots.com/api/v1.6/variables/53d2beb37625424630223dac/values"; // Replace the Variable ID with yours
    local string = {"value": value};
    local request = http.post(url, headers, http.jsonencode(string));
    local response = request.sendsync();
});

Create a Synthetic variable on Ubidots.

Just as you would add a new variable, go “Sources”–> “Electric Imp (your data source name)”–>”Add new Variable”:

Now enter this equation to calculate Dewpoint Value:

This is how it should look:

You should now begin seeing the Dewpoint data being updated every time your Imp pushed Temp and Humidity data to Ubidots!

Wrapping up

In this project we were able to connect an Imp to Ubidots, measuring and calculating physical variables to better understand our surroundings. You can explore more things like creating alerts when the Dewpoint is too high, or create shareable graphs like the ones at the beginning of this article.

Don’t forget to check out some of our latest posts:

If you’re new to Ubidots, click here to get started!