0

I'm trying to post data from my Arduino due to my web server (php) through POST using the CC3000 breakout board and for some reason, data with content length more than 104 is not being sent?

I've tried posting directly with postmaster with content length value greater than 104 to verify that it wasn't my server denying the request and it wasn't. So something with the Arduino or cc3000?

Right now, my post request looks like this which works:

POST /new/path/post.php HTTP/1.1
Host:  www.mywebsite.com
User-Agent: Arduino/1.0
Connection: close
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 104

userId=32&sscId=43&raVal=52&cvVal=62&bbvfgTempInF=723&bbvfgLevel=82345&humidity=92&lmassLevel=0283838383

And this is the one that doesn't (notice only one character difference in userId):

POST /new/path/post.php HTTP/1.1
Host:  www.mywebsite.com
User-Agent: Arduino/1.0
Connection: close
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 105

userId=322&sscId=43&raVal=52&cvVal=62&bbvfgTempInF=723&bbvfgLevel=82345&humidity=92&lmassLevel=0283838383

And here is my full code:

#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"

// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ   28  // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT  26
#define ADAFRUIT_CC3000_CS    22
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                                         SPI_CLOCK_DIVIDER); // you can change this clock speed

#define WLAN_SSID       "mynetwork"   // cannot be longer than 32 characters!
#define WLAN_PASS       "mypassword"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2

#define IDLE_TIMEOUT_MS  10000      // Amount of time to wait (in milliseconds) with no data 
                                   // received before closing the connection.  If you know the server
                                   // you're accessing is quick to respond, you can reduce this value.




/**************************************************************************/
/*!
    @brief  Sets up the HW and the CC3000 module (called automatically
            on startup)
*/
/**************************************************************************/

uint32_t ip;

void setup(void)
{
  Serial.begin(115200);
  Serial.println(F("Hello, CC3000!\n")); 

  Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);

  /* Initialise the module */
  Serial.println(F("\nInitializing..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Couldn't begin()! Check your wiring?"));
    while(1);
  }

  // Optional SSID scan
  // listSSIDResults();

  Serial.print(F("\nAttempting to connect to ")); Serial.println(WLAN_SSID);
  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
    Serial.println(F("Failed!"));
    while(1);
  }

  Serial.println(F("Connected!"));

  /* Wait for DHCP to complete */
  Serial.println(F("Request DHCP"));
  while (!cc3000.checkDHCP())
  {
    delay(100); // ToDo: Insert a DHCP timeout!
  }  

  /* Display the IP address DNS, Gateway, etc. */  
  while (! displayConnectionDetails()) {
    delay(1000);
  }

  ip = 0;
  // Try looking up the website's IP address
  Serial.print(WEBSITE); Serial.print(F(" -> "));
  while (ip == 0) {
    if (! cc3000.getHostByName(WEBSITE, &ip)) {
      Serial.println(F("Couldn't resolve!"));
    }
    delay(500);
  }

  cc3000.printIPdotsRev(ip);

  // Optional: Do a ping test on the website
  /*
  Serial.print(F("\n\rPinging ")); cc3000.printIPdotsRev(ip); Serial.print("...");  
  replies = cc3000.ping(ip, 5);
  Serial.print(replies); Serial.println(F(" replies"));
  */  

  /* Try connecting to the website.
     Note: HTTP/1.1 protocol is used to keep the server from closing the connection before all data is read.
  */
  Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
  if (www.connected()) {
    Serial.println("Connected");    
    String PostDatat = "userId=32&sscId=43&raVal=52&cvVal=62&bbvfgTempInF=723&bbvfgLevel=82345&humidity=92&lmassLevel=0283838383";


    Serial.println();
    Serial.println("POST /new/path/post.php HTTP/1.1");
    Serial.println("Host: www.mywebsite.com");
    Serial.println("User-Agent: Arduino/1.0");
    Serial.println("Connection: close");
    Serial.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    Serial.print("Content-Length: ");
    Serial.println(PostDatat.length());
    Serial.println();
    Serial.println(PostDatat);

    //HANGS HERE IF CONTENT LENGTH > 104

    www.println("POST /new/path/post.php HTTP/1.1");
    www.println("Host: www.mywebsite.com");
    www.println("User-Agent: Arduino/1.0");
    www.println("Connection: close");
    www.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    www.print("Content-Length: ");
    www.println(PostDatat.length());
    www.println();
    www.println(PostDatat);

    Serial.println();
    Serial.println("POST /new/path/post.php HTTP/1.1");
    Serial.println("Host:  www.mywebsite.com");
    Serial.println("User-Agent: Arduino/1.0");
    Serial.println("Connection: close");
    Serial.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    Serial.print("Content-Length: ");
    Serial.println(PostDatat.length());
    Serial.println();
    Serial.println(PostDatat);

    Serial.println("SENT");
  } else {
    Serial.println(F("Connection failed"));    
    return;
  }

  Serial.println(F("-------------------------------------"));

  /* Read data until either the connection is closed, or the idle timeout is reached. */ 
  unsigned long lastRead = millis();
  while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
    while (www.available()) {
      char c = www.read();
      Serial.print(c);
      lastRead = millis();
    }
  }
  www.close();
  Serial.println(F("-------------------------------------"));

  /* You need to make sure to clean up after yourself or the CC3000 can freak out */
  /* the next time your try to connect ... */
  Serial.println(F("\n\nDisconnecting"));
  cc3000.disconnect();

}

void loop(void)
{
 delay(1000);
}

/**************************************************************************/
/*!
    @brief  Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/

void listSSIDResults(void)
{
  uint32_t index;
  uint8_t valid, rssi, sec;
  char ssidname[33]; 

  if (!cc3000.startSSIDscan(&index)) {
    Serial.println(F("SSID scan failed!"));
    return;
  }

  Serial.print(F("Networks found: ")); Serial.println(index);
  Serial.println(F("================================================"));

  while (index) {
    index--;

    valid = cc3000.getNextSSID(&rssi, &sec, ssidname);

    Serial.print(F("SSID Name    : ")); Serial.print(ssidname);
    Serial.println();
    Serial.print(F("RSSI         : "));
    Serial.println(rssi);
    Serial.print(F("Security Mode: "));
    Serial.println(sec);
    Serial.println();
  }
  Serial.println(F("================================================"));

  cc3000.stopSSIDscan();
}

/**************************************************************************/
/*!
    @brief  Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

  if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

So after running this code and changing the PostDatat variable to the two data being 104 char long and 105 char long, the 104 char long goes all the way through and prints the message SENT to the Serial monitor. But the 105 char long just hangs after the first Serial.println() blocks and before the "www.println(..." Has anyone had or encountered this problem or can suggest a solution to this? Thanks!

0

I've gone through the library to locate the buffer where data is stored before being transmitted and I noticed that the minimum TX buffer size is set to 130 bytes. Certain control information in the form of headers are sent along with your data to the CC3000. The CC3000 interprets these headers, strips them away and re-formats your data for transmission to your gateway. So when your 105-byte content was added to these headers, the whole packet exceeded the 130-byte buffer and so caused it to overflow, overwrite a 'magic number' at its end, that is meant to detect this condition, and cause the program to stall indefinitely; this is all by design. From ccspi.h:

// The magic number that resides at the end of the TX/RX buffer (1 byte after the allocated size)
// for the purpose of detection of the overrun. The location of the memory where the magic number
// resides shall never be written. In case it is written - the overrun occured and either recevie function
// or send function will stuck forever.
#define CC3000_BUFFER_MAGIC_NUMBER (0xDE)

The value of the number itself doesnt matter; its presence/absence is what is important. So this is where your code most likely got stuck:

/* The magic number that resides at the end of the TX/RX buffer (1 byte after the allocated size)
   * for the purpose of overrun detection. If the magic number is overwritten - buffer overrun
   * occurred - and we will be stuck here forever! */
  if (wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER)
  {
    DEBUGPRINT_F("\tCC3000: Error - No magic number found in SpiWrite\n\r");
    while (1);  //Infinite loop; end of the road
  }

And the reason you didnt get this error message is because debug mode is disabled by default in the library. You can enable it by changing #define DEBUG_MODE (0) to #define DEBUG_MODE (1) in debug.h in the utility folder in the library. Note that this buffer limitation applies to incoming data, as well as outgoing data.

Now you can increase the buffer size by changing:

#define CC3000_MINIMAL_TX_SIZE      (130 + 1)

to:

#define CC3000_MINIMAL_TX_SIZE      (150 + 1) // buffer size is now 150 bytes

in cc3000_common.h in the utility folder in your library folder, save the file and try again.

Even if this works though, I suggest you choose a fixed chunk size for sending data, one that is reasonably below 130 bytes. What happens next time when you want to send a 200-byte string? You increase the buffer again and you deprive the rest of your sketch of valuable SRAM. What you do is pass your POST content to some function that splits the string into chunks and sends them one at a time. Assuming you send only ASCII strings:

#define CHUNK_SZ  80  // your chunk size

void send_all(Adafruit_CC3000_Client * conn, const uint8_t * data, uint16_t len){
  while (len >= CHUNK_SZ){   // while the length is greater than your chunk size
    conn->write(data, CHUNK_SZ);  // write a chunk to the socket
    data += CHUNK_SZ;  // advance the pointer to the start of the next chunk
    len -= CHUNK_SZ;  // get how many bytes are left 
  }
  if (len)  // send whatever remains if anything remains
    conn->write(data, len);
}

void loop(){
  ...

  Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
  const uint8_t[] PostDatat = "userId=32&sscId=43&raVal=52\r\n";
  send_all(&www, PostDatat, sizeof(PostDatat) - 1); // -1 is to ignore the null byte

  ...
}

The function takes a pointer to your CC3000 socket object, a pointer to your data and the size of the data (array or not) in bytes. It should work for C strings. Note that I explicitly added \r\n at the end of the POST content, since println() isnt used here. I used the uint8_t type so that you can also use the function to send an arrays of ints or other numeric types, as long as you cast the array to uint8_t * and specify the length of the array in bytes with sizeof().

Finally, try to make your chunk size as large as you can without getting too close to the limit. A large chunk size means fewer chunks, which means less overhead (remember those pesky headers) and time taken to transmit your whole request.

  • Changed it and checked, but still the same issue :( – dmantor Apr 12 '16 at 18:59
  • @dmantor try replacing all the www.println() and Serial.println() with www.println(F()) and Serial.println(F()). Use this only for string literals. – TisteAndii Apr 12 '16 at 21:10
  • Also have you tried other lengths like 120, for instance? Why are you printing the POST to Serial twice? – TisteAndii Apr 12 '16 at 21:15
  • I tried the replace to F() and still nothing. What does that do anyways? Also, I did try 120 and any length over 104 doesn't work :/ . And I have POST twice cause I wanted to see how the post looks before its sent even if it gets stuck at the actual POST. That was more for testing and seeing where my code was failing before. – dmantor Apr 13 '16 at 4:21
  • @dmantor I've edited my answer – TisteAndii Apr 14 '16 at 2:53
0

Does it print out the part "Free Ram: " If not, then I doubt its a memory problem. Not too familiar with the CC3000, but possibly related to a UART Break Condition.

  • Was thinking the same thing before, but it did show. Regardless, the problem has been solved by @TisteAndii 's solution :D – dmantor Apr 14 '16 at 18:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.