How to do SOAP Request

Hi Krvarma

Yes, this is how it looks like - but how to I post it from my Core?

POST http://192.168.1.8:1400/MediaRenderer/AVTransport/Control HTTP/1.1
HOST: 192.168.1.8:1400
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"
CONTENT-TYPE: text/xml; charset="utf-8"
Content-Length: 266

<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>

I can’t tell if Fiddler can be setup to recieve incoming requests. Any idea? If it can you could try to POST to fiddler and see what comes across.

If Fiddler doesn’t then you could always do a quick install of apache + php and code up a quick php page to dump the incoming request to a file. I’m sure there are faster / cleaner ways but since I do local web dev it would be quickest for me.

1 Like

@Vognsen, can you try this, I don’t have any server or Sonos :frowning:

char postdata[] = "s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>";
    
    client.println("HOST: 192.168.1.8:1400");
    client.println("POST http://192.168.1.8:1400/MediaRenderer/AVTransport/Control HTTP/1.1");
    client.println("SOAPACTION: \"urn:schemas-upnp-org:service:AVTransport:1#Play\"");
    client.println("CONTENT-TYPE: text/xml; charset=\"utf-8\"");
    client.println("Content-Length: 266");
    client.println();
    client.println(msg);
1 Like

Note, I think @krvarma meant it to be either

char msg[] 

or

client.println(postdata);
2 Likes

@Vognsen, @harrisonhjones is correct. My mistake :frowning:

Thanks both of yuo, but it didn't solve the issue - still nothing here.
I have tried setting up a webserver and listening on the correct port (with a webservice) - nothing. Is there any other way to figure out if the core is actually transmitting anything? Can I force it to connect through my USB cable using tethering?

BR
Michael

Edit - this is how the code looks now:

// Message to post
char msg[] = "<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>";
#include <string.h>
#define LIB_DOMAIN "192.168.1.8"
#define Action "urn:schemas-upnp-org:service:AVTransport:1#Play"
char postdata[] = "s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>";
TCPClient client;
// EXAMPLE USAGE

void setup()
{
  delay(1000);
  // or you can use DHCP for autoomatic IP address configuration.
  // Ethernet.begin(mac);
  Serial.begin(9600);
  
  Serial.println("connecting .test..");
   client.connect(LIB_DOMAIN, 1400);
   
   
   client.println("HOST: 192.168.1.8:1400");
    client.println("POST http://192.168.1.8:1400/MediaRenderer/AVTransport/Control HTTP/1.1");
    client.println("SOAPACTION: \"urn:schemas-upnp-org:service:AVTransport:1#Play\"");
    client.println("CONTENT-TYPE: text/xml; charset=\"utf-8\"");
    client.println("Content-Length: 266");
    client.println();
    client.println(postdata);
   
}

void loop() 
{
  Serial.println("connecting .test");
}

What happens if you use the example code on the docs?

TCPClient client;
byte server[] = { 74, 125, 224, 72 }; // Google
void setup()
{
  // Make sure your Serial Terminal app is closed before powering your Core
  Serial.begin(9600);
  // Now open your Serial Terminal, and hit any key to continue!
  while(!Serial.available()) SPARK_WLAN_Loop();

  Serial.println("connecting...");

  if (client.connect(server, 80))
  {
    Serial.println("connected");
    client.println("GET /search?q=unicorn HTTP/1.0");
    client.println("Host: www.google.com");
    client.println("Content-Length: 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(;;);
  }
}

Open up the serial port via usb if you can and look at the serial output of the core.

1 Like

@Vognsen, the following code worked for me (with a dummy web server), can you check?. There is no need to split the post data, but I just tried that.

char postdata1[] = "s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' ";
char postdata2[] = "s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>";
char postdata3[] = "<s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'>";
char postdata4[] = "<InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>";

byte ip[] = {
    10, 0, 0, 3
};

.
.
.

if(client.connect(ip, 8080)){
	client.println("POST / HTTP/1.1");
        client.println("HOST: 10.0.0.3:8080");
        client.println("SOAPACTION: \"urn:schemas-upnp-org:service:AVTransport:1#Play\"");
        client.println("CONTENT-TYPE: text/xml; charset=\"utf-8\"");
        client.println("Content-Length: 266");
        client.println();
        client.print(postdata1);
        client.print(postdata2);
        client.print(postdata3);
        client.print(postdata4);
        client.println();
            
        delay(100);
            
        char szTemp[2];
            
        while(client.available() && client.connected()){
        	sprintf(szTemp, "%c", client.read());
		Serial.print(szTemp);
	}
            
        client.flush();
        client.stop();

} else{
	Serial.println("Not connected.");       
}
1 Like

Great project idea! Would be awesome to integrate this and a hover board to make a sweet remote :slight_smile: and you could even have a little screen on it to see whats playing

@krvarma’s code above looks good, but if your having trouble you can also try changing the server to one you create at runscope. then you can see what the core is sending and compare :slight_smile: its saved me a bunch of messing around a while back.

1 Like

Hi Hootie

Thanks, it’s something like that I’m building ;).

Thanks a bunch Krvarma for taking your time. It’s still not working. I will try and setup an URL at runscope tonight and see what’s going on. I don’t think anything is being sent, though :/.

Anyway, here is my full code - is it me having done anything incredibly stupid? :wink:

// Message to post

char postdata1[] = "s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' ";
char postdata2[] = "s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>";
char postdata3[] = "<s:Body><u:Play xmlns:u='urn:schemas-upnp-org:service:AVTransport:1'>";
char postdata4[] = "<InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>";

TCPClient client;
// EXAMPLE USAGE
byte ip[] = {
    192, 168, 1, 8
};

void setup()
{
  delay(1000);
  // or you can use DHCP for autoomatic IP address configuration.
  // Ethernet.begin(mac);
  Serial.begin(9600);
  
  if(client.connect(ip, 1400)){
	client.println("POST / HTTP/1.1");
        client.println("HOST: 192.168.1.8:1400");
        client.println("SOAPACTION: \"urn:schemas-upnp-org:service:AVTransport:1#Play\"");
        client.println("CONTENT-TYPE: text/xml; charset=\"utf-8\"");
        client.println("Content-Length: 266");
        client.println();
        client.print(postdata1);
        client.print(postdata2);
        client.print(postdata3);
        client.print(postdata4);
        client.println();

        delay(100);
        
        client.flush();
        client.stop();

} else{
	Serial.println("Not connected.");       
}
  
  
   
}

void loop() 
{
  Serial.println("connecting .test");
}

This is the most useful bit, you have left out.. it will do two things, tell you if there is a reply and properly flush the return data :slight_smile:

and i think the POST line should be:

"POST /MediaRenderer/AVTransport/Control HTTP/1.1"

there is a good example here Jan-Piet Mens :: Sonos pause switch ill port it to the spark for you when i get a minute if you like..

2 Likes

@Vognsen Just found this… may save you alot of work… https://github.com/phhe/sparkSo

Here is another i just quickly ported

#include "application.h"

byte sonosip[] = { 192, 168, 1, 8 };
 
int debug = 1;
 
#define SONOS_PAUSE "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:Pause xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"><InstanceID>0</InstanceID></u:Pause></s:Body></s:Envelope>"
#define SONOS_PLAY  "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:Play xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>"
 
#define PLAY 1
#define PAUSE 0
 
const int buttonPin = D6;     // the number of the pushbutton pin
const int ledPin =  D7;      // the number of the LED pin
 
int state = LOW;      // the current state of the output pin
int reading;           // the current reading from the input pin
int previous = LOW;    // the previous reading from the input pin
 
long last = 0;         // the last time the output pin was toggled
long debounce = 200;   // the debounce time, increase if the output flickers
 
TCPClient client;
 
void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  if (debug) {
    Serial.begin(9600);
    while(!Serial.available()); // wait here for user to press ENTER in Serial Terminal
  }
}
 
void loop()
{
 
  reading = digitalRead(buttonPin);
 
  // if the input just went from LOW and HIGH and we've waited long enough
  // to ignore any noise on the circuit, toggle the output pin and remember
  // the time
  if (reading == HIGH && previous == LOW && millis() - last > debounce) {
    if (state == HIGH) {
      state = LOW;
      if (debug) {
        Serial.println("play");
      }
      sonos(PLAY);
    }
    else {
      state = HIGH;
      if (debug) {
        Serial.println("pause");
      }
      sonos(PAUSE);
    }
 
    last = millis();
  }
 
  digitalWrite(ledPin, state);
 
  previous = reading;
}
 
void out(const char *s)
{
  client.println(s);
  if (debug) {
    Serial.println(s);
  }
}
 
void sonos(int cmd)
{
  char buf[512];
 
  if (client.connect(sonosip, 1400)) {
    if (debug) {
      Serial.println("connected");
    }
 
    out("POST /MediaRenderer/AVTransport/Control HTTP/1.1");
    out("Connection: close");
    sprintf(buf, "Host: %d.%d.%d.%d:1400", sonosip[0], sonosip[1], sonosip[2], sonosip[3]);
    out(buf);
 
    sprintf(buf, "Content-Length: %d", (cmd == PLAY) ? strlen(SONOS_PLAY) : strlen(SONOS_PAUSE));
    out(buf);
 
    out("Content-Type: text/xml; charset=\"utf-8\"");
 
    sprintf(buf, "Soapaction: \"urn:schemas-upnp-org:service:AVTransport:1#%s\"", (cmd == PLAY) ? "Play" : "Pause");
    out(buf);
 
    out("");
    strcpy(buf, (cmd == PLAY) ? SONOS_PLAY : SONOS_PAUSE);
    out(buf);
    
    unsigned int count = 0;
    unsigned long lastTime = millis();
    while( client.available()==0 && millis()-lastTime<2000) { //2 second timeout for reply
    }  //do nothing
    lastTime = millis();
    while( client.available() && millis()-lastTime<500 ) {  //500 milliseconds timeout after last data
      char c = client.read();
      if (debug) {
        Serial.print(c);
      }
      lastTime = millis();
      count++;
    }
    client.flush();  //for safety

    //client.flush();
    delay(400);
    Serial.println();
    Serial.print("Done, Total bytes returned: ");
    Serial.println(count);
    
  } else {
    if (debug) {
      Serial.println("connection failed");
    }
  }
  client.stop();
}
3 Likes

Dude, thanks a bunch (All of you, actually).

Hootie - that code you ported is working (I just woke up my baby-girl, as she was sleeping in the room with that SONOS device: Wasn’t that optimistic, shame on me) :smiley:. Needless to say, I was more enthusiastic about it working than the mother!

The project I’m making will basically be a simple button to control volume, play/pause etc. for my SONOS zones, so I don’t have to open up the phone etc. to control it… And you’ve made this much easier!

Thanks a bunch one more time, you are awesome!

BR
Michael

4 Likes

Have a look at the github link in the post above, its for arduino and controlling the sonos with an IR remote. Most of the code is perfect for the spark already and will only require some very minor changes to get it working on the spark. (the IR library and a change to the TCPclient calls) It has pretty much every available command already and zones etc :slight_smile:

I am going to build one myself with a hover board to control it (as that’s the coolest way possible) as well as the IR remote (i think i have all the bits already to do it) but i have a few other projects i want to finish first… my message torch webpage is 98% complete, and I have the bits to build @Carsten4207’s light controller, and i need to finish the blinds controller i started 3 months ago… and make a case for my door access system, and i have all the bits to make the open energy thing that phec ported ages ago! my whole life at the moment is work, spark, sleep, work, spark, sleep! (ps at work i mostly think about spark too)

Got any info on your blinds project? I’ve been thinking about something like that as well.

@Carsten4207 its a super complicated one for the blinds, a RC servo should do the job nicely :slight_smile: and only requires a single wire to control each blind… ill 3d print a little square drive adapter to dive the shaft directly and a mounting bracket to hide the servo in the top box section bit. i was planning on using 3.5mm headphone leads for the servos and control 2 blinds at the front of the house and the 5 light/fans with your controller… there should be enough room for the servo code in with yours. Planning on it being a security thing to make it look like people are home.

The blinds controller is very cool. I did the same thing but with the a stepper because my blinds needed several rotations and I wasn’t willing to gear the servo. It sounds like you setup is pretty similar. My motivation was to open the blinds in the morning as part of my morning alarm clock. Tasker + Spark = Blinds open when I set my alarm to go off!

@Hootie81 the code you postet works perfekt but as the sonos device usally also post a response to what ever youre sending it do you have any idear on how i can acces this response ?

The response is printed out on serial, i did try parsing it but I couldnt work it out, I tried so I could get info displayed on the screen but there was something weird going on and I gave up… that was a long time ago and the project has been sitting on the shelf for a while
Sorry I can’t be of more help, work is chaos at the moment so I haven’t had time to dig out my toys

so i been working abit more on this subject and found that it seems that no response is send ore for unkown reaons my esp 8266 dos not recive this response any idear way?

this is my getResponse function so fare

    void getResponse() {
	Serial.println("response code");
	int time = millis();
	int timeout = time +5000;
	Serial.println("client.available return");
	Serial.println(client.available());
	  while (client.available() == 0) {
		  delay(1);
	    if (timeout < millis()) {
	    	 Serial.println("b");
	    	Serial.println(">>> Client Timeout !");
	    	 Serial.println("c");
	    	client.stop();
	    	 Serial.println("d");
	      return;
	    }
	  }
	  Serial.println("d2");
	  while(client.available()) {
		  Serial.println("e");
	    String line = client.readStringUntil('\r');
	    Serial.println("f");
	    Serial.print(line);
	    Serial.println("g");
	  }
}