Is this normal wi-fi / HTTP behavior (Adafruit cc3000)

I have an Adafruit cc3000 wi-fi shield that hangs frequently. The attached graph shows how many times "loop()" executed before it got hung up one day. Sometimes it makes 50 or so loops, sometimes 10, sometimes several hundred...(ignore the "weight" and "lb" - it's really counting loops). But it always eventually gets stuck, often at the code immediately following 4a and 8. Less often, the connection fails (at the if(client.connected) test).

Increasing the watchdog timer to 4 minutes (from the current 1 minute) doesn't help.

Is this normal behavior for a wi-fi connection? Can I tweak something to get more reliable behavior?

// Libraries
#include <Adafruit_CC3000.h>
#include <SPI.h>

#include <LiquidTWI2.h> // for LCD only
#include <Wire.h> // LCD only

#include <avr/wdt.h> // for watchdog timer

// Define CC3000 chip pins
#define ADAFRUIT_CC3000_IRQ   3
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10

// Create CC3000 instance
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                                         SPI_CLOCK_DIV2); // you can change this clock speed
                                        
// WLAN parameters
#define WLAN_SSID "blahblahblah"
#define WLAN_PASS "more blah"

// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2

// Xively parameters
#define WEBSITE  "api.xively.com"
#define API_key  "yet more blah"
#define feedID  "and even more"


uint32_t ip = 0xD834E978;  //xively ip in hex
int i=0;
int iMstrt; // for displaying available memory
unsigned long ulTstrt;      // for timing loop
volatile int counter;   // for watchdog timer
volatile int countmax = 8; // Timer expires after about 1 min
                            // if 8 sec interval is selected below
                            // 8 x 8 = 64 sec

String data;

// Connect via i2c, default address #0 (A0-A2 not jumpered)
LiquidTWI2 lcd(0);


void setup(void)
{

  data.reserve(100);  // data.length() = 90 (max, if three-digit weight)
  
  // Initialize
  Serial.begin(115200);
  
  Serial.println(F("\nInit..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Fail begin()-Chk wires"));
    while(1);
  }
 
  // set the LCD type
  lcd.setMCPType(LTI_TYPE_MCP23008); // used with small I2C backpack
  // set up the LCD's number of rows and columns:
  lcd.begin(8, 2);
  
  iMstrt = freeRam(); // memory at start  
}

void loop(void)
{
  
  //***************************************************************
  /* print testing info */
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(i+=1); //number of loop
  Serial.println();
  Serial.println(i);
  
  //***************************************************************
  /* Get data, prepare JSON for Xively */
  //int weight = analogRead(A0);
  int weight = i; // print number of loops for testing purposes
  int length = 0;
  data = "";
  data = data + "\n" + "{\"version\":\"1.0.0\",\"datastreams\" : [ {\"id\" : \"SouthPostWeight\",\"current_value\" : \"" + String(weight) + "\"}]}";
  length = data.length();
  
  lcd.setCursor(0, 1);
  lcd.print(weight);
  lcd.print(" ");
  lcd.print(length);
  
  lcd.setCursor(4,0);
  lcd.print("send");
  Serial.print(F("send... data length "));
  Serial.println(length);

  watchdogEnable();
  ulTstrt=millis();
  
  //*************************************************************** 
  /* Connect to WiFi Access Point...AP */
  // Hasn't hung on this in tests, but looks like it could hang.
  // Only returns false if cc3000 reports it is not initialized.
  // Takes approx 13 seconds...so can't use normal watchdog timer
  Serial.print("*1* ");
  cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY);
  
  //***************************************************************
  /* Wait for DHCP to complete */
  // takes approx 100 ms
  // 50% of hangs are here with watchdog at 8 seconds
  // Adding the global 64 sec watchdog eliminated most hangs here
  Serial.print("*2* ");
  while (!cc3000.checkDHCP())
  {
    delay(200);
  }  
  

  //***************************************************************  
  /* connect to XIVELY */
  // Normally takes approx 75 ms
  // the following code and the code after *4a* and *8* hang most often.
  Serial.print("*3* ");
  Adafruit_CC3000_Client client = cc3000.connectTCP(ip, 80);  
 
  //*************************************************************** 
  /* Send data to XIVELY  */
  // Normally takes about 9 seconds, total
  if (client.connected()) {
    Serial.print("*4a* "); // often hangs after printing *4a*
    client.println("PUT /v2/feeds/" + String(feedID) + ".json HTTP/1.0"); // approx 1.4 s
    Serial.print("*4b* ");
    client.println("Host: api.xively.com");                               // approx 0.9 sec max
    Serial.print("*5* ");
    client.println("X-ApiKey: " + String(API_key));                       // approx 2.3 sec max
    Serial.print("*6* ");
    client.println("Content-Length: " + String(length));                  // approx 0.8 sec max
    client.print("Connection: close");                                    // approx 0.7 sec max
    client.println();                                                     // approx 0.1 sec max
    Serial.print("*7* ");  
    client.print(data);  //occasional hangs here                             approx 3.8 sec max
    client.println();                                                     // approx 0.1 sec max
  } else {
    lcd.setCursor(4,0); 
    lcd.print("fail"); 
    Serial.println(F("Conn faild"));    
    delay(10000);
    i=i/2; // for testing only...divide by two to show connection failure in Xively graph
    return;  // go to beginning of loop()
  }
  //***************************************************************
  /* Read response.*/
  // Average time to execute = 500 ms
  // Max time (other than hang) = 1200 ms
  // Doing this read is apparently necessary
  // (if commented out, data does not show on Xively).
  // Often hangs here
  Serial.print("*8* ");
  while (client.connected()) {
    while (client.available()) {
      char c = client.read();
      Serial.print(c);
    }
  }
  
  //***************************************************************
  /* disconnect from XIVELY 1 ms and AP 2 ms */
  // has never hung on these two
  Serial.print("*9* ");
  client.close();
  cc3000.disconnect();

  wdt_disable();
  Serial.print("done... ");
  printTime();
  
  //***************************************************************
  lcd.setCursor(4,0);
  lcd.print("    "); 
  lcd.setCursor(4,0);
  lcd.print(iMstrt-freeRam()); // this has stayed constant at 141 throughout all tests
  
  // Wait
  //delay(600000UL);  // ten minutes=600,000
  delay(20000);  // integer limit is about 32,000, unsigned int 65,535 
}

void printTime()
{
  Serial.print((millis()-ulTstrt)/1000);
  Serial.println(" s");
}


int freeRam() 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void watchdogEnable()
{
  counter=0;
  cli();                              // disable interrupts

  MCUSR = 0;                          // reset status register flags

                                      // Put timer in interrupt-only mode:                                        
  WDTCSR |= 0b00011000;               // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                      // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001;    // set WDIE (interrupt enable...7th from left, on left side of bar)
                                      // clr WDE (reset enable...4th from left)
                                      // and set delay interval (right side of bar) to 8 seconds,
                                      // using bitwise OR operator.

  sei();                              // re-enable interrupts
  

  // delay interval patterns:
  //  16 ms:     0b000000
  //  500 ms:    0b000101
  //  1 second:  0b000110
  //  2 seconds: 0b000111
  //  4 seconds: 0b100000
  //  8 seconds: 0b100001

}

ISR(WDT_vect) // watchdog timer interrupt service routine
{
  counter+=1;

  if (counter < countmax)
  {
    wdt_reset(); // start timer again (in interrupt-only mode)
  }
  else             // then change timer to reset-only mode with short (16 ms) fuse
  {
    
    MCUSR = 0;                          // reset flags

                                        // Put timer in reset-only mode:
    WDTCSR |= 0b00011000;               // Enter config mode.
    WDTCSR =  0b00001000 | 0b000000;    // clr WDIE (interrupt enable...7th from left)
                                        // set WDE (reset enable...4th from left), and set delay interval
                                        // reset system in 16 ms...
                                        // unless wdt_disable() in loop() is reached first
  }
}

Xively screen shot.jpg

No love here, so I took it to the Adafruit forum. Good replies here: http://forums.adafruit.com/viewtopic.php?f=31&t=56234&p=284592#p284592

Also, my "global" watchdog might help others with this problem. It's in the code above, and discussed a bit here: Extend watchdog timer beyond 8 seconds - Programming Questions - Arduino Forum

Dave

This is "Perfect World" code. If the connection breaks or the server stalls, the "while(client.connected())" loop becomes an infinite loop. Is this where it locks up?

  while (client.connected()) {
    while (client.available()) {
      char c = client.read();
      Serial.print(c);
    }
  }

Yes, that's one place it hangs.

But just about as often it hangs here:

client.println("PUT /v2/feeds/" + String(feedID) + ".json HTTP/1.0");

I'm not sure how the cc3000 library works. I'm not sure if the client.println() function is a blocking type function. If it is blocking and the connection fails, it will also lock there.

If the connection fails or the server stalls when the server is responding, it will certainly lock in the condition I mentioned above. The code below has a timeout controlled by the variable connectLoop.

  // connectLoop controls the hardware fail timeout
  int connectLoop = 0;

  while(client.connected())
  {
    while(client.available())
    {
      inChar = client.read();
      Serial.write(inChar);
      // set connectLoop to zero if a packet arrives
      connectLoop = 0;
    }

    connectLoop++;

    // if more than 10000 milliseconds since the last packet
    if(connectLoop > 10000)
    {
      // then close the connection from this end.
      Serial.println();
      Serial.println(F("Timeout"));
      client.stop();
    }
    // this is a delay for the connectLoop timing
    delay(1);
  }

  Serial.println();

  Serial.println(F("disconnecting."));
  // close client end
  client.stop();

This is from my client code on the playground.
http://playground.arduino.cc/Code/WebClient

Thanks for the code suggestion - I'll give it a try. I implemented a similar means of handling a failure there (see code below), but upon recovery, I just got a series of subsequent hangs at the connectTCP(), so I gave up and figured out the global watchdog. But judging from your code, I should have done a client.close() before the "return" to the top of loop(). Maybe that would have worked.

Also, I think I understand your blocking comment WRT println() and have looked at the cc3000 library, but I am not enough of a wizard to figure it out.

But blocking or not, it seems odd to me that it would preferentially hang at one particular println() (the one with the PUT string). It's the first println() in a series of six or seven of them, immediately after an "if(client.connected())". Does that have something to do with it?

 // Original code, but with timer added by me;
strtT = millis(); // declared UL above
mxWait = 24000; // declared UL above
while (client.connected()) {
  while (client.available()) {     
      char c = client.read();
      Serial.print(c);
   }
   if (millis() - strtT >= mxWait) {
     Serial.println("err");
     return; // give up and start over...
                  // but during tests, after this "return"
                 // failed continuously after *3*!
    }
 }