Last active
July 22, 2020 02:08
-
-
Save savannahostrowski/1af38dd1b74b41ad9942ac26f4c4138c to your computer and use it in GitHub Desktop.
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
/* | |
* This sketch does a couple things: | |
1. Grabs your IP address | |
2. Grabs your location from the IP address | |
3. Uses the location to get the current weather's description from the Open Weather API | |
4. Uses that description to query the Unsplash API for a suitable image related to the current weather in your area | |
* Savannah Ostrowski | |
*/ | |
/************************** Configuration ***********************************/ | |
// edit the config.h tab and enter your Adafruit IO credentials | |
// and any additional configuration needed for WiFi, cellular, | |
// or ethernet clients. | |
#include "config.h" | |
/************************ Code Starts Here *******************************/ | |
#include <ESP8266WiFi.h> // WiFi Feather library | |
#include <ESP8266HTTPClient.h> // HTTP Feather library | |
//ArduinoJson can decode Unicode escape sequence, but this feature is disabled by default | |
//because it makes the code bigger. To enable it, you must define ARDUINOJSON_DECODE_UNICODE to 1 | |
#define ARDUINOJSON_DECODE_UNICODE 1 | |
// Provides the ability to parse and construct JSON objects | |
#include <ArduinoJson.h> | |
// SET UP IP STRUCT | |
// --------------------------- | |
typedef struct | |
{ //here we create a new data type definition, a box to hold other data types | |
String ip; | |
String cc; //for each name:value pair coming in from the service, we will create a slot | |
String cn; //in our structure to hold our data | |
String rc; | |
String rn; | |
String cy; | |
String ln; | |
String lt; | |
} GeoData; //then we give our new data structure a name so we can use it in our code | |
GeoData location; //we have created a GeoData type, but not an instance of that type, so we create the variable 'location' of type GeoData | |
// The setup function runs once when you press reset or power the board | |
void setup() | |
{ | |
Serial.begin(115200); | |
delay(10); | |
// Wait for serial monitor to open then print build details | |
while (!Serial); | |
// Print blank line | |
Serial.println(); | |
// Print that we are connection to the WiFi network | |
Serial.println("Connecting to " + String(WIFI_SSID)); | |
// Sets the WiFi mode to Station; Station (STA) mode is used to get ESP module connected to a Wi-Fi network established by an access point | |
WiFi.mode(WIFI_STA); | |
// Configures the SSID and password for the network | |
WiFi.begin(WIFI_SSID, WIFI_PASS); | |
while (WiFi.status() != WL_CONNECTED) | |
{ // While we are waiting to connect to the network, | |
// Print a . every 500ms | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println("Getting location"); | |
// Gets the location of the user based on IP address | |
getGeo(); | |
Serial.println("You are in " + location.cy + ", " + location.rc); | |
Serial.println("Getting the current weather..."); | |
// Gets the description of the weather from the Open Weather API | |
String weatherDescription = getCurrentWeatherDescription(); | |
Serial.println("One might describe the weather as: " + weatherDescription); | |
Serial.println("Fetching a suitable image..."); | |
// Fetches an image from the Unsplash API based on that weather description | |
String weatherImageUrl = getWeatherImageUrl(weatherDescription); | |
Serial.println("Your image is: " + weatherImageUrl); | |
} | |
// Nothing in the loop | |
void loop() {} | |
// A helper function to get the current weather's description from the Open Weather API | |
String getCurrentWeatherDescription() | |
{ | |
// Instantiates a client object | |
HTTPClient theClient; | |
// Instantiates the description as empty string...to be set later | |
String description = ""; | |
// Print to the serial monitor that we are starting the request | |
Serial.println("Making the weather HTTP request"); | |
// Format the url for the API request to the open weather API | |
theClient.begin("http://api.openweathermap.org/data/2.5/weather?q=" + location.cy + "," + location.rc + "," + location.cc + "&appid=" + WEATHER_API_KEY); | |
// Make a GET request to the client | |
int httpCode = theClient.GET(); | |
// If we have a status code | |
if (httpCode > 0) | |
{ | |
// Status code is 200; request was successful | |
if (httpCode == 200) | |
{ | |
//Print to serial monitor that we got data from the endpoint | |
Serial.println("Received HTTP payload."); | |
// Instantiate the object to store the JSON document/blob | |
StaticJsonDocument<1024> doc; | |
// Get the JSON blob back from the endpoint (it's serialized at this point) | |
String payload = theClient.getString(); | |
Serial.println("Parsing..."); | |
// Derialize the payload into the doc variable | |
deserializeJson(doc, payload); | |
// Instantiates an error variable which we will check in the next line | |
DeserializationError error = deserializeJson(doc, payload); | |
// If there was an error | |
if (error) | |
{ | |
//Print that deserialization failed to the serial monitor | |
Serial.print("deserializeJson() failed with error code "); | |
// Derialization failed so we print the error | |
Serial.println(error.c_str()); | |
return "error"; | |
} | |
// Sets the description variable to the description of the weather with spaces replaced with %20 | |
// for URL compatibility | |
description = replaceSpaces(doc["weather"][0]["description"].as<String>()); | |
} | |
else | |
{ | |
// Print that there's something wrong with the endpoint to the serial monitor | |
Serial.println("Something went wrong with connecting to the endpoint."); | |
} | |
} | |
// Return the weather description | |
return description; | |
} | |
// A helper function to get a related image URL for the weather description from the Unsplash API | |
String getWeatherImageUrl(String description) | |
{ | |
// Instantiates the client object | |
HTTPClient theClient; | |
// Instantiates an empty url | |
String imageUrl = ""; | |
//Defines/set the client using a fingerprint for HTTPS | |
theClient.begin("https://api.unsplash.com/photos/random/?client_id=" + String(UNSPLASH_API_KEY) + "&query=" + description, FINGERPRINT); | |
// Make a GET request to the client | |
int httpCode = theClient.GET(); | |
// If we have a status code | |
if (httpCode > 0) | |
{ | |
// Status code is 200; request was successful | |
if (httpCode == 200) | |
{ | |
// Instantiate the object to store the JSON document/blob | |
StaticJsonDocument<20000> doc; | |
// Get the reponse payload data | |
String payload = theClient.getString(); | |
// Derialize the payload into the doc variable | |
deserializeJson(doc, payload); | |
// Instantiates an error variable which we will check in the next linel | |
DeserializationError error = deserializeJson(doc, payload); | |
// If there was an error | |
if (error) | |
{ | |
//Print that deserialization failed to the serial monitor | |
Serial.print("deserializeJson() failed with error code "); | |
// Derialization failed so we print the error | |
Serial.println(error.c_str()); | |
return "error"; | |
} | |
// Get URL from JSON at the random index | |
imageUrl = doc["urls"]["raw"].as<String>(); | |
} | |
else | |
{ | |
Serial.println("Something went wrong with connecting to the endpoint."); | |
// Returns the string "error" | |
return "error"; | |
} | |
} | |
// Returns the image url | |
return imageUrl; | |
} | |
// A helper function to get the IP address from ipify | |
String getIP() | |
{ | |
HTTPClient theClient; // Instantiates the client object | |
String ipAddress; // Instantiates an ipAddress variable | |
//Defines/set the client as the ipify website | |
theClient.begin("http://api.ipify.org/?format=json"); | |
// Make a GET request to the client | |
int httpCode = theClient.GET(); | |
// If we have a status code | |
if (httpCode > 0) | |
{ | |
// Status code is 200; request was successful | |
if (httpCode == 200) | |
{ | |
// Instantiate the object to store the JSON document/blob | |
StaticJsonDocument<100> doc; | |
// Get the reponse payload data | |
String payload = theClient.getString(); | |
// Derialize the payload into the doc variable | |
deserializeJson(doc, payload); | |
// Grab the IP address from the doc and cast it to a string | |
ipAddress = doc["ip"].as<String>(); | |
} | |
else | |
{ | |
Serial.println("Something went wrong with connecting to the endpoint."); | |
// Returns the string "error" | |
return "error"; | |
} | |
} | |
// Returns the IP address | |
return ipAddress; | |
} | |
// A helper function which sets all the fields in our GeoData struct based on our IP address endpoint call | |
void getGeo() | |
{ | |
// Instantiates a client object | |
HTTPClient theClient; | |
// Print to the serial monitor that we are starting the request | |
Serial.println("Making HTTP request"); | |
// Return IP as .json object | |
theClient.begin("http://api.ipstack.com/" + getIP() + "?access_key=" + IP_ADDRESS_API_KEY); | |
// Make a GET request to the client | |
int httpCode = theClient.GET(); | |
// If we have a status code | |
if (httpCode > 0) | |
{ | |
// Status code is 200; request was successful | |
if (httpCode == 200) | |
{ | |
//Print to serial monitor that we got data from the endpoint | |
Serial.println("Received HTTP payload."); | |
// Instantiate the object to store the JSON document/blob | |
StaticJsonDocument<1024> doc; | |
// Get the JSON blob back from the endpoint (it's serialized at this point) | |
String payload = theClient.getString(); | |
Serial.println("Parsing..."); | |
// Derialize the payload into the doc variable | |
deserializeJson(doc, payload); | |
// Instantiates an error variable which we will check in the next line | |
DeserializationError error = deserializeJson(doc, payload); | |
// If there was an error | |
if (error) | |
{ | |
//Print that deserialization failed to the serial monitor | |
Serial.print("deserializeJson() failed with error code "); | |
// Derialization failed so we print the error | |
Serial.println(error.c_str()); | |
return; | |
} | |
// Using .dot syntax, we refer to the variable "location" which is of type GeoData, and place our data into the data structure. | |
// We cast the values as Strings because the 'slots' in GeoData are Strings | |
location.ip = doc["ip"].as<String>(); | |
location.cc = doc["country_code"].as<String>(); | |
location.cn = doc["country_name"].as<String>(); | |
location.rc = doc["region_code"].as<String>(); | |
location.rn = doc["region_name"].as<String>(); | |
location.cy = doc["city"].as<String>(); | |
location.lt = doc["latitude"].as<String>(); | |
location.ln = doc["longitude"].as<String>(); | |
} | |
else | |
{ | |
// Print that there's something wrong with the endpoint to the serial monitor | |
Serial.println("Something went wrong with connecting to the endpoint."); | |
} | |
} | |
} | |
// A helper function to create a string which has all spaces replaced with %20 for URL compatibility | |
String replaceSpaces(String myString) | |
{ | |
// Instantiates a string object that we will build up as we iterate | |
// In place modification of strings is ugly :) | |
String newString; | |
// We use a for loop to iterate over each character in the loop | |
for (int i = 0; i < myString.length(); i++) | |
{ | |
// Assign the current char to a variable to improve readability | |
char currentChar = myString[i]; | |
// Use a ternary operator to check if the current character is a space | |
// If it is, we append %20 to our new string | |
// If not, we append the current character to our new string | |
currentChar == ' ' ? newString += "%20" : newString += currentChar; | |
} | |
return newString; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment