Last active
July 21, 2020 01:19
-
-
Save savannahostrowski/d4ea6a4c4db8bb9e6851282f1dbce17f to your computer and use it in GitHub Desktop.
HCDE 598 - In-Class Exercise #4
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 takes in user input for a latitude and a longitude via the serial monitor, | |
* uses ipify to grab your IP address, calculates the bearing between the two coordinate | |
* pairs, and maps that bearing to a compass direction. | |
* The direction is both outputted to an OLED screen and an Adafruit IO dashboard. | |
* | |
* 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 | |
#include <Adafruit_GFX.h> // OLED graphics library | |
#include <Adafruit_SSD1306.h> // OLED library | |
#include <math.h> // Standard library with math functions | |
#include <Servo.h> // Servo 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 | |
#include <ArduinoJson.h> //provides the ability to parse and construct JSON objects | |
#define SCREEN_WIDTH 128 // OLED display width, in pixels | |
#define SCREEN_HEIGHT 32 // OLED display height, in pixels | |
#define DEBUG 0 // DEBUG mode: if 1 we are not logging to Adafruit, if 0 we are posting data! | |
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) | |
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) | |
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Configure display | |
// Set up your API key to get IP address | |
const char *key = "YOUR KEY HERE"; | |
// SET UP LOCATION VARIABLES | |
// --------------------------- | |
// Set up the geolocation feed for Adafruit IO | |
AdafruitIO_Feed *geolocation = io.feed("geolocation"); | |
AdafruitIO_Feed *northBool = io.feed("northBool"); | |
// Instantiate empty variable to store input latitude | |
float inputLat; | |
// Instantiate empty variable to store input longitude | |
float inputLon; | |
// Instantiate empty variable to store bearing | |
int bearing; | |
// SET UP SERVO VARIABLES | |
// --------------------------- | |
// Create a servo object | |
Servo servo; | |
// Configures the servo pin | |
const int SERVO_PIN = 2; | |
// 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(); | |
// User prompt to enter latitude | |
Serial.println("Enter your latitude: "); | |
//Wait for user input | |
while (Serial.available() == 0); | |
// Reading the input string from serial port | |
inputLat = Serial.readString().toFloat(); | |
Serial.println("Your latitude is: " + String(inputLat)); | |
// User prompt to enter longitude | |
Serial.println("Enter your longitude: "); | |
// Wait for user input | |
while (Serial.available() == 0); | |
// Reading the input string from the serial port | |
inputLon = Serial.readString().toFloat(); | |
Serial.println("Your longitude is: " + String(inputLon)); | |
// Tell the servo class which pin we are using | |
servo.attach(SERVO_PIN); | |
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("."); | |
} | |
// If not in debug mode | |
if (!DEBUG) | |
{ | |
Serial.println(); | |
// Print and then connect to io.adafruit.com | |
Serial.println("Connecting to Adafruit IO"); | |
// Connect to Adafruit IO | |
io.connect(); | |
// Wait for a connection | |
while (io.status() < AIO_CONNECTED) | |
{ | |
// Print a dot every 500ms | |
Serial.print("."); | |
delay(500); | |
} | |
// We are connected (print the status) | |
Serial.println(); | |
Serial.println(io.statusText()); | |
} | |
//Call the getGeo() helper | |
getGeo(); | |
// Prints the external IP address to the serial monitor | |
Serial.println("Your external IP address is " + location.ip); | |
//Prints the continent name and country code of the ESP to the serial monitor | |
Serial.print("Your ESP is currently in " + location.cn + " (" + location.cc + "),"); | |
//Prints the city name and region code of the ESP to the serial monitor | |
Serial.println(" in or near " + location.cy + ", " + location.rc + "."); | |
// Prints the latitude and longitude to the serial monitor | |
Serial.println("and located at (roughly) "); | |
Serial.println(location.lt + " latitude by " + location.ln + " longitude."); | |
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally | |
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) | |
{ // Address 0x3C for 128x32 | |
Serial.println(F("SSD1306 allocation failed")); | |
// Don't proceed, loop forever | |
for (;;) | |
; | |
} | |
// Set up the OLED display | |
// by default, we'll generate the high voltage from the 3.3v line internally | |
// Initialize with the I2C addr 0x3C (for the 128x32) | |
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); | |
// Normal 1:1 pixel scale | |
display.setTextSize(1); | |
// Draw white text | |
display.setTextColor(SSD1306_WHITE); | |
// Clear the display | |
display.clearDisplay(); | |
// Set the cursor to start printing in the top-left corner (at 0,0) | |
display.setCursor(0, 0); | |
// Delay after clearing | |
delay(500); | |
// Convert my IP latitude to a string | |
float myLat = String(location.lt).toFloat(); | |
// Convert my IP longitude to a string | |
float myLon = String(location.ln).toFloat(); | |
bool isThatNorth = isNorth(myLat, inputLat); | |
if (isThatNorth) | |
{ | |
servo.write(0); | |
display.print("That location is north of you!"); | |
geolocation->save("That location is north of you!"); | |
northBool->save(1); | |
} | |
else | |
{ | |
servo.write(180); | |
display.print("That location is south of you!"); | |
geolocation->save("That location is south of you!"); | |
northBool->save(0); | |
} | |
// Pushes data to the OLED display | |
display.display(); | |
} | |
void loop() | |
{ | |
// io.run(); is required for all sketches. | |
// it should always be present at the top of your loop | |
// function. it keeps the client connected to | |
// io.adafruit.com, and processes any incoming data. | |
io.run(); | |
} | |
bool isNorth(float myLat, float theirLat) | |
{ | |
return theirLat < myLat; | |
} | |
// 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=" + 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."); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment