Go Down

Topic: App using DHCP/DNS failing with Neocat Twitter Library (Read 994 times) previous topic - next topic

drewc

Hi...I'm working on a Twitter app that tweets sensor data via an OAuth token (using Neocat's great Twitter proxy site). Running Arduino 1.1, the code uses DHCP/DNS and things seem to be working (i.e., DHCP OK, IP assigned, etc.), except that I am receiving an error status from Twitter: "status 0". Complete code is too long to post here, but here's an excerpt from the Twitter post section:

Code: [Select]

Serial.println("connect...");
  if (ipState == DhcpStateLeased || ipState == DhcpStateRenewing) {
    if (twitter.post(message)) { // attempt to tweet the message
      int status = twitter.wait(); // receive the status
      digitalWrite(COMMLED,LOW); // turn off the communications LED
      delay(100);
      if (status == 200) {
        Serial.println("tweet ok");
      }
      else {
        Serial.print("tweet fail: code ");
        Serial.println(status); // if tweet fails, print the error code
        blinkLED(COMMLED,2,100); // ...and blink the communications LED twice
      }
      counter++; // iterate the message counter
      setCounter(counter); // store the message counter in EEPROM memory
    }
    else {
      Serial.println("connect fail"); // if connection fails entirely,
      blinkLED(COMMLED,4,100); // ...blink the communications LED 4 times
    }
  }
  else {
    Serial.println("DHCP fail"); // if connection fails entirely,
    blinkLED(COMMLED,6,100); // ...blink the communications LED 4 times
  }
  free(message); // free the allocated string memory
  free(str2);
  free(str4);
}


Here is the relevant debug stream:
...
mac: 31:13:31:13:31:13
token: 601291366-SUUChc6mPLhQR5Eamzfv1zVs3ywtkpzcPtI3b8w8
DHCP req
DHCP OK!
ip: 192.168.1.78
gw: 192.168.1.254
dns: 192.168.1.254
connect...
tweet fail: code 0
...
So, instead of Twitter status "200" indicating a successful tweet, I am getting status "0". This error return from NeoCat's proxy site is not particularly helpful in debugging...thanks in advance for any ideas as to what I am doing wrong...

pylon

Have you tried with

Code: [Select]
int status = twitter.wait(Serial);

This way the received characters are put through to the serial interface for debugging. Might get a hint what's going on. If that doesn't work, post full code plus the output of the serial line.


drewc

Hi...good suggestion which I just tried...here's the code fragment and then the serial result:
Code: [Select]

...
Serial.println("connect...");
  if (ipState == DhcpStateLeased || ipState == DhcpStateRenewing) {
    if (twitter.post(message)) { // attempt to tweet the message
      [b] int status = twitter.wait(&Serial); // receive the status/stream to serial[/b]
      digitalWrite(COMMLED,LOW); // turn off the communications LED
      delay(100);
      if (status == 200) {
        Serial.println("tweet ok");
      }
      else {
        Serial.print("tweet fail: code ");
        Serial.println(status); // if tweet fails, print the error code
        blinkLED(COMMLED,2,100); // ...and blink the communications LED twice
      }
      counter++; // iterate the message counter
      setCounter(counter); // store the message counter in EEPROM memory
    }
    else {
      Serial.println("connect fail"); // if connection fails entirely,
      blinkLED(COMMLED,4,100); // ...blink the communications LED 4 times
    }
  }
  else {
    Serial.println("DHCP fail"); // if connection fails entirely,
    blinkLED(COMMLED,6,100); // ...blink the communications LED 4 times
  }
  free(message); // free the allocated string memory
  free(str2);
  free(str4);
}
...


...and the corresponding serial output:

mac: 31:13:31:13:31:13
token: 601291366-SUUChc6mPLhQR5Eamzfv1zVs3ywtkpzcPtI3b8w8
DHCP req
DHCP OK!
ip: 192.168.1.78
gw: 192.168.1.254
dns: 192.168.1.254
connect...
tweet fail: code 0

As you can see no difference from previous post. I had assumed that I would get some debug output...I suspect now that something is amis in the DHCP lease as the same basic code does work if I hardcode the IP...hmmmm...there is a piece of code in Twitter.CPP that checks for valid HOSTNAME...I suppose if it can't resolve the Twitter API page all will fail:

Code: [Select]

...
bool Twitter::post(const char *msg)
{
#if defined(ARDUINO) && ARDUINO < 100
DNSError err = EthernetDNS.resolveHostName(LIB_DOMAIN, server);
if (err != DNSSuccess) {
return false;
}
#endif
parseStatus = 0;
statusCode = 0;
...

pylon

This code is not active in the current IDE (ARDUINO < 100 is false).

As you don't get debugging output, your connection gets closed immediately after sending the post. Are you able to sniff your network with something like WireShark? I'd try to do that as you see immediately what's going on and why it may fail.

To comment on the other thoughts you have to post your full code, as the initialisation is not shown in your snippet as well as variable definitions. It may also be good to show the code that works OK, as the forum might get ideas from looking at the difference.

drewc

Again, some good ideas here. I am attaching both code fragments - the one that works and the one that fails.
Here's what works (the vanilla Twitter Library sample code):
Code: [Select]

    // Ethernet Shield Settings
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// If you don't specify the IP address, DHCP is used(only in Arduino 1.0 or later).
byte ip[] = { 192, 168, 2, 250 };

// Your Token to Tweet (get it from http://arduino-tweet.appspot.com/)
Twitter twitter("YOUR-TOKEN-HERE");

// Message to post
char msg[] = "Hello, World! I'm Arduino!";

void setup()
{
  delay(1000);
  Ethernet.begin(mac, ip);
  // or you can use DHCP for automatic IP address configuration.
  // Ethernet.begin(mac);
  Serial.begin(9600);
 
  Serial.println("connecting ...");
  if (twitter.post(msg)) {
    // Specify &Serial to output received response to Serial.
    // If no output is required, you can just omit the argument, e.g.
    // int status = twitter.wait();
    int status = twitter.wait(&Serial);
    if (status == 200) {
      Serial.println("OK.");
    } else {
      Serial.print("failed : code ");
      Serial.println(status);
    }
  } else {
    Serial.println("connection failed.");
  }
}

void loop()
{
}


..and here's what isn't working:

Code: [Select]

// initialize Twitter object
Twitter twitter(TOKEN);

DhcpState ipState = DhcpStateNone; // a variable to store the DHCP state

void setup()  {
  serial = getSerial(); // create or obtain a serial number from EEPROM memory
  counter = getCounter(); // create or obtain a tweet count from EEPROM memory
  // Ethernet Shield Settings
  byte mac[] = { 0x31, 0x13, 0x31, 0x13, 0x31, 0x13 }; // use same MAC as Twitter examples
...
Serial.begin(9600);   // set the data rate for the hardware serial port
...
// start Ethernet
   EthernetDHCP.begin(mac, true); // start ethernet DHCP in non-blocking polling mode
...
dhcpCheck(); // check and update DHCP connection
  if (millis() % 60000 == 0 && ipState != DhcpStateLeased && ipState != DhcpStateRenewing) {
    blinkLED(COMMLED,1,30); // quick blnk of COMM led if there's no ip address
...
posttweet(URGENT_WATER);   // Post it to Twitter
...
}
// post a tweet
void posttweet(char* msg) {
  digitalWrite(COMMLED,HIGH); // light the Communications LED
  // assemble a string for Twitter, appending a unique ID to prevent Twitter's repeat detection
  char *str1 = " [";
  char *str2;
  str2= (char*) calloc (5,sizeof(char)); // allocate memory to string 2
  itoa(serial,str2,16); // turn serial number into a string
  char *str3 = "-";
  char *str4;
  str4= (char*) calloc (5,sizeof(char)); // allocate memory to string 4
  itoa(counter%10000,str4,10); // turn message counter into a string
  char *str5 = "]";
  char *message;
  // allocate memory for the message
  message = (char*) calloc(strlen(msg) + strlen(str1) + strlen(str2) + strlen(str3) + strlen(str4) + strlen(str5) + 1, sizeof(char));
  strcat(message, msg); // assemble (concatenate) the strings into a message
  strcat(message, str1);
  strcat(message, str2);   
  strcat(message, str3);
  strcat(message, str4);
  strcat(message, str5);
  Serial.println("connect...");
  if (ipState == DhcpStateLeased || ipState == DhcpStateRenewing) {
    Serial.println("Lease OK");
    if (twitter.post(message)) { // attempt to tweet the message
      int status = twitter.wait(&Serial); // receive the status + DEBUG
      digitalWrite(COMMLED,LOW); // turn off the communications LED
      delay(100);
      if (status == 200) {
        Serial.println("tweet ok");
      }
      else {
        Serial.print("tweet fail: code ");
        Serial.println(status); // if tweet fails, print the error code
        blinkLED(COMMLED,2,100); // ...and blink the communications LED twice
      }
      counter++; // iterate the message counter
      setCounter(counter); // store the message counter in EEPROM memory
    }
    else {
      Serial.println("connect fail"); // if connection fails entirely,
      blinkLED(COMMLED,4,100); // ...blink the communications LED 4 times
    }
  }
  else {
    Serial.println("DHCP fail"); // if connection fails entirely,
    blinkLED(COMMLED,6,100); // ...blink the communications LED 4 times
  }
  free(message); // free the allocated string memory
  free(str2);
  free(str4);
}


Again, as in the first example, I am seeing valid IP, gateway and DNS addresses and can ping the Ethernet shield. But somehow I am not getting through to the Twitter API via the Neocat proxy site (http://arduino-tweet.appspot.com/). One thought I just had was that maybe the "EthernetDHCP.begin()" statement has been deprecated in favor of simply "Ethernet.begin() as DHCP capability has been folded into IDE 1.0.

As before, any help is appreciated!

pylon

You told you're using version 1.0.1 of the IDE, so you should use

Code: [Select]
Ethernet.begin(mac);

to get your IP via DHCP (version 1.0 had a bug that freezed your program when using DHCP).

I see you're not using the same MAC address in the two code samples. I'd use the MAC address of the working code in the second version two (if for some reason a MAC address is used twice in a network you won't get that interface to work correctly).

Your code is not complete, you don't have #include statements. This will never compile. Are you sure you don't hide something relevant from us?

drewc

Thanks so much for your suggestions...my code is now working! Very briefly, I substituted "Ethernet.begin()" and "Ethernet.maintain()" statements for the problematic "EthernetDHCP()" counterparts as follows (these are fragments..I have elided non-essential parts):
Code: [Select]

// EthernetDHCP.begin(mac, true); // start ethernet DHCP in non-blocking polling mode
     Ethernet.begin(mac); // start ethernet and obtain IP lease
...
  int ipState1 = Ethernet.maintain(); //  Check and maintain Ethernet lease
  if (millis() % 60000 == 0 && ipState1 != 2 && ipState1 != 4) {
// dhcpCheck(); // check and update DHCP connection
// if (millis() % 60000 == 0 && ipState != DhcpStateLeased && ipState != DhcpStateRenewing) {
    Serial.println("no IP");
   blinkLED(COMMLED,1,30); // quick blnk of COMM led if there's no ip address
...
Serial.println("connect...");
// if (ipState == DhcpStateLeased || ipState == DhcpStateRenewing) {
//   Serial.println("Lease OK");
    if (twitter.post(message)) { // attempt to tweet the message
      int status = twitter.wait(&Serial); // receive the status + DEBUG
      digitalWrite(COMMLED,LOW); // turn off the communications LED
      delay(100);
      if (status == 200) {
        Serial.println("tweet ok");
      }
      else {
        Serial.print("tweet fail: code ");
        Serial.println(status); // if tweet fails, print the error code
        blinkLED(COMMLED,2,100); // ...and blink the communications LED twice
      }
      counter++; // iterate the message counter
      setCounter(counter); // store the message counter in EEPROM memory
    }
    else {
      Serial.println("connect fail"); // if connection fails entirely,
      blinkLED(COMMLED,4,100); // ...blink the communications LED 4 times
    }
// }
//  else {
//   Serial.println("DHCP fail"); // if connection fails entirely,
//   blinkLED(COMMLED,6,100); // ...blink the communications LED 4 times
//  }
  free(message); // free the allocated string memory
  free(str2);
  free(str4);
}


Yes, it's not elegant and needs additional editing but it works well enough for me to continue the project. I still don't know why the original code is not working, but I'll have to leave that to the experts. Thanks again for your help.

Go Up