Raspberry Pi DHT22 Temp/Humidity Sensor and API Endpoint Tutorial

In this tutorial, I am going to connect a DHT22 temperature/humidity sensor to a Raspberry Pi. Then I am going to create an API endpoint on AWS to capture and save readings from your sensor every 15 seconds. I will also show you how to access your temperature and humidity data via a web browser.

What you’ll need:

  • Raspberry Pi 3 B+ (May work on other models)
  • DHT22 Temperature/Humidity sensor
  • 3x Female to Female DuPont jumpers
  • AWS Account
  • Web Browser
  • Postman API Tool (Optional)
  • Serverless Framework (Installed)
  • Python 2.7 (Installed on Pi) (May work on other versions)
  • Git (Installed on Pi)

Let’s begin. Connect your DHT22 sensor to your Raspberry Pi as shown below…

Connection Pinout:

  • Red – Raspberry Pi pin #2 (+5v)
  • Blue – Raspberry Pi pin #7 (GPIO4)
  • Black – Raspberry Pi pin #6 (GND)

Next, SSH into your Pi. We need to install some software.

First, lets make sure Python version 2.7 is installed on your Pi. Type the following command:

$ python --version
Python 2.7.13

This command should return version information similar to above. If it does not, you will need to install Python on your Pi.

Next, we will need to download the Python code needed for your sensor. The great folks at Adafruit have created an awesome repo on Github that has everything we need.

First, make sure that git is installed on your Pi by entering the following command:

$ git --version
git version 2.11.0

This command should return version information similar to above. If it does not, you will need to install Git on your Pi.

$ sudo apt-get install git

Next, create a new directory on your Pi and change to it. From inside your new directory, clone the Adafruit repo using the following command:

$ git clone https://github.com/adafruit/Adafruit_Python_DHT.git

This should create a new directory as shown below:

$ ls -la
total 12
drwxr-xr-x  3 pi pi 4096 Jan 25 17:29 .
drwxr-xr-x 17 pi pi 4096 Jan 30 19:40 ..
drwxr-xr-x 10 pi pi 4096 Jan 25 17:59 Adafruit_Python_DHT

Change directories to the Adafruit_Python_DHT directory and verify it contains a structure similar to this:

$ ls -la
total 64
drwxr-xr-x 10 pi   pi   4096 Jan 25 17:59 .
drwxr-xr-x  3 pi   pi   4096 Jan 25 17:29 ..
drwxr-xr-x  2 pi   pi   4096 Jan 25 17:59 Adafruit_DHT
drwxr-xr-x  2 root root 4096 Jan 25 17:59 Adafruit_DHT.egg-info
drwxr-xr-x  5 root root 4096 Jan 25 17:59 build
drwxr-xr-x  2 root root 4096 Jan 25 17:59 dist
drwxr-xr-x  2 pi   pi   4096 Jan 26 05:31 examples
drwxr-xr-x  8 pi   pi   4096 Jan 25 17:29 .git
drwxr-xr-x  2 pi   pi   4096 Jan 25 17:29 .github
-rw-r--r--  1 pi   pi     47 Jan 25 17:29 .gitignore
-rw-r--r--  1 pi   pi   1085 Jan 25 17:29 LICENSE
-rw-r--r--  1 pi   pi     45 Jan 25 17:29 MANIFEST.in
-rw-r--r--  1 pi   pi   2038 Jan 25 17:29 README.md
-rw-r--r--  1 pi   pi   5130 Jan 25 17:29 setup.py
drwxr-xr-x  6 pi   pi   4096 Jan 25 17:29 source

We will be using the Python pip installer to install software. Install pip using the following command:

$ sudo apt-get install python-pip

Next, install the following Python applications using pip:

$ sudo python -m pip install --upgrade pip setuptools wheel

Locate the setup.py file in your Adafruit_Python_DHT directory. In the screenshot below, it is the second last file in the list.

Adafruit_Python_DHT $ ls -la
total 64
drwxr-xr-x 10 pi   pi   4096 Jan 25 17:59 .
drwxr-xr-x  3 pi   pi   4096 Jan 25 17:29 ..
drwxr-xr-x  2 pi   pi   4096 Jan 25 17:59 Adafruit_DHT
drwxr-xr-x  2 root root 4096 Jan 25 17:59 Adafruit_DHT.egg-info
drwxr-xr-x  5 root root 4096 Jan 25 17:59 build
drwxr-xr-x  2 root root 4096 Jan 25 17:59 dist
drwxr-xr-x  2 pi   pi   4096 Jan 26 05:31 examples
drwxr-xr-x  8 pi   pi   4096 Jan 25 17:29 .git
drwxr-xr-x  2 pi   pi   4096 Jan 25 17:29 .github
-rw-r--r--  1 pi   pi     47 Jan 25 17:29 .gitignore
-rw-r--r--  1 pi   pi   1085 Jan 25 17:29 LICENSE
-rw-r--r--  1 pi   pi     45 Jan 25 17:29 MANIFEST.in
-rw-r--r--  1 pi   pi   2038 Jan 25 17:29 README.md
-rw-r--r--  1 pi   pi   5130 Jan 25 17:29 setup.py
drwxr-xr-x  6 pi   pi   4096 Jan 25 17:29 source

Issue the following command to set up your sensor code:

$ sudo python setup.py install

Change directories to the ‘examples’ directory

$ cd examples

Test your code using the following command:

$ sudo ./AdafruitDHT.py 2302 4

In that command string, the ‘2302’ is the actual model number of your DHT22 sensor. Verify your model number. Possible choices are ’11’, ’22’, and ‘2302’.

Congratualtions! You have now successfully connected your sensor to your Pi and got it working. But you will notice, right now each execution of the code produces a single result. Also, there is no API endpoint connectivity. We will be building those next.

Serverless Framework

Serverless is a Node.js CLI tool so the first thing you need to do is to install Node.js on your machine.

Go to the official Node.js website, download and follow the installation instructions to install Node.js on your local machine.

Note: Serverless runs on Node v4 or higher.

You can verify that Node.js is installed successfully by running node --version in your terminal. You should see the corresponding Node version number printed out.

For this you will need the Serverless Framework. You can type

$ npm install -g serverless

to install Serverless onto your laptop or desktop.

Also for this tutorial, you will need AWS login credentials. You can get those here. I had to install the AWS CLI on my laptop:

In order to use Serverless Framework with AWS, you will need to create a new AWS IAM user. For this, you will need a valid AWS account. Create a new IAM user and access keys following the steps below.

Log in to your AWS account and got the the IAM page.

In the upper left hand corner of the page click the blue ‘Add user’ button.

Enter a name in the first field to remind you this User is the Framework, like ‘serverless-agent’. Enable Programmatic access by clicking the checkbox.

Click Next to go through to the Permissions page. Click on ‘Attach existing policies directly’. Click on Create policy. Select the JSON tab, replace the policy that appears with the following JSON file you’ll find at https://gist.github.com/ServerlessBot/7618156b8671840a539f405dea2704c8

When you are finished, select Review policy. You can assign this policy a Name and Description. Check everything looks good and click Create policy.

I had to go back to ‘Users’->’Add user’, create my new user, and then assign my newly created policy to my new user. Do this now.

Upon creating your new user, the page will display your Access Key and Secret Acccess Key. Record these for future use. You will need them min a later step.

You can export your new keys as environment variables using the following:

$ export AWS_ACCESS_KEY_ID=<your-key-here>
$ export AWS_SECRET_ACCESS_KEY=<your-secret-key-here>

$ sudo serverless deploy

# 'export' command is valid only for unix shells. In Windows - use 'set' instead of 'export'

Create and Deploy API Endpoint

On your laptop or desktop, create a new directory and cd into it. Once inside, issue the following command to start a new app:

$ npm init -f

This will create a new scaffold for your app, including an empty package.json file. Next, install Express Framework into your app.

$ npm install --save express serverless-http

Next, inside your app directory, create a file index.js, containing the following content:

// index.js

const serverless = require('serverless-http');
const bodyParser = require('body-parser');
const express = require('express')
const app = express()
const AWS = require('aws-sdk');


const ENVIRONMENT_TABLE = process.env.ENVIRONMENT_TABLE;
const dynamoDb = new AWS.DynamoDB.DocumentClient();

app.use(bodyParser.json({ strict: false }));

app.get('/', function (req, res) {
  res.send('Hello KerBoB!')
})

// Get Environment endpoint
app.get('/environment/:deviceId', function (req, res) {
  const params = {
    TableName: ENVIRONMENT_TABLE,
    Key: {
      deviceId: req.params.deviceId,
    },
  }

  dynamoDb.get(params, (error, result) => {
    if (error) {
      console.log(error);
      res.status(400).json({ error: 'Could not get environment' });
    }
    if (result.Item) {
      const {deviceId, temp, humidity} = result.Item;
      res.json({ deviceId, temp, humidity });
    } else {
      res.status(404).json({ error: "Device not found" });
    }
  });
})

// Create Environment endpoint
app.post('/environment', function (req, res) {
  const { deviceId, temp, humidity } = req.body;
  if (typeof deviceId !== 'string') {
    res.status(400).json({ error: '"deviceId" must be a string' });
  } else if (typeof temp !== 'string') {
    res.status(400).json({ error: '"temp" must be a string' });
  }

  const params = {
    TableName: ENVIRONMENT_TABLE,
    Item: {
      deviceId: deviceId,
      temp: temp,
      humidity: humidity,
    },
  };

  dynamoDb.put(params, (error) => {
    if (error) {
      console.log(error);
      res.status(400).json({ error: 'Could not create environment' });
    }
    res.json({ deviceId, temp, humidity });
  });
})

module.exports.handler = serverless(app);

Next, inside your app directory, create a file serverless.yml, with the following content:

# serverless.yml

service: coderdx-io-dashboard

custom:
  tableName: 'environment-table-${self:provider.stage}'

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: us-east-1
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - { "Fn::GetAtt": ["EnvironmentDynamoDBTable", "Arn" ] }
  environment:
    ENVIRONMENT_TABLE: ${self:custom.tableName}

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

resources:
  Resources:
    EnvironmentDynamoDBTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        AttributeDefinitions:
          -
            AttributeName: deviceId
            AttributeType: S
        KeySchema:
          -
            AttributeName: deviceId
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:custom.tableName}

Next, deploy your API server using the following command:

$ sudo sls deploy

This command will produce output similar to below. Make special note of the endpoint URL…

$ sudo sls deploy
[sudo] password for dx: 
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (673.25 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..........
Serverless: Stack update finished...
Service Information
service: coderdx-io-dashboard2
stage: dev
region: us-east-1
stack: coderdx-io-dashboard2-dev
api keys:
  None
endpoints:
  ANY - https://97kmb3cw21.execute-api.us-east-1.amazonaws.com/dev
  ANY - https://97kmb3cw21.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  app: coderdx-io-dashboard2-dev-app
layers:
  None
$

Just to test everything so far, copy the URL form the output and visit it in a browser tab. The string “Hello KerBob!” should appear. Very good!

Next, return to your SSH session on your Pi. Locate the file Adafruit_Python_DHT/examples/AdafruitDHT.py. Open it an replace the content with the content below. Be sure to substitute your correct URL for the one shown below…

#!/usr/bin/python
# Copyright (c) 2014 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.
import sys
import Adafruit_DHT
import pycurl
import time

# Parse command line parameters.
sensor_args = { '11': Adafruit_DHT.DHT11,
                '22': Adafruit_DHT.DHT22,
                '2302': Adafruit_DHT.AM2302 }
if len(sys.argv) == 3 and sys.argv[1] in sensor_args:
    sensor = sensor_args[sys.argv[1]]
    pin = sys.argv[2]
else:
    print('Usage: sudo ./Adafruit_DHT.py [11|22|2302] <GPIO pin number>')
    print('Example: sudo ./Adafruit_DHT.py 2302 4 - Read from an AM2302 connected to GPIO pin #4')
    sys.exit(1)

while True:
    # Try to grab a sensor reading.  Use the read_retry method which will retry up
    # to 15 times to get a sensor reading (waiting 2 seconds between each retry).
    humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

    # Un-comment the line below to convert the temperature to Fahrenheit.
    temperature = temperature * 9/5.0 + 32

    # Note that sometimes you won't get a reading and
    # the results will be null (because Linux can't
    # guarantee the timing of calls to read the sensor).
    # If this happens try again!
    if humidity is not None and temperature is not None:
        print('Temp={0:0.1f}*  Humidity={1:0.1f}%'.format(temperature, humidity))
    else:
        print('Failed to get reading. Try again!')
        sys.exit(1)

    # send to AWS
    try:
        # python 3
        from urllib.parse import urlencode
    except ImportError:
        # python 2
        from urllib import urlencode

        c = pycurl.Curl()
        c.setopt(c.URL, 'https://97kmb3cw21.execute-api.us-east-1.amazonaws.com/dev/environment')
        c.setopt(pycurl.HTTPHEADER,['Content-Type: application/json'])

        post_data = '{"deviceId": "1", "temp": "' + str(temperature) + '", "humidity": "' + str(humidity) + '"}'
            
        # Form data must be provided already urlencoded.
        #postfields = urlencode(post_data)

        print(post_data)
        # Sets request method to POST,
        # Content-Type header to application/x-www-form-urlencoded
        # and data to send in request body.
            
        c.setopt(c.POSTFIELDS, post_data)

        c.perform()
        c.close()

        time.sleep( 15 )

Finally, on your Pi, issue the following command to run your app:

$ sudo ./AdafruitDHT.py 2302 4

Go back to your browser tab containing the link to your API endpoint and refresh the page. Your temperature and humidity should refresh every 15 seconds. These results will not automatically update in the browser, you will need to reload the page every time to see your updates.

Congratulations, you did it!

Please leave any comments or questions in the feedback section below. If you found this tutorial helpful, please click on the PayPal Donate button below and make a donation. Donations are greatly appreciated.





Installing Android Studio on Linux Mint 18.2

I recently enrolled in a free online React Native bootcamp offered by tylermcginnis.com. In the past I also took his React bootcamp, and found that educational and informative as well.

At one point in the React Native bootcamp, the instructor talks about IDE’s and mentioned Xcode and Android Studio. I don’t have a Mac right now, so I went ahead and installed Android Studio on my MSI GT72-2QD Dominator. That machine sports an Intel i7 CPU, 32 GB RAM, and 2, 525GB M.1 SATA drives, on a RAID-0 mirror. Read More

Why Are We Here

Why are we here? That’s a question that has been asked by man since the beginning of time.  I am now going to attempt to answer that question, albeit in a considerably more narrow context.

CoderDx.io is my attempt at creating a website that will become the all encompassing reflection of me and who I am, both personally and professionally.  This site will feature:

My observation of things I see and experience
Tutorial blog posts about cool things I learned
Information about my tech meetup, Austin Internet of Things
Hardware and software projects I have done and am currently working on

Hope you will find my site useful.

Cheers,

Dave Brixius
@CoderDx