DIY Temperature Sensor

Last weekend I set out on a challenge to test my software and computer engineering skills, my research abilities, and my patience - could I build a homebrew temperature monitoring system using a Raspberry Pi and my laptop? Throughout this journey the challenge evolved to whether I could configure a Raspberry Pi to take temperature readings, send those readings to a REST API that was hosted with AWS, and email myself a daily report of the temperature readings? TL:DWTR - Yes.

Paying Homage

I wouldn't have gotten very far without these helpful resources.

Architecture

Temperature Sensor Architecture

Notes

  1. The API was designed using the native Flask routers, rather than Flask-RESTful (similar to Django REST Framework). The temperature reading is the only resource here, and will only use GET and POST to begin with, where GET will retrieve all temperature readings and POST will write a new reading. The HTTP methods are handled with native Flask @app.route decorators on native Python functions.
@app.route('/.../api/v1.0/temps', methods=['GET'])
@auth.login_required
def get_temps():
    ''' validates user IP, if trusted, return all data '''

@app.route('/.../api/v1.0/temps', methods=['POST'])
@auth.login_required
def create_record():
    '''
    validates user IP and request payload, parses data
    if data not in database, writes data return 201
    if data in database, return record with 200
    else 404
    '''

Extending the API is relatively straighforward - we can add a new Flask router and be on our way.

@app.route('/.../api/v1.0/temps/<int:year>', methods=['GET'])
@auth.login_required
def get_temps_by_year():
    ''' gets all temperature readings for specific year '''
  1. After testing the API locally I spent the better part of the day getting it running on EC2. I'm using a t2.micro instance of Ubuntu 14.04 and mod_WSGI to serve the API. The documentation for mod_WSGI wasn't bad after finding my way around the site, but the largest trouble I had was having the API, rather than Apache, receive the authentication headers, which was sorted out with WSGIPassAuthorization set to ON within the API directory configuration settings. Now the API was open to a restricted set of IP addresses who also had the right username and password credentials.
  2. The code on the Raspberry Pi had to change to grow accomodate the API specifications and authentication extensions. I started with the basic modmypi script to read data off of the temperature sensor, and then made the following changes: Converting from UTC to EST, which "datetime" doesn't provide tooling for Building a datetime stamp in the format yyyy-mm-dd-hh-mm-ss, which will function as the unique identifier prior to being written to the database Creating a JSON object to send in requests
data = {
    'date': data_list[0],
    'year': data_list[1],
    'month': data_list[2],
    'day': data_list[3],
    'hour': data_list[4],
    'minute': data_list[5],
    'second': data_list[6],
    'temperature': data_list[7]
}

response = requests.post(api_url,
    headers=header_info,
    data=json.dumps(data),
    auth=HTTPBasicAuth('USER', PASSWORDDICT['USER']))

Assuming that everything goes well, I can send a GET request to the API and receive a JSON object with all of the data in response.

{
    "temps":
    [
        {
          "date": "2015-12-20-14-55-43",
          "day": 20,
          "hour": 14,
          "minute": 55,
          "month": 12,
          "second": 43,
          "temperature": 22.5,
          "year": 2015
        },
        {
          "date": "2015-12-20-14-56-14",
          "day": 20,
          "hour": 14,
          "minute": 56,
          "month": 12,
          "second": 14,
          "temperature": 22.5,
          "year": 2015
        },...
    ]
}
  1. The API uses SQLite as it's persistence layer, and as such the SQLAlchemy object definition resides in the API code. The reporting and mailing system won't be running from within the API so it needs to autoload the class definitions to access the tables through the SQLAlchemy ORM, which will ease the computations at the reporting stage. For now the entire reporting module is a cronjob, using the ideas detailed in Mike Bayer's autoloading post (shown above) to get the data from the previous day, calculate the hourly average temperature, and email me at 07:00 each morning.
0:00 average temp: 22.377157
1:00 average temp: 22.350896
2:00 average temp: 22.313643
3:00 average temp: 22.300139
4:00 average temp: 22.264557
5:00 average temp: 22.250000
6:00 average temp: 22.218226
7:00 average temp: 22.187009
8:00 average temp: 22.162948
9:00 average temp: 22.127696
10:00 average temp: 22.123914
11:00 average temp: 22.118974
12:00 average temp: 22.085763
13:00 average temp: 22.062543
14:00 average temp: 22.061461
15:00 average temp: 22.058226
16:00 average temp: 22.035043
17:00 average temp: 22.008552
18:00 average temp: 22.014557
19:00 average temp: 22.143278
20:00 average temp: 22.708983
21:00 average temp: 22.727672
22:00 average temp: 22.373835
23:00 average temp: 22.309913

Finished Product

Raspberry Pi Temperature Monitor

Shown above is the actual Raspberry Pi setup that's running in my home. Every 30 seconds it reads the temperature from the sensor and sends a POST request to the API, which is hosted on AWS EC2. Once a day a scheduled cronjob grabs the previous days data and sends an email report to me with the average hourly temperatures.

The next phase of the project will be to setup an interactive visualization system over the next few weeks.

social