How to do SOAP Request

Hi there

I’m having some issues making a POST (SOAP) to my SONOS device. I can’t see what the error is - but it’s definately not working. I used this post as reference: https://community.spark.io/t/making-a-get-or-post-request-from-the-core/2288

// 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"
#define CONTENTTYPE "text/xml; charset='utf-8'"
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("POST /MediaRenderer/AVTransport/Control HTTP/1.0");
    client.println("Host: " LIB_DOMAIN);
    client.println("SOAPACTION: " Action);
    client.println("CONTENT-TYPE: " CONTENTTYPE);

    client.print("Content-Length: ");
    client.println(266);
    client.println();
    client.print(msg);
    client.print("&status=");
}

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

What am I doing wrong?

Best regards
Michael

Are you getting anything on your SOAP device (incoming logs?)? Also are we talking about this: http://www.sonos.com/?r=1 when you say “SONOS”?

Hi Harrison
Thanks for your post.
I don’t have logs at my device, so I really don’t know. My next step would be to reach my computer instead and monitor what comes in.
Yes, it is indeed the SONOS you linked to I’m trying to control. I am able to do it using Fiddler from my PC.

BR
Michael

Ok. I’ve been looking into the SONOS API a bit. Sounds do-able. Looks like what you have SHOULD work.

Just to confirm: when you use fiddler you are sending the same message, headers, etc to the SONOS and it works?

What’s your background as far as code development/apis/etc goes? Do you mind installing something on your computer (it sounds like you wouldn’t)

1 Like

@Vognsen, I may be wrong but after looking into Sonos forums, is the client.print("&status="); is needed?

1 Like

Good question, the document I was looking at (http://wallbox.weebly.com/3-sonos-api.html) doesn’t have that line

You are right - the &status was a leftover from the twitter example. I have removed it now but the same result remains - nothing.
I’m a software engineer, but I’m a C# / Javascript guy NOT a c++ which is why I’m struggling, hehe. This is y first microcontroller that I have been developing towards in years.

I have tried hitting a webserver on my local PC and have Fiddler running to monitor any traffic - there isn’t any :/.

//Edit - no I don’t mind installing some new software.

@Vognsen, if you can control using Fiddler, then you can copy the raw HTML request and use it to POST from Spark Core?

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 http://jpmens.net/2010/03/16/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