CC3000 - Send GET request to an app on heroku

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

CC3000 - Send GET request to an app on heroku

Post by jdsimms »

I'm struggling to get the CC3000 to connect to a heroku hosted app, and other examples posted online haven't turned on the light bulb quite yet for me.

The gist is that I collect some temperature and humidity readings, and then send those via a GET request using the CC3000. The heroku app then reads the parameters and displays them on a page.

The CC3000 hardware is A-OK, as I've tested it using the test sketches. The sketch I've put together (on github: https://github.com/jsimms/compost_monitor) works except for the last bit where it needs to connect to the server and send the HTTP request. Which makes me wonder if this is a heroku quirk, or if I'm just screwing something up right at the finish line.

First, I define everything

Code: Select all

#define WEBSITE      "myapp.herokuapp.com"
String route = "/sensor";
uint32_t ip;
int port = 80; 
Then during setup, I use getHostByName to assign the ip variable. I print it out, just to confirm it was able to snag an ip. I then close it to make sure I don't screw up anything during the loop.

Code: Select all

ip = 0;
  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);
  Serial.println("");

  // Close the connection
  Serial.println(F("Closing connection..."));
  cc3000.disconnect();
  Serial.println(F("Connection closed."));
During the loop, I create a String with the request, and then call a function that actually does the request.

Code: Select all

String request = "GET " + route + "?temp=" + temperature + "&hum=" + humidity + " HTTP/1.0\r\n";
send_request(request);
Finally, the send_request function is supposed to take care of connecting via TCP and sending it.

Code: Select all

void send_request (String request)
{
  // Connect to server 
  Serial.println("Connecting to server...");
  Adafruit_CC3000_Client www = cc3000.connectTCP(ip, port); 
 
  // Send the request 
  if (www.connected()) {
      www.println(request);
      www.println(F("User-agent: CompostMonitor/1.0\r\n"));      
      www.println(F("\r\n"));
      Serial.println("Connected & data sent successfully...");
    } 
    else {
      Serial.println(F("Connection failed."));    
    }

    while (www.connected()) {
      while (www.available()) {

      // Read answer
      char c = www.read();
      Serial.println(c); 
      }
    }
  
  // Disconnect 
  Serial.println("Closing connection...");
  Serial.println(""); 
  www.close(); 
}
However, I never get anything but connection failed.

Any insight and help would be wonderfully appreciated. :)

-Jake

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: CC3000 - Send GET request to an app on heroku

Post by adafruit_support_rick »

Clearly, your server is not accepting the connection.
So, you're certain that gethostbyname returns the right IP address?
Is 80 the right port number?

jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

Re: CC3000 - Send GET request to an app on heroku

Post by jdsimms »

Ok. Good, so it's not the code (or looks like it isn't the code).

Looking into it more, Heroku might use port 5000 by default. I'll try that right now.

jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

Re: CC3000 - Send GET request to an app on heroku

Post by jdsimms »

Ok, so I tried a few different things.

First, I ran the program as is, and changed the port from 80 to 5000. No success.

Then, I moved the getHostByName lookup to the send_request function. There was no success there either, but I did get a different error message (both using port 80 and 5000): http://cl.ly/image/3S0r0B3S353P

It looks like it grabs the IP the first time, but then can't resolve it afterward.

User avatar
tdicola
 
Posts: 1074
Joined: Thu Oct 17, 2013 9:11 pm

Re: CC3000 - Send GET request to an app on heroku

Post by tdicola »

Do you mind posting the full sketch too? It could help troubleshoot what could be going wrong finding or talking to the server.

jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

Re: CC3000 - Send GET request to an app on heroku

Post by jdsimms »

Sure thing. Thanks for helping out.

Code: Select all

// Include required libraries
#include <Adafruit_CC3000.h>
#include <Adafruit_CC3000_Server.h>
#include <ccspi.h>
#include <string.h>
#include <SPI.h>
#include <SHT1x.h>
#include "utility/debug.h"

// define CC3000 pins
const int ADAFRUIT_CC3000_IRQ  =  3;   // IRQ Must be on an interrupt pin!
const int ADAFRUIT_CC3000_VBAT =  5;   // Can be any pin
const int ADAFRUIT_CC3000_CS   =  10;  // Can be any pin
//Create the CC3000 instance (The SPI library sets remaining pins, for the uno: SCK = 13, MISO = 12, MOSI = 11)
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                                       SPI_CLOCK_DIVIDER);

// define sht10 pins
const int DATA_PIN  = 6;  // Blue wire!
const int CLOCK_PIN = 7;  // Yellow wire!
//Creates the sht10 instnace
SHT1x sht10 (DATA_PIN, CLOCK_PIN);


// Provide Your Wifi Network Information
const char* WIFI_SSID = "yourWifi"; // must be less than 32 characters
const char* WIFI_PASS = "yourPass";
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2, and I guess it is an int
const int WIFI_SECURITY =  WLAN_SEC_WPA2;

// Server settings and info (where you want to send requests)
// ToDo: I should ask Adafruit why I have to use #define to make getHostByName to work. Something to do with how pointers in C work?
#define WEBSITE      "weathervane.herokuapp.com"
String route = "/sensor";
uint32_t ip;
int port = 80; 

void setup(void)
{
  Serial.begin(115200);
  Serial.println("If you start me up...");

  // Initialise the CC3000
  Serial.println(F("\nInitialising the CC3000..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
    while(1);
  }

  // Delete old connection data
  Serial.println(F("Deleting old connection profiles..."));
  if (!cc3000.deleteProfiles()) {
    Serial.println(F("Failed!"));
    while(1);
  }

  // Connect to Wifi
  Serial.print(F("Attempting to connect to ")); Serial.print(WIFI_SSID); Serial.println(F("..."));

  if (!cc3000.connectToAP(WIFI_SSID, WIFI_PASS, WIFI_SECURITY)) {
    Serial.println(F("Failed!"));
    while(1);
  }

  Serial.print(F("Connected to ")); Serial.print(WIFI_SSID); Serial.println(F("..."));


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


  // Show the connection details
  while (! displayConnectionDetails()) {
    delay(1000);
  }


  // Look up the website's IP address
  // ToDo: Ask Adafruit how exactly this works. I couldn't figure out how an ip is returned by looking at the libraries
  ip = 0;
  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);
  Serial.println("");

  // Close the connection
  Serial.println(F("Closing connection..."));
  cc3000.disconnect();
  Serial.println(F("Connection closed."));

 }



void loop(void)
{
  // create variables, get data from the sensor
  float t_f = sht10.readTemperatureF();
  float h = sht10.readHumidity();

  // transform data to a string
  String temperature = String((int) t_f);
  String humidity = String((int) h);

  // print data to serial port
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.print(" F /");
  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.print("%");
  Serial.println("");

  // Create the request. Since we will already have IP and PORT from TCP connection, request should look like "directory/file.rb?param1=dog&param2=man"
  String request = "GET " + route + "?temp=" + temperature + "&hum=" + humidity + " HTTP/1.0\r\n";
  // Send the request
  send_request(request);

  // include at least a 3.6 second delay between pairs of temperature & humidity measurements.
  delay(4000);

}


/* ########## Other Functions ########## */
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!"));
    return false;
  }
  else
  {
    Serial.println("Connection info...");
    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("\n");
    return true;
  }
}


// This is the function that the wifi weather station used in their example
void send_request (String request)
{
  // Connect to server 
  Serial.println("Connecting to server...");
  Adafruit_CC3000_Client www = cc3000.connectTCP(ip, port); 
 
  // Send the request 
  if (www.connected()) {
      www.println(request);
      www.println(F("User-agent: CompostMonitor/1.0\r\n"));      
      www.println(F("\r\n"));
      Serial.println("Connected & data sent successfully...");
    } 
    else {
      Serial.println(F("Connection failed."));    
    }

    while (www.connected()) {
      while (www.available()) {

      // Read answer
      char c = www.read();
      Serial.println(c); 
      }
    }
  
  // Disconnect 
  Serial.println("Closing connection...");
  Serial.println(""); 
  www.close(); 
}

User avatar
tdicola
 
Posts: 1074
Joined: Thu Oct 17, 2013 9:11 pm

Re: CC3000 - Send GET request to an app on heroku

Post by tdicola »

Thanks for posting the full sketch. A couple things to note:

- For this comment "// Wait for DHCP to finish (why?)" and the loop to wait for DHCP, it's there to block execution until the CC3000 has confirmed that it's connected to the network and has an IP address. Connecting with the CC3000 actually takes some time (usually a few seconds) so the code needs to wait for the chip to signal that it's connected. If you skipped this then there could some instability as your first connection attempts would fail until the device is on your network.

- For this comment "// Look up the website's IP address
// ToDo: Ask Adafruit how exactly this works. I couldn't figure out how an ip is returned by looking at the libraries"

The IP is actually returned as a 32 bit value, made up of 4 8-bit values. When you see an address like 192.168.1.1 it breaks down into the 4 values 192, 168, 1, and 1 which can all be packed into a 32 bit long . Check out the code that prints the IP address to see how the values are interpreted. In general you probably don't need to crack open the IP--most functions just take the 32 bit value directly and figure out the components as necessary.

- In general the sketch looks pretty good and doesn't look like it's doing anything obviously wrong. The only concern I have is with the use of the String class. Internally string will use malloc and free to grab some dynamic memory for holding string values. This can be problematic in a loop like this because the Arduino only has 2k of memory (about a few hundred bytes of that are taken up by CC3000 buffers). With so little dynamic memory available it's very easy for it to get fragmented and memory allocation to fail. You'll usually see this as the processor locking up or sketch behaving erratically unfortunately. If you can you might look at switching to just C language character buffers that are static or allocated once in the setup. It's a little more painful than using the String class but can save a lot of headaches.

As far as the issue with connecting to your server, can you connect to it from your PC? For example try using a telnet client to access the address 'weathervane.herokuapp.com' on port 5000 or port 80 (whichever port your heroku app uses). I'm curious to confirm there isn't a firewall or something else in the way which is preventing access to the server. It would be great to confirm you can successfully connect to rule it out.

jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

Re: CC3000 - Send GET request to an app on heroku

Post by jdsimms »

tdicola, you are a CC3000 wizard. Thank you for looking into everything and the thoughtful explanations.

In regards to your concerns over the string class. I could change the "String route" to something else like #define ROUTE, but the "String request" would have to be dynamic. It changes depending on the reading. From what I understand, I'd have to define that as a String variable. Is that not the case?

Using telnet, I was able to connect to weathervane.herokuapp.com on port 80. Once connected I could do not do any successful GET requests. 400s across the board.

If I use http://www.hurl.it/ I get a 200 for the index, and 502 for the /sensor?temp=1&hum=2 requests. Interestingly enough, the app still posts the updates to the site when using hurl.it, despite the error. I think I need to double back and check my app. I must be doing something wrong there.

Thank you for helping me debug.

jdsimms
 
Posts: 6
Joined: Fri Nov 29, 2013 3:21 pm

Re: CC3000 - Send GET request to an app on heroku

Post by jdsimms »

Ok. So, I was able to identify by telnet'ing the web app, that there was something wrong with the HTTP requests. I made a small tweak to the app, but the real issue was that I wasn't including Host: weathervane.herokuapp.com in the header. FML! All works using telnet now, so I'm going to test out sending the request with the Arduino later tonight. Hopefully that puts an end to this HTTP 101 lesson I've been doing.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”