Joseph SatterwhiteChaitanya Gokule
Published

MEGR 3171 Temperature Sensor and Display

DHT22 Temperature Sensor used to display data on Adafruit. io and on an Adafruit SSD1360 OLED display.

BeginnerFull instructions provided2 hours1,025
MEGR 3171 Temperature Sensor and Display

Things used in this project

Hardware components

Photon
Particle Photon
×2
USB-A to Mini-USB Cable
USB-A to Mini-USB Cable
×2
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
Used an Adafruit SSD1360 OLED Display
×1
Breadboard (generic)
Breadboard (generic)
×2
Jumper wires (generic)
Jumper wires (generic)
×12

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
Adafruit.io

Story

Read more

Schematics

Circuit Diagrams for Project

Code

dht-logger.ino

C/C++
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_IO_Client.h"

// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_IO_Arduino.h"

// This #include statement was automatically added by the Particle IDE.
#include "DHT.h"

//
// Copyright (c) 2016 Nic Jansma, http://nicj.net
//
// Built on top of other's hard work, see README.md for details
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

//
// Includes
//
#include "DHT.h"
#include "application.h"

//
// Configuration
//

// device name
#define DEVICE_NAME "chaitanya"

// sensor type: [DHT11, DHT22, DHT21, AM2301]
#define DHTTYPE DHT22

// which digital pin for the DHT
#define DHTPIN D3

// which digital pin for the Photon/Spark Core/Electron LED
#define LEDPIN D7

// whether to use Farenheit instead of Celsius
#define USE_FARENHEIT 1

// min/max values (sanity checks)
#define MIN_TEMPERATURE 30
#define MAX_TEMPERATURE 100

#define MIN_HUMIDITY 0
#define MAX_HUMIDITY 100

// sensor sending interval (seconds)
#define SEND_INTERVAL 5

// AdaFruit integration
#define ADAFRUIT_ENABLED 1
#define ADAFRUIT_API_KEY "2098c01d2574477bb03130db4fe99877"
#define ADAFRUIT_FEED_TEMPERATURE "Temperature"
#define ADAFRUIT_FEED_HUMIDITY "Humidity"
#define ADAFRUIT_FEED_HEAT_INDEX "Heat Index"

// ThingSpeak integration
#define THINGSPEAK_ENABLED 0
#define THINGSPEAK_CHANNEL 123456
#define THINGSPEAK_API_KEY "YOURAPIKEY"

// HTTP POST integration
#define HTTP_POST 0
#define HTTP_POST_HOST "myhost.com"
#define HTTP_POST_PORT 80
#define HTTP_POST_PATH "/"

// Particle event
#define PARTICLE_EVENT 1
#define PARTICLE_EVENT_NAME "dht-logger-log"

//
// Integration Includes
//

// AdaFruit.io
#if ADAFRUIT_ENABLED
#include "Adafruit_IO_Client.h"
#include "Adafruit_IO_Arduino.h"
#endif

// ThingSpeak
#if THINGSPEAK_ENABLED
#include "ThingSpeak/ThingSpeak.h"
#endif

#if HTTP_POST
#include "HttpClient/HttpClient.h"
#endif

//
// Locals
//
TCPClient tcpClient;

DHT dht(DHTPIN, DHTTYPE);

float humidity;
float temperature;
float heatIndex;

char humidityString[10];
char temperatureString[10];
char heatIndexString[10];

int failed = 0;

// last time since we sent sensor readings
int lastUpdate = 0;

#if ADAFRUIT_ENABLED
Adafruit_IO_Client aio = Adafruit_IO_Client(tcpClient, ADAFRUIT_API_KEY);
Adafruit_IO_Feed aioFeedTemperature = aio.getFeed(ADAFRUIT_FEED_TEMPERATURE);
Adafruit_IO_Feed aioFeedHumidity = aio.getFeed(ADAFRUIT_FEED_HUMIDITY);
Adafruit_IO_Feed aioFeedHeatIndex = aio.getFeed(ADAFRUIT_FEED_HEAT_INDEX);
#endif

#if HTTP_POST
HttpClient http;

// Headers currently need to be set at init, useful for API keys etc.
http_header_t httpHeaders[] = {
    { "Content-Type", "application/json" },
    { "Accept" , "*/*"},
    { NULL, NULL }
};

http_response_t response;
http_request_t request;
#endif

// for HTTP POST and Particle.publish payloads
char payload[1024];

/**
 * Setup
 */
void setup() {
    pinMode(LEDPIN, OUTPUT);
    digitalWrite(LEDPIN, HIGH);

    // configure Particle variables - float isn't accepted, so we have to use string versions
    Particle.variable("temperature", &temperatureString[0], STRING);
    Particle.variable("humidity", &humidityString[0], STRING);
    Particle.variable("heatIndex", &heatIndexString[0], STRING);

    Particle.variable("status", &failed, INT);

    // start the DHT sensor
    dht.begin();

#if THINGSPEAK_ENABLED
    ThingSpeak.begin(tcpClient);
#endif

#if ADAFRUIT_ENABLED
    aio.begin();
#endif

#if PARTICLE_EVENT
    // startup event
    sprintf(payload,
            "{\"device\":\"%s\",\"state\":\"starting\"}",
            DEVICE_NAME);

    Spark.publish(PARTICLE_EVENT_NAME, payload, 60, PRIVATE);
#endif

    // run the first measurement
    loop();
}

/**
 * Event loop
 */
void loop() {
    int now = Time.now();

    // only run every SEND_INTERVAL seconds
    if (now - lastUpdate < SEND_INTERVAL) {
        return;
    }

    // turn on LED when updating
    digitalWrite(LEDPIN, HIGH);

    lastUpdate = now;

    failed = 0;

    // read humidity and temperature values
    humidity = dht.readHumidity();
    temperature = dht.readTemperature(USE_FARENHEIT);

    if (temperature == NAN
        || humidity == NAN
        || temperature > MAX_TEMPERATURE
        || temperature < MIN_TEMPERATURE
        || humidity > MAX_HUMIDITY
        || humidity < MIN_HUMIDITY) {
        // if any sensor failed, bail on updates
        failed = 1;
    } else {
        failed = 0;

        // calculate the heat index
        heatIndex = dht.computeHeatIndex(temperature, humidity, USE_FARENHEIT);

        // convert floats to strings for Particle variables
        sprintf(temperatureString, "%.2f", temperature);
        sprintf(humidityString, "%.2f", humidity);
        sprintf(heatIndexString, "%.2f", heatIndex);

#if THINGSPEAK_ENABLED
        // set all 3 fields first
        ThingSpeak.setField(1, temperatureString);
        ThingSpeak.setField(2, humidityString);
        ThingSpeak.setField(3, heatIndexString);

        // send all fields at once
        ThingSpeak.writeFields(THINGSPEAK_CHANNEL, THINGSPEAK_API_KEY);
#endif

#if ADAFRUIT_ENABLED
        aioFeedTemperature.send(temperature);
        aioFeedHumidity.send(humidity);
        aioFeedHeatIndex.send(heatIndex);
#endif

        sprintf(payload,
            "{\"device\":\"%s\",\"temperature\":%.2f,\"humidity\":%.2f,\"heatIndex\":%.2f}",
            DEVICE_NAME,
            temperature,
            humidity,
            heatIndex);

#if HTTP_POST
        request.hostname = HTTP_POST_HOST;
        request.port = HTTP_POST_PORT;
        request.path = HTTP_POST_PATH;
        request.body = payload;

        http.post(request, response, httpHeaders);
#endif

#if PARTICLE_EVENT
        Particle.publish("3171project", payload, 60, PUBLIC);
#endif
    }

    // done updating
    digitalWrite(LEDPIN, LOW);
}

Adafruit_IO_Arduino.cpp

C/C++
//
// Via https://github.com/adafruit/Adafruit_IO_Arduino
// with slight mods for Particle
//

// The MIT License (MIT)
//
// Copyright (c) 2015 Adafruit Industries
// Author: Tony DiCola
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "Adafruit_IO_Arduino.h"

FeedData::FeedData() {
    // Feed data is invalid without a value so write all zeros.
    memset(_value, 0, sizeof(_value));
}

FeedData::FeedData(const FeedData& copy) {
    // Copy the value from the provided FeedData object.
    memset(_value, 0, sizeof(_value));
    strncpy(_value, copy._value, sizeof(_value)-1);
}

FeedData::FeedData(const char* value) {
    // Copy the provided string value as the feed data value.
    memset(_value, 0, sizeof(_value));
    strncpy(_value, value, FEEDDATA_LENGTH-1);
}

FeedData::FeedData(Stream& stream, uint16_t length, uint32_t timeoutMS) {
    // Load the data value from the provided stream.  Will read a value up to
    // length characters in size.  If the data can't be read for some reason,
    // like the timeout exceeding, then the FeedData will be left in an invalid
    // state.
    memset(_value, 0, sizeof(_value));
    if (length > FEEDDATA_LENGTH-1) {
        // Not enough space to store the value.
        DEBUG_PRINTLN(F("Not enough space to store feed data!"));
        return;
    }
    stream.setTimeout(timeoutMS);
    if (stream.readBytes(_value, length) != length) {
        // Didn't find enough data, set the value as invalid.
        DEBUG_PRINTLN(F("Failed to find expected data!"));
        memset(_value, 0, sizeof(_value));
    }
}

bool FeedData::intValue(int* value) {
    // Attempt to convert the value to an integer.  Returns true if it succeeds,
    // and false if it fails for some reason.
    char* endptr;
    *value = (int)strtol(_value, &endptr, 10);
    return (*_value != 0 && *endptr == 0);
}

bool FeedData::uintValue(unsigned int* value) {
    // Attempt to convert the value to an unsigned integer.  Returns true if it
    // succeeds, and false if it fails for some reason.
    char* endptr;
    #ifdef ESP8266
        // For some reason strtoul is not defined on the ESP8266 platform right now.
        // Just use a strtol function and hope for the best.
        *value = (unsigned int)strtol(_value, &endptr, 10);
    #else
        *value = (unsigned int)strtoul(_value, &endptr, 10);
    #endif
    return (*_value != 0 && *endptr == 0);
}

bool FeedData::longValue(long* value) {
    // Attempt to convert the value to a long.  Returns true if it
    // succeeds, and false if it fails for some reason.
    char* endptr;
    *value = strtol(_value, &endptr, 10);
    return (*_value != 0 && *endptr == 0);
}

bool FeedData::ulongValue(unsigned long* value) {
    // Attempt to convert the value to an unsigned long.  Returns true if it
    // succeeds, and false if it fails for some reason.
    char* endptr;
    #ifdef ESP8266
        // For some reason strtoul is not defined on the ESP8266 platform right now.
        // Just use a strtol function and hope for the best.
        *value = (unsigned long)strtol(_value, &endptr, 10);
    #else
        *value = strtoul(_value, &endptr, 10);
    #endif
    return (*_value != 0 && *endptr == 0);  
}

bool FeedData::floatValue(float* value) {
    // Attempt to convert the value to a float.  Returns true if it succeeds,
    // and false if it fails for some reason.
    char* endptr;
    *value = (float)strtod(_value, &endptr);
    return (*_value != 0 && *endptr == 0);
}

bool FeedData::doubleValue(double* value) {
    // Attempt to convert the value to a double.  Returns true if it succeeds,
    // and false if it fails for some reason.
    char* endptr;
    *value = strtod(_value, &endptr);
    return (*_value != 0 && *endptr == 0);
}

// Buffer to store values converted from numbers to strings before sending to IO.
static char _converted[FEEDDATA_LENGTH];

bool Adafruit_IO_Feed::send(int value) {
    // Convert int to string, then send the value (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    itoa(value, _converted, 10);
    return _adapter->send(_name, _converted, _key, false);
}

bool Adafruit_IO_Feed::send(unsigned int value) {
    // Convert uint to string, then send the value (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    utoa(value, _converted, 10);
    return _adapter->send(_name, _converted, _key, false);
}

bool Adafruit_IO_Feed::send(long value) {
    // Convert long to string, then send the value (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    ltoa(value, _converted, 10);
    return _adapter->send(_name, _converted, _key, false);
}

bool Adafruit_IO_Feed::send(unsigned long value) {
    // Convert ulong to string, then send the value (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    ultoa(value, _converted, 10);
    return _adapter->send(_name, _converted, _key, false);
}

bool Adafruit_IO_Feed::send(float value) {
    // Convert float to string using scientific notation, then send the value 
    // (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    #if defined(ARDUINO_ARCH_AVR)
        // Use avrlibc dtostre function on AVR platforms.
        dtostre(value, _converted, 10, 0);
    #else
        // Otherwise fall back to snprintf on other platforms.
        snprintf(_converted, sizeof(_converted)-1, "%f", value);
    #endif
    return _adapter->send(_name, _converted, _key, false);
}

bool Adafruit_IO_Feed::send(double value) {
    // Convert double to string using scientific notation, then send the value 
    // (being careful not to quote it).
    memset(_converted, 0, sizeof(_converted));
    #if defined(ARDUINO_ARCH_AVR)
        // Use avrlibc dtostre function on AVR platforms.
        dtostre(value, _converted, 10, 0);
    #else
        // Otherwise fall back to snprintf on other platforms.
        snprintf(_converted, sizeof(_converted)-1, "%f", value);
    #endif
    return _adapter->send(_name, _converted, _key, false);
}

Adafruit_IO_Arduino.h

C/C++
//
// Via https://github.com/adafruit/Adafruit_IO_Arduino
// with slight mods for Particle
//

// The MIT License (MIT)
//
// Copyright (c) 2015 Adafruit Industries
// Author: Tony DiCola
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef ADAFRUIT_IO_ARDUINO_H
#define ADAFRUIT_IO_ARDUINO_H

#include "application.h"

// Control how big the buffer is for storing a data instance's value.
// Keep at least a value of ~33 to store large numeric and float values.
#define FEEDDATA_LENGTH 33

// How large the received data buffer is in bytes.  This is used for reading
// the response of HTTP requests like header names and response codes.
#define IO_RECV_LENGTH 33

// Uncomment/comment to enable & disable debug output.
//#define ADAFRUIT_IO_DEBUG

// Macros for debug output (only enabled when debug mode is enabled.)
#ifdef ADAFRUIT_IO_DEBUG
    #define DEBUG_PRINT(...) { Serial.print(__VA_ARGS__); }
    #define DEBUG_PRINTLN(...) { Serial.println(__VA_ARGS__); }
#else
    #define DEBUG_PRINT(...) {}
    #define DEBUG_PRINTLN(...) {}
#endif


// Adafruit IO feed data instance.  Exposes functions to read the value of the
// feed as different types.
class FeedData {
public:
    FeedData();
    FeedData(const FeedData& copy);
    FeedData(const char* value);
    FeedData(Stream& stream, uint16_t length, uint32_t timeoutMS = 5000);

    // Functions to support checking if this data instance was able to be read
    // from the AIO service.
    bool isValid() { return _value != NULL; }

    // Allow implicit conversion to a C string.
    operator char*() { return _value; }

    // Explicit conversion functions that convert to the specified value.  Each
    // will return true if the conversion succeeded and false if it could not
    // be converted.
    bool intValue(int* value);
    bool uintValue(unsigned int* value);
    bool longValue(long* value);
    bool ulongValue(unsigned long* value);
    bool floatValue(float* value);
    bool doubleValue(double* value);

private:
    char _value[FEEDDATA_LENGTH];
};


// Interface for an AIO service that defines functions to send and receive data
// to the AIO REST API.  Concrete implementations for FONA, CC3000, and ESP8266
// are what users will actually use.
class AIOService {
public:
    virtual ~AIOService() {}
    virtual bool send(const char* feed, const char* value, const char* key,
                      bool quoted) = 0;
    virtual FeedData receive(const char* feed, const char* key) = 0;
};


// Main IO feed class that uses an AIO service reference to send and receive
// data with IO.
class Adafruit_IO_Feed {
public:
    Adafruit_IO_Feed(const char* name, const char* key, AIOService* adapter):
        _name(name), _key(key), _adapter(adapter)
    {}
    
    bool send(const char* value) { 
        return _adapter->send(_name, value, _key, true);
    }
    bool send(int value);
    bool send(unsigned int value);
    bool send(long value);
    bool send(unsigned long value);
    bool send(float value);
    bool send(double value);

    FeedData receive() {
        return _adapter->receive(_name, _key);
    }

private:
    const char* _name;
    const char* _key;
    AIOService* _adapter;
};


#endif

Adafruit_IO_Client.cpp

C/C++
//
// Via https://github.com/adafruit/Adafruit_IO_Arduino
// with slight mods for Particle
//

// The MIT License (MIT)
//
// Copyright (c) 2015 Adafruit Industries
// Author: Tony DiCola
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "Adafruit_IO_Client.h"

bool Adafruit_IO_Client::send(const char* feed, const char* value, 
                              const char* key, bool quoted) {
    // Make HTTP POST to send feed data as JSON object.

    // First make sure a connection to the service is available.
    if (!connected()) {
        DEBUG_PRINTLN(F("Failed to connect to service!"));
        return false;
    }

    // Compute size of request.
    uint16_t len = 10 + strlen(value);
    if (quoted) {
        len += 2;
    }

    // Send HTTP POST and headers.
    _client.print(F("POST /api/feeds/"));
    _client.print(feed);
    _client.print(F("/data/send HTTP/1.1\r\n"));
    sendHeaders(key);
    _client.print(F("Content-Type: application/json\r\n"));
    _client.print(F("Content-Length: "));
    _client.print(len, DEC);
    _client.print(F("\r\n\r\n"));

    // Send HTTP POST data.
    _client.print(F("{\"value\":"));
    if (quoted) {
        _client.print('"');
        _client.print(value);
        _client.print('"');
    }
    else {
        _client.print(value);
    }
    _client.print('}');

    // Now wait to read response (up to the client's configured stream timeout).
    // First read the HTTP/1.1 response.
    char recvbuffer[IO_RECV_LENGTH] = {0};
    if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)) != 8) ||
        (strcmp_P(recvbuffer, PSTR("HTTP/1.1")) != 0)) {
        DEBUG_PRINTLN(F("Failed to find expected HTTP/1.1 response!"));
        _client.stop();
        return false;
    }
    // Now read the status code and expect a 200-level response.
    memset(recvbuffer, 0, sizeof(recvbuffer));
    if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)-1) != 3) ||
        (recvbuffer[0] != '2')) {
        DEBUG_PRINT(F("HTTP POST failed with error code: "));
        DEBUG_PRINTLN(recvbuffer);
        _client.stop();
        return false;
    }

    // Ignore parsing the response data for now.  Close connection and return
    // success.
    _client.stop();
    return true;
}

FeedData Adafruit_IO_Client::receive(const char* feed, const char* key) {
    // Make HTTP GET request to read latest feed item and then parse response
    // into FeedData object.

    // First make sure a connection to the service is available.
    if (!connected()) {
        DEBUG_PRINTLN(F("Failed to connect to service!"));
        return FeedData();
    }

    // Send HTTP GET and headers.
    _client.print(F("GET /api/feeds/"));
    _client.print(feed);
    _client.print(F("/data/last.txt HTTP/1.1\r\n"));
    sendHeaders(key);
    _client.print(F("Accept: text/plain\r\n\r\n"));

    // Parse HTTP GET response.
    // First read the HTTP/1.1 response.
    char recvbuffer[IO_RECV_LENGTH] = {0};
    if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)) != 8) ||
        (strcmp_P(recvbuffer, PSTR("HTTP/1.1")) != 0)) {
        DEBUG_PRINTLN(F("Failed to find expected HTTP/1.1 response!"));
        _client.stop();
        return FeedData();
    }
    // Now read the status code and expect a 200-level response.
    memset(recvbuffer, 0, sizeof(recvbuffer));
    if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)-1) != 3) ||
        (recvbuffer[0] != '2')) {
        DEBUG_PRINT(F("HTTP GET failed with error code: "));
        DEBUG_PRINTLN(recvbuffer);
        _client.stop();
        return FeedData();
    }
    // Read the rest of the line.
    if (!_client.find("\r\n")) {
        DEBUG_PRINT(F("Unexpected HTTP GET response!"));
        _client.stop();
        return FeedData();
    }
    // Now parse all the header lines and look for an explicit content length.
    // If no content length is found then assume a chunked transfer encoding.
    uint16_t len = 0;
    while (true) {
        char c = _client.peek();
        // Check for \r\n blank line to signify end of headers.
        if (c == '\r') {
            if (!_client.find("\r\n")) {
                // Something unexpected, fail.
                DEBUG_PRINT(F("Expected blank line after headers!"));
                _client.stop();
                return FeedData();
            }
            // Else found the end of the headers so stop parsing them.
            break;
        }
        // Parse a header name.
        char recvbuffer[IO_RECV_LENGTH] = {0};
        if (!_client.readBytesUntil(':', recvbuffer, sizeof(recvbuffer)-1)) {
            DEBUG_PRINT(F("Expected header name!"));
            _client.stop();
            return FeedData();
        }
        // Check for content-length header and read its value, otherwise just
        // swallow the header value.
        if (strcmp_P(recvbuffer, PSTR("Content-Length")) == 0) {
            len = (uint16_t)_client.parseInt();
        }
        if (!_client.find("\r\n")) {
            DEBUG_PRINT(F("Failed to find end of header line!"));
            _client.stop();
            return FeedData();
        }
    }
    // If we didn't see a content-length header then assume a chunked transfer
    // encoding and read the length as the first line.
    if (len == 0) {
        len = (uint16_t)_client.parseInt();
        if (!_client.find("\r\n")) {
            DEBUG_PRINT(F("Failed to find end of chunk size line!"));
            _client.stop();
            return FeedData();
        }
    }

    // Let FeedData parse out the result.
    return FeedData(_client, len);
}

bool Adafruit_IO_Client::connected() {
    // Create connection to AIO service.  Return true if successful and connection
    // is open, or false if something failed.

    // If connection is already open close it to start fresh.
    if (_client.connected()) {
        _client.stop();
    }

    // Create connection.
    return _client.connect(_serviceHost, _servicePort) != 0;
}

void Adafruit_IO_Client::sendHeaders(const char* key) {
    // Send standard HTTP headers used by AIO REST requests.
    _client.print(F("Connection: close\r\n"));
    _client.print(F("User-Agent: Adafruit_IO_Client\r\n"));
    _client.print(F("Host: "));
    _client.print(_serviceHost);
    _client.print(':');
    _client.print(_servicePort, DEC);
    _client.print(F("\r\n"));
    _client.print(F("X-AIO-Key: "));
    _client.print(key);
    _client.print(F("\r\n"));
}

Adafruit_IO_Client.h

C/C++
//
// Via https://github.com/adafruit/Adafruit_IO_Arduino
// with slight mods for Particle
//

// The MIT License (MIT)
//
// Copyright (c) 2015 Adafruit Industries
// Author: Tony DiCola
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef ADAFRUIT_IO_CLIENT_H
#define ADAFRUIT_IO_CLIENT_H

#include "Adafruit_IO_Arduino.h"


// Arduino Client class implementation of AIO REST service.  Good for using with
// hardware like the ESP8266 or Ethernet shield.
class Adafruit_IO_Client: public AIOService {
public:
    Adafruit_IO_Client(TCPClient& client, const char* defaultKey,
                       const char* serviceHost = "io.adafruit.com",
                       uint16_t servicePort = 80):
        _client(client),
        _defaultKey(defaultKey),
        _serviceHost(serviceHost),
        _servicePort(servicePort)
    {}

    Adafruit_IO_Feed getFeed(const char* name, const char* key = NULL) {
        return Adafruit_IO_Feed(name, key == NULL ? _defaultKey : key, this);
    }

    bool begin() { return true; }  // Nothing to do, no initialization required.

    virtual bool send(const char* feed, const char* value, const char* key, 
                      bool quoted);

    virtual FeedData receive(const char* feed, const char* key);

private:
    TCPClient& _client;
    const char* _defaultKey;
    const char* _serviceHost;
    uint16_t _servicePort;

    bool connected();
    void sendHeaders(const char* key);
};

#endif

DHT.cpp

C/C++
//
// Based on https://gist.github.com/wgbartley/8301123
// and https://github.com/adafruit/DHT-sensor-library/blob/master/DHT.cpp
//

#include "DHT.h"
#include "math.h"

/**
 * Constructor
 */
DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) {
    _pin = pin;
    _type = type;
    _count = count;
    _firstReading = true;
}

/**
 * Sets up the pins
 */
void DHT::begin(void) {
    // set up the pins!
    pinMode(_pin, INPUT);

    digitalWrite(_pin, HIGH);

    _lastReadTime = 0;
}

/**
 * Gets the current temperature
 */
float DHT::readTemperature(bool farenheit) {
    float temp = NAN;

    if (read()) {
        switch (_type) {
            case DHT11:
                temp = _data[2];

            case DHT22:
            case DHT21:
                temp = _data[2] & 0x7F;
                temp *= 256;
                temp += _data[3];
                temp /= 10;

                // negative temp
                if (_data[2] & 0x80) {
                    temp *= -1;
                }
        }

        if (farenheit) {
            temp = convertCtoF(temp);
        }
    }

    return temp;
}

/**
 * Converts Celsius to Farenheit
 */
float DHT::convertCtoF(float c) {
    return c * 9 / 5 + 32;
}

/**
 * Converts Farenheit to Celsius
 */
float DHT::convertFtoC(float f) {
  return (f - 32) * 0.55555;
}

/**
 * Gets the current humidity
 */
float DHT::readHumidity(void) {
    float humidity = NAN;

    if (read()) {
        switch (_type) {
            case DHT11:
                humidity = _data[0];

            case DHT22:
            case DHT21:
                humidity = _data[0];
                humidity *= 256;
                humidity += _data[1];
                humidity /= 10;

                return humidity;
        }
    }

    return humidity;
}

/**
 * Computes the Heat Index
 */
float DHT::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) {
    // Using both Rothfusz and Steadman's equations
    // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
    float hi;

    if (!isFahrenheit) {
        temperature = convertCtoF(temperature);
    }

    hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094));

    if (hi > 79) {
        hi = -42.379 +
             2.04901523 * temperature +
            10.14333127 * percentHumidity +
            -0.22475541 * temperature*percentHumidity +
            -0.00683783 * pow(temperature, 2) +
            -0.05481717 * pow(percentHumidity, 2) +
             0.00122874 * pow(temperature, 2) * percentHumidity +
             0.00085282 * temperature*pow(percentHumidity, 2) +
            -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);
    }

    if ((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) {
        hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);
    } else if ((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) {
        hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
    }

    return isFahrenheit ? hi : convertFtoC(hi);
}

/**
 * Gets a reading from the DHT
 */
bool DHT::read(void) {
    uint8_t lastState = HIGH;
    uint8_t counter = 0;
    uint8_t j = 0, i = 0;
    unsigned long currentTime;

    // pull the pin high and wait 250 milliseconds
    digitalWrite(_pin, HIGH);
    delay(250);

    currentTime = millis();
    if (currentTime < _lastReadTime) {
        // ie there was a rollover
        _lastReadTime = 0;
    }

    if (!_firstReading && ((currentTime - _lastReadTime) < 2000)) {
        //delay(2000 - (currentTime - _lastReadTime));
        return true; // return last correct measurement
    }

    _firstReading = false;
    Serial.print("Currtime: "); Serial.print(currentTime);
    Serial.print(" Lasttime: "); Serial.print(_lastReadTime);
    _lastReadTime = millis();

    // zero-out the data
    _data[0] = _data[1] = _data[2] = _data[3] = _data[4] = 0;

    // now pull it low for ~20 milliseconds
    pinMode(_pin, OUTPUT);
    digitalWrite(_pin, LOW);
    delay(20);
    noInterrupts();
    digitalWrite(_pin, HIGH);
    delayMicroseconds(40);
    pinMode(_pin, INPUT);

    // read in timings
    for (i = 0; i < MAXTIMINGS; i++) {
        counter = 0;

        while (digitalRead(_pin) == lastState) {
            counter++;
            delayMicroseconds(1);

            if (counter == 255) {
                break;
            }
        }

        lastState = digitalRead(_pin);

        if (counter == 255) {
            break;

        }

        // ignore first 3 transitions
        if ((i >= 4) && (i%2 == 0)) {
            // shove each bit into the storage bytes
            _data[j/8] <<= 1;

            if (counter > _count) {
                _data[j/8] |= 1;
            }

            j++;
        }
    }

    interrupts();

    // check we read 40 bits and that the checksum matches
    if ((j >= 40) && (_data[4] == ((_data[0] + _data[1] + _data[2] + _data[3]) & 0xFF))) {
        return true;
    }

    return false;
}

DHT.h

C/C++
//
// Based on https://gist.github.com/wgbartley/8301123
// and https://github.com/adafruit/DHT-sensor-library/blob/master/DHT.h
//
#ifndef DHT_H
#define DHT_H

#include "application.h"

#define MAXTIMINGS 85

#define DHT11 11
#define DHT22 22
#define DHT21 21
#define AM2301 21

#define NAN 999999

class DHT {
    private:
        uint8_t _data[6];
        uint8_t _pin, _type, _count;
        unsigned long _lastReadTime;
        bool _firstReading;

        bool read(void);

    public:
        DHT(uint8_t pin, uint8_t type, uint8_t count = 6);

        void begin(void);

        float readTemperature(bool farenheit = false);
        float readHumidity(void);

        float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit);

        float convertCtoF(float);
        float convertFtoC(float f);
};

#endif

display.ino

C/C++
/*********************************************************************
This is an example for our Monochrome OLEDs based on SSD1306 drivers

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/category/63_98

This example is for a 128x64 size display using SPI to communicate
4 or 5 pins are required to interface

Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

Written by Limor Fried/Ladyada  for Adafruit Industries.
BSD license, check license.txt for more information
All text above, and the splash screen must be included in any redistribution
*********************************************************************/

#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

/* Uncomment this block to use hardware SPI
// If using software SPI (the default case):
#define OLED_MOSI   D0
#define OLED_CLK    D1
#define OLED_DC     D2
#define OLED_CS     D3
#define OLED_RESET  D4
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
*/

// use hardware SPI
#define OLED_DC     D3
#define OLED_CS     D4
#define OLED_RESET  D5
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);


#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2



#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16

static const unsigned char logo16_glcd_bmp[] =
{ 0B00000000, 0B11000000,
  0B00000001, 0B11000000,
  0B00000001, 0B11000000,
  0B00000011, 0B11100000,
  0B11110011, 0B11100000,
  0B11111110, 0B11111000,
  0B01111110, 0B11111111,
  0B00110011, 0B10011111,
  0B00011111, 0B11111100,
  0B00001101, 0B01110000,
  0B00011011, 0B10100000,
  0B00111111, 0B11100000,
  0B00111111, 0B11110000,
  0B01111100, 0B11110000,
  0B01110000, 0B01110000,
  0B00000000, 0B00110000 };

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//Get Temperature data from cloud
void tempF(const char *, const char *dataF)
{
  // display the temperature
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.printlnf("%s Fahrenheit\n", dataF, 3);
  display.display();
  display.clearDisplay(); // clears the screen and buffer
}

void setup()   {
  Serial.begin(9600);

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC);
  // init done
  display.display(); // show splashscreen
  delay(100);
  display.clearDisplay();  // clears the screen and buffer

  // Subscribe to tempearture variable
  Particle.subscribe("3171project", tempF);

}

Credits

Joseph Satterwhite
1 project • 0 followers
Contact
Chaitanya Gokule
1 project • 0 followers
Contact

Comments

Please log in or sign up to comment.