Help reading Sonos call response

Hey @Hootie81,

If you have a few moments to help, it would be much appreciated. I am beginner coder trying to modify your code for the hover Sonos controller combining it with the button click play/pause button Sonos control I think you helped someone else with in another thread.

I am basically trying to make a button that will shutdown my sonos speaker but I want have logic check the current state of my speaker and if it is playing, pause it. If it is paused, then play it.

In the Sonos play/pause code you shared here (How to do SOAP Request) it keeps track of the last command that was sent and sends the opposite command, but any change to the play/pause state of the speaker made via the app or hitting the button on the speaker would not be accounted for.

I did some research and found that there is a “GetTransportInfo” command to get the current play status and added a case statement to the Sonos function.

case SONOS_GETPLAYSTATUS:
strcpy(cmdbuf, "GetTransportInfo");
strcpy(resp1, "CurrentPlayStatus");
break;

I pass this command in the loop function when the button is pressed.

sonos(SONOS_GETPLAYSTATUS, data1, nullbuf);

When I open the serial monitor I see the response coming back as “PAUSED_PLAYBACK” or “PLAYINGOK1” but I can’t figure out how to read that response in the program so I can evaluate it and based on that response either send a pause or play command.

Again, any help would be much appreciated. My full code below.

Best,
Brian


    /*
    * ======================================================================
    * Hover + Spark - Spark Core code for controlling Sonos devices with gestures using 
    * 				  the Hover board (http://www.hoverlabs.co/#hover) 
    *
    * Modified from SonosIR - Arduino sketch for infrared control of Sonos ZonePlayer (c) 2010
    * Simon Long, KuDaTa Software
    *
    * Original Sonos control Arduino sketch by Jan-Piet Mens. Uses the Arduino
    * IRremote library by Ken Shirriff.
    *
    * Use is at the user's own risk - no warranty is expressed or implied.
    * ======================================================================
    *
    * Changes:
    * 2010-04-24 simon added volume control
    * 2014-12-10 Hootie81 Modified for Hover control
    *
    * Wiring diagram found Here : http://www.hoverlabs.co/spark
    */
    
    #include "application.h"
    
    
    /*----------------------------------------------------------------------*/
    /* Macros and constants */
    /*----------------------------------------------------------------------*/
    /* Sonos SOAP command packet skeleton */
    #define SONOS_CMDH "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>"
    #define SONOS_CMDP " xmlns:u=\"urn:schemas-upnp-org:service:"
    #define SONOS_CMDQ ":1\"><InstanceID>0</InstanceID>"
    #define SONOS_CMDF "</s:Body></s:Envelope>"
    /* Sonos SOAP command packet enumeration */
    #define SONOS_PAUSE 0
    #define SONOS_PLAY 1
    #define SONOS_PREV 2
    #define SONOS_NEXT 3
    #define SONOS_SEEK 4
    #define SONOS_NORMAL 5
    #define SONOS_REPEAT 6
    #define SONOS_SHUFF 7
    #define SONOS_SHUREP 8
    #define SONOS_MODE 9
    #define SONOS_POSIT 10
    #define SONOS_GETVOL 11
    #define SONOS_SETVOL 12
    #define SONOS_GETPLAYSTATUS 13
    /* State machine for A-B repeat and intro scan functions */
    #define MODE_NORMAL 0
    #define MODE_SCAN 1
    #define MODE_A 2
    #define MODE_AB 3
    
    /* IP addresses of Arduino and ZonePlayer */
    #define IP1 192
    #define IP2 168
    #define IP3 1
    #define IP4 11 /* livingroom */
    
    /* Enable DEBUG for serial debug output */
    //
    //#define DEBUG
    /*----------------------------------------------------------------------*/
    /* Global variables */
    /*----------------------------------------------------------------------*/
    
    
    
    /* IP address of ZonePlayer to control */
    byte sonosip[] = { IP1, IP2, IP3, IP4 };
    /* Millisecond timer values */
    unsigned long lastcmd = 0;
    unsigned long lastrew = 0;
    unsigned long lastpoll = 0;
    /* A-B repeat and intro scan state */
    int mode;
    /* Second timer values used for A-B repeat */
    int posa, posb;
    /* Global used to store number of seconds to seek to in a Sonos command */
    int desttime;
    /* Global used for volume setting */
    int newvol;
    /* Buffers used for Sonos data reception */
    
    /* Current play state for speaker */
    char speakerplaystate[20];
    //char teststate;
    
    char data1[20];
    char data2[20];
    /* Global null buffer used to disable data reception */
    char nullbuf[1] = {0};
    
    const int buttonPin = D6;     // the number of the pushbutton pin
    const int ledPin =  D5;      // 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
    
    /* Ethernet control */
    TCPClient client;
    /*----------------------------------------------------------------------*/
    /* Function defintions */
    /*----------------------------------------------------------------------*/
    /*----------------------------------------------------------------------*/
    /* setup - configures Arduino */
    void setup() {
    
    mode = MODE_NORMAL;
    pinMode(buttonPin, INPUT_PULLUP);
    pinMode(ledPin, OUTPUT);
    
    
    /*
    #ifdef DEBUG
        Serial.begin(115200);
        while(!Serial.available()); // wait here for user to press ENTER in Serial Terminal
        Serial.print("Initializing ...please wait.");
    #endif*/
    }
    /*----------------------------------------------------------------------*/
    /* loop - called repeatedly by Arduino */
    void loop() {
        int res;
       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 && millis() - last > debounce) {
    
                sonos(SONOS_GETPLAYSTATUS, data1, nullbuf);
    
                
    
     
        last = millis();
      }
     
      digitalWrite(ledPin, state);
     
    
    
    }
    
    /*----------------------------------------------------------------------*/
    /* sum_letters - adds ASCII codes for all letters in supplied string */
    int sum_letters(char *str) {
    int tot = 0;
    char *ptr = str;
    while (*ptr) {
    tot += *ptr;
    ptr++;
    }
    return tot;
    }
    /*----------------------------------------------------------------------*/
    
    /* out - outputs supplied string to Ethernet client */
    void out(const char *s) {
    
    client.write( (const uint8_t*)s, strlen(s) );
    
    #ifdef DEBUG
    Serial.write( (const uint8_t*)s, strlen(s) );
    #endif
    }
    /*----------------------------------------------------------------------*/
    /* sonos - sends a command packet to the ZonePlayer */
    void sonos(int cmd, char *resp1, char *resp2) {
    //char buf[512];
    char buf[380];
    char cmdbuf[32];
    char extra[64];
    char service[20];
    char *ptr1;
    char *ptr2;
    char *optr;
    char copying;
    unsigned long timeout;
    extra[0] = 0;
    strcpy(service, "AVTransport");
    //delay(2000);
    if (client.connect(sonosip, 1400)) {
    #ifdef DEBUG
    Serial.println("connected");
    #endif
    /*
    * prepare the data strings to go into the desired command
    * packet
    */
    switch (cmd) {
    case SONOS_PLAY:
    strcpy(cmdbuf, "Play");
    strcpy(extra, "<Speed>1</Speed>");
    break;
    case SONOS_PAUSE:
    strcpy(cmdbuf, "Pause");
    break;
    case SONOS_GETPLAYSTATUS:
    strcpy(cmdbuf, "GetTransportInfo");
    strcpy(resp1, "CurrentPlayStatus");
    break;
    }
    
    
    
    
    /* output the command packet */
    sprintf(buf, "POST /MediaRenderer/%s/Control HTTP/1.1\r\n", service);
    out(buf);
    out("Connection: close\r\n");
    sprintf(buf, "Host: %d.%d.%d.%d:1400\r\n", sonosip[0], sonosip[1], sonosip[2], sonosip[3]);
    out(buf);
    sprintf(buf, "Content-Length: %d\r\n", 231 + 2 * strlen(cmdbuf) + strlen(extra) + strlen(service));
    out(buf);
    out("Content-Type: text/xml; charset=\"utf-8\"\r\n");
    sprintf(buf, "Soapaction: \"urn:schemas-upnp-org:service:%s:1#%s\"\r\n", service, cmdbuf);
    out(buf);
    out("\r\n");
    sprintf(buf, "%s<u:%s%s%s%s%s</u:%s>%s\r\n", SONOS_CMDH, cmdbuf, SONOS_CMDP, service, SONOS_CMDQ, extra, cmdbuf, SONOS_CMDF);
    out(buf);
    /* wait for a response packet */
    timeout = millis();
    while ((!client.available()) && ((millis() - timeout) < 1000));
    /*
    * parse the response looking for the strings in resp1 and
    * resp2
    */
    ptr1 = resp1;
    ptr2 = resp2;
    copying = 0;
    while (client.available()) {
    char c = client.read();
    /*
    * if response buffers start with nulls, either no
    * response required, or already received
    */
    if (resp1[0] || resp2[0]) {
    /*
    * if a response has been identified, copy
    * the data
    */
    if (copying) {
    /*
    * look for the < character that
    * indicates the end of the data
    */
    if (c == '<') {
    /*
    * stop receiving data, and
    * null the first character
    * in the response buffer
    */
    copying = 0;
    *optr = 0;
    if (copying == 1)
    resp1[0] = 0;
    else
    resp2[0] = 0;
    } else {
    /*
    * copy the next byte to the
    * response buffer
    */
    *optr = c;
    optr++;
    }
    } else {
    /*
    * look for input characters that
    * match the response buffers
    */
    if (c == *ptr1) {
    /*
    * character matched -
    * advance to next character
    * to match
    */
    ptr1++;
    /*
    * is this the end of the
    * response buffer
    */
    if (*ptr1 == 0) {
    /*
    * string matched -
    * start copying from
    * next character
    * received
    */
    copying = 1;
    optr = resp1;
    ptr1 = resp1;
    }
    } else
    ptr1 = resp1;
    /*
    * as above for second response
    * buffer
    */
    if (c == *ptr2) {
    ptr2++;
    if (*ptr2 == 0) {
    copying = 2;
    optr = resp2;
    ptr2 = resp2;
    }
    } else
    ptr2 = resp2;
    }
    }
    
    
    
    #ifdef DEBUG
    Serial.print(c);
    
    
    #endif
    }
    } else {
    #ifdef DEBUG
    Serial.println("connection failed");
    #endif
    }
    while (client.available()) client.read(); 
    
    delay(100);
    client.stop();
    
    
    
    }
    /* End of file */
    /* ====================================================================== */

You are sending in nullbuf as resp2 and the comment for it says

If you can locate the output statement that prints the playback responses are looking for, you could use this there and then or make sure to pass that info back out of the function.

Hi ScruffR,

Thanks for the idea. Having difficulty figuring out but I will keep trying.