[solved] Aduino Yun - Sketch access to Linino command line / open programm

Good day to everybody

I have an Arduino Yun and I am having problems to open programs from the sketch.

Problem description:
I try to play a song from sketch in the linux side with the program "cmus" (command line musk player).
https://wiki.ubuntuusers.de/cmus/

Where the problem is not:

  • The program "cmus" is working perfectly: When I connect from my PC with Putty over ssh and enter the exact same commands every thing goes well.
  • The shell/run commands are working as I can execute all other commands that are not opening programs

Debug information's:
The program get stuck(not executing next line) already at the first command calling cmus.
As soon as it is stuck i can't open cmus from putty as it says:
cmus: cmus is already listening on socket /root/.cmus/socket
then I restart Arduino and try again

My code A:
#include <Console.h>
#include <Process.h>
Process p;

void setup() {

Bridge.begin();
Console.begin();
while(!Console)
{;}
Console.println("Connected");

p.runShellCommand("cmus"); // <- Stuck here
Console.println("1"); // Never executed
p.runShellCommand(":add ~/sound"); // Never executed
Console.println("2"); // Never executed
p.runShellCommand(":player-play /root/sound/ItCouldWork.mp3"); // Never executed
Console.println("3"); // Never executed

}

void loop() {

}

My code B:
p.begin("cmus");
p.run(); // <- Stuck here
Console.println("1"); // Never executed
p.begin(":add ~/sound"); // Never executed
p.run();
Console.println("2"); // Never executed
p.begin(":player-play /root/sound/ItCouldWork.mp3"); // Never executed
p.run();
Console.println("3"); // Never executed

The problem:
Obviously I am doing something terribly wrong, can someone help me?
By the way I have the same problem on all other programs of same type (vi (text editor), …)
Please do not propose me other music programs as workaround

The solution:
Big thx to ShapeShifter that posted the solution. I tested it, it is working just fine :slight_smile:

p.runShellCommand() will execute the process, and wait for the Linux process to finish before runShellCommand() returns. It appears that cmus is not finishing, as it's waiting for interactive input from the user. It then looks like you are trying to send input to that process using another runShellCommand() invocation. If the code was able to reach that call, it would start another Linux process and pass it the command ":add ~/sound" which would not be understood by the command shell - this would be the equivalent of starting a new SSH session to enter that command.

runShellCommand() always starts a new command shell instance, gives the parameter to the command interpreter to be run, and then waits for the process to finish on its own.

What you want is to start a process, and then interact with that process. You want to run your process asynchronously.

Use either p.runShellCommandAsynchronously(), or p.begin() followed by p.runAsynchronously(). The difference between the two options is that runShellCommandAsynchronously() will start a new Linux process using the command shell, which then gets the runCommandShellAsynchronously() parameter, parses it, and then runs the requested process. On the other hand, runAsynchronously() runs the specified process directly, without first starting a new command shell. runAsynchronously() is more efficient, but only works to run an executable file: it cannot handle the extra syntax used to redirect I/O, handle environment variables, etc. In your case, you are running an executable process directly, so runAsynchronously() is a bit more efficient, but runShellCommandAsynchronously() is simpler, and since you are only calling it once, the few milliseconds of extra overhead is not an issue.

So, now you have your processing running asynchronously, and you want to interact with it. The Process class inherits from Stream, so you can interact with it just like you would with a Serial port or the Console. You can use p.available() and p.read() to read the output from the process, and you can use p.print() and p.println() to send data to it. Basically, however you are most comfortable talking to a Serial port, you can do it with a Process object. Just think of the Process class as a special Serial port that is connected to the Linux process that you started.

To accomplish what you want, you might try something like this: (I did not try to compile or run it, so it might need some tweaking)

  p.runShellCommandAsynchronously("cmus");
  p.printLn(":add ~/sound");
  p.println(":player-play /root/sound/ItCouldWork.mp3");

From the looks of the screenshots you attached, it appears that cmus outputs a lot of data making full screen drawings. I'm guessing that this will quickly fill up some output buffers and cause the program to block until the output stream is cleared. Even if you don't care about the output, you may have to periodically read the output from the Process object to keep it running. I've not tried it myself, but it appears that p.flush() should discard any waiting output. If that doesn't work, you could periodically try something like this:

  while (p.available())
  {
    p.read();
  }

You asked that we not propose another music player. However, being that cmus appears to be an interactive screen based console application, and you appear to be trying to use it as a command line player, it is likely to add a lot of communications overhead that you don't need, and which might cause issues or slow performance. There are other music players out there that are designed to be run from a command line. As a user sitting at a screen and operating a program, a full screen console application is much easier to use that a command line program. However, for your sketch, a command line oriented player would be more efficient.