Arduino-serial: C code to talk to Arduino

[NOTE! arduino-serial has been greatly updated. See the “Arduino-serial: updated!” post for details]

The Arduino’s USB port is actually a serial port in disguise. To your computer it appears as a ‘virtual’ serial port. This is good news if you want to write custom code on your computer to talk with the Arduino, as talking to serial ports is a well-solved problem. (Unfortunately, so well-solved that there’s many ways of solving it.)

On the Arduino forum there’s been a few requests for some example C code of how to talk to Arduino. The nice thing about standard POSIX C code is that it works on every computer (Mac/Linux/PC) and doesn’t require any extra libraries (like what Java and Python need). The bad thing about C is that it can be pretty incomprehensible.

Here is arduino-serial.c (github for full source), a command-line C program that shows how to send data to and receive data from an Arduino board. It attempts to be as simple as possible while being complete enough in the port configuration to let you send and receive arbitrary binary data, not just ASCII. It’s not a great example of C coding, but from it you should be able to glean enough tricks to write your own stuff.

Usage

laptop% ./arduino-serial
Usage: arduino-serial -b <bps> -p <serialport> [OPTIONS]
Options:
  -h, --help                 Print this help message
  -b, --baud=baudrate        Baudrate (bps) of Arduino (default 9600)
  -p, --port=serialport      Serial port Arduino is connected to
  -s, --send=string          Send string to Arduino
  -S, --sendline=string      Send string with newline to Arduino
  -r, --receive              Receive string from Arduino & print it out
  -n  --num=num              Send a number as a single byte
  -F  --flush                Flush serial port buffers for fresh reading
  -d  --delay=millis         Delay for specified milliseconds
  -e  --eolchar=char         Specify EOL char for reads (default '\n')
  -t  --timeout=millis       Timeout for reads in millisecs (default 5000)
  -q  --quiet                Don't print out as much info

Note: Order is important. Set '-b' baudrate before opening port'-p'.
      Used to make series of actions: '-d 2000 -s hello -d 100 -r'
      means 'wait 2secs, send 'hello', wait 100msec, get reply'

Example Use

Send the single ASCII character “6” to Arduino

laptop% ./arduino-serial -b 9600 -p /dev/tty.usbserial -s 6

This would cause the Arduino to blink 6 times if you’re using the serial_read_blink.pde sketch from Spooky Arduino.

Send the string “furby” to Arduino

laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -s furby

Receive data from Arduino

laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -r
read: 15 Hello world!

The output is what you would expect if you were running the serial_hello_world.pde sketch from Spooky Arduino.

Send ASCII string “get” to Arduino and receive result

laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -s get -r
read: d=0

Internals

There are three interesting functions that show how to implement talking to serial ports in C:

  • int serialport_init(const char* serialport, int baud)
    — given a serial port name and a speed, return a file descriptor to the open serial port.
  • int serialport_write(int fd, const char* str)
    — write out a string on the given a serial port file descriptor
  • int serialport_read_until(int fd, char* buf, char until, int timeout)
    — read from serial port into a buffer until a given character is received or timeout reached

You can and should write improved versions of the read and write functions that better match your application.

Update 8 Dec 2006:
Justin McBride sent in a patch because it turns out Linux’s termios.h doesn’t define B14400 & B28800. I’ve updated arduino-serial.c to include the patch, but commented out for now. No one uses those baudrates much anyway. :) If you need them, uncomment the additions out, or better yet, download Justin’s tarball that includes the changes and a Makefile to auto-detect your platform.

Update 26 Dec 2007:
Added ability to sent binary bytes with the ‘-n’ flag.
Added a delay option so you can open a port, wait a bit, then send data. This is useful when using an Arduino Diecimila which resets on serial port open.

Update 29 Apr 2013:
I apologize to everyone who has commented on this post but who hasn’t received a reply. This code has had a much longer life than I expected and it was hard to get back to it to fix some of its obvious deficiencies.

I did finally get back to it (but not the comments). I’ve rewritten arduino-serial a bit and added some new options. Hopefully this will address many of the issues people have had. You can read about the changes in the “Arduino-serial: updated!” post.

Also, arduino-serial now lives on Github at:
   https://github.com/todbot/arduino-serial   
Please post issues and patches there. Thanks!

186 Replies to “Arduino-serial: C code to talk to Arduino”

  1. The 2000 ms is probably from the opening delay you specified as a command-line argument to arduino-serial. This delay is needed because opening the serial port causes the Arduino to reset, and it’s standard policy to wait about 2 seconds to wait for the Arduino to come out of reset.

  2. Is there some reason why this interesting utility takes 2 seconds to start up? That’s 2000 ms. That’s an eternity. I can’t develop any good applications when each read takes 2 seconds.

  3. Hi Tod,
    Looks like mabe, its not working as expected for me.
    when i issue the following command it returns read but its same as the data ive sent.

    ./arduino-serial -b 115200 -p /dev/Arduiuno -s STW/13/1 -r

    however with Arduino serial monitor it works fine when i send STW/13/1 it returns 13/1

    I have tried using delay -d but it does not work.

    Any help is highly appriciated

  4. Hi todbot,
    Thank you very much for you code. After some tweaking it works great.
    The only bug I have found so far is that the script can only handle 17 writes, no matter how big the data string is. The 18th write returns -1 at serialport_write. int n = write(fd, str, len); returns n=-1. So there is some error in writing.
    I first thought that the input buffer got flooded but the size of the strings you send makes no difference. Have you got any idea what is causing this?

    Frank

  5. I’m working with Windows / Microsoft Visual Studio 2010 using ANSII C (not ++ or #) really simple. Does anyone have code to send serial data over my USB port? The examples all seem to use UNIX or Linux libraries I don’t have.

  6. Hi Todbot,

    Please delete the previous post (copy and paste error).

    I finally got it all working, this my code (it’s not good code, it’s just meant to be the minimal code to get this working – note the settings will need to be changed, but this works with my Arduino Uno on 19200 baud settings), hope it helps someone else who is stuck on this:

    int serialport_init(const char* serialport);
    int serialport_write(int fd, const char* str);
    int serialport_read_until(int fd, char* buf, char until);
    
    int main(int argc, char *argv[]){
      int fd = 0,n,Prev=0;
      char buf[256],b[1];
      /* change this appropiately */
      if((fd=serialport_init("/dev/ttyUSB0"))==-1){
        return -1; /* error - needs to be handled */
      }  
      while(1){
        if((n=read(fd,b,1))==1){
          if(Prev!=n){
    	printf("Received: ");
          }
          printf("%c",b[0]);
        }
        else{
          if((serialport_write(fd,"Wibble\n"))==-1){
    	return -1; /* error - needs to be handled */
          }
          else
    	puts("Sent: Wibble");
          sleep(1);
        }
        Prev=n;
      }
      close(fd);
      return 0;
    }
    
    int serialport_write(int fd, const char* str){
        int len=strlen(str);
        int n=write(fd, str, len);
        if(n!=len) 
          return -1;
        return 0;
    }
    
    int serialport_init(const char* serialport){
      struct termios toptions;
      int fd;
      fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
      if (fd == -1)  {
        perror("init_serialport: Unable to open port ");
        return -1;
      }
      if (tcgetattr(fd, &toptions) new("/dev/ttyUSB0");
        $port->databits(8);
        $port->baudrate(19200);
        $port->parity("none");
        $port->stopbits(1);
        
        in Perl, then doing:
        
        print Dumper($port);
        
        This will provide all the settings for your serial port in case you are having troubles. 
        Note different serial ports will have different settings and they will vary for baud rates. 
        Perl seems to autodetect them, to save you the googling ;)
        
      */
    
      toptions.c_cflag=2238;
      toptions.c_iflag=1;
      toptions.c_lflag=0;
      toptions.c_oflag=0;
      toptions.c_cc[VMIN]=0;
      toptions.c_cc[VTIME]=20;
      if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
        perror("init_serialport: Couldn't set term attributes");
        return -1;
      }
      return fd;
    }
    
  7. Hi Owl,
    Agreed, you should view all code with suspicion. And while arduino-serial is not without its bugs, it has been used by quite a few people for many years. The “readback” of the write buffer can be viewed as a bug or a feature. :)

  8. If you are stuck and you are happy to use perl for this (not ideal), you may want to try:

    #!/usr/bin/perl
    
    # Sample Perl script to transmit number
    # to Arduino then listen for the Arduino
    # to echo it back
    
    use Device::SerialPort;
    
    # Set up the serial port
    # 19200, 81N on the USB ftdi driver
    
    my $port = Device::SerialPort->new("/dev/ttyUSB0");
    $port->databits(8);
    $port->baudrate(19200);
    $port->parity("none");
    $port->stopbits(1);
    
    my $count = 0;
    while (1) {
        # Poll to see if any data is coming in
        my $char = $port->lookfor();
    
        # If we get data, then print it
        # Send a number to the arduino
        if ($char) {
            print "Received character: " . $char . " \n";
        } else {
            sleep(1);
            $count++;
            my $count_out = $port->write("$count\n");
            print "Sent     character: $count \n";
        }
    }
    
  9. Erm, this C code REALLY does not work!! I just spent an evening trying to work out why my code behaves differently, and it turns out that in the C code that is supplied, the same buffer is used to send the data as to receive the data and it’s not cleared out between send and receive, so actually… when you add a memset(buf,0,256) between the send and receive, the code doesn’t receive anything in actual fact. It just looks like it does.

    I would view this code with extreme suspicion if i were you.

  10. It took me a while to sort out but if your tty is already configured comment out the tcsetattr at the bottom of the init function. I’m not certain which of the setting were glitching out but it wouldn’t work until I did, I had already setup the tty so it wasn’t needed.

Leave a Reply

Your email address will not be published. Required fields are marked *