Arduino esp8266 JSON requests to SONOFF

I'm trying to connect to a Sonoff Mini in DIY mode to turn off and on but also need to get the on off state in my code too. I'm struggling! My code below returns 200 so connecting ok, but from the codes on Sonoff API pasted in, it just errors. I'm following the Get Device Info section on SONOFF DIY developer documentation-BASICR3/RFR3/MINI HTTP API - SONOFF Official
I have pasted stright into the body using Advanced Rest Client and it works as expected. What am I doing wrong?
Here is my code:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <Arduino_JSON.h>

const char* ssid = "MySIDD";
const char* password = "P.$$W0rd";

//Your Domain name with URL path or IP address with path
const char* serverName = "http://192.168.1.122:8081/zeroconf/info";

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
// Timer set to 10 minutes (600000)
//unsigned long timerDelay = 600000;
// Set timer to 5 seconds (5000)
unsigned long timerDelay = 5000;

String sensorReadings;
float sensorReadingsArr[3];

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
 
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");
}

void loop() {
  //Send an HTTP POST request every 10 seconds
  if ((millis() - lastTime) > timerDelay) {
    //Check WiFi connection status
    if(WiFi.status()== WL_CONNECTED){
      WiFiClient client;
      HTTPClient http;
      
      // Your Domain name with URL path or IP address with path
      http.begin(client, serverName);

      // If you need an HTTP request with a content type: application/json, use the following:
      http.addHeader("Content-Type", "application/json");
      // JSON data to send with HTTP POST
      String httpRequestData = "{\"deviceid\": \"data\"}"; 
      //{ "deviceid": "", "data": { } } tried and get error
      // Send HTTP POST request
      int httpResponseCode = http.POST(httpRequestData);
      
     
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
        
      // Free resources
      http.end();
    }
    else {
      Serial.println("WiFi Disconnected");
    }
    lastTime = millis();
  }
}

i store all my sonoff data in an array of this structure
and query the devices as below

typedef struct sDiy {
  char deviceid[12];//shortened mdns hostname eg eWelink_ir_spotty => ir_spotty
  char alexa[13];//known to alexa as
  char sw[4];//on-off
  char pulse[4];//on-off
  unsigned long  pulseWidth;
  int sig;//wifi rssi
  int port;//http port
  byte alive;// found or not
  char ip[16];//10.0.0.22
} ;

void chk_sw(sDiy *sw) {
  WiFiClient client;
  HTTPClient http;
  StaticJsonDocument<400> doc;
  String ip = sw->ip;
  if (http.begin(client, "http://" + ip + ":" + String (sw->port) + "/zeroconf/info")) { // scrape solarPanelVolts
    int httpCode = http.POST("{\"deviceid\": \"\",\"data\": { }}");
    if (httpCode > 0) {
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        //        Serial.println(payload);
        lastck = millis();
        DeserializationError error = deserializeJson(doc, payload );
        if (error == 0) {
          //                   serializeJsonPretty(doc, Serial);
          if (doc.containsKey("data")) {
            auto tmp = doc["data"]["switch"].as<char*>();
            strncpy(sw->sw, tmp, 3);
            //auto tmp1 = doc["data"]["deviceid"].as<char*>();
            //strncpy(sw->deviceid, tmp1, 11);
            auto tmp2 = doc["data"]["pulse"].as<char*>();
            strncpy(sw->pulse, tmp2, 3);
            sw->pulseWidth = doc["data"]["pulseWidth"];
            sw->sig = doc["data"]["signalStrength"];
            lastck = millis();
          }
        }
      }
    }
    http.end();
  }
}

Thanks for your reply, I'll have a go at that in a bit. How do you get from:-
{
"deviceid": "",
"data": { }
}
to the referenced code above?
I think i need to understand what the conversion is so I can progress.

in a " "delimited str the only way to include a " chr is to use an escape sequence so " becomes \"
oops looks like a stray ) crept in my post which i will correct
http.POST("{"deviceid": "","data": { }})");
should be
http.POST("{"deviceid": "","data": { }}");

You can also skip all the escaping and use a "raw string"

char payload[] = R"({
"deviceid": "",
"data": { }
})";
...

Then would you just use http.POST = payload;

Im not used to C programming as i got stuck in the vb.net realm after inheriting vb from a predecessor, so all your help is greatly appreciated.
I managed to get a massive string of all the data but now need to split it up into the individual items.
Failing to use doc the way you have as it keeps saying StaticJasonDocument not declared. I might just start afresh and use your code completely. After i get this working, the next task is reading modbus from inverter. Yes, im turning an appliance off and on when solar producing enough power.

You will need the ArduinoJson library

and include it in your ino file ie.
#include <ArduinoJson.h>

Had the wrong JSON library installed.

no, you would pass it as a parameter to POST

http.POST( payload );

Can you see what is wrong with this? I'm trying to post so the switch turns on now. This isn't doing anything though.

int httpResponseCode = http.POST("{\"deviceid\": \"\",\"data\": {\"switch\": \"on\"}}");

snippets of code like that are pretty useless to try and debug
that is not a way that i would use to place data into a post request body

i use this where "char* cd " is either on or off

void set_sw(sDiy *sw, char* cd ) {
  WiFiClient client;
  HTTPClient http;
  StaticJsonDocument<400> doc;
  String ip =  sw->ip;
  //  Serial.println();
  //  Serial.print(ip);
  Serial.print(cmd);
  if (http.begin(client, "http://" + ip + ":" + String (sw->port) + "/zeroconf/switch")) {
    int httpCode = http.POST("{\"deviceid\": \"\",\"data\": {\"switch\":\"" + String (cmd) + "\"}}");
    if (httpCode > 0) {
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        //Serial.println(payload);
        DeserializationError error = deserializeJson(doc, payload );
        if (error == 0) {
          serializeJsonPretty(doc, Serial);
          if (doc.containsKey("error")) {
            int err = doc["error"];
            if (err == 0)
             strncpy(sw->sw, cd, 3);
          }
        }
      }
    }
    http.end();
  }
}

Thanks for that. I was being stupid too and forgot to change the address from info to switch.
/zeroconf/switch

I'll have another go at using a raw string that blh64 mentioned too. Failed last time but this could have been because of the wrong address.

Morning racpi, I noticed you haven't told the switch to turn off or on. If you leave blank does it just change the state?

when I call the set_sw(sDiy sw, char cd ) function
the parameter "char* cd " will equal "on" or "off"
my snippet was not quite correct cmd should have been cd

my actual code , i had redacted it a bit [poorly alas] not to confuse the issue because
i am not sure if standard diy sonoffs accept a value along with on or off
i have customized all of mine several times over and forget what original
even looked like
richard

void set_sw(sDiy *sw, char* cd , int amt) {
 WiFiClient client;
 HTTPClient http;
 StaticJsonDocument<400> doc;
 String ip =  sw->ip;
 String cmd = cd;
 //  Serial.println();
 //  Serial.print(ip);
 Serial.print(cmd);
 if (http.begin(client, "http://" + ip + ":" + String (sw->port) + "/zeroconf/switch")) {
   int httpCode = http.POST("{\"deviceid\": \"\",\"data\": {\"switch\":\"" + cmd + "\",\"val\":" +  String(amt) + "}}");
   if (httpCode > 0) {
     if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
       String payload = http.getString();
       Serial.println(payload);
       DeserializationError error = deserializeJson(doc, payload );
       if (error == 0) {
         serializeJsonPretty(doc, Serial);
         if (doc.containsKey("error")) {
           int err = doc["error"];
           //if (err == 0)
           // strncpy(sw->sw, cd, 3);
         }
       }
     }
   }
   http.end();
 }
}

since i was assembling the Strings on the fly the raw literal has no advantage that i can see other than its a bit easier on the eyes

All working now so thanks for your help. Still confused by the breakout for strings but viewing your examples helped a lot.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.