Last active
December 3, 2018 16:10
-
-
Save mjvo/e960cc12b70ca33ebd4923e6d5c34df9 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// Arduino library dependencies | |
#include <SPI.h> | |
#include <WiFi101.h> | |
#include <timer.h> | |
#include <ArduinoJson.h> | |
// ************ | |
// Enter your wifi access credentials and base64 encoded Twitter API | |
// key and secret in the tab: arduino_secrets.h | |
// ************ | |
#include "arduino_secrets.h" | |
char ssid[] = SECRET_SSID; | |
char pass[] = SECRET_PASS; | |
char token[] = TWITTER_TOKEN; | |
int wifi_status = WL_IDLE_STATUS; // the WiFi radio's status | |
String access_token; // var for Bearer token generated by getBearerToken() function | |
String twitter_user = "DukeU"; // screen name of Twitter user to query | |
// vars to keep track of results of last Twitter API call | |
long last_tweet_id = 0; | |
int last_retweet_count = 0; | |
int last_favorite_count = 0; | |
long last_followers_count = 0; | |
// instantiate a secure WiFi client | |
// (most APIs require https connections) | |
WiFiSSLClient client; | |
// create a timer with default settings | |
auto doQuery = timer_create_default(); | |
void setup() { | |
//Initialize serial: | |
Serial.begin(9600); | |
// Feather M0-specific WiFi pins | |
WiFi.setPins(8, 7, 4, 2); | |
// do initial WiFi connect | |
wifiConnect(); | |
// Query Twitter API for Bearer token | |
getBearerToken(); | |
// Initial query of user's latest Tweet | |
queryTwitter(false); | |
// Instantiate timer for regular API calls | |
// (There are limits on how many requests per hour can | |
// be made against the API for a particular client app) | |
setQueryIntervals(); | |
} | |
void setQueryIntervals() { | |
// syntax: doQuery.every(milliseconds, callbackFunction); | |
//doQuery.every(100, someFunc); // call the someFunc() function every 100 ms | |
doQuery.every(30000, queryTwitter); // queries Twitter every 30 secs | |
} | |
// ******************************* | |
// This is the main function for querying and parsing Twitter's API | |
// ******************************* | |
bool queryTwitter(void *) { | |
client.setTimeout(10000); | |
client.connect("api.twitter.com", 443); | |
// Send HTTP request | |
client.print("GET /1.1/statuses/user_timeline.json?count=1&screen_name="); | |
client.print(twitter_user); | |
client.println(" HTTP/1.1"); | |
client.println(F("Host: api.twitter.com")); | |
client.print("Authorization: Bearer "); | |
client.println(access_token); | |
if (client.println() == 0) { | |
Serial.println(F("Failed to send request")); | |
return true; | |
} | |
// Check HTTP status | |
char status[32] = {0}; | |
client.readBytesUntil('\r', status, sizeof(status)); | |
if (strcmp(status, "HTTP/1.1 200 OK") != 0) { | |
Serial.print(F("Unexpected response: ")); | |
Serial.println(status); | |
return true; | |
} | |
// Skip response headers | |
char endOfHeaders[] = "\r\n\r\n"; | |
client.find(endOfHeaders); | |
// Allocate JsonBuffer based on ArduinoJSON assistant | |
// https://arduinojson.org/v5/assistant/ | |
const size_t bufferSize = 4 * JSON_ARRAY_SIZE(0) + | |
4 * JSON_ARRAY_SIZE(1) + | |
8 * JSON_ARRAY_SIZE(2) + | |
JSON_ARRAY_SIZE(5) + | |
3 * JSON_OBJECT_SIZE(1) + | |
JSON_OBJECT_SIZE(2) + | |
8 * JSON_OBJECT_SIZE(3) + | |
3 * JSON_OBJECT_SIZE(4) + | |
6 * JSON_OBJECT_SIZE(5) + | |
2 * JSON_OBJECT_SIZE(10) + | |
JSON_OBJECT_SIZE(25) + | |
JSON_OBJECT_SIZE(41) + 10000; | |
StaticJsonBuffer<bufferSize>jsonBuffer; | |
// Parse response | |
JsonArray& root = jsonBuffer.parseArray(client); | |
// ******************************* | |
// BEGIN JSON PARSE | |
// ******************************* | |
// Latest Tweet info from JSON | |
JsonObject& latest_tweet = root[0]; | |
long latest_tweet_id = latest_tweet["id"]; | |
const char* latest_tweet_content = latest_tweet["text"]; | |
int latest_retweet_count = latest_tweet["retweet_count"]; | |
int latest_favorite_count = latest_tweet["favorite_count"]; | |
// User info from JSON | |
JsonObject& user = latest_tweet["user"]; | |
const char* full_name = user["name"]; | |
const char* screen_name = user["screen_name"]; | |
long followers_count = user["followers_count"]; | |
// Do things with JSON data | |
// This code just prints to Serial but you can | |
// leverage to trigger actuators or send OSC or | |
// MQTT messages, etc | |
Serial.print("Latest Tweet from "); | |
Serial.print(screen_name); | |
Serial.print(" / "); | |
Serial.print(full_name); | |
Serial.print(", who has "); | |
Serial.print(followers_count); | |
Serial.println(" followers."); | |
if (followers_count > last_followers_count) { | |
Serial.print("New followers since last check: "); | |
Serial.println(followers_count - last_followers_count); | |
} | |
Serial.print("Tweet ID: "); | |
Serial.println(latest_tweet_id); | |
Serial.println(latest_tweet_content); | |
Serial.print("Favorited: "); | |
Serial.println(latest_favorite_count); | |
Serial.print("Retweets: "); | |
Serial.println(latest_retweet_count); | |
if (latest_tweet_id == last_tweet_id) { | |
if (latest_favorite_count > last_favorite_count) { | |
Serial.print("New favorites since last check: "); | |
Serial.println(latest_favorite_count - last_favorite_count); | |
} | |
if (latest_retweet_count > last_retweet_count) { | |
Serial.print("New retweets since last check: "); | |
Serial.println(latest_retweet_count > last_retweet_count); | |
} | |
} else { | |
last_favorite_count = 0; | |
last_retweet_count = 0; | |
} | |
// store data to compare against future Twitter API queries | |
last_tweet_id = latest_tweet_id; | |
last_retweet_count = latest_retweet_count; | |
last_favorite_count = latest_favorite_count; | |
last_followers_count = followers_count; | |
// ******************************* | |
// END JSON PARSE | |
// ******************************* | |
// Disconnect from Twitter API | |
client.println(F("Connection: close")); | |
client.stop(); | |
return true; // repeat | |
} | |
void loop() { | |
// check if wifi lost and if yes, reconnect. | |
// non-blocking version | |
if (WiFi.status() != WL_CONNECTED) { | |
Serial.println("WiFi connection lost. Reconnecting..."); | |
wifi_status = WL_IDLE_STATUS; | |
wifiConnect(); | |
} | |
// for timer | |
doQuery.tick(); | |
} | |
// connect to WiFi (or reconnect if WiFi lost) | |
void wifiConnect() { | |
// check for the presence of the shield: | |
if (WiFi.status() == WL_NO_SHIELD) { | |
Serial.println("WiFi chip not present or accessible"); | |
// don't continue: | |
while (true); | |
} | |
// attempt to connect to WiFi network: | |
while ( wifi_status != WL_CONNECTED) { | |
Serial.print("Attempting to connect to SSID: "); | |
Serial.println(ssid); | |
// Connect to WiFi: | |
if (sizeof(pass) <= 1) { | |
wifi_status = WiFi.begin(ssid); | |
} | |
else { | |
wifi_status = WiFi.begin(ssid, pass); | |
} | |
// wait 2 seconds for connection: | |
delay(2000); | |
} | |
// you're connected now, so print out a success message: | |
Serial.println("You're connected to the network"); | |
// print your Feather's IP address: | |
IPAddress ip = WiFi.localIP(); | |
Serial.print("Device IP Address: "); | |
Serial.println(ip); | |
} | |
// function to request Twitter API Bearer token | |
// runs once in setup() and assigns it to `access_token` | |
void getBearerToken() { | |
client.setTimeout(10000); | |
client.connect("api.twitter.com", 443); | |
// Send HTTP request | |
client.println(F("POST /oauth2/token HTTP/1.1")); | |
client.println(F("Host: api.twitter.com")); | |
client.println(F("User-Agent: My Twitter App v1.0.23")); | |
client.print("Authorization: Basic "); | |
client.println(token); | |
client.println(F("Content-Type: application/x-www-form-urlencoded;charset=UTF-8")); | |
client.println(F("Content-Length: 29")); | |
client.println(""); | |
client.println(F("grant_type=client_credentials")); | |
if (client.println() == 0) { | |
Serial.println(F("Failed to send request")); | |
return; //false; | |
} | |
// Check HTTP status | |
char status[32] = {0}; | |
client.readBytesUntil('\r', status, sizeof(status)); | |
if (strcmp(status, "HTTP/1.1 200 OK") != 0) { | |
Serial.print(F("Unexpected response: ")); | |
Serial.println(status); | |
return; //false; | |
} | |
// Skip response headers | |
char endOfHeaders[] = "\r\n\r\n"; | |
client.find(endOfHeaders); | |
// Allocate JsonBuffer | |
const size_t capacity = JSON_ARRAY_SIZE(3) | |
+ 8 * JSON_OBJECT_SIZE(1) | |
+ 4 * JSON_OBJECT_SIZE(4) | |
+ 300; | |
StaticJsonBuffer<capacity>jsonBuffer; | |
// Parse response | |
JsonObject& root = jsonBuffer.parseObject(client); | |
// Extract the array "query.results.channel" | |
JsonArray& results = root["access_token"]; | |
// Extract values | |
//Serial.println("Bearer Token: "); | |
//Serial.println(root["access_token"].as<String>()); | |
access_token = (root["access_token"].as<String>()); | |
// Disconnect | |
client.println(F("Connection: close")); | |
client.stop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment