Warning:
I hope that this post illustrates how using only 2-3 dozen lines of code, we have a 'free' web-based datastore for this project. What follows is a primer for future posts where we will tie everything together.Google App Engine will allow us to easily handle the Arduino's requests, store data, and let us retrieve data to display in a web browser.
To get started with Google App Engine you can sign up at: appengine.google.com.
For the purposes of this blog/tutorial, I will assume that you have some working knowledge of App Engine, that you can gain by working through the python tutorial.
If you are very technical, hopefully you can likely stich together a working app using this blog and source code provided alone (without completing the tutorial).
Once you are up and running with the App Engine Launcher and have created your first application, it's quite simple to log data.
The Model:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DailyWeatherEntry(db.Model): | |
date = db.IntegerProperty() | |
temp_entry = db.TextProperty() | |
humidity_entry = db.TextProperty() |
Since database reads / writes are limited (and we want to keep within App Engine's free-quota limits), storing a day of entries in a single string will be a hacky-effective solution.
I am also storing the date as a unix timestamp integer to remove some complexity of working with timestamps when it's time to graph them.
Defining an API:
Quite simply, the Arduino will pass a temperature and humidity value. Our Google App Engine application will receive these values, and record them to the database. An example request might look like:
myproject.appspot.com/t=7205&h=4302 which represents an entry of 72.05 degrees F and 43.02% humidity. Timestamp isn't required because that is handled server side.
Handling Requests from the Arduino:
The following code is heavily commented, and all records are formatted to be super-friendly to use with Flot (javascript based graphing library) in future posts.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Weather(webapp.RequestHandler): | |
def get(self): | |
temp = str(float(cgi.escape(self.request.get('t')))/100) #Convert from Arduino's 4 digit format | |
humidity = str(float(cgi.escape(self.request.get('h')))/100) | |
# Determine timestamp of now and one day ago to micro-manage database records | |
rightNow = int(time.time()) | |
dayAgo = rightNow-86400 | |
# Get a record that is within the last day | |
recent_record = DailyWeatherEntry.gql("WHERE date > :1 ORDER BY date DESC",dayAgo) | |
# Convert timestamp to a string for appending to other strings | |
rightNow = str(rightNow) | |
# If there is an existing record for the past day, append the well-formed entry (for easily graphing later) | |
if recent_record.count()!=0: | |
dayObj = recent_record[0] # First record if any exist | |
dayObj.temp_entry = dayObj.temp_entry + '['+rightNow+','+temp+'],' # Formatting here is for use with Flot (js graphing library) | |
dayObj.humidity_entry = dayObj.humidity_entry + '['+rightNow+','+humidity+'],' | |
dayObj.put() # This writes the new entry to the datastore | |
# Otherwise there isn't an entry within the past day, create a new one | |
else: | |
newEntry = DailyWeatherEntry( | |
date = int(time.time()), | |
temp_entry = '['+rightNow+','+temp+'],', | |
humidity_entry = '['+rightNow+','+humidity+'],' | |
) | |
newEntry.put() | |
self.response.headers.add_header("X-Arduino-Data", temp +','+ humidity ) |
The above code will be used to handle requests made from the Arduino. Note the use of the X-Arduino-Data header. This will be used for debugging the Arduino sketch.
Displaying Posts:
This code will be used to handle requests from the browser in order to display our temperature/humidity records from the datastore:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ShowWeather(webapp.RequestHandler): | |
def get(self): | |
the_weather = db.GqlQuery( | |
"SELECT * FROM DailyWeatherEntry ORDER BY date ASC") | |
template_values = { | |
'weather' : the_weather, | |
#'debug' : debug | |
} | |
path = os.path.join(os.path.dirname(__file__), 'weather.html') | |
self.response.out.write(template.render(path, template_values)) |
Confused? Probably. We'll be pulling everything together in the next post(s).
No comments:
Post a Comment