Skip to content

Instantly share code, notes, and snippets.

@savannahostrowski
Last active July 21, 2020 01:19
Show Gist options
  • Save savannahostrowski/d4ea6a4c4db8bb9e6851282f1dbce17f to your computer and use it in GitHub Desktop.
Save savannahostrowski/d4ea6a4c4db8bb9e6851282f1dbce17f to your computer and use it in GitHub Desktop.
HCDE 598 - In-Class Exercise #4
/*
* 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