Change REST url

Hi,

Normally you can send commands to the arduino with REST:

www.arduino.local/arduino/digital/13/1 writes digitalWrite(13, HIGH);.

Can I change the highlighted text to an another command?

Normally:

if (command == "digital") {
    // CODE
}

What I want:

if (command == "writePin") {
    // CODE
}

floriaan15:
Can I change the highlighted text to an another command?

Yes.

if (command == "writePin") {

// CODE
}

And there you go, you just did it. 8)

When the Linux side of the Yun receives an HTTP request of the form http://arduino.local/arduino/whatever/you/want, it sees that the first token is /arduino/ so it then passes everything after that (whatever/you/want) to the sketch for processing. That text can be whatever you want it to be, it all depends on the code you write to parse it. Make it as easy (preferable) or complicated as you want, and add any commands and values you want. Make it your own. That example is just that -- an example, and not an indication of what commands you should include.

The only real restriction is that the URL must be a properly formatted URL.

ShapeShifter:
Yes.

And there you go, you just did it. 8)

When the Linux side of the Yun receives an HTTP request of the form http://arduino.local/arduino/whatever/you/want, it sees that the first token is /arduino/ so it then passes everything after that (whatever/you/want) to the sketch for processing. That text can be whatever you want it to be, it all depends on the code you write to parse it. Make it as easy (preferable) or complicated as you want, and add any commands and values you want. Make it your own. That example is just that -- an example, and not an indication of what commands you should include.

The only real restriction is that the URL must be a properly formatted URL.

Yes,

but I mean that if I change the function that is nested in the if-statement that doesn't work.

This is what I copied from the bridge example at arduino.cc:

if (command == "writePin") {
     digitalCommand(client);
}

And this is what I want...

if (command == "writePin") {
    anotherfunction(client);
}

But that does not work.

floriaan15:
But that does not work.

And what do you mean by that? It's such an open-ended comment it could mean just about anything.

Does it not compile? Does it not get called? Does it get called but not do what you want? Does trying to access the URL give an error?

Please give us a more descriptive explanation of the problem. Also post the complete sketch, not just a snippet, and also show us the exact URL you are using to try and access that function.

The little piece of code you posted looks reasonable. So the problem must be somewhere in the bigger picture. And it's probably going to a subtle little detail that will be missed without the complete picture.

ShapeShifter:
And what do you mean by that? It's such an open-ended comment it could mean just about anything.

Does it not compile? Does it not get called? Does it get called but not do what you want? Does trying to access the URL give an error?

Please give us a more descriptive explanation of the problem. Also post the complete sketch, not just a snippet, and also show us the exact URL you are using to try and access that function.

The little piece of code you posted looks reasonable. So the problem must be somewhere in the bigger picture. And it's probably going to a subtle little detail that will be missed without the complete picture.

Here is the code:

#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>

// Listen to the default port 5555, the Yún webserver
// will forward there all the HTTP requests you send
YunServer server;

void setup() {
  int myPins[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13};
  pinMode(13, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(7, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);

  server.begin();
}

void loop() {
  // Get clients coming from server
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    client.setTimeout(5); // improves server speed
    // Process request
    process(client);

    // Close connection and free resources.
    client.stop();
  }

  delay(50); // Poll every 50ms
}

void process(YunClient client) {
  // read the command
  String command = client.readStringUntil('/');
  if (command == "writePin") {
    writePin(client); // will not work
  }
  if (command == "digital") {
    digitalCommand(client);
  } 
 
}
void writePin(YunClient client) {
  int i = 0;
  while(i < 14) {
    // Send feedback to client
  client.print(F("
Pin D"));
  client.print(i);
  client.print(F(" set to "));
  client.println(digitalRead(i));
  i++;
  }
  i=0;
  while(i < 6) {
    // Send feedback to client
  client.print(F("
Pin A"));
  client.print(i);
  client.print(F(" set to "));
  client.println(analogRead(i));
  i++;
  }
}
void digitalCommand(YunClient client) {
  int pin, value;
  int myPins[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13};
  // Read pin number
  pin = client.parseInt();

  // If the next character is a '/' it means we have an URL
  // with a value like: "/digital/13/1"
  if (client.read() == '/') {
    value = client.parseInt();
    digitalWrite(pin, value);
  }
  else {
    value = digitalRead(pin);
  }
  int i = 0;
  while(i < 13) {
    // Send feedback to client
  client.print(F("
Pin D"));
  client.print(i);
  client.print(F(" set to "));
  client.println(digitalRead(i));
  i++;
  }
  i=0;
  while(i < 6) {
    // Send feedback to client
  client.print(F("
Pin A"));
  client.print(i);
  client.print(F(" set to "));
  client.println(analogRead(i));
  i++;
  }
  // Update datastore key with the current pin value
  String key = "D";
  key += pin;
  Bridge.put(key, String(value));
}

The function writePin() doesn't work. I don't know where the bug is.
If I place digitalWrite or client.print in the function writePin, it will not be called.

Try to use only lower case character like "writepin".

Maybe the string that comes from the linux side is transformed to lower case so the condition "writepin" == "writePin" fails.

Angelo9999:
Try to use only lower case character like "writepin".

Maybe the string that comes from the linux side is transformed to lower case so the condition "writepin" == "writePin" fails.

Yes! Thank you Angelo9999! It worked, I changed the writePin to writepin.

Angelo9999:
Maybe the string that comes from the linux side is transformed to lower case so the condition "writepin" == "writePin" fails.

Wow, why would it do that? In my mind, that's a serious limitation. URLs are meant to be case sensitive (once you get past the domain name) so why would the REST API code break that? I could see where the case of a REST API call could be significant (like when trying to pass a string variable value.)

Good catch, I wouldn't have thought of that.

RFC 2616 ( DRAFT STANDARD ): URIs case-insensitive

RFC 7230 (PROPOSED STANDARD): URIs case-sensitive, The Yun OS (linux) follow.

PROPOSED STANDARD is a bit less definitive than DRAFT STANDARD.

Change back to RFC 2616;-

if (command.toLowerCase() == "digital") {
    // CODE
}


if (command.toLowerCase() == "writepin") {
    // CODE
}

sonnyyu:
URIs case-sensitive, The Yun OS (linux) follow.

I'm confused. It sounds like you are saying the Yun follows the standard where URLs are case sensitive. But this clearly isn't the case.

I wasn't aware of the difference in the RFCs (there are far too many for the average person to know them all.) But it would seem that URLs are generally case sensitive, since when I get the case wrong on a directory name or filename in a URL, the file isn't found. At least that' the case with the servers running my domains, which run Apache on Linux boxes. I suppose directory and filenames wouldn't be case sensitive on Windows servers, but that would be because the Windows file systems are not case sensitive, not because the server is changing the case of the URLs.

Linux has a case sensitive file system. It makes no sense for the Yun to be changing the case of any of the URLs that it receives, as it would tend to break access to files that have a different case than the converted URL, or for code that is expecting a different case structure in the URL (like in the example in this thread.)

It's one thing to make URL detection case-insensitive, but it's another to mangle the case of valid URL strings. Yes, converting to all lower case is one way to do a case-insensitive string comparison, but that only works when the code has access to both the source and comparison strings, and can convert both to lower case. In this instance, that isn't the case, as there is no access to the comparison string, therefore no control over the case of the comparison string, and therefore unilaterally converting the source string to lower case seems inappropriate and a bad idea.

If the server wants to convert it to lower case so it can do a case-insensitive comparison for "/arduino/" at the beginning of the URL, that's fine. But it should pass the original unmodified string to the sketch, not the lower case converted version.

At URI, host/domain names and protocol are not case-sensitive cross board.

"http://forum.arduino.cc/index.php?topic=313885.0"

"http"="Http"

"forum.arduino.cc"="Forum.arduino.Cc"

RFC 1738: Domain names are not case-sensitive

Microsoft IIS is case-insensitive (RFC 2616)

But with Isapi app (Internet Server Application Programming Interface ) could convert it to RFC 7230.

Apache is case-sensitive (RFC 7230)

But with mod_speling module, which is part of the standard apache distribution. back to RFC 2616

CheckSpelling On
CheckCaseOnly On

Flip coin to decide which way to go?

I vote myself:

if (command.toLowerCase() == "digital") {
    // CODE
}

Since "http://www.arduino.local/arduino/digital/13/1" is input from end user, you can not control user type it as "http://www.arduino.local/arduino/Digital/13/1".

ShapeShifter:
...
Linux has a case sensitive file system. It makes no sense for the Yun to be changing the case of any of the URLs that it receives, as it would tend to break access to files that have a different case than the converted URL, or for code that is expecting a different case structure in the URL (like in the example in this thread.)

It's one thing to make URL detection case-insensitive, but it's another to mangle the case of valid URL strings. Yes, converting to all lower case is one way to do a case-insensitive string comparison, but that only works when the code has access to both the source and comparison strings, and can convert both to lower case. In this instance, that isn't the case, as there is no access to the comparison string, therefore no control over the case of the comparison string, and therefore unilaterally converting the source string to lower case seems inappropriate and a bad idea.
...

At OP example, there is no file system access it is free run. But at other hand if file system access exist then you are right.

sonnyyu:
At OP example, there is no file system access it is free run.

Exactly. Which is why I don't think it's correct to make any changes to the incoming URL. The server has no control over what the sketch will be doing with the URL string, so I feel that the server has no right to make any changes to it. If the person writing the sketch wants it to be case-insensitive, let them convert it to lower case before making the comparisons.

ShapeShifter:
Exactly. Which is why I don't think it's correct to make any changes to the incoming URL. The server has no control over what the sketch will be doing with the URL string, so I feel that the server has no right to make any changes to it. If the person writing the sketch wants it to be case-insensitive, let them convert it to lower case before making the comparisons.

@ShapeShifter,
don't forget the CGI on the linux side is controlled by LUCI, and that documentation is poor. I still have not figure out LUCI, and how the pieces fit together; it's a blackbox to me..

Jesse

jessemonroy650:
don't forget the CGI on the linux side is controlled by LUCI

Ok, but it doesn't matter where the URL mangling is happening, I personally don't think it should be done.

Suppose you're writing a REST API to display messages to an LCD. HTTP://arduino.local/arduino/LCD/show/Hello will result in the display of "hello" and not "Hello" as expected. :frowning:

And yes, it's indeed a deep, dark, black box.