[SOLVED] Php curl post to particle function (New issue, multi curl not working)

I have a question regarding php and curl to particle functions. I am using curl instead of js to do my post because I am trying to have this done via a cron job. I am pretty new with the particle functions and I am having some trouble getting the result I was expecting. I think that the curl code is working but I am confused why is it not triggering the function to turn the relay on.

Here is the original js code that does work.

  var deviceID    = "xxxxxxxx";
  var accessToken = xxxxxxxxxxxxxxxxxxxxxxx";
	 
  function setRelay() {
	  
	  var id = "2";
	  var setRelay = "relay" + id; //Set Relay Variable
	  var statusRelay = "on"; 	
 		
		var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + setRelay + "/";
        $.post( requestURL, { params: statusRelay, access_token: accessToken });
 
  }

  setRelay();

Here is the new curl code that seems to work but my relay does not come on.

    $my_device = "XXXXXXXX";
    $my_token = "XXXXXXXXXXXXXXXXXXXXXXXx";
    $url = "https://api.particle.io/v1/devices/".$my_device."/relay2?access_token=".$my_token;
    $ch = curl_init( $url );
    curl_setopt( $ch, CURLOPT_POST, true);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt( $ch, CURLOPT_POSTFIELDS, array('args' => "on"));
    $response = curl_exec( $ch );
    curl_close( $ch );

    echo $response;

Here is the response

{"id":"xxxxxxxxxxxxxxxxxx","last_app":"","connected":true,"return_value":-1}

Try moving the access token from the url to the POSTFIELDS array. That should do it.

So I have tried this code but I get the following error.

$my_device = "xxxxxxxxxxxx";
$my_token = "Authorization: Bearer xxxxxxxxxxxxxxxxxxxx";
$url = "https://api.particle.io/v1/devices/".$my_device."/relay2";
$ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_POST, true);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt( $ch, CURLOPT_POSTFIELDS, array('args' => "on", 'access_token' => $my_token));
$response = curl_exec( $ch );
curl_close( $ch );
{"error":"invalid_request","error_description":"The access token was not found"}

I have tried it with and without “Authorization: Bearer”

I have also tried it this way but again I get back a response but the function does not fire.

$headers = [
	'args' => 'on',
	'access_token' => $accessToken,
];
    
	// Initialize curl
    $curl = curl_init();
	$externalscriptaddress = "https://api.particle.io/v1/devices/".$deviceID."/relay2";
    // Configure curl optionsled -d arg='on' -d access_token=my_access_token
    $opts = array(
        CURLOPT_URL             => $externalscriptaddress,
        CURLOPT_RETURNTRANSFER  => 1,
        CURLOPT_CUSTOMREQUEST   => 'POST',
        CURLOPT_POST            => 1,
		CURLOPT_HTTPHEADER		=> $headers,
    );

    // Set curl options
    curl_setopt_array($curl, $opts);
    // Get the results
    $result = curl_exec($curl);
    // Close resource
    curl_close($curl);
    echo $result;

Here is the function if that matters

 Particle.function("relay2",relayToggle2);

int relayToggle2(String command) {
    if (command=="on") {
        digitalWrite(relay2,LOW);
        Particle.variable("pos_relay2", &pos_relay2, INT);
        pos_relay2 = 1;
        return 1;
    }
    else if (command=="off") {
        digitalWrite(relay2,HIGH);
        pos_relay2 = 0;
        Particle.variable("pos_relay2", &pos_relay2, INT);
        return 0;
    }
    else {
        return -1;
    }
}

Try the format shown here:

With URL encoded name-value pairs. The POST definitely will not work with the access token in the URL.

PHP is not my usual thing so when I have questions I ask @wgbartley to pitch in! Maybe he has a better way.

1 Like

It Worked! Thanks for the help.

Here is the code that did it.

$my_device = xxxxxxxxxxxxxxx";
$url = "https://api.particle.io/v1/devices/".$my_device."/relay2";

$my_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

$fields = array(
	'args' => urlencode("off"),
	'access_token' => urlencode($my_token)
);

//url-ify the data for the POST
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string, '&');

//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);

//execute post
$result = curl_exec($ch);

//close connection
curl_close($ch);
1 Like

It's also a good idea to url encode your fields_string. See this post for more information.

Can we mark this post [SOLVED]?

1 Like

So this worked for a single request but the point of this was to add it to a cron jobs that would ultimately fire off and change multiple relays on multiply boards. So I have a new problem. I have been playing around with different methods but it continues to error after the first post with this error.
{“error”:“invalid_token”,“error_description”:“The access token provided is invalid.”}

I don’t have more than one board so I can not test it with different tokens, but I actually need to trigger multiple functions on this board first. I tried curl_multi_init but I had the same result. Is this the best method for doing this or so I try something else? Here is my project just so you know what I am doing.

I have a relay board with 8 relays, I have a schedule setup and at certain times I need to turn on some of the relays and turn off others. I have this entire routine setup in a database and each relay is set to a particle.function. Ultimately I would like to have lots of different routines setup for lots of different boards. So I could imagine that this might need to cycle through 50 - 100 change relay commands on 50 - 100 boards.

I use this to fire off any number of needed cron jobs,

$action = $_POST['action'];

////////////////////////////////////////////////////////


$url = "https://api.particle.io/v1/devices/<yourdeviceid>/<functionname>/?access_token=<accesstoken>";

$ch = curl_init($url);
curl_setopt( $ch, CURLOPT_POST, 1);
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt( $ch, CURLOPT_HEADER, 0);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt( $ch, CURLOPT_POSTFIELDS, array('arg' => $action));
$data = curl_exec($ch);

if (curl_errno($ch)) {
print "Error: " . curl_error($ch);
} else {
// Show me the result
curl_close($ch);
print $data ;
}

You can copy and paste this multiple times into one php file and run a cron on that or copy this into many files and run a cron on those.

Then we are getting different results. If I use your function I am back to the old problem of it appears that the function has been run but the command is not passed so my function is pass back -1
{“id”:“xxxxxxxxxxxxxxxxxxxxxx”,“last_app”:"",“connected”:true,“return_value”:-1}

And if I use the urlencode, the function gets the command string and triggers the relay but only the first attempt before I start getting error like no such function when I know it exists.

Keaner - I think you are on the right path because it does allow me to fire off as many as I want and I get a return each time but for some reason the args are not being passed in your method for me.

Question it is ARGS with an S correct? you had arg.

The cloud does not care what you call this.

which args?

in my code where i have “<.functionname>” make sure to use the exact name, minus the <>.

Looking at your js code from the first post your call should be

$url = "https://api.particle.io/v1/devices/<yourdeviceid>/relay2/?access_token=<accesstoken>";

Keaner could you verify this? I feel this is the correct way to do it but I am just missing something.

$action = “on”;
$url = “https://api.particle.io/v1/devices///?access_token=”;

$ch = curl_init($url);
curl_setopt( $ch, CURLOPT_POST, 1);
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt( $ch, CURLOPT_HEADER, 0);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt( $ch, CURLOPT_POSTFIELDS, array(‘arg’ => $action));
$data = curl_exec($ch);

if (curl_errno($ch)) {
print "Error: " . curl_error($ch);
} else {
// Show me the result
curl_close($ch);
print $data ;
}

When I run this I get
{“id”:“XXXXXX”,“last_app”:"",“connected”:true,“return_value”:-1}

So it connects fine but it is not passing the POST data. I have captured the results of the command String in a cloud variable and it is NULL.

It is a very simple function that I am checking against
Particle.function(“relay1”,relayToggle1);

int relayToggle1(String command) {
if (command==“on”) {
return 1;
}
else if (command==“off”) {
return 0;
}
else {
return -1;
}
}

I can get the post data to work if I do it the original way I posted but again it fails when you run them back to back.

the below string is what you make a call to. You need to call the actual function

https://api.particle.io/v1/devices/<--yourdeviceid-->/relayToggle1/?access_token=

It looks like your not including the actual function name in your url. See how in mine i have the actual function name in the url as well?

Thanks to everyone for helping I finally got the solution that worked for me. The answer was to put this process into a function and then call the function each time. Here is the final code that is working.

This is Per Rick Thanks!!!
PHP code:

<?php

changeRelay("relay1", "on");
changeRelay("relay2", "off");
changeRelay("relay3", "invalid");

function changeRelay($relay, $state) {
    $my_device = "xxxx";
    $my_token = "xxxx";

    $url = "https://api.particle.io/v1/devices/".$my_device."/".$relay;


    $fields = array(
        'args' => urlencode($state),
        'access_token' => urlencode($my_token)
    );

    //url-ify the data for the POST
    foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
    rtrim($fields_string, '&');


    //open connection
    $ch = curl_init();

    //set the url, number of POST vars, POST data
    curl_setopt($ch,CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_POST, count($fields));
    curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);

    //execute post
    $result = curl_exec($ch);

    echo "$relay $state result: $result";

    //close connection
    curl_close($ch);
}

?>

Photon code:

#include "Particle.h"

SerialLogHandler logHandler;

int relayToggle1(String command);
int relayToggle2(String command);
int relayToggle3(String command);

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

    Particle.function("relay1",relayToggle1);
    Particle.function("relay2",relayToggle2);
    Particle.function("relay3",relayToggle3);
}

void loop() {

}



int relayToggle1(String command) {
    if (command=="on") {
        Log.info("relay 1 on");
        return 1;
    }
    else if (command=="off") {
        Log.info("relay 1 off");
        return 0;
    }
    else {
        Log.info("relay 1 invalid parameter");
        return -1;
    }
}

int relayToggle2(String command) {
    if (command=="on") {
        Log.info("relay 2 on");
        return 1;
    }
    else if (command=="off") {
        Log.info("relay 2 off");
        return 0;
    }
    else {
        Log.info("relay 2 invalid parameter");
        return -1;
    }
}

int relayToggle3(String command) {
    if (command=="on") {
        Log.info("relay 3 on");
        return 1;
    }
    else if (command=="off") {
        Log.info("relay 3 off");
        return 0;
    }
    else {
        Log.info("relay 3 invalid parameter");
        return -1;
    }
}

PHP output:

{"id":"xxx","last_app":"","connected":true,"return_value":1}
relay1 on result: 1
{"id":"xxx","last_app":"","connected":true,"return_value":0}
relay2 off result: 1
{"id":"xxx","last_app":"","connected":true,"return_value":-1}
relay3 invalid result: 1

Serial output:

0000773110 [app] INFO: relay 1 on
0000773469 [app] INFO: relay 2 off
0000773766 [app] INFO: relay 3 invalid parameter