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. How would implement this in Visual Studio .NET 2005 ?
    The POSIX libraries are not supported.

  2. Another common idiom for loops which are either “infinite” or broken out of in the middle is “for(;;) { … }”. This has the advantage of having the control conditions be explicitly empty – you don’t have to look twice to check if the “1” is a lower case letter L, for example.

  3. Hi Christopher,
    Your questions aren’t stupid. In fact they get some of the less clear aspects of how people typically write C code.

    The while(1){ } block is used to wrap the call to getopt_long(). Each time that function is called, it parses another chunk of the command line argument string. So the while(1){ } says “keep parsing forever”. But there is a if (opt==-1) break; that will break out of that while loop if there are no more arguments to parse.

    Structuring the code this way is a way you’ll see some simple C programs written where it implies that program’s functionality is simple and controlled entirely via command-line arguments. That is, the style of the source code implies the intended use of the program. You’ll find this kind of philosophy in a lot of C code. There’s so many ways to do the same thing in C that people adopt “idioms” in how they code to hint at what they’re trying to do. I just copied it from some other code I had. :)

  4. A little off topic, as this is more of a c question.

    I am Very new to all of this, so please pardon what is probably a stupid question;

    In your C code, you are parsing out the options sent to the app with a while statement.

    Why do you have a ‘while(1) { }’ Isn’t that true by default?

    and, how is this able to parse all options?

    any illumination on this is greatly appreciated….

    Thanks!

    Christopher Lund

  5. I have succeeded in my project to use Diecimila
    with SimpleMessageSystem & linux, via shell scripts.
    Run this now: ‘wget http://207.14.167.161/SMS1.tgz‘
    It only works when my system is booted, so keep trying. Finally got the right data format from readAD-1: Comma-separated values. This imports to OpenOffice calc easily, for further analysis or graphing. Try it.

  6. Arduino features an FTDI chip to do serial communication with an USB host, but of course this limits us to use the FTDI driver, and thus forces us to install drivers and limits us to our own software. Since the USB spec does not allow bulk endpoints for low speed devices, it’s always possible that a new version of e.g. Windows does not allow them.

  7. I don’t think removing O_NDELAY on serial port open() will affect your issues. But it will probably make it so some of your reads() never time out (I’ve not checked this with serial ports)

    Does the arduino-serial.c program read data correctly for you? (it will print to the stdout, but it’s easy to change that to print to a file)

    The fact that you’re reading garbage means either you’re at the wrong baudrate, wrong number of stopbits, or you’re not checking the return value from read() to see how many bytes were actually read.

  8. I am trying to write a c program that logs the data from the serial port then saves it to a file.

    I have been having some trouble reading the data from the port. The port seems to be set up correctly( I get a 3 back) but when I try a read I get a -1.

    Arduino monitor and cutecom can read the arduino just fine.

    thanks

  9. Hi Tod, that’s great – I’m so thankfull! I use it to write on an LCd Screen (4 lines 40 Col) and so I can send the bytes to control it.
    best wishes and an hapy new year
    Klaus

  10. Hi Klaus,
    To send the byte value 65 instead of the character ‘a’ (ascii 65), you need to do an ascii-to-integer conversion. There’s a function called “atoi()” in C that does this. There’s a more general function called “strtol()” (“string to long”) that will even parse decimal, hex, and other number formats.

    I’ve updated the arduino-serial.c program above to do this with a new option ‘-n ‘.

    To use it you would do something like this:

    % arduino-serial -b 19200 -p /dev/USBS0 -d 2000 -n 65 -r

    This will open up serial port “/dev/USBS0” at 19200 bps, wait for 2 seconds, then send the number 65 and read back any response terminated by a newline.

  11. Hi Tod great site. I’m new with arduino and C.
    I need that the programm send binary not ascii e..g -s 65 = a and not 65. How can I change the arduino-serial.c ?
    Thanks sorry for my english I hop you understand my Question
    Klaus Germany Berlin

  12. I think /dev/tty.usbserial-A10?16fZ works from the command line beacuse bash interprets ? as a wildcard (whereas C doesn’t), so the ? is a typo (kinda like how /dev/tty.usbserial-A10*16fZ works)

  13. ah, this is a new installation already… so maybe a way to change my arduino’s serial number?

  14. Download the latest version of the Arduino software. Inside there is a directory called “drivers” containing driver installers.

  15. That’s very strange. I’ve never seen a USB-to-serial driver create a virtual serial port that wasn’t just alphanumeric. Creating a symlink to a more reasonable name like you did is probably the best solution.

    Aside: A symlink (“symbolic link”) is the unix way of doing aliases. The “ln -s” command creates symlinks. The “sudo” command lets you “do” things that normally a “su”per user (admin user) can only do. Creating symlink in the /dev directory is one of those things.

    Again, it’s very strange to have a question-mark in the serial port name. Are you running the latest serial drivers that come with Arduino? Has anyone on the Arduino forums had a similar problem?

Leave a Reply

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