There's a lot of data on the internet, and it can make your Arduino or Raspberry pi projects so much cooler. I have a project where my bedside lamp adjusts it's color based on the current weather and foretasted high temperature for the day. It means when I wake up and turn on my lamp, I know how to dress for the day!
I haven't seen many great tutorials on this on the internet, so I wanted to make a guide that walks you through this process. The goal will be to change the color of an LED based on your commute time, so you know if you should leave work now or spend 30 minutes trying to catch up on some tasks while traffic clears out.
Getting Started
You'll need a few things:
-
A lot of web services offer things called APIs, which are protocols that spell out how to send and receive data. A lot of these are free, but you need an account to get started. Also note: These can cost money if you're making a lot of requests, or a few requests at a very fast rate... so track your usage carefully and never share your API key with anyone!
For this example, we'll be using Google's APIs which offer a tonne of cool services, from geolocation to traffic. You can also use Google's APIs to make devices that can check your calendar, send email messages, or track the mood of the internet.
- You'll need an account on console.particle.io/ to act as an intermediary between your device and the internet. (This works even if you're using a Raspberry pi!)I strongly prefer using Particle's Command Line Interface over any of their web tools. I'll be using that throughout this post, so you might want to go ahead and install it.*It's possible, but a bit of a pain, to call the API directly from your connected device (Photon or Raspberry Pi), but that means you have to parse the response on that device. This can be problematic because the Photon only has 128 kb of RAM, and a verbose response from the sever might get cut into chunks or data will get lost.
- You'll need the hardware. For this example I'll be using a particle photon connected to a 8mm diffused thru-hole neopixel RGB led, although you could do this project with any LEDs you have.
Setting Up Your Photon
Skip this section if you've already set up a Photon or are using another device, like a Raspberry Pi.
Update the Firmware on your Photon to the latest version
- Put your device in DFU mode.
- In your terminal or command line, enter:
particle update
Run the setup utility on your photon
- Put your device in Listening mode.
- In your terminal or command line, enter:
particle setup
- Follow the prompts to setup your photon and connect to WiFi.
Setting Up The Webhook
You can enter an address like this in your browser to preview what the server response will look like. Feel free to edit the Origin and Destination address, and make sure to insert your API Key where it says "YOUR_API_KEY_HERE"
https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=1045+Regent+Dr+Boulder,CO&destinations=1535+Pearl+St,+Boulder,CO+80302&departure_time=now&key=YOUR_API_KEY_HERE
The response is formatted in JSON:
{ "destination_addresses" : [ "1535 Pearl St, Boulder, CO 80302, USA" ], "origin_addresses" : [ "1045 Regent Dr, Boulder, CO 80302, USA" ], "rows" : [ { "elements" : [ { "distance" : { "text" : "1.7 mi", "value" : 2656 }, "duration" : { "text" : "7 mins", "value" : 401 }, "duration_in_traffic" : { "text" : "6 mins", "value" : 347 }, "status" : "OK" } ] } ], "status" : "OK" }
The variable I want is {{duration_in_traffic.value}} (347 seconds); however to get there I need to navigate first throw the {{rows}} and next through the {{elements}}tag. To setup my webhook, I'll create a JSON file that contains my request, and a key of how to read the results and give the variable of interest back to my photon. That file looks like this:
{ "event": "googleDistanceMatrix", "url": "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=1045+Regent+Dr+Boulder,CO&destinations=1535+Pearl+St,+Boulder,CO+80302&departure_time=now&key=YOUR_API_KEY_HERE", "requestType": "POST", "headers": null, "query": null, "responseTemplate": "{{#rows}}{{#elements}}{{duration_in_traffic.value}}{{/elements}}{{/rows}}", "json": null, "auth": null, "mydevices": true }
Some of these values might change based on different APIs, but what I've shown here is pretty standard. If you'd like a more complex example navigating a larger response and returning more variables, check out this one that uses the Weather Underground API.
I want to save this file and then publish it to the web using the command in the terminal:
particle webhook create GitHub/smart/googleDistanceMatrix.json
(your file directory will change depending on where you save it).
I can confirm the webhook is working by opening the particle.io events console and then entering this command in the terminal:
particle publish googleDistanceMatrix
If all goes well, my console should show something like this:
Coding your Photon
In your setup function, you'll want to subscribe to any time there's an update in commute time, from any of your devices:
void setup() { // Subscribe to relevant events Particle.subscribe("hook-response/googleDistanceMatrix", trafficHandler, MY_DEVICES); // Iniltize Neopixels strip.begin(); }
The trafficHandler variable in the middle is defining a new function we need to create that is called any time any of MY_DEVICES gets a googleDistanceMatrix response. That function will parse the incoming data (sent as a string) and convert it to an integer to use however I like. In this case, I'm asking it to set my lights to green if traffic is fine, yellow if traffic is medium, and red if traffic is heavy.
void trafficHandler(const char *name, const char *data){ String str = String(data); int trafficTime=str.toInt(); updateTrafficTime = Time.now(); // This displays the correct color, but i'm not showing the function here dispTemp(map(trafficTime,1000,1700,50,100),1); }
Finally, we need to tell the device to query the API if it hasn't been called in a while. The free limit on the API we're using is 2,500 calls/day, or roughly 1 call every 2 minutes.
void loop(){ //check every 120 seconds if((Time.now()-updateTrafficTime)>120){ Particle.publish(TRAFFIC_PUB); unsigned long wait = millis(); //wait for subscribe to kick in or 5 secs max while ((Time.now()-updateTrafficTime)>120 && (millis()-wait < 5000)) Particle. process(); //Check for new posts if ((Time.now()-updateTrafficTime)>120) Serial.println("Traffic update failed"); } }