Arduino-serial: updated!

In late 2006 I wrote “arduino-serial“, mostly for myself, to help with stuff I was working on at the time. It was a very simple & small, cross-platform tool written in basic C for reading/writing serial ports.

Now nearly seven years later I still get regular questions and frustrations about it. Part of this is due to how Arduinos have changed over time. You used to have to hand-reset an Arduino board, now the act of opening the serial port resets it. This has its plusses and minuses, but it really made my original use-case of arduino-serial fail. Then there were just all the minor deficiencies of the program.

To address some of these issues, but still keep things small & light, I’ve done a bit of fix-up of arduino-serial. It’s now hosted on Github at:
https://github.com/todbot/arduino-serial/

Changes & Improvements

Some changes that I recently made to arduino-serial:

  • Separation of the application (arduino-serial.c) from the library (arduino-serial-lib.{c,h})
  • Fixed probable --read bug
  • Fixed --port open to allow re-opens
  • Added --sendline command to send a string followed by a newline
  • Added --flush command to clear out receive buffer
  • Added --eolchar option to let you specify your own end-of-line character if ‘\n’ isn’t appropriate
  • Added --timeout option to specify a read timeout (reads no longer block infinitely)
  • Added --quiet flag to make output more terse/machine-readable

Here’s what the new usage help screen looks like:

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'

Using arduino-serial

arduino-serial has always been designed so you can “pipeline” commands/options, but it wasn’t implemented very consistently. It’s a bit better now. You can do multiple send/read pairs, even use multiple serial ports, all from a single command-line invocation.

For example, if you have the “SerialCallResponseASCII” sketch from the Communications examples loaded onto your Arduino, you can run commands to take multiple data readings. In the example below, the order of operations are:

  1. serial port is opened (at 9600)
  2. the string “A” is sent (a single-byte)
  3. the first line is read
  4. sleep for 1000 milliseconds
  5. send “A” again
  6. read second data line
  7. flush read buffer (just to show we can)
  8. send “A” a third time
  9. and take a final reading
laptop% ./arduino-serial -b 9600 -p /dev/tty.usbmodemfd131 \
            -s "A" -r  -d 1000  -s "A" -r  -F  -s "A" -r
send string:A
read string:465,396,0

sleep 1000 millisecs
send string:A
read string:358,352,0

flushing receive buffer
send string:A
read string:307,305,0

The flush was put in there to demonstrate that you could flush the receive buffer mid-command if you wanted. It’s not required, but might help some situations.

To use multiple serial ports, you can do something like the below, which opens up one serial port at 9600 bps, does a send & receive, then opens another at 57600 bps and does a send & receive on it:

laptop% ./arduino-serial -b 9600 -p /dev/tty.usbmodemfd131 \
        -s "A" -r \
        -b 57600 -p /dev/tty.usbserial-A800f8ib \
        -s "hello" -r 

15 Replies to “Arduino-serial: updated!”

  1. It was, in fact, cockpit error:
    arduino-serial -b 9600 -p /dev/cu.usbmodem1421 -S 'V' -t 10000 -e '\r' -r
    does not set the end char to [return], but rather to backslash. The right way to quote it in bash is:
    arduino-serial -b 9600 -p /dev/cu.usbmodem1421 -S 'V' -t 10000 -e $'\r' -r

  2. serialport_read_until() method needs some optimization, in particular these 2 fixes!

    1) More proper way to declare char:

    char b[1]; change to char b;
    int n = read(fd, b, 1); change to int n = read(fd, &b, 1);
    buf[i] = b[0]; change to buf[i] = b;
    while( b[0] != until ... change to while( b != until ...

    2) Overflow problem

    while( ... && i < buf_max ... change to while( ... && i+1 < buf_max ...

    Unless the documentation says that buf_max should be one less than the size of the buffer (which would be counterintuitive and error-prone)

    Also, since i is checked at the end, even with this fix, the code will still store one byte if you pass in buf_max == 0 (but without the fix it will store two bytes). So that’s another bug.

    https://github.com/todbot/arduino-serial/issues/10

  3. Hi Tasya,
    You are probably running into a common issue we all have to deal with: opening up the serial port on the computer side of things causes the Arduino to reset and wait in the Arduino bootloader for a bit.

    To guard against this, you’ll see computer-side code often have the following flow:
    1. Open serial port
    2. Wait some amount of time (2 seconds is common)
    3. Write to serial port (or read, depending on your situation)
    4. Keep doing 3 until you no longer want to talk to the Arduino
    5. Close serial port

    To do something like this with “arduino-serial” commandline tool , you would do:

     ./arduino-serial -b 9600 -p /dev/ttyACM0 -d 2000 -s "a"
    

    This will open /dev/ttyACM0 at 9600 bps, wait 2 seconds, then send “a”, then close /dev/ttyACM0. But then if you run “arduino-serial” again, it re-opens the serial port, thus causing the Arduino to reset.

  4. Hi! Todbot! I changed my code to send numbers and the case to numbers instead and it worked. The thing is, I need to run “arduino-serial” on the command line first everytime I change the Arduino code before I run the C program for it to work. Do you have any idea of what’s causing this?

  5. Hi Todbot! Thank you for the great library. However, I have a problem. When using arduino-serial from the terminal I can send message just fine to the arduino. When using a C program, however, the message seems to not reach the arduino although no error were shown.

    The following is my C code:

    #include "arduino-serial-lib.c"
    #include "arduino-serial-lib.h"
    
    main(void)
    {
        const char* mes = "a";
        int dev = serialport_init("/dev/ttyACM0", 9600);
        serialport_write(dev, mes);
    }
    
    

    and my arduino code is:

    int incomingByte;
    char button;
    
    void setup() {
      // put your setup code here, to run once:
      pinMode(13, OUTPUT);
      Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      if (Serial.available() > 0) {
        // read the incoming byte:
        incomingByte = Serial.read();
        Serial.print(incomingByte;
    
        switch(incomingByte) {
          case "a":
            digitalWrite(13, HIGH);
            break;
          case "b":
            digitalWrite(13, LOW);
            break;
        }
      }
    }
    

    Do you know what might be the problem?

    Thanks!

    tasya

  6. Hi Eva,

    You can just include `arduino-serial-lib.h` and `arduino-serial-lib.c` in your C project and then do something like:

    char str_to_send[] = "Up";
    int dev = serialport_init("/dev/ttyUSB0", 9600);
    serialport_write(dev, send_str, strlen(send_str) );
    
  7. Hello Tod,

    i’m actually trying to use your code because I would like to send some string to my Arduino.
    But I do not want to do it from the linux terminal, I have a long code that uses several parameters to determine the direction of the strip leds connected to the Arduino.

    I just want the code to be able to take a printf(“Up”) that comes from the previous code and send Up to the Arduino.
    I do not need any getopt, so I do not know how to change the case ‘s’ and the serialport_write

    Thank you very much

  8. Thanks, this solved my problem perfectly. I have a number of arduino-controlled projects for the care and entertainment of my cats. They suddenly stopped working. (I using echo to write to the serial port.) With your program, everything works again.

  9. Hello,

    I am very new to all of this but I got an arduino starter kit and I am trying to use voice recognition with it. I have downloaded the files: arduino-serial.c, arduino-serial0lib.c, and arduino-serial.lib.h and put them on the root of my system. I also downloaded Xcode for my computer and enabled the command line tools.

    I am now trying to compile arduino-serial so I can communicate with my little toy and I get a bunch of warnings and 2 errors that prevent me from using arduino-serial.

    Here I copy what my terminal tells me. Any idea what I am doing wrong? Thank you very much,
    Bene

    error: expected identifier or ‘(‘
    {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
    ^
    ./arduino-serial-lib.h:55:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    ./arduino-serial-lib.h:64:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    ./arduino-serial-lib.h:71:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    ./arduino-serial-lib.h:80:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    ./arduino-serial-lib.h:91:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    ./arduino-serial-lib.h:106:2: warning: missing terminating ‘ character
    [-Winvalid-pp-token]
    \’a0\’a0
    ^
    arduino-serial.c:156:21: warning: implicit declaration of function
    ‘serialport_close’ is invalid in C99 [-Wimplicit-function-declaration]
    serialport_close(fd);
    ^
    arduino-serial.c:160:22: warning: implicit declaration of function
    ‘serialport_init’ is invalid in C99 [-Wimplicit-function-declaration]
    fd = serialport_init(optarg, baudrate);
    ^
    arduino-serial.c:163:17: warning: implicit declaration of function
    ‘serialport_flush’ is invalid in C99 [-Wimplicit-function-declaration]
    serialport_flush(fd);
    ^
    arduino-serial.c:168:22: warning: implicit declaration of function
    ‘serialport_writebyte’ is invalid in C99 [-Wimplicit-function-declaration]
    rc = serialport_writebyte(fd, (uint8_t)n);
    ^
    arduino-serial.c:168:48: error: use of undeclared identifier ‘uint8_t’
    rc = serialport_writebyte(fd, (uint8_t)n);
    ^
    arduino-serial.c:177:22: warning: implicit declaration of function
    ‘serialport_write’ is invalid in C99 [-Wimplicit-function-declaration]
    rc = serialport_write(fd, buf);
    ^
    arduino-serial.c:183:17: warning: implicit declaration of function
    ‘serialport_read_until’ is invalid in C99
    [-Wimplicit-function-declaration]
    serialport_read_until(fd, buf, eolchar, buf_max, timeout);
    ^

  10. Hi Goatboy,
    Yeah the reason why it’s commented out was because it can’t work. On Mac OS X & Linux (I think), the act of opening the port toggles DTR, regardless of the settings.

  11. I’ve poked around your code a bit and I was wondering if you’d be able to either make the change or discuss with me how to deal with the DTR/HUPCL “auto-reset Arduino every damn time you wanna use serial” issue. I love your code, it’s very simple and clean, does what it needs to. I just couldn’t get the commented-out

    Line 39: ioctl(fd, TIOCMBIC, &iflags); // turn off DTR

    to work in arduino-serial-lib.c. I also uncommented the

    Line 37: int iflags = TIOCM_DTR;

    line above it.

    Any help would be greatly appreciated as I am banging my head over trying to get serial to work without resetting the Arduino.

    Much thanks!

Leave a Reply

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