Turn a fan on when the temperature is too high! - Electric Imp Tutorial

In our last Electric Imp tutorial, we learned how to measure temperature, humidity and overall thermal comfort with Ubidots. This time we are going to make a simple on-off fan control. We will log the temperature to Ubidots and trigger an event when it is over 30ºC so that the Electric Imp turns on a fan, and then a trigger to turn it off when the temperature is below 27ºC.

It really works like a charm, so here we go:

Materials

  • Three female to female wires

  • 10k resistor

  • DHT11 humidity and temperature sensor

-A little fan

-A PowerSwitch tail

Wiring

This is how all the components should be connected:

…and just one thing left: plug in your fan!

Coding

  • Login to the Electric Imp IDE and take note of the url of your Imp:

Copy the next two codes.In the Agent code, we will use a function to handle HTTP requests to Ubidots, don’t forget to put your Ubidots’ token and variables id.

  • Agent Code:
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();
});
device.on("hum", function(value) {
    //server.log("Trying to post to Ubi the value:hola");
    //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/53e541e57625422c7d900a1d/values"; // Replace the Variable ID with yours
    local string = {"value": value};
    local request = http.post(url, headers, http.jsonencode(string));
    local response = request.sendsync();
});

function requestHandler(request, response) {
  try {
    // check if the user sent led as a query parameter
    if ("relay" in request.query) {
      server.log("entre");
      // if they did, and led=1.. set our variable to 1
      if (request.query.relay == "1" || request.query.relay == "0") {
        // convert the led query parameter to an integer
        local relayState = request.query.relay.tointeger();
        // send "led" message to device, and send ledState as the data
        device.send("relay", relayState); 
      }
    }
    // send a response back saying everything was OK.
    response.send(200, "OK");
  } catch (ex) {
    response.send(500, "Internal Server Error: " + ex);
  }
}
 
// register the HTTP handler
http.onrequest(requestHandler);
  • Device Code:

Note the last part of the code where there’s a function to turn on/off the fan with the Powerswitch tail.

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);
    }
}
relay <- hardware.pin8;
spi  <- hardware.spi257;
rele.configure(DIGITAL_OUT);

function setRelay(relayState){
    server.log("relayState:" + relayState );
    relay.write(relayState);
}  
agent.on("relay", setRelay);

function mainLoop(){imp.wakeup(1.0, mainLoop);
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);
agent.send("hum",data.rh);
}mainLoop();

Set up Ubidots Events

  • In your Ubidots account, create one data source with two variables: Temperature and Humidity

  • Click on the “Events” section and add one:

  • Choose Electric imp as source and temperature (temp) as a variable.

  • Put a condition when the temp is over 30ºC.

  • Choose Request URL ” and type your Imp URL plus “?relay=1”

  • Add another event with this conditional.

  • Choose “Request URL” and type your Imp URL plus “?relay=0”

Wrapping up

In this project, we were able to control an electric fan from the internet with Electric Imp. Just as we controlled a fan using temperature data, you could also log any other data and take actions based on it. For example, you could open your garage door whenever your cat approaches a motion sensor (meaning it wants to go out!).

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

If you’re new to Ubidots, check out this great starter piece and sign up for free today!