Linux serial comm with Uno rev3 problems.

I have been working on this over the last several days and haven't been able to figure out what the problem is.

I have an arduino Uno rev3 running the following sketch compiled with Arduino 1.0.1

const int relay1 = 3;
const int relay2 = 4;
const int relay3 = 5;
const int relay4 = 6;

//char relay = 0;
//char state = 0;
//i/nt out = 0;

int value = 0;
int channel;
int c;

void setup() {
  pinMode(relay1, OUTPUT);      
  pinMode(relay2, OUTPUT);      
  pinMode(relay3, OUTPUT);      
  pinMode(relay4, OUTPUT);      
  digitalWrite(relay1, 0);
  digitalWrite(relay2, 0);
  digitalWrite(relay3, 0);
  digitalWrite(relay4, 0);  
  
  Serial.begin(9600);
  
}

void loop(){
  while (!Serial.available());
  c = Serial.read();
  if((c >= '0') && (c <= '9')) {
    value = 10*value + c - '0';
  } else {
    if( c == 'c'){
      channel = value;
    } else if(c == 'w') {
      if(channel == 1) {
        digitalWrite(relay1, value);
      }
      if(channel == 2) {
        digitalWrite(relay2, value);
      }
      if(channel == 3) {
        digitalWrite(relay3, value);
      }
      if(channel == 4) {
        digitalWrite(relay4, value);
      }
    }
    value = 0;
  }
}

I can send the apropriate string just fine with the serial monitor (ie 1c1w).
With picocom under linux it seems to require either sending the string twice or with an extra character to start with.

How ever when I try to use a program or use the terminal it acts like it doesn't receive the string.

I have tried this:
http://todbot.com/blog/2006/12/06/arduino-serial-c-code-to-talk-to-arduino/

And this:
http://playground.arduino.cc//Interfacing/LinuxTTY

As well as giving this a shot:
http://playground.arduino.cc//Interfacing/PERL

And my own C program.

None of those seem to get the arduino to receive correctly. I have plugged in another computer to make sure the string is being sent and that does work.

I am left scratching my head as to why this doesn't seem to work. Terminal emulation programs work fine it seems but not the outside C/Perl/Bash. I have tried this on a Ubuntu 11.10 install and a CentOS 6 install.

Have you try to print back what ever string arduino receive?
I think, the problem could be in the structure of your code, you clearing value when all 4 symbols match to pattern, what happened if only part of the string received?
I'm using UNO and Ubuntu for awhile w/o any problem, only my longest commands compose of 2 characters....

Yes I have. And all characters and values come out correctly at least with the terminals.

Hi, there are a couple of things I would consider:

  • opening a serial communication causes the Uno to reset, so if your programs don't wait a few seconds after connecting, whatever they send is lost.
  • it may not make much difference, but depending on how the command is sent (e.g., print vs println) you may receive a terminating cr-lf. Your code is not prepared to handle these characters ('\r' or '\n').

spatula I tried your suggestion of waiting a few seconds. That does work. I didn't realize the arduino reset every time a serial connection is made. Is this constant across the arduino line? Also has me wondering if there is a way to prevent the reset on opening the port. At the moment the most immediate solution that comes to mind is writing a daemon to handle serial comm with the controller.

Your thought on my not handling the \r \n characters. Technically I do handle any characters that I am not interested in. Those just get overwritten with the next character from the serial stream. Which probably isn't overly elegant but does make the code simple.

Thank you for your help. I will have to follow up with some daemon or other code once I have something working.

ok as promised here is some daemon code that I threw together. This is a combination of examples from the perl cookbook and from the arduino playground. This right now works under linux and doesn't handle much else aside from my application so far. But could be used as an example.

The server/daemon:

#!/usr/bin/perl

use IO::Socket;
use Device::SerialPort;

my $sock;
my $sockname = '/tmp/serialcom';
my $MAXLEN =4;
my $mesg;

# init serial port
my $port = Device::SerialPort->new("/dev/ttyACM0");
$port->baudrate(9600); # you may change this value
$port->databits(8); # but not this and the two following
$port->parity("none");
$port->stopbits(1);

# setup the Unix socket
unlink $sockname;
$sock = IO::Socket::UNIX->new(Local     => $sockname,
                              Type      => SOCK_DGRAM,
                              Listen    => 5) or die $@;
# Wait for arduino to reset
sleep(5);

while(1) {
  $sock->recv($mesg, $MAXLEN);
  $port->write($mesg);
}

Here is the client:

#!/usr/bin/perl

use IO::Socket;

my ($mesg) = @ARGV;

my $sockname = '/tmp/serialcom';
my $MAXLEN = 4;
my $sock;

$sock = IO::Socket::UNIX->new(Peer     => $sockname,
                              Type     => SOCK_DGRAM,
                              Timeout  => 10) or die $@;

if( length($mesg) < 5) {
  $sock->send($mesg);
} else {
  print "Message too long\n";
}

Mind this is just quick and dirty code that has a fair amount of hard coded values. But I am putting this up in hopes that this might help someone else.

cyber_mage:
spatula I tried your suggestion of waiting a few seconds. That does work. I didn't realize the arduino reset every time a serial connection is made. Is this constant across the arduino line? Also has me wondering if there is a way to prevent the reset on opening the port. At the moment the most immediate solution that comes to mind is writing a daemon to handle serial comm with the controller.

Your thought on my not handling the \r \n characters. Technically I do handle any characters that I am not interested in. Those just get overwritten with the next character from the serial stream. Which probably isn't overly elegant but does make the code simple.

Thank you for your help. I will have to follow up with some daemon or other code once I have something working.

Hi, there are some threads in this forum about the reset behavior, the last one I know of is here http://arduino.cc/forum/index.php/topic,153237. A solution that works on Linux is to redirect the stdin and stdout file descriptors of a new process to the Arduino serial port (using < and >). A 10uF capacitor between RESET and GND also works. The behavior is not universal (does not apply to the Leonardo) but pretty common.