Help formatting PUT request -- Philips Hue

Hi guys,
I have been experimenting with talking to the Philips Hue base station to begin controlling the lights.

So far I’ve been able to use the tcp client example to make a GET command.

TCPClient client;
byte server[] = { 192, 168, 1, 10 }; // IP of bridge
void setup()
{
  Serial.begin(9600);
  delay(1000);
  Serial.println("connecting...");

  if (client.connect(server, 80))
  {
    Serial.println("connected");
    client.println("GET /api/newdeveloper/lights HTTP/1.0");
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
}

void loop()
{
  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected())
  {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    for(;;)
      ;
  }
}

But I’m a bit a bit of a loss on how to successfully make a PUT request to actually control the lights.

following the dev guide on philips site: http://developers.meethue.com/gettingstarted.html

I’m tried to format it like this:

if (client.connect(server,80))
{
   client.println("PUT /api/newdeveloper/lights/3/state HTTP/1.1");
   client.println("{\"on\":false}");
}

I get some type of response, but the light doesn’t actually change, running the same command through the philips gui works fine.

In addition, I’m wondering how to properly format my code to be able to update the PUT requests in the loop so I can begin to manipulate the lights through hardware buttons.

If anyone has any guidance on this it would be a huge help!

edit: this example code probably needs to be updated becuase it still blocks the cloud from connecting to the core afer the tcp connection has been made per: https://community.spark.io/t/known-issue-long-delays-or-blocking-code-kills-the-connection-to-the-cloud/950/20
any fixes for this?

@callil at first glance, without trying the code myself, it looks like you need to add a content-length header to your request.

Your request should look something like this:

PUT /api/newdeveloper/lights/3/state HTTP/1.1
Content-Length: 14
Content-Type: application/json

{\"on\":false}

Let me know if that works

1 Like

Thanks for your response! I will try this when I get home.
So the structure might look something like:

client.println("PUT /api/newdeveloper/lights/3/state HTTP/1.1");
client.println("Connection: keep-alive");
client.println("Content-Type:   text/plain; charset=UTF-8");
client.println("Content-Length: 14");
client.println("{\"on\":false}");

I don’t know much about requests, so if I wanted to make a new request after this would it just be a matter of making an empty client.println(""); or do I actually have to do something like client.println("Connection: keep-alive"); and then just send more requests with the PUT command.

Also, do I need to be changing the content length for every different command that is sent?
edit: sorry if this stuff is really basic, I’ve been trying to find a good intro to learning this stuff but haven’t been very successful.

I just noticed that the Host header is also required for all HTTP 1.1 requests, so you’ll want to make sure to include that as well.

PUT /api/newdeveloper/lights/3/state HTTP/1.1
Host: 192.168.1.10
Content-Type: application/json
Content-Length: 14

{\"on\":false}

To do multiple requests per connection, you specify the Connection: keep-alive header on the first request, then Connection: close on the last request like this:

PUT /api/newdeveloper/lights/3/state HTTP/1.1
Host: 192.168.1.10
Connection: keep-alive
Content-Type: application/json
Content-Length: 14

{\"on\":false}
PUT /api/newdeveloper/lights/3/state HTTP/1.1
Host: 192.168.1.10
Connection: close
Content-Type: application/json
Content-Length: 13

{\"on\":true}

And yes, you need to set the content-length for each request.

2 Likes

Thanks Hypno! I’ve made some progress. Able to turn on and off lights and set states so long as I know what I want. Now I’m attempting to hook up a pot value to the brightness. This is where I am:

TCPClient client;
byte server[] = { 192, 168, 1, 10 }; // Google

int potPin = A0;  
int val = 0;  
int prev = 0;

void setup()
{
  Serial.begin(9600);
  delay(1000);
    if (client.connect(server, 80)){
        Serial.println("connected");
    }
    else{
        Serial.println("fail");
    }
}

void loop(){
    val = analogRead(potPin);
    val = map(val, 2, 4093, 0, 255);

    Serial.println(val);
    
    if (val != prev){
    if (client.connected()){
        client.println("PUT /api/newdeveloper/lights/3/state HTTP/1.1");
        client.println("Host: 192.168.1.10");
        client.println("Connection: keep-alive");
        client.println("Content-Type: application/json");
        client.println("Content-Length: 51");
        client.println();
        client.print("{\"on\":true,\"sat\":255,\"bri\":");
        client.print(val);
        client.println(",\"hue\":10000}");
        Serial.println("sent"); 
    }
    else{
        Serial.println("not connected/sending");
     }
    }
    
    if (client.available())
    {
    //char c = client.read();
    //Serial.print(c);
    //delay(1000);
    }
    
    delay(10);
    prev = val;
}

The issue is that this works the first time through the loop but nothing after that.

some obvious problems:
I don’t know how to get my content length if the int can be from 0-255.
I am probably making too many PUT requests (dont know whats the limit or how to throttle)

1 Like

HI @callil

Since the ADC does not return exactly the same value every time but +/- 1 or 2, maybe you should change you test to be:

if ( abs(val - prev) > threshold ){
...

for some threshold.

You can use itoa() to convert the value to a char* string and then do strlen() on that.

1 Like

As far as the keep-alive stuff, maybe try a slight delay in between requests? When I was trying it, I was using a bash script + telnet to send multiple requests. Perhaps I should try to do it on a core. It also might have to do with the particular server you’re working with. It’s possible the server doesn’t support keep-alive requests.

Hey guys,
Thanks for all your help so far, I’ve made a bit more progress but I’m still having that issue where any subsequent requests don’t have any effect despite the fact that the client is connected. It’s only the first time through the loop that does anything. Maybe there is something I have to do to terminate the requests?

Another option would be to try to port the JSON arduino library? https://github.com/interactive-matter/aJson

This guy: https://github.com/oguime/Hue_W5100_HT6P20B/blob/master/Hue_W5100_HT6P20B.ino made a working thing with arduino ethernet and he used this structure:

boolean SetHue()
{
  if (client.connect(hueHubIP, hueHubPort))
  {
    while (client.connected())
    {
      client.print("PUT /api/");
      client.print(hueUsername);
      client.print("/lights/");
      client.print(hueLight + 1);  // hueLight zero based, add 1
      client.println("/state HTTP/1.1");
      client.println("keep-alive");
      client.print("Host: ");
      client.println(hueHubIP);
      client.print("Content-Length: ");
      client.println(hueCmd.length());
      client.println("Content-Type: text/plain;charset=UTF-8");
      client.println();  // blank line before body
      client.println(hueCmd);  // Hue command
    }
    client.stop();
    return true;  // command executed
  }
  else
    return false;  // command failed
}

Notice the client.stop(); but all this does for me is hang the sketch. So I had to revert from using this structure.
Still trying a lot of stuff!

My current sketch is below:

  TCPClient client;
    byte server[] = { 192, 168, 1, 10 }; // Google
    
    int potPin = A0;  
    int val = 0;  
    int prev = 0;
    String stringOne;
    
    
    void setup()
    {
      Serial.begin(9600);
       delay(1000);
        if (client.connect(server, 80)){
            Serial.println("connected");
        }
        else{
            Serial.println("fail");
        }
    }
    
    void loop(){
        val = analogRead(potPin);
        val = map(val, 2, 4093, 0, 255);
        String stringOne =  String(val); 
        unsigned int len = stringOne.length();
    
        Serial.println(val);
        Serial.println(len);
        
        
        if (val != prev){
            if (client.connected()){
                client.println("PUT /api/newdeveloper/lights/3/state HTTP/1.1");
                client.println("Connection: keep-alive");
                client.println("Host: 192.168.1.10");
                client.println("Content-Type: text/plain;charset=UTF-8");
                client.print("Content-Length: ");
                client.println(10+len);
                client.println();  // blank line before body
                client.print("{\"bri\":");
                client.print(val);
                client.println("}");
                Serial.print("sent");  // command executed
                delay(100);
              }
              else{
                Serial.print("not connected");  // command failed
              }
        }
             
        
        delay(10);
        prev = val;
    }

This might not be related, but you typically have a space after the colon in a JSON:

    client.print("{\"bri\": ");
    client.print(val);
    client.println("}");

The Arduino code you pointed us at does that as well. Might not matter–depends on the parser, but worth a quick shot.

The Arduino code does client.stop() since it reconnects every time with a client.connect(). You only connect in setup, so your code never reconnects after a stop and seems hung (really client.connected() is never true).

Thanks for the catch! I’ve finally got it working!!
Seems like something was blocking before, but changing the structure helped.

Again, thanks @bko and @Hypnopompia!
I’ll probably be back with more questions soon.

TCPClient client;
byte server[] = { 192, 168, 1, 10 }; // Local light server
int potPin = A0;  // Potentiometer Pin
int val = 0;  
int prev = 0;
String stringOne; // empty string to store val length
unsigned int len; // length of val

void setup()
{
    Serial.begin(9600);
    delay(1000);
    client.connect(server, 80);
    if (client.connected()){ //initiate connection
        Serial.println("connected");
    }
    else{
        Serial.println("failed");
    }
}

void loop(){
    if (client.connect(server, 80)) {
        if(client.connected())
        {
            val = analogRead(potPin); //read potentiometer
            val = map(val, 2, 4093, 0, 255); //map it
            String stringOne =  String(val);  //convert to string
            unsigned int len = stringOne.length(); //get length
            if (val != prev ){ // if new reading is different than the old one
                client.println("PUT /api/newdeveloper/lights/3/state HTTP/1.1");
                client.println("Connection: keep-alive"); //
                client.println("Host: 192.168.1.10"); //same as server
                client.println("Content-Type: text/plain;charset=UTF-8"); //
                client.print("Content-Length: "); //param
                client.println(11+len); //brightness string + val length
                client.println();  // blank line before body
                client.print("{\"bri\": ");
                client.print(val); //value of potentiometer
                client.println("}");
                Serial.print("sent");  // command executed
                delay(100); // slight delay IMPORTANT
                prev = val; //set prev
            }
        }
        client.stop();
        Serial.println("stopping");
     }
    delay(10);
}
3 Likes

I think moving the client.connect() call into the loop is what fixed it. I suspect that even though all their examples include the Connection: keep-alive header, that they don’t actually support keep-alive requests. By moving the client.connect() into the loop, you are initiating a new connection each time.

If you want to be pedantic, you could replace Connection: keep-alive with Connection: close, which was what I was originally going to suggest. However, I’m guessing that this won’t make any difference to anything, either.

2 Likes

@callil
Could you please share the code that you used to turn the philips hue lights on or off using telnet? I’ve been trying to do that very thing. Just the simple command of all lights on or all lights off using a telnet command string.

1 Like