-->
Page 1 of 4

[SOLVED] http gets return null

PostPosted: Tue Sep 01, 2015 10:22 pm
by BryanLee
Overview: I have a Nest thermostat, and a window AC unit in a "under powered" room. I want to sync the cooling status. So that when the Nest turns on central AC, the window also goes on, and vice versa. I have an esp8266 on top of the window unit with a lip, vibration sensor (to tell if the window unit is on or not), and a IR LED (to send power on/off codes to window ac).

I'm trying to do an http get to a remote http server, which returns simply a one word response "cooling" "off" or "heating". When I test this url from my web browser, it returns accurate info, immediately, every time.

Now I'm trying to write code from my Spark Thing esp82666.

Boot
Connect to wifi
http request
read external sensor
compare sensor value to hvac state.
send IR code if needed
deep sleep with WAKE_RF_DISABLED

My problem is this. The initial time it runs after power-on, it runs perfectly. From there on out, it appears to randomly return either null string, or the proper http word response.

Below is the code, don't make fun ;-) Lots of debugging prints in there, etc...

Code: Select all#include <ESP8266WiFi.h>
#include <Phant.h>

const char* ssid     = "wifi-ssid";
const char* password = "wifi-pass";

const char* host = "sync-nest.appspot.com";

static const uint8_t powerCodeTable[] = {0,0,0,1, 0,0,0,0,
                                         1,0,1,0, 1,1,1,1,
                                         1,0,0,0, 1,0,0,0,
                                         0,1,1,1, 0,1,1,1} ;
const int irLED = 4 ;
static const int sensorReads = 5 ;
static const int sensorPause = 500 ;
static const int sensorThreshold = 350 ;
static const int wifiConnectRetries = 20 ;
static const int phantConnectRetries = 5 ;
static const int wifiClientConnectRetries = 5 ;

////////////////
// Phant Keys //
////////////////
const char PhantHost[] = "data.sparkfun.com";
const char PublicKey[] = "<public key>";
const char PrivateKey[] = "<private key>";

void pulseLED(uint16_t pulseTime) {
  uint32_t end = micros() + pulseTime ;
  uint32_t now ;
  do {
    now = micros() ;
    digitalWrite(irLED, HIGH) ;
    delayMicroseconds(13) ;
    digitalWrite(irLED, LOW) ;
    delayMicroseconds(13) ;
  } while (now < end) ;
}

void powerAC () {
  // Header
  Serial.println("Sending Power IR Code") ;
  pulseLED(9159) ;
  delayMicroseconds(4455) ;
 
  // commands
  uint8_t i ;
  for (i=0; i < 32 ; i++) {
    if (powerCodeTable[i] == 1) {
      pulseLED(639) ;
      delayMicroseconds(1615) ;
    } else {
      pulseLED(639) ;
      delayMicroseconds(486) ;
    }
  }
 
  // Footer
  pulseLED(637) ;
     
}
int postToPhant(int sensorValue)
{
  // Declare an object from the Phant library - phant
  Phant phant(PhantHost, PublicKey, PrivateKey);

  // Do a little work to get a unique-ish name. Append the
  // last two bytes of the MAC (HEX'd) to "Thing-":
  uint8_t mac[WL_MAC_ADDR_LENGTH];
  WiFi.macAddress(mac);
  String macID = String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) +
                 String(mac[WL_MAC_ADDR_LENGTH - 1], HEX);
  macID.toUpperCase();
  String postedID = "Thing-" + macID;

  // Add the four field/value pairs defined by our stream:
  phant.add("id", postedID);
  phant.add("vibration", sensorValue);

  // Now connect to data.sparkfun.com, and post our data:
  WiFiClient phantclient;
  const int httpPort = 80;
  int connectTries = 0 ;
  while (!phantclient.connect(PhantHost, httpPort))
  {
    delay(100) ;
    if (connectTries++ > phantConnectRetries) {
      // If we fail to connect, return 0.
      return 0;
    }
  }
  // If we successfully connected, print our Phant post:
  phantclient.print(phant.post());
  delay(5000) ;
 
  // Read all the lines of the reply from server and print them to Serial
  while(phantclient.available()){
    String line = phantclient.readStringUntil('\r');
    //Serial.print(line); // Trying to avoid using serial
  }

  phantclient.stop() ;

  return 1; // Return success
}


boolean acRunning() {
  int sensorTotal = 0 ;
  for (int i = 0 ; i < sensorReads ; i++) {
    sensorTotal += analogRead(A0) ;
    delay(sensorPause) ;
  }
  int sensorAverage = (int) sensorTotal / sensorReads ;
  Serial.print("Sensor Average: ") ;
  Serial.println(sensorAverage) ;

  // post to phant
  //Serial.println(postToPhant(sensorAverage)) ;
 
  if (sensorAverage > sensorThreshold) {
    Serial.println("AC is Running") ;
    return true ;
  } else {
    Serial.println("AC is Off") ;
    return false ;
  }
}

void setup() {
  Serial.begin(115200);
  delay(5000);
  pinMode(irLED, OUTPUT) ;

  // We start by connecting to a WiFi network

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);

  int connectTries = 0 ;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    Serial.print(WiFi.status()) ;
    if (connectTries++ >= wifiConnectRetries) {
      Serial.print("Wifi Connection tries > ") ;
      Serial.print(wifiConnectRetries) ;
      Serial.println(", restarting") ;
      ESP.restart() ;
    }
  }

  Serial.println("");
  Serial.println("WiFi connected"); 
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

 

  Serial.print("Connecting to: ");
  Serial.println(host);
 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  connectTries = 0 ;
  while (!client.connect(host, httpPort)) {
    delay(100) ;
    Serial.println("connection failed");
    if (connectTries++ > wifiClientConnectRetries) {
      Serial.print("Wifi Client Connection tries > ") ;
      Serial.print(wifiClientConnectRetries) ;
      Serial.println(", restarting") ;
      ESP.restart() ;
    }
  }
 
  // We now create a URI for the request
  String url = "/nest-thermostat-status.php";
 
  Serial.print("Requesting URL: ");
  Serial.println(url);
 
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Accept: text/html\r\n" +
               "Accept-Encoding: identity\r\n" +         
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  Serial.println("Delaying for response") ;
  int getDelay = 0 ;
  while ((!client.available()) && (getDelay++ < 1500) ) {
    delay(10);
  }

 
  // Read all the lines of the reply from server and print them to Serial
  String line = "" ;

  //Serial.println("Begin http Response") ;
  while(client.available()){
    line = client.readStringUntil('\n');
    //Serial.println(line);
  }
  //Serial.println();
  String acState = line ;

  Serial.println("End http Response") ; 
  //Serial.println("Closing http connection");
  client.stop() ;
 
  Serial.print("HVAC State: ") ;
  Serial.println(acState) ;
 
  if (acState == "cooling") {
       Serial.println("HVAC cooling, check vibration sensor to see if window AC is running") ;
       if (!acRunning()) {
         powerAC() ;
       }
  } else if (acState == "off") {
       Serial.println("HVAC off, check vibration sensor to see if window AC is off") ;
       if (acRunning()) {
         powerAC() ;
       }
  } else {
       Serial.println("Can't get HVAC status") ;
  }
  Serial.println("Deep sleep: 20 seconds") ;
  ESP.deepSleep(20000000,WAKE_RF_DISABLED) ;
  delay(5000);
  //delay(60000) ;


}

void loop() {
  // Never get here
  Serial.println("In main loop - shouldn't be here, resetting") ;
  delay(5000) ;
  ESP.restart() ;
}



And here is the results:
Code: Select allConnecting to wifi-ssid
.6.6.6.6.6.6.6.6.6.6.6.3
WiFi connected
IP address: 192.168.1.121
Connecting to: sync-nest.appspot.com
Requesting URL: /nest-thermostat-status.php
Delaying for response
End http Response
HVAC State: cooling
HVAC cooling, check vibration sensor to see if window AC is running
Sensor Average: 37
AC is Off
Sending Power IR Code
Deep sleep: 20 seconds
<boot noise>
Connecting to wifi-ssid

WiFi connected
IP address: 192.168.1.121
Connecting to: sync-nest.appspot.com
Requesting URL: /nest-thermostat-status.php
Delaying for response
End http Response
HVAC State:
Can't get HVAC status
Deep sleep: 20 seconds


Any thoughts?

Re: http gets return null

PostPosted: Wed Sep 02, 2015 8:53 am
by martinayotte
Probably it is the number of calls to their cloud is reach the maximum, since they are limiting them :

For REST and REST Streaming calls, each access token has a limited number of calls. Data rate limits apply to read/write calls via REST, and on read calls via REST Streaming. To avoid errors, we recommend you limit requests to one call per minute, maximum.

Re: http gets return null

PostPosted: Wed Sep 02, 2015 8:50 pm
by BryanLee
martinayotte wrote:Probably it is the number of calls to their cloud is reach the maximum, since they are limiting them :


No, firstly because in "production" I only wake the esp every 5 minutes. But also, immediately after the esp fails, I can manually load the website in my desktop browser several times, and it will instantly return the proper response with no lag.

Re: http gets return null

PostPosted: Wed Sep 02, 2015 10:01 pm
by BryanLee
I've also done a tcpdump on my router, listening to traffic from/to my esp. The web server sends the response several times to the esp.

Code: Select allesp->server http get
server->esp 80->409 [ACK] Seq = 1 Ack=145 Win=43952 Len=0
server->esp [TCP Dup ACK 15#1] 80->409 [ACK] Seq = 1 Ack=145 Win=43952 Len=0
server->esp HTTP/1.1 200 OK (text/html)  (Contains the "cooling" response)
server->esp 80->409 [FIN, ACK] Seq=152 Ack=145 Win=43952 Len=0
server->esp [TCP Retransmission] HTTP/1.1 200 OK (text/html)  (Contains the "cooling" response)
server->esp [TCP Retransmission] HTTP/1.1 200 OK (text/html)  (Contains the "cooling" response)


My router then goes on to try to do another ARP request for the esp's IP address, with no response.
The router sends a DHCP offer to the esp.
The ESP then tries to do an ARP request for it's own IP address (as if it forgot who it was).
Then the ESP Does an IGMPv2 membership Report for group 224.0.0.1.

I have also further tested and verified that the ESP will respond to pings when it is not in deep sleep, regardless of weather that wake cycle performs a valid http get or not.