Twitter Update API String Read via WiFi

I think this is the first time for me to post in the forum, I did do some research and such to determine that there isn't anything (that I've found via targeted Google searches) that seems to apply to my issue.

I'm trying to read a string from my Twitter account. I've attained my various tokens. I'm using the Adafruit Huzzah board to control a stepper motor via a tweet. I've got all my code for internet connectivity working, I've got the code and function blocks for my stepper control working as well. I just cannot seem to get the Twitter string read to work.

I downloaded the "Arduino LED On/Off Twitter".ino (probably screwed up the file name, working from memory in an airport) and have not had any success. I know Twitter recently made changes to the API, which I assume is my issue but I'm pretty ignorant to anything web related. Any general guidance would be appreciated. I'm not looking for someone to write out all the code, just something to point me in the right direction so I can figure it out. I learn better if I have to struggle to figure it out.

Thank you in advance. On my next flight I plan to compile all my various bits of code from different sketches into one sketch so that I can show you what I have thus far. Presently I have a separate sketch to connect to the net, control the stepper, and attempt to read Twitter.

So on my first flight of the day I was able to get my code cleaned up a bit and looking better. Everything compiles now, so that’s a good thing. I’m still not sure if it will work with my Hazzah, but I’d imagine it would since the Hazzah runs Arduino and has enough memory for my program. I’ll eventually go back and add some stuff to put the board to sleep and in deep sleep to save power.

I’d greatly appreciate any feedback anyone may have.

#include <Stepper.h>
#include <ESP8266WiFi.h>

/*    Chris Cronan
Rev 0 December 4, 2015; original coding
Rev 1 December 27, 2015; changes made for twitter account read
Rev 2 January 8, 2016; Assistance request from Arduino forums
Rev 3 January 8, 2016; Code cleanup, added for loops to raise/lower functions*/
  // On board LED setup
  int LED = 0 ;                             // GPIO 0 (onboard LED)
  
  // Network Connection Settings
  const char* ssid = "Work Sucks, Quit Now";
  const char* password = "password";

  // Twitter Connection Tokens & Setup
  const String kAccessToken = "my access token";
  const String kAccessTokenSecret = "my secret token";
  const String kAPI_Key = "mykey";
  const String kAPI_Secret = "token";
  String sTweet="";
  char server[] = "apkit.esy.es";            // name for apkit server using DNS
  const unsigned long nRequestInterval = 100000;
  boolean bRequested;                        // Has a request been made since connecting
  unsigned long nLastAttemptTime = 0;        // last time server connection established
  String sCurrentLine = "";                  // String to store text from server
  String sLastTweet = "";                   
  boolean bReadingTweet = false;
  String sUsername = "Tipsez";
  String sCountStatus = "1";
  WiFiClient client;
  
  void connectToServer()
  {
    // Attempt to connect, and wait a moment
    Serial.println("Connecting to server...");
    if (client.connect(server, 80))
    {
      Serial.println("Connected to server");
      // Make HTTP Request
      client.println ("GET /twitter-oauth/status.php?username=" + sUsername + "&access_token=" + kAccessToken + "&access_token_secret=" + kAccessTokenSecret + "&count=" + sCountStatus + " HTTP/1.1");
      client.println("Host: apkit.esy.es");
      client.println("Connection: close");
      client.println();
    }
    else 
    {
      //kill function if no response from server
      Serial.println ("Connection to server failed");
      for (int l_i = 0; l_i < 20; l_i++)
      {
        pinMode (LED, HIGH);
        delay (10);
        pinMode (LED, LOW);
        delay (10);
      }
    }
    nLastAttemptTime = millis();
  }
  
  //*28BYJ-48 Stepper Motor Data and setup
  const int kStepsPerRev = 200;     // Max steps for one revolution
  const int RPM = 30;              // Max RPM permitted
  
  // GPIO Pins on Huzzah for Stepper Motor Drive output to Motor Driver Board
  int STBY = 5;                             // GPIO 5 is Driver Standby
  
  // initialize Stepper lib
  Stepper myStepper(kStepsPerRev, 16, 14, 12, 13); // GPIO Pins for Stepper Motor Driver

  // intial postion of flag; 0 is down, 1 is half, 2 is full
  int nPos = 0;
  
  // Raise
  void fRaise()
  {
    for (int l_i = 0; l_i < 5; l_i++)
    {
    Serial.println ("Clockwise"); // for debugging
    myStepper.step(kStepsPerRev); // one rotation clockwise
    delay (50);
    }
  }

  // Lower
  void fLower ()
  {
    for (int l_i = 0; l_i < 5; l_i++)
    {
    Serial.println("Counter-Clockwise");
    myStepper.step(-kStepsPerRev);
    delay (50);
    }
  }

  // Function to be input to cause Hazzah to go into sleep

  // Function to be input to cause Hazzah to go into deep sleep

void setup() 
{
  // Set stepper speed for best ratio of speed v torque
  myStepper.setSpeed(RPM);

  // Initialize serial port for debugging  
  Serial.begin(115200);
  delay(100);
  
  // First connect WiFi
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(" ");
    Serial.println("WiFi Connected! :) ");
    Serial.print ("IP address: ");
    Serial.print (WiFi.localIP());
    Serial.println ("");    
  }
   // Prep Onboard LED, blink if not connected otherwise stay on if connected
   // will swap this for power saving once debugging complete
  pinMode (LED, OUTPUT);
    if (WiFi.status() == WL_CONNECTED)
    { 
      digitalWrite (LED, HIGH);
    }
    else 
    {
      digitalWrite (LED, LOW);
      delay (10);
      digitalWrite (LED, HIGH);
      delay (10);
      digitalWrite (LED, LOW);
      Serial.println ("WiFi is not connected");
     } 
}

void loop() 
{
  WiFiClient client;
  if (client.connected())
  {
    if (client.available())
    {
    // read incoming bytes
    char inChar = client.read();
    // add incoming byte(s) to EOL;
    sCurrentLine += inChar;
    // If you get a new line, clear the line
    if (inChar == '\n')
    {
        sCurrentLine = "";
    }
      //if current line ends with text, it is followed by the tweet
    if (sCurrentLine.endsWith ("[text] => "))
      {
        // tweet is beginning; clear tweet string
        bReadingTweet = true;
        sTweet = "";
      }
      //if currently reading bytes of tweet, add them to tweet string
    if (bReadingTweet)
      {
        if (inChar != '[')
        {
          sTweet += inChar;
        }
        else
        {
          //if you got a [ character, you've reached the end of the tweet
          bReadingTweet = false;
          Serial.println ("Your Status: ");
          sTweet.trim();
          Serial.print (sTweet);
        }
      }
    }
  }
  else if (millis() -nLastAttemptTime > nRequestInterval)
    {
      // if you're not connected and two minutes have passed, attempt to connect
      connectToServer();
    }
  // Lower flag from full staff to half staff
  if ( sTweet == "Half" && nPos == 2)
  {
    fLower();
    delay (500);
    nPos = 1;
  }
  // Raise flag from bottom of pole to half staff
  if (sTweet == "Half" && nPos == 0)
  {
    fRaise();
    delay (500);
    nPos = 1;
  }
  // Raise flag from half staff
  if (sTweet == "Full" && nPos == 1)
  {
    fRaise();
    delay (500);
    nPos = 2;
  }
  // Raise flag from bottom of pole
  if (sTweet == "Full" && nPos == 0)
  {
    fRaise();
    delay (500);
    fRaise();
    delay (500);
    nPos = 2;
  }

// end of void loop closing bracket       
}

So I made it back home to give everything a test, sketch uploads and seems to be working but I’m getting hung up at the same place; reading Twitter. I’m using the Serial Monitor to debug and it just cycles between “Connecting to server…” and “Connected”; as if it is in an infinite loop.

Now I could really use a helping mind to figure out what I’m missing. Below is the most recent code.

#include <Stepper.h>
#include <ESP8266WiFi.h>

/*    Christopher Cronan
Rev 0 December 4, 2015; original coding
Rev 1 December 27, 2015; changes made for twitter account read
Rev 2 January 1, 2016; Inital code cleanup
Rev 3 January 8, 2016;  Final code cleanup, LED blinks added for notifications,
                        blink delay increased to avoid "flashing" to quick to see
 
      BLUE LED will mean a positive event happened, number of blink dictates the event; see below:
                    5 blinks indicates WiFi connection established
      RED LED will mean a negative event happened, number of blinks dictates the event; see below:
                    10 blinks indicates server connection failed
                    20 blinks indicated WiFi is not connected, this should cycle with a short delay until
                        WiFi is properly connected, at which time 5 blue blinks occur
                    */
  // On board LED setup
  int LEDblue = 2 ;                             // GPIO 0 (onboard LED for good status)
  int LEDred = 0 ;                              // GPIO 2 (onboard LED for Errors
  // Blink Functions, BlinkRed for Errors; BlinkBlue for go status(s)
  void blinkRed()
  {
    pinMode (LEDred, HIGH);
    delay (250);
    pinMode (LEDred, LOW);
    delay (250);
  }

  void blinkBlue ()
  {
    pinMode (LEDblue, HIGH);
    delay (250);
    pinMode (LEDblue, LOW);
    delay (250);
  }
  
  // Network Connection Settings
  const char* ssid = "Work Sucks, Quit Now";
  const char* password = "myPassword";

  // Twitter Connection Tokens & Setup
  const String kAccessToken = "myToken";
  const String kAccessTokenSecret = "mySecretToken";
  const String kAPI_Key = "myAPI";
  const String kAPI_Secret = "mySecretAPI";
  String sTweet="";
  char server[] = "apkit.esy.es";            // name for apkit server using DNS
  const unsigned long nRequestInterval = 100000;
  boolean bRequested;                        // Has a request been made since connecting
  unsigned long nLastAttemptTime = 0;        // last time server connection established
  String sCurrentLine = "";                  // String to store text from server
  String sLastTweet = "";                   
  boolean bReadingTweet = false;
  String sUsername = "Tipsez";
  String sCountStatus = "1";
  WiFiClient client;
  
  void connectToServer()
  {
    // Attempt to connect, and wait a moment
    Serial.println("Connecting to server...");
    delay (1000);
    if (client.connect(server, 80))
    {
      Serial.println("Connected to server");
      // Make HTTP Request
      client.println ("GET /twitter-oauth/status.php?username=" + sUsername + "&access_token=" + kAccessToken + "&access_token_secret=" + kAccessTokenSecret + "&count=" + sCountStatus + " HTTP/1.1");
      client.println("Host: apkit.esy.es");
      Serial.println ("Connected to Server");
      delay (500);
      client.println("Connection: close");
      client.println();
    }
    else 
    {
      //kill function if no response from server
      Serial.println ("Connection to server failed");
      for (int l_i = 0; l_i < 10; l_i++)
      {
        blinkRed();
      }
    }
    nLastAttemptTime = millis();
  }
  
  //*28BYJ-48 Stepper Motor Data and setup
  const int kStepsPerRev = 200;     // Max steps for one revolution
  const int RPM = 30;              // Max RPM permitted  
  // GPIO Pins on Huzzah for Stepper Motor Drive output to Motor Driver Board
  int STBY = 5;                             // GPIO 5 is Driver Standby  
  // initialize Stepper lib
  Stepper myStepper(kStepsPerRev, 16, 14, 12, 13); // GPIO Pins for Stepper Motor Driver

  // intial postion of flag; 0 is down, 1 is half, 2 is full
  int nPos = 0;
  
  // Raise
  void fRaise()
  {
    for (int l_i = 0; l_i < 5; l_i++)
    {
    Serial.println ("Clockwise"); // for debugging
    myStepper.step(kStepsPerRev); // one rotation clockwise
    delay (50);
    }
  }

  // Lower
  void fLower ()
  {
    for (int l_i = 0; l_i < 5; l_i++)
    {
    Serial.println("Counter-Clockwise");
    myStepper.step(-kStepsPerRev);
    delay (50);
    }
  }
  // Function to be input to cause Hazzah to go into sleep

  // Function to be input to cause Hazzah to go into deep sleep

void setup() 
{
  // Set stepper speed for best ratio of speed v torque
  myStepper.setSpeed(RPM);

  // Initialize serial port for debugging  
  Serial.begin(115200);
  delay(100);
  
  // First connect WiFi
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(" ");
    Serial.println("WiFi Connected! :) ");
    Serial.print ("IP address: ");
    Serial.print (WiFi.localIP());
    Serial.println ("");    
  }
   // Prep Onboard LED, blink if not connected otherwise stay on if connected
   // will swap this for power saving once debugging complete
    if (WiFi.status() == WL_CONNECTED)
    { 
      for (int l_i = 0; l_i < 5; l_i++)
      {
        blinkBlue();
      }
    }
    else 
    {
      for (int l_i = 0; l_i < 20; l_i++)
      {
        blinkRed();
      }
      Serial.println ("WiFi is not connected");
     } 
}

void loop() 
{
  connectToServer();
  WiFiClient client;
  if (client.connected())
  {
    if (client.available())
    {
    // read incoming bytes
    char inChar = client.read();
    // add incoming byte(s) to EOL;
    sCurrentLine += inChar;
    // If you get a new line, clear the line
    if (inChar == '\n')
    {
        sCurrentLine = "";
    }
      //if current line ends with text, it is followed by the tweet
    if (sCurrentLine.endsWith ("[text] => "))
      {
        // tweet is beginning; clear tweet string
        bReadingTweet = true;
        sTweet = "";
      }
      //if currently reading bytes of tweet, add them to tweet string
    if (bReadingTweet)
      {
        if (inChar != '[')
        {
          sTweet += inChar;
        }
        else
        {
          //if you got a [ character, you've reached the end of the tweet
          bReadingTweet = false;
          Serial.println ("Your Status: ");
          sTweet.trim();
          Serial.print (sTweet);
        }
      }
    }
  }
  else if (millis() -nLastAttemptTime > nRequestInterval)
    {
      // if you're not connected and two minutes have passed, attempt to connect
      connectToServer();
    }
    
  // Lower flag from full staff to half staff
  if ( sTweet == "Half" && nPos != 0 && nPos != 1)
  {
    fLower();
    delay (500);
    nPos = 1;
  }
  // Raise flag from bottom of pole to half staff
  if (sTweet == "Half" && nPos != 2 && nPos != 1)
  {
    fRaise();
    delay (500);
    nPos = 1;
  }
  // Raise flag from half staff to full staff
  if (sTweet == "Full" && nPos != 0 && nPos != 2)
  {
    fRaise();
    delay (500);
    nPos = 2;
  }
  // Raise flag from bottom of pole to full staff
  if (sTweet == "Full" && nPos != 0 && nPos != 1)
  {
    fRaise();
    delay (500);
    fRaise();
    delay (500);
    nPos = 2;
  }

// end of void loop closing bracket       
}

What is the purpose of connecting to the server, if you don't give a rats ass what the server has to say?

Why do you print "Connected to server" twice? Exactly what serial output do you get? What else do you expect to see?

All valid questions. To the point if not caring of the server response, that is my ignorance to hyper text. Here’s a generic program flow/idea. On my phone travelling so sorry it is breif, but thank you for the reply.

  1. Connect to Web over wifi
  2. Connect to twitter
  3. Pull my recent tweets from my twitter account, only tweets I have wrote
  4. If tweet is “half” move stepper accordingly, if full, move stepper accordingly.

The second serial print was for debugging to figure out where I was getting hung up, eventually I will remove all serial stuff to lighten up the program.

I’m expecting to see the stepper turn when I tweet either “full” or " half".

I should be back to my computer in about 4 hours.

I'm expecting to see the stepper turn when I tweet either "full" or " half".

What actually happens?

Every example I've seen reads the server response. There IS a reason for that. The response just might include a clue-by-four.

My apologies for the slow response time, finally back to my computer. Here is what my Serial Monitor Shows:

WiFi Connected! :) IP address: 0.0.0.0 WiFi Connected! :) IP address: 192.168.0.20 Connecting to server... Connected to server Connected to Server Connecting to server... Connected to server Connected to Server Connecting to server...

etc...

So the program just cycles between these two spots. Eventually, it will lose connection (I assume from too many incorrect login attempts) and will flash the red LED 10 times indicating that the server connection failed. I'm spending this evening/tonight, studying the Twitter Developer pages a bit more to make sure I didn't miss something goofy.

Would it have anything to do with trying to append so many things to the "GET" string all on one line? Should these be broken up into several "client.print" commands, rather than one long "client.println"?

PaulS: Every example I've seen reads the server response. There IS a reason for that. The response just might include a clue-by-four.

I'll go back and look for an example that reads the server response and add that into my code to see what I'm getting back.

So I found an example to obtain the response from the Twitter server (after some slight modification) here.

I learned that I am getting response error code "400", which according to Twitter (and I imagine general http) means that "The request was invalid or cannot be otherwise served. An accompanying error message will explain further. In API v1.1, requests without authentication are considered invalid and will yield this response."

So now I know that my request is bad, using the same string that I have in my original code, with a server of https://api.twitter.com/1.1/ -- followed by my "GET" string. I'm going to work on adding the error code into my original program to facilitate debugging; I'm just not sure if I'm submitting the OAuth incorrectly, if my "GET" command is not formatted correctly.

In doing some more research, it appears that Twitter required HTTP*S* for OAUTH, which I cannot do with my ESP8266 breakout... There is some interesting "WiFiClientSecure.h" stuff on Github, but I cannot seem to find any documentation to show me how to use this header, or create a secure connection. I know that memory and certificate authorization is critical with this, but again... I cannot seem to find any documentation on Github.

Here are some links to what I've found on Github:

Top Level ESP8266 Github

This link for the "WiFi Secure"

And here is a page with a ton of information about the Secure Server efforts on going; towards the bottom is where the discussion leads to the header being realized and (from what I can gather, but not understand or implement) no longer needing a PHP server.

https://github.com/esp8266/Arduino/issues/43

Well… I’ve made “progress”. I was able to locate a program to connect to a secure connection using this new WiFiSecure header file. The program below will function as it should.

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid = "Work Sucks, Quit Now";
const char* password = "xxxxx";

const char* host = "api.github.com";
const int httpsPort = 443;

// Use web browser to view and copy
// SHA1 fingerprint of the certificate
const char* fingerprint = "CF 05 98 89 CA FF 8E D8 5E 5C E0 C2 E4 F7 E6 C3 C7 50 DD 5C";

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  delay(10000);
  Serial.println(WiFi.localIP());

  // Use WiFiClientSecure class to create TLS connection
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }

  if (client.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }

  String url = "/repos/esp8266/Arduino/commits/master/status";
  Serial.print("requesting URL: ");
  Serial.println(url);

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");

  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

void loop() {
}

So next I do a targeted Google search for the Twitter 1.1 API certificate fingerprint and found one HERE. Problem is, this certificate is expired, though I would have thought that by using the “Fingerprint” it would renew automatically. Again, my ignorance to these things. Working on getting a Twitter certificate now, while trying to connect to Twitter I still get an Error 400.