Go Down

Topic: Process.write() does not seem to work as expected. (Read 376 times) previous topic - next topic

cydonia

I have been struggling with this issue for a while. As ShapeShifter explained in 2015 and as far as I understood from the documentations I could find, Process.write() is supposed to write to the standard input of the script running in the Linux side of the Arduino. Here is what ShapeShifted said:

Quote
Anything sent with Process.write() can be read by the Python script as standard input.
Here is the post.

However no matter what I tried to do, I cannot make this work. Here is my Arduino code:

Code: [Select]
#include <Console.h>
#include <Process.h>

Process p;

void setup() {
  Bridge.begin();
  Console.begin();
  while(!Console);
  Console.println("Console ready.");
  p.runShellCommandAsynchronously("/usr/bin/python /root/test.py");

}

void loop() {
    Console.println(p.write("Testing"));
    p.flush();
    delay(500);
}


The reason why I print the output of p.write() is that I want to check how many bytes is sent, because as you know, Process.write() returns the number of bytes written. I have tried with single characters, strings of various lengths, but the results is always 1. That is, p.write() thinks it's writing only 1 byte. Things on the Python side is also odd:

Code: [Select]

import sys

with open("test.txt", "a") as fd:
        fd.write(val + "\n")

while True:
        val = sys.stdin.readline().strip()
        with open("test.txt", "a") as fd:
                fd.write(val + "\n")


What this code snippet is supposed to do is actually pretty straightforward: It takes input and saves to a text file in an infinite loop. When I ssh into the Linux console and run this script manually, everything works as expected. Besides, the line p.runShellCommandAsynchronously("/usr/bin/python /root/test.py"); successfully launches the script from the sketch. Here is the output of ps | grep python command:

Code: [Select]

 1741 root      7376 S    python -u bridge.py
 2272 root      1492 S    /bin/ash -c /usr/bin/python /root/test.py
 2273 root      5764 S    /usr/bin/python /root/test.py
 3461 root      1492 S    grep python



However, there wouldn't be any file "text.txt" this time. At first I was thinking that the code blocks at the point where it is waiting for an input, but it seems that the very first lines of the code isn't executed either.

By the way, I have no trouble at the reverse process. That is, I can successfully read output of a Linux process in the sketch using Process.available() and Process.parseInt() (Yes I was trying to communicate integer values). I have no idea why Process.write() just wouldn't work. Any help is sincerely appreciated.

ShapeShifter

#1
Sep 20, 2019, 12:22 pm Last Edit: Sep 20, 2019, 02:08 pm by ShapeShifter
Where are you looking for file.txt? I'll bet that the file is being created, just not where you are expecting.

When you ssh in and run your script manually, the file is created in your local directory. But when you run it from the Arduino sketch, the file is run from whatever system directory the Bridge library code is using as its default RJ directory (I don't recall what that is off hand, but I know it is NOT the root home directory.)

Instead of using an unqualified name like "test.txt" use the fully qualified name of where you want the file to be, like "/mnt/sda1/test.txt" to put it at the root of the SD card.

Also, I would recommend adding -u before your script name when calling it from your Process object (in your ps listing, compare the command line for the bridge.py process to your own.) this will run Python in unbuffered mode, which is necessary if you will want to read from your script in the future.

One more comment: you can make things slightly more efficient by calling p.runAsynchronously() rather than calling p.runShellCommandAsynchronously(). The latter spins up a command interpreter the process the string you pass it, which then starts up Python. Using runAsynchonously skips the command interpreter and just runs your process directly. As long as you are running a standalone process (like Python) and don't specifically need the command interpreter, you can use the simplified call.

cydonia

Thank you very much for your help. I changed my Python code to this:

Code: [Select]

import sys
import time

dir = "/root/test.txt"

with open(dir, "a") as f:
        f.write("Start testing\n")

while True:
        val = sys.stdin.readline().strip()
        with open(dir, "a") as fd:
                fd.write(val + "\n")



Now I can see test.txt created under /root, but I still cannot write any data to the file. The file only consists of "Start testing" line, which means p.write() still does not work. I tried with a single character as well, but is not written either. Why would p.write() wouldn't work, I really don't understand. I also added the parameter -u to the script as you recommend:

Code: [Select]

  p.begin("/usr/bin/python");
  p.addParameter("-u");
  p.addParameter("/root/test.py");
  p.runAsynchronously();


I still appreciate further help on this issue.

ShapeShifter

I think I see it now. You are using sys.stdin.readline() to read the input from the sketch. This function reads a line of text, and waits for a newline character (a '\n") before returning. But your sketch is writing a short string using the write() function, which just writes a raw sequence of bytes, without adding a newline character. So readling() is taking in everything you are sending it, but won't return to the Python script until it sees a newline, which it never will with your sketch.

You have two options:
  • p.write("Testing\n")
  • p.println("Testing")


The println() function automatically adds a newline to the end of whatever it is printing. It may not list println() as an option on the Process class reference page, but it will work. Process inherits from the Stream class, so any of the functions that you can use with a Stream object, you can also use with a Process object. Basically, anything you can do with a Serial port object to format output data or read input data, you can also do with a Process object (Serial inherits from Stream, just as Process does.)

cydonia

p.println worked, I cannot thank you enough :)  I wonder why it didn't work with p.write("Testing\n") though. Maybe because Process.write expects C strings as input.

ShapeShifter

I wonder why it didn't work with p.write("Testing\n") though.
That's a really good question, I would've thought it should work. Very curious.

Quote
Maybe because Process.write expects C strings as input.
But that IS a C string you're giving it.

Go Up