http://www.perlmonks.org?node_id=993096

dburke has asked for the wisdom of the Perl Monks concerning the following question:

Hi guys! This is my first post, but don't spare me. :) Any suggestions/comments are appreciated; the negative ones tend to be the most enlightening! I'm currently trying to make a perl script to help me handle the cycling of my aquaponics system. I have the script connect to my Arduino Uno and then issue very simple commands to have the arduino control a pump.

Currently, I've found I get the best results by having my script fork a process that deals with listening for incoming signals (from the arduino) over the serial connection while the main process deals with handling user input and issuing the commands to the arduino over the serial connection. I did this because the script couldn't finish what it was doing fast enough to catch the incoming data from the arduino (I think).

The problem that I'm having is that the output of this script has formatting issues that sometimes cut the first few characters off of a print function; I also occasionally get undesired non-alphabetical characters in the messages sometimes (only coming from the arduino). At first, I thought this was due to something related to the serial connection because every message from the Arduino seems to get cut, but as I've changed my script prints from the script that have nothing to do with info coming from the arduino are also getting cut. I've temporarily added a space or line break at the beginning of these messages to avoid having a bit of the messages cut off.

Another related issue is that the prints from the child process (that prints the incoming arduino messages) tend to alternate coming in before and after the prints from the parent. The end result of this is a bunch of confusing output like this:

Listening... Please pick an option you'd like to use: [1] Cycle [2] Relay OFF [3] Relay ON [4] Config [ ]: 1 Sent cmd: 1 Waiting for plant bed to fill... [1] Cycle [2] Relay OFF [3] Relay ON [4] Config [1]: From arduino: �Relay on 1 Sent cmd: 1 Waiting for plant bed to fill... [1] Cycle [2] Relay OFF [3] Relay ON [4] Config [1]: 3

OR

Listening... Please pick an option you'd like to use: [1] Cycle [2] Relay OFF [3] Relay ON [4] Config [ ]: 1 Sent cmd: 1 Waiting for plant bed to fill... [1] Cycle [2] Relay OFF [3] Relay ON [4] Config [1]: From arduino: Relay on

So far, I'm thinking some sort of "buffer" needs to be made to deal with the order in which the messages come in or perhaps somehow make these messages print in the same spot every time to create some sort of consistency.

TL;DR

I need to figure out a way to make the "interface" or output that's created more consistent. And I'm open to exploring new ways of handling the connection to my arduino. Perhaps forking was a bad idea? I know this question is a little vague, but I'm not sure where to look for answers. Any help is greatly appreciated.

Below is my code and..

Thank you!

My perl script (I apologize for the poor formatting):

#!/usr/bin/perl -w # Sample Perl script to transmit number # to Arduino then listen for the Arduino # to echo it back use strict; use Device::SerialPort; use Switch; use Time::HiRes qw ( alarm ); # Set up the serial port # 19200, 81N on the USB ftdi driver my $device = '/dev/ttyACM0'; # Tomoc has to use a different tty for testing #$device = '/dev/ttyS0'; my $port = new Device::SerialPort ($device) or die('Unable to open connection to device');; $port->databits(8); $port->baudrate(19200); $port->parity("none"); $port->stopbits(1); my $lastChoice = ' '; my $pid = fork(); my $signalOut; my $args = shift(@ARGV); # Parent must wait for child to exit before exiting itself on CTRL+C $SIG{'INT'} = sub { waitpid($pid,0) if $pid != 0; exit(0); }; # What child process should do if($pid == 0) { # Poll to see if any data is coming in print "\nListening...\n\n"; while (1) { my $char = $port->lookfor(); # If we get data, then print it if ($char) { print "\nFrom arduino: " . $char . "\n\n"; } } } # What parent process should do else { if ($args eq "cycle") { $SIG{ALRM} = sub { print "Expecting plant bed to be full; please +check.\n"; $signalOut = $port->write('2'); # Signal to se +t pin 3 low print "Sent cmd: 2\n"; }; $signalOut = $port->write('1'); # Signal to arduino to + set pin 3 High print "Sent cmd: 1\n"; print "Waiting for plant bed to fill...\n"; alarm (420); die "Done."; } else { sleep(1); my $choice = ' '; print "Please pick an option you'd like to use:\n"; while(1) { print " [1] Cycle [2] Relay OFF [3] Relay ON [4] Co +nfig [$lastChoice]: "; chomp($choice = <STDIN>); switch ($choice) { case /1/ { $SIG{ALRM} = sub { print "Expecting plant + bed to be full; please check.\n"; $signalOut = $port->wr +ite('2'); # Signal to set pin 3 low print "Sent cmd: 2\n"; }; $signalOut = $port->write('1'); # Sign +al to arduino to set pin 3 High print "Sent cmd: 1\n"; print "Waiting for plant bed to fill.. +.\n"; alarm (300); $lastChoice = $choice; } case /2/ { $signalOut = $port->write('2'); # Sign +al to set pin 3 low print "Sent cmd: 2"; $lastChoice = $choice; } case /3/ { $signalOut = $port->write('1'); # Sign +al to arduino to set pin 3 High print "Sent cmd: 1"; $lastChoice = $choice; } case /4/ { print "There is no configuration avail +able yet. Please stab the developer."; } else { print "Please select a valid option. +\n\n";} } } } }

Replies are listed 'Best First'.
Re: Perl stdout timing/formatting
by BrowserUk (Patriarch) on Sep 12, 2012 at 01:33 UTC

    Try adding $|++; at the top of your script.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

      This seemed to work really well, thank you. It took me a while to figure out how to Google that though. :P So now I have a very basic understanding of what that is and why I would put it in my script, but I still have one question; does it only need to be put at the beginning of my script and only once? I currently have it at the start of every while(1) so that it 'flushes' before each part of the script runs. Is that the right thing to do or is it just redundant?

      Thank you for the help!

        I still have one question; does it only need to be put at the beginning of my script and only once?

        Once at the top is all that is needed. More than that is redundant.

        This is an often recommended reference.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        RIP Neil Armstrong

Re: Perl stdout timing/formatting
by Anonymous Monk on Sep 12, 2012 at 01:32 UTC
    Around these parts of the Internet, first-posters are always treated just as graciously as old hands. Welcome.
      Thank you, good sir! :)
Re: Perl stdout timing/formatting
by shijiang1130 (Initiate) on Sep 12, 2012 at 01:03 UTC
    have you tried " my $char = $port->lookfor(); " to "my $char = $port->lookfor($count)" . the $count is numbers of charactors

      Have you looked at Device::SerialPort::Arduino? See example below. Seems to work fine through a chipKIT Uno USB port.

      James

      # Perl serial interface to Arduino Example # Reads characters being sent from the chipKIT UNO and prints them to +the screen. # The output looks like the mpide serial monitor. # Use mpide to compile and run the communications->graph example to ge +nerate the character stream. # From the CPAN Perl module Device::SerialPort::Arduino # Written by Simone Marzulli # Modified for USB interface and annotated by James Lynes, Jr. June 5, +2012 # Tested under Ubuntu 10.10 and Perl v5.10.1 on the chipKIT UNO board +(communicate as yet untested - requires # a modified chipKIT sketch that expects an incoming message. "graph" +only transmits characters. # Documentation under: Perldoc Device::SerialPort # Perldoc Device::SerialPort::Arduino # Uncomment the section below which you would like to test: receive(), + receive(with delay), communicate use strict; use warnings; # Initialize the serial port - creates the serial port object $Arduino use Device::SerialPort::Arduino; my $Arduino = Device::SerialPort::Arduino->new( port => '/dev/ttyUSB0', baudrate => 9600, databits => 8, parity => 'none', ); # Reading from Arduino via Serial - uses Device::SerialPort "lookfor" +method while (1) { print $Arduino->receive(), "\n"; } # Reading from Arduino via Serial with a delay - uses Device::SerialPo +rt "lookclear" method and a sleep call # Argument is number of seconds to sleep between receives # while (1) { # print $Arduino->receive(5), "\n"; # } # Send something via Serial - uses Device::SerialPort "write" method # $Arduino->communicate('oh hi!!11') # or die 'Warning, empty string: ', "$!\n";

      "Processing" code that is downloaded into the chipKIT UNO for use with the above sample code.

      Graph A simple example of communication from the Arduino board to the compu +ter: the value of analog input 0 is sent out the serial port. We call thi +s "serial" communication because the connection appears to both the Arduino and +the computer as a serial port, even though it may actually use a USB cable. Bytes are sent one after another (serially) from the Ard +uino to the computer. You can use the Arduino serial monitor to view the sent data, or it c +an be read by Processing, PD, Max/MSP, or any other program capable of r +eading data from a serial port. The Processing code below graphs the data r +eceived so you can see the value of the analog input changing over time. The circuit: Any analog input sensor is attached to analog in pin 0. created 2006 by David A. Mellis modified 14 Apr 2009 by Tom Igoe and Scott Fitzgerald This example code is in the public domain. http://www.arduino.cc/en/Tutorial/Graph void setup() { // initialize the serial communication: Serial.begin(9600); } void loop() { // send the value of analog input 0: Serial.println(analogRead(A0)); // wait a bit for the analog-to-digital converter // to stabilize after the last reading: delay(10); }
        @ jmlynesjr, Woah! I was not aware that this existed. Hm, not sure how I missed it either! Thank you for bringing that to my attention; I will definitely be trying it out soon.

      This seems very clunky and leaves me reluctant to implement it. Already I have message coming from the arduino that are of differing lengths and it seems very cumbersome to change the code every time I add a new message to the system or change my messages.

      Am I understanding that right? Is there something I'm missing? Thank you for your suggestion; I do believe this part of the code can be improved upon.