Go Down

Topic: Arduino Yun and Python script (Read 5317 times) previous topic - next topic

Clary

Hi everyone!
 why can't I run a Python script from Arduino Yun, using the function runShellCommand()?

I've tried everything, but I'm not able to do this.

Every Python script (or script bash, it's the same) doesn't run from Arduino, but if I try to execute it from shell, it works fine.

Thanks to all!

ShapeShifter

It works for me. Please post an example, including the error message you are getting or issues you are seeing.

One common source of problems is file paths. When you run a script from the shell, you are in your home directory, or you have explicitly changed to a specific directory. When you run a script with a Process object, the current directory is down in the system tree. Unless you use full path names for all of your referenced files and modules, your script is unlikely to find them.

Note that each time you run something from a Process object, you are starting with a new execution context. So you can use a Process call to change directory, then assume that the next Process call will be running in that directory - that won't work.

What you want to do does work, i do it all the time. Please provide more details so we can figure out why it's not working for you.

Clary

It works for me. Please post an example, including the error message you are getting or issues you are seeing.

One common source of problems is file paths. When you run a script from the shell, you are in your home directory, or you have explicitly changed to a specific directory. When you run a script with a Process object, the current directory is down in the system tree. Unless you use full path names for all of your referenced files and modules, your script is unlikely to find them.

Note that each time you run something from a Process object, you are starting with a new execution context. So you can use a Process call to change directory, then assume that the next Process call will be running in that directory - that won't work.

What you want to do does work, i do it all the time. Please provide more details so we can figure out why it's not working for you.
Hi! Thank you for your answer.
I have no errors from the compiler, because the sketch runs but nothing happens.
I've tried to pass "./myscript.sh" or the absolute path "./home/.../myscript.sh", and then "python myscript.py" or "python /home/.../myscript.py", but nothing still happens.

It happens whatever I write into the script.

Thank you

ShapeShifter

You say nothing happens - what are you expecting to happen?

If you post your sketch and your Python script, as well as a description of what you expect it to do, we can be more helpful. Otherwise, we can only talk in generalities.

Clary

You say nothing happens - what are you expecting to happen?

If you post your sketch and your Python script, as well as a description of what you expect it to do, we can be more helpful. Otherwise, we can only talk in generalities.
Sorry, you're right!

Here you can find gPhoto2 library for Python.

The script I want to run is the following:

Code: [Select]
from __future__ import print_function

import logging
import os
import subprocess
import sys

import gphoto2 as gp

def main():
    logging.basicConfig(
        format='%(levelname)s: %(name)s: %(message)s', level=logging.WARNING)
    gp.check_result(gp.use_python_logging())
    camera = gp.check_result(gp.gp_camera_new())
    gp.check_result(gp.gp_camera_init(camera))
    print('Capturing image')
    file_path = gp.check_result(gp.gp_camera_capture(
        camera, gp.GP_CAPTURE_IMAGE))
    print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name))
    target = os.path.join('/tmp', file_path.name)
    print('Copying image to', target)
    camera_file = gp.check_result(gp.gp_camera_file_get(
            camera, file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL))
    gp.check_result(gp.gp_file_save(camera_file, target))
    subprocess.call(['xdg-open', target])
    gp.check_result(gp.gp_camera_exit(camera))
    return 0

if __name__ == "__main__":
    sys.exit(main())
from https://github.com/jim-easterbrook/python-gphoto2/blob/master/examples/capture-image.py

ShapeShifter

That's half f the picture. If you run this from the command shell, it runs properly, correct? Does it run no matter what directory you are in? Try changing to some different directories, and make sure it runs properly. Note that I do mean from the command shell, not from a Python prompt.

You can do that by specifying the Python interpreter and passing your script name as a parameter, but I prefer putting a "#!/usr/bin/python -u" shebang as the first line of the script. This way, as long as the script has the execution permission bit set, you can run it by simply typing the name of the script from the command line.

Note that I added the "-u" option - it's probably not important in this case, but I tend to do it to be safe. Normally, Python runs in buffered mode, where it saves up output until a rather large buffer is filled before it sends the output to the Process object in the sketch. Because of this, unless the Process object won't return any output until a large amount of data had been generated, or the script is complete. By adding the "-u" option, Python runs in ubuffered mode, and any output that is generated is immediately sent up to the Process object in the script. In your case, the script should complete relatively quickly, so it shouldn't be an issue.

Only once the Python is running properly from the command shell, from any directory, do we worry about running it from a sketch. Once you are at that point, post your sketch that is trying to call this script, and we'll see what's going on.

Clary

That's half f the picture. If you run this from the command shell, it runs properly, correct? Does it run no matter what directory you are in? Try changing to some different directories, and make sure it runs properly. Note that I do mean from the command shell, not from a Python prompt.
...
Yeah, it runs properly, from command shell, obviously. I try to put the script in various directories and it works.
I've done the changes you suggested to me and nothing is changed.

Now, my sketch is the following

Code: [Select]

#include <Bridge.h>

void setup(){
    Bridge.begin();

    capture();
    }

void loop(){
    }

void capture(){
    Process p;

    p.begin("python");
    p.addParameters("capture-image.py");
    p.run();
    }



The script is in the same directory of the sketch, but nothing changes if I put the path of a copy of the script of another directory.

ShapeShifter

I try to put the script in various directories and it works.
I think you misunderstood me, I must not have been clear. I wasn't suggesting that you move the script to various directories. Keep the script in your desired directory, and then change to other directories and try running the script. For example, if the script is in /mnt/sda1/capture/capture-image.py, go to a different directory and try running it from there while the script is still in the original directory. For example, "cd /" followed by "python /mnt/sda1/capture/capture-image.py"

The key is that when you run a script from a Process object, you cannot make assumptions about what the current directory will be when it runs the script. The script must be written so that it can be RUN from any directory, where that directory is NOT the directory that contains the script.

Quote
The script is in the same directory of the sketch
This is meaningless. The sketch is loaded into the flash memory of the Yun's '32U4 processor, and has no correlation to the file system on the Linux side. You may have a copy of the sketch somewhere on the Linux side of the Yun, but that has no significance at all. And it certainly doesn't mean that any directory that happens to include a copy of the sketch will be the current directory when a Process object tries to access any files.

Code: [Select]

    p.begin("python");
    p.addParameters("capture-image.py");
    p.run();


This won't work. It will attempt to run the capture-image.py script from whatever directory the Process object is using as its default directory. Odds are that you have not placed a copy of the script in that directory. In my experience you must use a full path to your script file, or the Process object will not be able to find it. For example:
Code: [Select]

    p.begin("python");
    p.addParameters("/mnt/sda1/capture/capture-image.py");
    p.run();


Another thing that may be helpful, at least during development, is to echo any output you get from the Process object to the Serial port (or Console if you are working over a network connection.) That way, if the Process object returns any error messages, you can see what they are. My guess is that it is returning a message that it can't find capture-image.py.

Take a look at the Bridge Library Process Tutorial for an example of getting the output from the Process object and sending it to the Serial port. This is the interesting bit of code that you could call after p.run():

Code: [Select]
while (p.available()>0) {
    char c = p.read();
    Serial.print(c);
  }
  Serial.flush();


Of course, you will also need to set up the Serial port in the setup() function. Or, if you are using a network connection, you would want to use the Console class instead of the Serial class.

Clary

I think you misunderstood me, I must not have been clear. I wasn't suggesting that you move the script to various directories. Keep the script in your desired directory, and then change to other directories and try running the script...
Ok, sorry, I've misunderstood you.

Now, I understand, and yes the script runs properly whatever directory I'm in.

In my experience you must use a full path to your script file, or the Process object will not be able to find it. ...
I also have done this, but it still doesn't work. I'm desperate!

Another thing that may be helpful, at least during development, is to echo any output you get from the Process object to the Serial port (or Console if you are working over a network connection.) That way, if the Process object returns any error messages, you can see what they are. My guess is that it is returning a message that it can't find capture-image.py.
I also try this, but nothing, no message is written on the Serial Monitor, and I've setup Serial port with

Serial.begin(9600)

that is the Serial monitor's baud.

Seriously, I have no idea.

ShapeShifter

I also try this, but nothing, no message is written on the Serial Monitor, and I've setup Serial port with

Serial.begin(9600)
It just occurred to me that you are calling your capture() function from inside setup(). This means that it will run right away. It is very likely that any output that is generated by your Python script has been sent to the Serial Monitor before you even have a chance to see the output. Maybe it will help to add this line right after your Serial.begin() call:

Code: [Select]
 while (!Serial);
This will create a small little spin loop that won't continue with your sketch until you've connected with the Serial Monitor. It should be after the Serial.begin() call, but before the call to capture(). Once you get things working, you will probably want to remove this, or it will never run without the Serial Monitor being connected.

In the mean time, I'm going to try and come up with a simple test sketch and script to help eliminate some variables.

ShapeShifter

I tried a quick test, which worked (mostly) as I expected.

The sketch:
Code: [Select]

#include <Bridge.h>

void setup() {
   Serial.begin(9600);  // Initialize serial USB port
   while (!Serial);     // Wait for Serial Monitor to connect

   Serial.println("Starting Bridge...");
   Bridge.begin();
   Serial.println("Bridge started.");
}

void loop() {
   Serial.println();
   Serial.println("Calling capture()");
   capture();
   Serial.println("capture() returned");
   delay(2000);       // A little delay to prevent messages being printed too quickly
}

void capture() {
   Process p;

   // Call the Python script.
   p.begin("python");
   p.addParameter("/mnt/sda1/PythonTest.py");
   p.run();

   if (p.available())         // Is there any output from the script?
   {
      Serial.print("Output from script: \"");
      while (p.available())   // As long as there is output waiting from the script...
      {
         char c = p.read();   // Read a character of output
         Serial.print(c);     // Echo the character to the serial port
      }
      Serial.println("\"");   // Print a closing quote character and new line
   }
   else
      Serial.println("No output received from script");
}


If I run this with no Python script in place, I get:
Code: [Select]

Starting Bridge...
Bridge started.

Calling capture()
No output received from script
capture() returned

It's curious that there isn't any kind of error message that it couldn't find the script.

If I create a very simple /mnt/sda1/PythonTest.py script:
Code: [Select]
print "PythonTest script!"

I then get this output:
Code: [Select]
Starting Bridge...
Bridge started.

Calling capture()
Output from script: "PythonTest script!
"
capture() returned


I'm surprised that there isn't an error message when the script is not found, I suspect that this is your problem.

Try running the code I posted above, and see if it works for you. If so, please post your updated sketch, the one that echos the output and references the Python script with a full path name, and I will try it on my Yun. (Note that the sketch you posted earlier doesn't compile - you are calling p.addParameters(), when the function name is actually p.addParameter() - the name is not plural.)

Clary

#11
Feb 08, 2018, 11:43 am Last Edit: Feb 08, 2018, 03:55 pm by Clary
First of all, I want to thank you very very much for your help!

... Try running the code I posted above, and see if it works for you. If so, please post your updated sketch, the one that echos the output and references the Python script with a full path name, and I will try it on my Yun. (Note that the sketch you posted earlier doesn't compile - you are calling p.addParameters(), when the function name is actually p.addParameter() - the name is not plural.)
I've made the changes, I've tried your sketch, and now I have an output, but this output is

Code: [Select]

Starting Bridge...
Bridge started.

Calling capture()
No output received from script
capture() returned


in loop.
Obviously, I corrected that "p.addParameter()", even if I think it was a typing error.

Anyway, the path I used is /home/raspberry-desktop/test-python.py , that is like your script with only the println; the path is that of the file properties. This path properly works from the command shell.

Is it relevant that I'm using Raspberry Pi3 with Ubuntu Mate?

ShapeShifter

Is it relevant that I'm using Raspberry Pi3 with Ubuntu Mate?
Yes, that would be a very important detail!  :smiley-eek:

Now I'm very confused. I thought you were using a Yun. How does the RPi factor into this? Just what are you trying to do, and what is your overall configuration?

I didn't look too deeply at the Python code you provided, I was just looking to see if it made any relative references to files. I saw that it was using an image capture library, but I didn't look at it, I just figured you were trying to capture an image from a USB camera plugged into the Linux side of the Yun. Are you running the Python script on the RPi and trying to capture an image from the Pi's camera? If so, a very different mechanism is going to need to be used, as the Process object just runs a command on the local Linux processor. You will need to get into some networking and remote procedure calls to execute something on the RPi.

Quote
Anyway, the path I used is /home/raspberry-desktop/test-python.py , that is like your script with only the println; the path is that of the file properties. This path properly works from the command shell.
Is that the Yun's or the Pi's command shell? Above, when I ask if you can run the script from any directory using the command shell, I mean the command shell you get when you SSH int your Yun.

Clary

Yes, that would be a very important detail!  :smiley-eek:

Now I'm very confused. I thought you were using a Yun. How does the RPi factor into this? Just what are you trying to do, and what is your overall configuration?

I didn't look too deeply at the Python code you provided, I was just looking to see if it made any relative references to files. I saw that it was using an image capture library, but I didn't look at it, I just figured you were trying to capture an image from a USB camera plugged into the Linux side of the Yun. Are you running the Python script on the RPi and trying to capture an image from the Pi's camera? If so, a very different mechanism is going to need to be used, as the Process object just runs a command on the local Linux processor. You will need to get into some networking and remote procedure calls to execute something on the RPi.

Is that the Yun's or the Pi's command shell? Above, when I ask if you can run the script from any directory using the command shell, I mean the command shell you get when you SSH int your Yun.

Sorry, again, but I thought that I told you this detail :(

I'm using Raspberry as a pc and Yun is connected to the Pi.

A few days ago, I've tried to use ssh, but I had had problems with gPhoto2 library installation. I think that it was a wrong way.

So, I try to take this way and I'll let you know.

Thank you for your patience!

Go Up