Arduino-Yun Wifi - Internet Commands?

I'm now back at my computer (was using a tablet for the post above) and I tried compiling your sketch.

At first, I only got one error: "fatal error: process.h: No such file or directory" and pointing at the first line where process.h is being included. I changed "process" to "Process" and it compiles past that point. Very curious: I'm on Windows and I thought file names were not case sensitive? But here, it seems to make a difference (looking at the examples, they all seem to use Process with a capital "P" when including the file.)

Fixing that, and recompiling, there are more errors:

  • Line 19: missing terminating " character - You have a string that starts with a double quote character (the second quote in the string is escaped with a backslash, putting the double quote character in the string) but at the end, you just have two double quote characters with no escaping backslash. The first double quote ends the string. The next double quote starts a new string with the characters ");" in it (adjacent constant strings are automatically concatenated) but then you have the end of the line, so the compiler complains about the missing double quote to terminate that second string. You could fix the error by adding another quote at the end of the string, and that would make the compiler happy, but that would not fix your problem, as you would not have a double quote as part of the devid string. You need to add a backslash between the 5 in the devid string, and the double quote that you want to be part of the string. That will include that quote character in the string itself. The next quote that follows it will then properly terminate the string instead of trying to create a new one.
  • Line 20: expected ')' before 'p' - This is an example of an error being caused by a previous error. In the line above, the compiler took the closing parenthesis and semicolon as part of the unterminated character string, so it doesn't realize you were intending to end that statement. Therefore, it reads the next statement as a continuation of the previous statement, but it doesn't make sense. So it complains that it thinks there should be a closing parenthesis there. Of course, nothing is missing there, and nothing needs to be fixed on that line. That line will compile properly when the line above it is fixed.
  • Line 25: 'c' was not declared in this scope - It's complaining that the variable c is not declared in the Serial.print(c) statement. But it is declared in the line immediately above. So why is this a problem? The line above the declaration is a while statement. That will execute the next single statement as long as the condition is true. That next statement is a new declaration scope. The statement in that scope after the while statement is the definition of the variable c, and a statement to read from the process. The scope of that variable is only that one statement associated with the while loop. The next statement (the print) is not part of the while loop, and the scope goes back to the scope that existed before the while statement, and that scope does not include the variable c. That triggers the error. The real problem is that you want to have two statements that are part of the while loop, and the solution is to use curly braces after the while statement to start a new block. Everything that is inside the opening and closing braces is treated as a single statement by the while loop. The definition of c at the beginning of that block of statements (in this case it would be two statements) remains valid until the end of the block (the closing curly brace) so the print statement will then compile.

This last error is a common one. This is the code in question:

 while (p.available()>0)
   char c = p.read();
   Serial.print(c);
 Serial.flush();

You have it indented where it appears that you want both the read() and print() statements to be part of the while loop. That would make sense. In some languages, like Python, doing that indenting is enough to make the indented statements part of the while loop. But C++ (which is what the Arduino is using) totally ignores indenting - doing the indenting is just to make it more readable for you. This is a very common error. As far as the compiler is concerned, what it's really doing is this: (using the Auto Format command from the Tools menu will reformat it to look like this, making the problem more obvious)

 while (p.available()>0)
   char c = p.read();
 Serial.print(c);
 Serial.flush();

Here, it's clear that only the read() statement is in the loop. It will read all characters, and essentially throw them away. You reach the print() statement only when the loop finishes, and at that point the variable c (as well as all of the characters that have been read) are gone and no longer accessible. The fix is to add the curly braces:

 while (p.available()>0)
 {
   char c = p.read();
   Serial.print(c);
 }
 Serial.flush();

This code will work as you expect. The variable declaration, and the read() and print() calls are all inside of the while loop, and it will repeatedly read and print character as long as they are available.

Another common error is to add a semicolon when one isn't needed. Many people automatically add a semicolon at the end of each line, thinking it is a line terminator. In reality, the semicolon is a statement separator. Consider this code, which is the same as the code above with the addition of a semicolon after the while statement:

 while (p.available()>0);
 {
   char c = p.read();
   Serial.print(c);
 }
 Serial.flush();

In this case, that added semicolon causes lots of problems. It is actually putting an empty NULL statement after the while statement, separating that NULL statement from the block of statements in the curly braces. What this is doing is telling the while statement to do nothing as long as characters are available. Once characters are no longer available, it will enter the block of statements denoted by the curly braces. It will create a variable c, read a single character into it, print the character, and then end the block. That will destroy the variable c, making it no longer accessible. That block will only be executed once, because it is no longer associated with the while statement - that semicolon has told the while statement to do nothing. In this case, the problems isn't really that the block statement will only be executed once. In reality, it will never be executed: since nothing is done inside the while statement to consume characters from p, the loop will never end. That infinite loop will run forever, and the sketch will never get past that line.

So that gives an example of type of things you need to look for when trying to get a sketch to compile. It's the details that count - every little character could be important. When you see an error message, you've got to look at the smallest of details to see what might be wrong. In the discussion above, I show three errors: a missing backslash which needs to be added, an error on the next line that isn't really an error and which will be fixed by fixing the first error, and some missing curly braces to make the loop do what you want it to do. Fix those errors, and then try to verify the sketch again. You could very well end up with a new set of errors. This can be an iterative process where you have to go through several cycles of fixing errors and verifying again.

Once you have the sketch successfully verified, then the hard work begins: debugging the code and getting it to do what you want it to do!

Thanks to both of you - ShapeShifter and DarkSabre- very informative and educational.

For starters, I'm curious if this language (C++ ?) cares about blank lines and/or indents- sounds like it doesn't, but it does seem to care about capitalization, right? Odd because there are some strange ways of capitalizing certain words and not others...

Secondly, more related to what I'm trying to do here- I'm not seeing that it is going to runCurl nor runCpuInfo unless and until I tell it to from within the void loop() section, is that right?

Basically I'm trying to merge two fairly simple tasks, and I know that the serial monitoring bit isn't really appropriate for the finished product, but would love to sort of monitor what the MCU is doing while I tweak this code...maybe that's sort of foolish, not sure.

Anyway- I'm trying to merge the standard 'button' sketch on an if/then loop to where it can monitor a button (my doorbell) and then, if it is pressed, shoot pushingbox my device id # which will notify my cell phone. I've perfected the button side of things, so now I'm trying to figure out how to get this Dragino Yun in combo with a Leonardo Arduino board to send the curl call (?) to pushingbox's api. I've (obviously) not quite got the curl section figured out yet....but it's been an interesting journey getting there...

BTW- in case it matters to anyone else reading this now or in the future...the device id listed herein is not accurate on purpose. The id has been changed to protect the innocent! Thx

stgeorge:
For starters, I'm curious if this language (C++ ?) cares about blank lines and/or indents- sounds like it doesn't, but it does seem to care about capitalization, right? Odd because there are some strange ways of capitalizing certain words and not others...

Whitespace is generically defined as characters that don't make a printed mark on the page: this includes spaces, tabs, carriage return, new line, etc. C++ ignores all whitespace (or more properly, takes all sequential whitepsace and considers it a single separator. Some places you need a separator (like between a type name and variable name in a variable declaration) and some times it's optional (like between a number and an operator.) Anywhere whitespace is required or optional, you can use any combination of whitespace" a single space works the same as 10 spaces, which works the same as a combination of spaces and tabs, as well as any combination of spaces, tabs, carriage returns, etc. All of the following are identical as far as the compiler is concerned:

char c;
     char c;
char      c;
char c     ;
char
            c
    ;
char
c
;
char





c





                           ;

Names are case sensitive - this goes for function names, class names, type names, variable names, etc. The following code defines three different variables, all of which are valid and unique variables, even if some languages that are not case sensitive (like BASIC or Python) would treat them as all the same variable (and complain about duplicate definitions:

byte variable;
byte Variable;
byte vAriAblE;

Arduino sketches are based on C++. There's a little pre-processing performed by the IDE to make it a little simpler, but the language that you are writing is regular C++. It sounds like you should read up on some C++ programming tutorials - while there are some slight differences in the way you set up a C++ program vs a sketch (like the sketch sets up the main() entry point for you) most of what you read about C++ will turn out to be very valuable.

If you want the definitive word on C++, that would be the book [u]The C++ Programming Language[/u], by Bjarne Stroustrup. He's the creator of the language, and that book is his official description of the language. It's not necessarily beginner material, however, so you are probably better off looking for an online introductory tutorial. But if you are serious about programming, you may eventually want it.

Secondly, more related to what I'm trying to do here- I'm not seeing that it is going to runCurl nor runCpuInfo unless and until I tell it to from within the void loop() section, is that right?

It's valid to put them in setup(), and they will get called. But they will only be called once. So that's valid for a one-time setup type of operation, but not particularly useful for what you are planning on doing.

Instead, you will want the code to be in your loop() function, which gets called over and over again, forever. That code will look for a button press, and if it finds one, it will call runCurl(). You say you have the button press code already working - are you debouncing the button press? When you press the button, it can actually make several make/break actions for a few milliseconds, both when you press the button and when you release it. The Arduino is fast enough to be able to detect these multiple activations. If you are just using it to light an LED, it doesn't matter, because while it will cause the LED to flicker when the button is being pressed or released, your eye won't see it. But your code will probably see it, and may call runCurl() multiple times for a single button press, causing multiple notifications to be sent. If you haven't already, it's worth doing some research on button debouncing.

To really perfect the button code, you want it to the point where you can print out a message on the serial port saying that the button is pressed, and that message is only ever printed once per press, regardless of how hard or soft you mash the button, and how long you hold it. When you have it to the point where it only prints once per press, then you want to add the call to runCurl() where you are printing out that message.

Basically I'm trying to merge two fairly simple tasks, and I know that the serial monitoring bit isn't really appropriate for the finished product, but would love to sort of monitor what the MCU is doing while I tweak this code...maybe that's sort of foolish, not sure.

No, not at all foolish! That serial output will be invaluable while debugging the code. It's very common to add a lot of Serial print statements when developing code, so that you can see what's going on, and hopefully figure out how to fix it when it doesn't do what you want.

The part that will be particularly helpful is your while available() loop where you echo any output from the Process object to the serial port. If the curl command generates any kind of error message, that will let you see what it is. If it doesn't work, it will be VERY hard to figure out why if you can't see the error messages.

Well...for some reason, it's not sending the device id to the website. I don't know curl well at all, so I'm curious if you think the http (i.e. the website) call should be prior to calling out the "-d" and the device id # ?

Oddly, I'm not seeing anything in the monitor either, except that it has 'connected' !?

I've sent the runCurl() code to pushingbox to see if they have any thoughts as to why it's not working.

I'm not an expert on curl.

When I'm experimenting with something like that, I will first SSH into the Yun, and try the commands out on the command line. That often allows more visibility to any error messages and status. It also allows tring a variety of options much faster than it could be done by updating/loading/running a sketch each time. If it doesn't work by typing the command directly on the Linux command line, there is virtually no chance of getting it to work with a Process object in a sketch.

Two options to consider using while testing, which may give you more information about why it's not working:

  • ```*
  • -v*
  • ```*
  • this turns on verbose mode which tells you lots of details about what it's doing (or not doing)
  • ```*
  • -- trace*
  • ```*
  • this causes even more detailed information to be printed

That's a good idea- you mean from a bash prompt on root? When I open a terminal and SSH into the Yun, I login as root and get to a prompt...do you then just start typing each line of that runCurl code?

How do you use the v and/or trace? Do you mean, try putting them in the sketch somehow?

stgeorge:
I login as root and get to a prompt...do you then just start typing each line of that runCurl code?

Yes, that's exactly what I mean. But just to be accurate (in case you go searching the web for help) it's not a bash prompt, it's ash. It's mostly the same, but there are some differences that could catch you.

How do you use the v and/or trace? Do you mean, try putting them in the sketch somehow?

I would use them when you're experimenting on the command line. Just add the options to whatever command you're testing. For example, your basic curl command is:

curl -d "devid=v0123456789ABCDE" http://api.pushingbox.com/pushingbox

To add the verbose command:

curl -v -d "devid=v0123456789ABCDE" http://api.pushingbox.com/pushingbox

To add the trace command:

curl --trace -d "devid=v0123456789ABCDE" http://api.pushingbox.com/pushingbox

Each of those are the command as you would type it on the command line.

Thanks- before reading your last comment, I tried that line (without the -v or --trace) and am getting a bigger problem...that is, I'm getting:

curl: (6) Couldn't resolve the host 'api.pushingbox.com'

So on a hunch, I went into my admin controls on the Yun and looked at my wifi setup, thinking that since I had locked down the IP address on my LAN for this Arduino, I may have failed to properly set the DNS address. I had indeed left it blank thinking that it would then default to the router's DNS settings, so I added the router's address in that blank (aka the gateway address).

Retried it via command line and I'd love to tell you that it worked, but alas, same problem.... ugh!

Any thoughts?

stgeorge:
Any thoughts?

To get you past this current stumbling block, I would try substituting the IP address 213.186.33.19 in place of api.pushingbox.com. (I got that address just now by running "nslookup api.pushingbox.com" on my computer.) You're better off using the name rather than the numeric address, in case the address ever changes in the future.

In the long run, you will have to sort out your DNS issues, but this should work as temporary work-around.

Thanks- tried that too- now I'm getting lots of fun gobbledy gook! I think I need to have a chat with the pushingbox folks....

stgeorge:
I think I need to have a chat with the pushingbox folks....

Good plan. This is the place for support to figure out how to call curl on the Yun. But they are the place for help on what parameters to use when calling curl.

Once you work with them and figure out a curl command that works when typed on the Yun's command line, we can make it work in the sketch.

Breakthrough!

Ok- firstly, I have sent a note to the French Pushingbox folks, but they must be on vacation or something because no response yet.

But- then I traded a few emails with a very helpful technician with Dragino Yun over in China and it seems I had two network problems. First, I mistakenly thought the 'broadcast' address was the address that would be broadcast out over the LAN (similar to broadcast ssid) but that is obviously wrong, so we got that set correctly. Second, which was probably the real issue, the Admin GUI imbedded in the Yun where I set my fixed IP address on the LAN has a field entitled, "Use Custom DNS Server(s): __________" and I had left that blank because my experience with other networked devices has been that, in the absence of a customized dns setting, the device will look to the router's dns settings. This goes back to the language / cultural misunderstandings that I discussed earlier. Anyway, I set the 'custom' dns to my router's address and boom, it works great!

I executed the curl command from an ash prompt and pow- straight through successfully!

Now that I have it communicating properly with the outside world, I need to go back and experiment with my various iterations of this sketch...

Congratulations! Great work chewing your way through the issues.

This issue / misunderstanding is causing a lot of people to have the same / similar troubles. If you google Arduino couldn't resolve... many of them pop up- most old, but I'm wondering if there's not a way to somehow post this as a common misunderstanding with the Yun's GUI...

So I'm now trying to make this as simple as possible....

As we have confirmed- when I SSH into the Dragino Yun on top of an Arduino Leonardo board, and type the following at a command prompt:

curl -d "devid=vBECBF1F4A7D765" http://api.pushingbox.com/pushingbox

...it works like a champ.

Now, attempting to merge that curl command with a fairly typical (and working) button check, I have the following sketch:

// set pin number:
const int buttonPin = 3; // the number of the doorbell input
const int ledPin = 13; // the number of the LED pin

// variables will change:
int buttonState = 0; // variable for reading the pushbutton status

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

void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Bridge.begin();
}

void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);

// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(ledPin, HIGH);
runCurl();
} else {
// turn LED off:
digitalWrite(ledPin, LOW);
}
}

void runCurl() {
Process p;
p.begin("curl");
p.addParameter("-d");
p.addParameter(""devid=vBECBF1F4A7D765"");
p.addParameter("http://api.pushingbox.com/pushingbox");
p.run();
}

And again, it compiles fine, and loads fine, and the button works properly (i.e. the LED on the Arduino lights up when the button is pressed etc), but the call (listed at top) is not effectively executed after the LED lights up.

Do you see anything that would otherwise cause it to not work correctly here? Thanks

Try explicitly giving the full path to curl. Inside runCurl(), change the p.begin() call to:

p.begin("/usr/bin/curl");

The command shell will normally search for the right executable on the path, but here you are not using a command shell and calling the file directly. It may be that it can't find the executable file.

If you had used p.runShellCommand(), then it would launch a command shell, which would then search for the file, and execute it. Instead, you are using p.run(), which does not launch a command shell. Since we already know the location of the curl command on the system, and don't need the command processor's help, there is no need to add the extra overhead of runShellCommand() - I think run() is the proper choice in this case.

If that doesn't work, put in a loop that echos the output from the command. It's probably returning an error string that might give a good clue to the problem:

 while (p.available()>0)
 {
   char c = p.read();
   Serial.print(c);
 }

Of course, you will need to call Serial.begin() in setup().

In looking at your code, you are calling runCurl(), any time that the button is high. Done this way, runCurl() will likely get called multiple times when the button is pressed, and will keep getting called over and over again as long as the button is pressed. It doesn't matter if you turn on the LED thousands of times while the button is pressed, but you probably don't want to be inundated with countless notifications for each press.

Take a look at the Debounce Example and try to understand what it is doing. You will almost certainly need to do something similar. You will want to call runCurl() where the example code is toggling the LED state.

Thanks

Re-Debounce- I'm sure that is going to be a problem that I'll need to deal with, but I'm trying to stay focused here first to get it actually working, plus that debounce (toggle on/off) was hurting by brain looking at, so for now, I'm putting that on the back burner. I agree though, once I get this working, I'm going to need a simple, effective way to just shoot one call out every time it is actually pressed.

So, I replaced "curl" with the "/usr/bin/curl" and connected the pin (i.e. pressed the button), and nota.

For the echo - do I put the while statement just below runCurl(); in the loop and yes, call Serial.begin() in setup...is that all?

Also- why #include <Bridge.h> and <Process.h> at the start- I sort of figured that needed to be there, but I don't really understand what they are doing.

Thank you

Also- the first draft yielded the following on Serial.begin();

no matching function for call to 'Serial_::begin()'

I think I'll need to add 9600 baud rate?

Also- p.available needs to be called out ahead someplace, right? It's getting hung on that too.

Thanks

stgeorge:
I agree though, once I get this working, I'm going to need a simple, effective way to just shoot one call out every time it is actually pressed.

The issue is that you are probably going to be making a bunch of calls, one right after the other, and that could possibly cause issues that would not show up if you just made a single call. As a compromise until you get proper debouncing working, you could add a delay() call immediately after calling runCurl(). For a "production" system, this would not be a good thing, but as a quick proof of concept it should work. If you add a delay(1000); after the runCurl() call, then it will call the function only once if you press the button less than a second, and will call it once a second if you hold the button down. You could make the delay longer if you want to make sure it's only called once for a longer press. But this delay will tend to make the program unresponsive, so you don't want it to be longer than necessary.

So, I replaced "curl" with the "/usr/bin/curl" and connected the pin (i.e. pressed the button), and nota.

Well, it was worth a try.

For the echo - do I put the while statement just below runCurl(); in the loop and yes, call Serial.begin() in setup...is that all?

No, put it inside runCurl() after the p.run() call. If you put it outside of runCurl(), the p object will not be defined. Not only will this cause a compile error, but even if you could call it from there, the p object will be automatically destroyed when runCurl() exits, along with any data you would be trying to read.

Also- why #include <Bridge.h> and <Process.h> at the start- I sort of figured that needed to be there, but I don't really understand what they are doing.

Those files define the Bridge and Process classes, respectively. It's telling the compiler that the classes exist, and what their interface is - the functions and data that the class provides. This lets the compiler check that you are calling any functions properly, and it tells the compiler how to generate the proper code to call those class functions.

Without those include statements, it won't know about the Bridge object and how to call Bridge.begin(), and it won't know about the Process class and won't know how to create the p object of that class, nor what functions can be called in that class.

stgeorge:
I think I'll need to add 9600 baud rate?

Yes, you will need to add some baud rate, and it generally must match what you are using in the Serial Monitor. The exception is when the USB port is managed directly by the AVR chip, like when using a '32U4 processor that is on a Leonardo, Pro Micro, or genuine Yun board - in that case, the baud rate is meaningless and can be anything, but something must still be provided.

Also- p.available needs to be called out ahead someplace, right? It's getting hung on that too.

You probably went ahead and put the p.available() loop in your loop() function after calling runCurl(). The compiler does know about the available() function as it applies to the Process class (because you did the #include of Process.h.) What it's no doubt complaining about is that it doesn't know what the "p" object is. Inside the runCurl() function it's defined to be an object of the Process class, but that variable declaration does not extend outside of the runCurl() function. Therefore, the compiler doesn't know what type it is, doesn't know that it's a Process object, and therefore doesn't know that the available() function is available for that object.

The answer is to put that available loop at the end of runCurl(), after the p.run() command, and before the function's closing curly brace.

Well...fits & starts I guess. I made those various changes (see below), and it compiles fine, but not only does it not make the 'call' to the website, now it doesn't even light up the LED when I 'press the button' ?!

Maybe you can look and see what I've done wrong? I can't get the serial monitor to connect either, so I have no idea.

I do think that delay idea is a good one temporarily- in fact, I thought it was possible that due to multiple calls, the website wasn't reacting, so I put in a 1200 ms delay and nope.

Anyway- here's what I have currently...

// set pin number:
const int buttonPin = 3; // the number of the doorbell input
const int ledPin = 13; // the number of the LED pin

// variables will change:
int buttonState = 0; // variable for reading the pushbutton status

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

void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Bridge.begin(9600);
Serial.begin(300);
}

void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);

// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(ledPin, HIGH);
runCurl();
delay(1200);

} else {
// turn LED off:
digitalWrite(ledPin, LOW);
}
}

void runCurl() {
Process p;
p.begin("curl");
p.addParameter("-d");
p.addParameter(""devid=vBECBF1F4A7D765"");
p.addParameter("http://api.pushingbox.com/pushingbox");
p.run();
while (p.available()>0)
{
char c = p.read();
Serial.print(c);
}
}