Question with HTTP client

I am trying to send this to my Home Automation server:

http://192.168.1.59:3480/data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=40

which in turn bounces back some data for my Spark.function( )

I’m using this with no results:

const char veraIpAddress[] = {192,168,1,59};
TCPClient myVera;

void setup() 
{
  Serial.begin(9600);
}

void loop() 

{
    
    startupSyncVera();
    delay (10000);
    
}

void startupSyncVera() // RUNS A VERA SCENE ON STARTUP... Push the dimmer value from this scene
{
  Serial.println("");
  Serial.println("Connecting to Vera...");
  if (myVera.connect(veraIpAddress, 3480)) //starts client connection, checks for connection
  {  
    Serial.println("asking for dimmer level");
    myVera.print("GET ");
    myVera.print("/data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=40");
    myVera.println(" HTTP/1.1");
    myVera.println("Connection: close");  //close 1.1 persistent connection 
    myVera.println("Host: ");
    myVera.println(veraIpAddress);
    myVera.println("Accept: text/html, text/plain");
    myVera.println(); //end of get request
    delay(1);
    myVera.flush();
    myVera.stop();
  }
}

2 things jump out

myVera.println("Host: ");

should be just

myVera.print("Host: "); 

and you MUST read the response before calling flush and stop.. even if you just throw it away. otherwise you will end up with a hard fault.

have a look at this post here for a good example of how to use the TCPclient.

2 Likes

I needed a break from the code i was writing, so i wrote this for you… give it a go and let us know how you get on!

#include "application.h"
boolean DEBUG = true; // enable or disable Debug

IPAddress veraIpAddress(192, 168, 1, 6);  
int veraPort = 3480;
char Path[] = "/data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=40";

// Input and Output Pins 
int LED = D7; // Built in LED

TCPClient client;
 
char reply[512];


void setup() {

    pinMode(LED, OUTPUT); // sets LED as output
    
    if(DEBUG){
        digitalWrite(LED,HIGH); // Light the onboard LED so you know it's time to open your Serial Terminal
        Serial.begin(9600);
        while(!Serial.available()); // wait here for user to press ENTER in Serial Terminal
        digitalWrite(LED,LOW);

        Serial.print("SSID: ");
        Serial.println(WiFi.SSID());
        Serial.print("Core IP: ");
        Serial.println(WiFi.localIP());
        Serial.print("Gateway: ");
        Serial.println(WiFi.gatewayIP());
        Serial.print("Mask: ");
        Serial.println(WiFi.subnetMask());
        Serial.print("WiFi RSSI: ");
        Serial.println(WiFi.RSSI());
    }
    

}

void loop() {

digitalWrite(LED,HIGH);
startupSyncVera(veraIpAddress, 3480, Path); 
delay (10000); 

}

/*----------------------------------------------------------------------*/
/* out - outputs supplied string to TCPclient */
void out(const char *s) {

client.write( (const uint8_t*)s, strlen(s) );

if (DEBUG)Serial.write( (const uint8_t*)s, strlen(s) );

}
/*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*/
/* in - reads from TCPclient, with timeout */
void in(char *ptr, uint8_t timeout) {
        int pos = 0;
        unsigned long lastTime = millis();
        
        while( client.available()==0 && millis()-lastTime<timeout) { //timeout
        }  //do nothing
        lastTime = millis();
        unsigned long lastdata = millis();
        
        while ( client.available() || (millis()-lastdata < 500)) {  //500 millisecond timeout
            if (client.available()) {
                char c = client.read();
                if(DEBUG)Serial.print(c);
                lastdata = millis();
                ptr[pos] = c;
                pos++;
            }
            if (pos >= 512 - 1)
            break;
        }
        ptr[pos] = '\0'; //end the char array
        while (client.available()) client.read(); // makeshift client.flush()
        client.flush();  //for safety
        delay(400);
        client.stop();
        if(DEBUG){
            Serial.println();
            Serial.print("Done, Total bytes: ");
            Serial.println(pos);
        }
        
}
/*-----------------------------------------------------------------------*/


void startupSyncVera(IPAddress hostname, int port, char *url) {
    
    char line[255];
    client.stop();

    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        
        sprintf(line, "POST %s HTTP/1.1\r\n", url);
        out(line);

        sprintf(line, "Host: %d.%d.%d.%d:%d\r\n", hostname[0], hostname[1],hostname[2],hostname[3], port);
        out(line);

        out("Accept: text/html, text/plain\r\n");

        out("Connection: close\r\n\r\n");

        in(reply, 3000);  // you can parse "reply" later for the data you want
        digitalWrite(LED, LOW);
     }
    else {
        while (client.available()) client.read(); // makeshift client.flush()
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }
}


1 Like

Thanks for all the help…

while you were dong that, I got this to work:

IPAddress server(192,168,1,59);
TCPClient client;
char reply[512]; 

void setup() 
{
  Serial.begin(9600);
  startupSyncVera();
}

void loop() 

{
}

void startupSyncVera() // RUNS A VERA SCENE ON STARTUP... Push the dimmer value from this scene
{
  Serial.println("Connecting to web server: ");
  if (client.connect(server, 3480)) 
  {
    Serial.println("Connected...");
    out("GET /data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=40 HTTP/1.1\r\n");
    out("Host: ubuntu.ashnet\r\n");
    out("User-Agent: Spark/1.0\r\n");
    out("Content-Type: text/html\r\n");
    out("Connection: close\r\n\r\n");
    Serial.println("Closing Connection...");
    in(reply, 3000);
  } 
  else 
  {
    Serial.println("Connection failed");
    Serial.println("Disconnecting.");
    client.stop();
  }
}

void out(const char *s) 
{
  client.write( (const uint8_t*)s, strlen(s) );
  Serial.write( (const uint8_t*)s, strlen(s) );
}

void in(char *ptr, uint8_t timeout) 
{
  int pos = 0;
  unsigned long lastTime = millis();
  while( client.available()==0 && millis()-lastTime<timeout) 
  {
  }  //do nothing
  lastTime = millis();
  unsigned long lastdata = millis();
  while ( client.available() || (millis()-lastdata < 500)) 
  {
    if (client.available()) 
    {
      char c = client.read();
      Serial.print(c);
      lastdata = millis();
      ptr[pos] = c;
      pos++;
    }
    if (pos >= 512 - 1)
      break;
  }
  ptr[pos] = '\0'; //end the char array
  while (client.available()) client.read(); // makeshift client.flush()
  client.flush();  //for safety
  delay(400);
  client.stop();
}

although I have no idea what all this is for:

out("Host: ubuntu.ashnet\r\n");
out("User-Agent: Spark/1.0\r\n");
out("Content-Type: text/html\r\n");

the Host should be the IP of your Vera unit, not the Ubuntu.ashnet. have a look at my example to see how to send the IPAddress with the out function.
User-Agent tells the the server what device is calling it, so it could send a different response for a spark sending the request to a chrome browser…
Content type is for when you send a body in the request (normally POST) so it knows the type of data you are sending

@Hootie81

Thanks again for your help...

I'm struggling a bit with the blocking nature of what you gave me (in no way should you infer that I don't appreciate your writing the code for me :smile: ) I'd just like to just dump all what comes back

Before I start to hack this up, can you tell me why are we stopping here for so long?

This is LAN traffic, so pretty fast... How might we speed this up to basically dump everything that comes back?

My objective is to simply issue the http 'command' to the Vera, which responds with a header and an "OK". There is server-side code that sends an update to a Spark.function( ) so I'm not interested in parsing what is essentially an ack... I'm not even interested to know if there was an ack.

This is nice, as I didn’t know about the precaution about consuming all available chars on socket before flushing and closing it.

Thanks !

there is 400ms there + 500ms after the last char comes in… so almost a full second!!! I agree its very long! For my doorbell Im not worried at all, but on my Sonos remote control i have dropped them down a bit to make it more responsive.

play with the numbers and see what you get. the 400ms is possibly a bit of code that i have left in there from ages ago, from memory there was a race condition or something between the flush and the stop. but we may have decided it was due to the flush not flushing the same a read does. so could be left out now.

with the 500ms after last char, you could probably drop that right down because all you will get is the header, if you get more than 1000bytes back you will need to keep it in there as it will start coming in bursts. If there is a tiny delay for whatever reason and more data comes in after and builds up over time then expect a red sos, it took months to work out why after a dozen times of working correct i was getting the hard fault…

@Hootie81

Similar to the http request above, I can make a call to that same device over the internet using the MIOS servers (fwd1.mios.com) this way:

https://fwd1.mios.com/<USER_ID>/<PASSWORD>/<DEVICE_NUMBER>/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=16

So, I am trying to do that using your code like this:

TCPClient client;
char reply[512];

void setup()
{
  getAlarmState();
}
void loop()
{

}

void getAlarmState()
{
  if (client.connect("fwd1.mios.com", 3480))
  {
    out("GET /<USER_ID>/<PASSWORD>/<DEVICE_NUMBER>/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=16 HTTP/1.1\r\n");
    out("Host: fwd1.mios.com\r\n");
    out("User-Agent: Spark/1.0\r\n");
    out("Content-Type: text/html\r\n");
    out("Connection: close\r\n\r\n");
    //DEBUG_PRINTLN("Closing Connection...");
    in(reply, 3000);
  }
}

void out(const char *s)
{
  client.write( (const uint8_t*)s, strlen(s) );
  //Serial.write( (const uint8_t*)s, strlen(s) );
}

void in(char *ptr, uint8_t timeout)
{
  int pos = 0;
  unsigned long lastTime = millis();
  while( client.available()==0 && millis()-lastTime<timeout)
  {
  }  //do nothing
  lastTime = millis();
  unsigned long lastdata = millis();
  while ( client.available() || (millis()-lastdata < 500))
  {
    if (client.available())
    {
      char c = client.read();
      //DEBUG_PRINTLN(c);
      lastdata = millis();
      ptr[pos] = c;
      pos++;
    }
    if (pos >= 512 - 1)
      break;
  }
  ptr[pos] = '\0'; //end the char array
  while (client.available()) client.read(); // makeshift client.flush()
  client.flush();  //for safety
  delay(400);
  client.stop();
}

since it is an internet call, I tried ports 80, 3480 (local port) and 8080… it seems to me 80 should be correct

on my local lan, I used the local ip of the device here:

out("Host: fwd1.mios.com\r\n");

so I am trying the MIOS server address…

Do you see anything that I am doing wrong?

The obvious difference to me is that in the command that you are apparently typing in to the URL bar of a browser is HTTPS (port 443 by default), whereas your Core code is HTTP (port 80 by default).

If you make the call using HTTP then, obviously, your username/password/device are not protected as they pass through the internet.

1 Like

@timx

OK port 443… I’ll try that, I didn’t know. the call works with http so it may be as simple as changing the url.

Yes, I appreciate your comment on security and understand the limitations there.

thanks for the pointer, I will try as soon as I can!

Yeah the core can’t do https so I wouldn’t bother with port 443. Try changing the call to the loop and wait 5 seconds before calling your function… sometimes it takes that long for the cc3k to initialize and allow a connection.