Help with HTTP Post using Arduino YUN

Hello community, this is my first topic, I'm new to Arduino and I'm trying to send using HTTP Posts and eventually read using HTTP get, let me explain my setup;

I have a home automation controller with web connection that accepts HTTP requests, I have tested these using my cellphone (NFC tags), IFTTT and even through the web browser. I want to create a new interface for sensors which are not supported by this home automation controller and send/receive data using HTTP request but all my attempts have been unsuccesful.

As I mentioned I'm new to Arduino, I did some testing with RFID transmitter/receiver and I2C LED display succesfully but I cannot make the HTTP post to work. This is the POST command I need to send;

URL = https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150
No need of headers or Body, for obvious reasons I have swaped MYSERIAL and MYAPIKEY, I have found many examples for HTTP post using YUN but most of them use complicated functions to assemble the HTTP request, also I understand there are different ways of doing it. I picked up the most simple one I could find and I modified it so it looks like this.

#include <Bridge.h>
#include <Process.h>

void setup() {
// put your setup code here, to run once:
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
Bridge.begin();
Serial.begin(9600);
while (!Serial); // wait for Network Serial to open
Serial.println("Zipato Post");
digitalWrite(3, HIGH);
}

void loop() {
// put your main code here, to run repeatedly:
delay(5000); // wait for a second
digitalWrite(2, HIGH); // turn the LED on (HIGH is the voltage level)
postToZipato();
delay(5000);
digitalWrite(2, LOW); // turn the LED off by making the voltage LOW
}

void postToZipato() {
Process pZbox;
pZbox.begin("curl");
//pZbox.addParameter("-k"); // allow insecure (not https)
pZbox.addParameter("-X"); // use POST instead of default GET
pZbox.addParameter("POST");
pZbox.addParameter("-H"); // Any headers go after -H
pZbox.addParameter("Content-Type:application/x-www-form-encoded");

pZbox.addParameter("-d");
pZbox.addParameter("150");
pZbox.addParameter("https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150");
pZbox.runAsynchronously();
Serial.println("https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150");
}

Provide a link to the manual page of that web call. It seems that you mix POST and GET calls. Usually the parameters in a POST request are sent in the body and not as part of the URL. It might be that your service has a broken API that does such bad stuff but I cannot tell without seeing the manual.

Unfortunatelly there is no proper manual to their API, but I know it works because I can use maker channel on IFTTT to make a post request with that URL without body or headers and it does work...

https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150

You can even use any web browser and type that URL and the value 150 will be sent to the virtual device, what I really don't understand is how to assemble the HTTP request on Arduino.

Other attempts without look...

#include "Bridge.h"

#include "HttpClient.h"

//Zipato Settings

String ZipatoAPI = "https://my.zipato.com/zipato-web/remoting/attribute/";;

String MySerial = "MyZipatoSerial";

String MyAPIkey = "MyAPIkey";

String ValueString="value15=150";

const int PostInterval = 15000;

// Variable Setup

long lastConnectionTime = 0;

void setup()

{

pinMode(LED_BUILTIN, OUTPUT);

Bridge.begin();

Serial.begin(9600);

}

void loop(){

delay (1000);

digitalWrite(LED_BUILTIN,HIGH);

HTTP_PostZipato1(ZipatoAPI,MySerial,MyAPIkey,ValueString);

delay(2000);

HTTP_PostZipato2(ZipatoAPI,MySerial,MyAPIkey,ValueString);

delay(2000);

HTTP_PostZipato3(ZipatoAPI,MySerial,MyAPIkey,ValueString);

;

delay(PostInterval);

digitalWrite(LED_BUILTIN,LOW);

}

void HTTP_PostZipato(String ZipatoAPI, String MySerial, String MyAPIkey, String ValueString)

{

//https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MyZipatoSerial&apiKey=MyAPIkey&value15=150

HttpClient client;

String talkBackCommand;

char Response;

String URL = ZipatoAPI + "set?serial=" + MySerial + "&apiKey=" + MyAPIkey + ValueString;

client.get(URL);

Serial.println(URL);

while (client.available()) {

Response = client.read();

talkBackCommand += Response; // talkBackCommand = talkBackCommand + CharIn

Serial.println(talkBackCommand);

}

}

void HTTP_PostZipato2(String ZipatoAPI, String MySerial, String MyAPIkey, String ValueString)

{

String URL = ZipatoAPI + "set?serial=" + MySerial + "&apiKey=" + MyAPIkey + ValueString;

Process p;

p.runShellCommand(URL);

Serial.println(URL);

while(p.running());

// Read command output.

while (p.available()) {

int result = p.parseInt();

Serial.println(result);

}

}

void HTTP_PostZipato3(String ZipatoAPI, String MySerial, String MyAPIkey, String ValueString)

{

//POST https://my.zipato.com/zipato-web/remoting/attribute/set?value15=150&ep=MySerial&serial=MyZipatoSerial&apiKey=MyAPI

HttpClient client;

String talkBackCommand;

char Response;

String URL = "POST https://my.zipato.com/zipato-web/remoting/attribute/set?value15=150&ep=MySerial&serial=MyZipatoSerial&apiKey=MyAPI";;

client.get(URL);

Serial.println(URL);

while (client.available()) {

Response = client.read();

talkBackCommand += Response; // talkBackCommand = talkBackCommand + CharIn

Serial.println(talkBackCommand);

}

}

Put code tags around that code, that's completely unreadable! (that's that </> button in the upper left corner of the editor)

Amacias:
Hello community, this is my first topic, I'm new to Arduino and I'm trying to send using HTTP Posts and eventually read using HTTP get, let me explain my setup;

I have a home automation controller with web connection that accepts HTTP requests, I have tested these using my cellphone (NFC tags), IFTTT and even through the web browser. I want to create a new interface for sensors which are not supported by this home automation controller and send/receive data using HTTP request but all my attempts have been unsuccesful.

As I mentioned I'm new to Arduino, I did some testing with RFID transmitter/receiver and I2C LED display succesfully but I cannot make the HTTP post to work. This is the POST command I need to send;

URL = https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150
No need of headers or Body, for obvious reasons I have swaped MYSERIAL and MYAPIKEY, I have found many examples for HTTP post using YUN but most of them use complicated functions to assemble the HTTP request, also I understand there are different ways of doing it. I picked up the most simple one I could find and I modified it so it looks like this.

void postToZipato() {

Process pZbox;
 pZbox.begin("curl");
 //pZbox.addParameter("-k"); // allow insecure (not https)
 pZbox.addParameter("-X"); // use POST instead of default GET
 pZbox.addParameter("POST");
 pZbox.addParameter("-H"); // Any headers go after -H
 pZbox.addParameter("Content-Type:application/x-www-form-encoded");
 
 pZbox.addParameter("-d");
 pZbox.addParameter("150");
 pZbox.addParameter("https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150");
 pZbox.runAsynchronously();
 Serial.println("https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&value15=150");
}

I think your Curl parameters are incorrect.

based on what I can gleen:

curl -d "serial=MYSERIAL&apiKey=MYAPIKEY&value15=150" https://my.zipato.com/zipato-web/remoting/attribute/set

or possibly this

curl -d "value15=150" https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY

Those are all the commands you should pass to curl.

The -X -H are wrong.

the -H Content-Type:application/x-www-form-encoded"

Is telling the server to expect "key=data" pairs in the message body, but you are not doing that with your code.

Get your Curl commands functioning before you move it to the Arduino.

Run Curl from a console prompt to verify your server responds as expected first.

Chuck.

Hello again,

Thanks a lot for your responses. I apologize, next time I will use code button.

@chucktodd,

While looking for some curl information I came across this website www.onlinecurl.com, I made at test and posted the address with the POST request I use to write values to my virtual meter and got the response attached, I can confirm it changes the value on my system succesfuly. Then using the same webpage I use the curl command provided (which is basically adding "curl" to the beginning of the string) I also get a response and a succesful write to the virtual meter.

Then I went to the Arduino YUN opened terminal and typed the string as shown in the command part, no response, post request was not succesful.

I also tried this on the YUN without luck;

#include "Process.h"

const int PostInterval = 15000; 

Process Zbox;

void setup()
  {
  pinMode(LED_BUILTIN, OUTPUT);
  Bridge.begin();
  Serial.begin(9600);
  }
void loop(){
  delay (1000);
  digitalWrite(LED_BUILTIN,HIGH);
  Zbox.begin("curl https://my.zipato.com/zipato-web/remoting/attribute/set?serial=MYSERIAL&apiKey=MYAPIKEY&ep=MYEP&value1=333");
  Zbox.run();
  delay(PostInterval);
  digitalWrite(LED_BUILTIN,LOW);
  }

Any thought?

Hello All;

After a lot of searching I found another library which made possible my POST command, I think part of the solution were the JSON headers, see code below;

#include <Bridge.h>
#include <BridgeHttpClient.h>

BridgeHttpClient client;

// Define Constants
const String ZboxURL="https://my.zipato.com/zipato-web/remoting/attribute/";
const String ZboxSerial="MYSERIAL";
const String ZboxApiKey="MYAPIKEY";
const String ZboxEp="MYEP";
const String ZboxValue="value15";

//Define Variables
String ZipatoPostURL;
int ValueOut;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  Bridge.begin(); // Initialize Bridge
  digitalWrite(LED_BUILTIN, HIGH);

  SerialUSB.begin(9600);
  while (!SerialUSB); // wait for a serial connection

  ValueOut=78; //Generate Random Value
  char * ZipatoPostChar=ZipatoBuildPost(ZboxURL,ZboxSerial,ZboxApiKey,ZboxEp,ZboxValue,ValueOut); //Build Zipato URL using ZipatoBuildPost function
      
  SerialUSB.println(ZipatoPostChar);  //DO NOT REMOVE, FOR SOME REASON IT DOES NOT BUILD THE CHAR IF REMOVED
  ZipatoAsyncPost(ZipatoPostChar);  //SEND ZIPATO ASYNC POST
  SerialUSB.print("Sending request");
}

void loop() {
  
  if (client.finished()) {

    SerialUSB.println();
    SerialUSB.println("Response Body:");
    while (client.available() > 0) {
      char c = client.read();
      SerialUSB.print(c);
    }

    SerialUSB.print(" Response Code: ");
    SerialUSB.println(client.getResponseCode());

    while (1) {} // stop

  } else {
    // not finished yet, wait and retry
    SerialUSB.print(".");
    delay(100);
  }
}

//BUILD ZIPATO STRING FUNCTION
char * ZipatoBuildPost(String Zurl, String Zserial, String Zapi, String Zep, String Zvalue,int MtrVal){
  String result;
  result=Zurl+"set?serial="+Zserial+"&apiKey="+Zapi+"&ep="+Zep+"&"+Zvalue+"=";
  result += MtrVal;
  int str_len = result.length()+1; //Plus 1 for null terminator
  char resultchar[200];
  result.toCharArray(resultchar,str_len);
  return resultchar;
  }

//ZIPATO ASYNC POST FUNCTION
void ZipatoAsyncPost(char * JSONpost){
  client.addHeader("Accept: application/json");
  client.addHeader("Content-Type: application/json");
  client.enableInsecure(); // Using HTTPS and peer cert. will not be able to auth.
  client.postAsync(JSONpost, "");
}

I have a question, perhaps you know the answer. If I remove the line "SerialUSB.println(ZipatoPostChar); //DO NOT REMOVE, FOR SOME REASON IT DOES NOT BUILD THE CHAR IF REMOVED" just before calling the ASYNC Post function call the POST request does not work, I get a response 0 from server.

Amacias:
Hello All;

After a lot of searching I found another library which made possible my POST command, I think part of the solution were the JSON headers, see code below;

  char * ZipatoPostChar=ZipatoBuildPost(ZboxURL,ZboxSerial,ZboxApiKey,ZboxEp,ZboxValue,ValueOut); //Build Zipato URL using ZipatoBuildPost function

SerialUSB.println(ZipatoPostChar);  //DO NOT REMOVE, FOR SOME REASON IT DOES NOT BUILD THE CHAR IF REMOVED
 ZipatoAsyncPost(ZipatoPostChar);  //SEND ZIPATO ASYNC POST
 SerialUSB.print("Sending request");
}

//BUILD ZIPATO STRING FUNCTION
char * ZipatoBuildPost(String Zurl, String Zserial, String Zapi, String Zep, String Zvalue,int MtrVal){
 String result;
 result=Zurl+"set?serial="+Zserial+"&apiKey="+Zapi+"&ep="+Zep+"&"+Zvalue+"=";
 result += MtrVal;
 int str_len = result.length()+1; //Plus 1 for null terminator
 char resultchar[200];
 result.toCharArray(resultchar,str_len);
 return resultchar;
 }

//ZIPATO ASYNC POST FUNCTION
void ZipatoAsyncPost(char * JSONpost){
 client.addHeader("Accept: application/json");
 client.addHeader("Content-Type: application/json");
 client.enableInsecure(); // Using HTTPS and peer cert. will not be able to auth.
 client.postAsync(JSONpost, "");
}




I have a question, perhaps you know the answer. If I remove the line "SerialUSB.println(ZipatoPostChar); //DO NOT REMOVE, FOR SOME REASON IT DOES NOT BUILD THE CHAR IF REMOVED" just before calling the ASYNC Post function call the POST request does not work, I get a response 0 from server.

What you are experiencing is a dangling pointer.
The code:

  char * ZipatoPostChar=ZipatoBuildPost(ZboxURL,ZboxSerial,ZboxApiKey,ZboxEp,ZboxValue,ValueOut);

allocates 2 bytes of RAM to contain a pointer to a string of characters.

char * ZipatoBuildPost(String Zurl, String Zserial, String Zapi, String Zep, String Zvalue,int MtrVal){
  String result;
  result=Zurl+"set?serial="+Zserial+"&apiKey="+Zapi+"&ep="+Zep+"&"+Zvalue+"=";
  result += MtrVal;
  int str_len = result.length()+1; //Plus 1 for null terminator
  char resultchar[200];
  result.toCharArray(resultchar,str_len);
  return resultchar;
  }

creates a temporary String object on the Stack, concatenates all of your String values into one big String.

Then allocates a 200 characters block on the Stack,

Then copies the concatenated Strings into this temporary Stack buffer,

Then returns a pointer to this temporary Stack buffer.

At which time that temporary Stack buffer is no longer valid (reserved).

Here is how you could actually accomplish what you want:

const char ZboxURL="https://my.zipato.com/zipato-web/remoting/attribute/";
const char ZboxSerial="MYSERIAL";
const char ZboxApiKey="MYAPIKEY";
const char ZboxEp="MYEP";
const char ZboxValue="value15";

void loop(){

#define BUFLEN 200
  char ZipatoPostChar[BUFLEN]; // allocate 200 bytes for your postchar buffer

  bool success = ZipatoBuildPost(&ZipatoPostChar,BUFLEN,ValueOut);  

  if(success) { // actually use the bult ZipatoPostChar buffer

// other code

    }
  else { // building post buffer failed, describe error
    SerialUSB.println("Building Post Command failed.");
    SerialUSB.println(ZipatoPostChar);
    SerialUSB.print(" ZipatoPostChar length =");
    SerialUSB.println(strlen(ZipatoPostChar),DEC);
    }
}

// constrained string append helper function

bool append(char *buf, uint16_t bufLen, const char appendStr[]){
// is there room in buf[] to append appendStr[] ?
bool good =(strlen(buf)+strlen(appendStr))<bufLen;
if(good){ // actually append the string
  strcat(buf,appendStr);
 }
else ; // do nothing except return an error
return good;
}

bool ZipatoBuildPost(char* buf, uint16_t bufLen, int MtrVal){
bool good=true;
buf[0] ='\0'; // empty buffer
// append each section to buf, fail if longer than allocated buffer.
if(good) good=append(buf,bufLen,Zurl);
if(good) good=append(buf,bufLen,"set?serial=");
if(good) good=append(buf,bufLen,Zserial);
if(good) good=append(buf,bufLen,"&apiKey=");
if(good) good=append(buf,bufLen,Zapi);
if(good) good=append(buf,bufLen,"&ep=");
if(good) good=append(buf,bufLen,Zep);
if(good) good=append(buf,bufLen,"&");
if(good) good=append(buf,bufLen,Zvalue);
if(good) good=append(buf,bufLen,"=");
if(good&&((strlen(buf)+6)<bufLen)) { // room to add value, maximum positive int is 5 digits 
  sprintf(&buf[strlen(buf)],"%d",MtrVal); // convert MtrVal to character representation at end of buf[]
  }
return good;
}

I don't like the String object, it is inefficient, causes memory fragmentation, which results in unstable programs.

Chuck.