How to read serial data from an Arduino in Linux with C: Part 4

Parts one, two, and three dealt with non-canonical reading of serial data coming from an Arduino. Part 4 is how to do it in canonical mode.

I programmed the Arduino to wait until it receives a byte from the PC and then transmit “Hello” back. The (super simple) Arduino sketch is here: https://gist.github.com/2980344

I’m frustrated with the WordPress code posting functionality, so I put the program that does the reading to and receiving from the Arduino on Github. It’s called Canonical Arduino Read.

Here’s a breakdown of the important bits:


/* open serial port */
 fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);
 printf("fd opened as %i\n", fd);

 /* wait for the Arduino to reboot */
 usleep(3500000);

As I’ve said before, you have to give the Arduino some time to reboot after you call open(). Notice that I’m opening this as a blocking port by leaving out O_NDELAY as an option.


/* get current serial port settings */
 tcgetattr(fd, &toptions);
 /* set 9600 baud both ways */
 cfsetispeed(&toptions, B9600);
 cfsetospeed(&toptions, B9600);
 /* 8 bits, no parity, no stop bits */
 toptions.c_cflag &= ~PARENB;
 toptions.c_cflag &= ~CSTOPB;
 toptions.c_cflag &= ~CSIZE;
 toptions.c_cflag |= CS8;
 /* Canonical mode */
 toptions.c_lflag |= ICANON;
 /* commit the serial port settings */
 tcsetattr(fd, TCSANOW, &toptions);

Nothing fancy here. I’m getting the current port configuration, setting the input/output speed to 9600 baud, setting the data expectations to be 8 bits per word, with no parity bit and no stop bit, setting canonical mode, and then committing those changes back to the serial port.

My past few posts have been about trouble I’ve had reading data, and the fixes were all in the code above that determines the serial port settings. I accidentally (by virtue of a sloppy copy/paste of another program I wrote) had  “&= CS8” instead of “|=CS8”. That was dumb mistake number one. Number two was that I was setting c_iflag to ICANON instead of c_lflag. That’s a TOUGH typo to catch.


/* Send byte to trigger Arduino to send string back */
 write(fd, "0", 1);
 /* Receive string from Arduino */
 n = read(fd, buf, 64);
 /* insert terminating zero in the string */
 buf[n] = 0;

printf("%i bytes read, buffer contains: %s\n", n, buf);

This last part of the program writes out one byte to the Arduino. The Arduino sees this, and then writes back “Hello”. I read that “Hello” into my buf[] array, and then terminate that array with 0 so that when I use printf() on the next line it doesn’t go into undefined parts of the array.

And that’s that. I started working on this as part of a larger project on Thursday. It’s Sunday evening now and I finally got this one part working perfectly. I don’t regret getting so stuck. I’ve had to really dig into the terminal interface and I feel much more confident with it now. Now that this chunk of code is working, I’ll fit it into the larger program.

The larger program will query the Arduino, and the Arduino will send back a voltage read from a temperature sensor. Super similar to what I’m doing above, which queries the Arduino (by sending it a byte) and gets serial data back. The next hump will be figuring out how to convert the string sent back into a numeric type like float or int. I don’t recall Unix having a standard ascii-to-int function. No doubt this is a solved problem, so I won’t have to search too hard to find a solution.

Leave a comment below or shoot me a message on Twitter @cheydrick if you have questions!

This entry was posted in Hobbies, Programming. Bookmark the permalink.

14 Responses to How to read serial data from an Arduino in Linux with C: Part 4

  1. FozzTexx says:

    The functiosn you’re looking for are atoi() and atof(). If you’ve got a full install you should also have the man pages and you can use man -k to do searches.

    I’ve found that after my Arduinos have been runing for a while the serial link will stop working. One of two things usually happens: either the Arduino will seem to no longer be responding although it hasn’t crashed, or Linux will disconnect and reconnect the USB serial device. I’ve had to put in a lot of workarounds in my code to deal with the problem.

  2. Maximiliano says:

    This post is exactly what I was looking for. Thank you for putting so much effort on building this tutorial.

  3. zj says:

    Thanks a lot!

  4. Harris says:

    Hi, do you know how to send int to PC instead of string in the example?

  5. Anonymous says:

    sorry,i can not open the website https://gist.github.com/2980344

    • Works for me. Here’s what’s at the link:

      /* This sketch waits for incoming serial data to be present
      and then sends a message serially.

      After Serial.println() is a call to Serial.read() to remove
      the received byte from the input queue. If you send more than
      one byte to the Arduino, you’ll get more than one response sent
      back at you.
      */

      void setup()
      {
      Serial.begin(9600);
      }

      void loop()
      {
      if (Serial.available() > 0)
      {
      Serial.println(“Hello”);
      Serial.read();
      }
      }

  6. Rodrigo Zanatta says:

    Man… if it was not for these observations you explicitly made about the “c_lflag” and the “&=” typos, I guess I would have never found what was wrong about my code… There are days that I try to config this comm right… Gone through all the HowToos in the internet, specially those ubiquitous ones (Serial Programming Guide, Serial HOWTO…), from head to tale. Even got to understand a little bit about those pins on the old RS-232 conector (who cares?) Found your post yesterday late in the night, almost sleeping. Coundn’t wait to wake up today and check it.

    I have to say, this is a light in the dark for a non-professional. Including the tips on the meaning of (or the way to understand the meaning of) the |= and &= operators…

    Tnx.

  7. I also have a problem reading an arduino by serial…
    Something is really weird :
    I’ve tried from my PC (linux laptop), and everything is OK when I read my arduino.
    I’ve then plugged it on my raspberry PI, but I always miss the 1st byte of the string sent by the arduino !!
    I’m pulling my hairs !!

    Any tips ?

    Regards

  8. ntech42 says:

    But is there a way to do the select(2) call on it? Because that doesn’t seem to be working. 😦

  9. wanxuanzhang says:

    Why I the code cannot work anymore once I open serial monitor of Arduino?
    And for the c programming, it worked well in previous test, but currently, the data it reads shows -1..

  10. Adam says:

    Great post! I am trying to run this on mac OSX (EL Capitan 10.11.6) and the program hangs on

    fd = open(“/dev/ttyACM0”, O_RDWR | O_NOCTTY);

    It doesn’t return anything, it just hangs, and I’m not sure why. Any thoughts? There were no compiling errors.

  11. what should be done on the tk1 side to access the serial port to transmit data serially to the Arduino serial monitor?
    anyone??

  12. Karl says:

    Hi there, when you say you programmed the Arduino, did you only use the rx and tx lines between the Arduino and TX2? I am having problems uploading sketches. I have to connect a USB dongle to the USB port of the TX2 and connect the rx and tx of it to the Arduino pins. Any help would very very much appreciated!!

  13. Anonymous says:

    Thank you !!!!!!

Leave a comment