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:
- serial port is opened (at 9600)
- the string “A” is sent (a single-byte)
- the first line is read
- sleep for 1000 milliseconds
- send “A” again
- read second data line
- flush read buffer (just to show we can)
- send “A” a third time
- 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
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
serialport_read_until() method needs some optimization, in particular these 2 fixes!
1) More proper way to declare char:
char b[1];
change tochar b;
int n = read(fd, b, 1);
change toint n = read(fd, &b, 1);
buf[i] = b[0];
change tobuf[i] = b;
while( b[0] != until ...
change towhile( b != until ...
2) Overflow problem
while( ... && i < buf_max ...
change towhile( ... && 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
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:
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.
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?
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:
and my arduino code is:
Do you know what might be the problem?
Thanks!
tasya
Thanks, this code was really useful. The only issue I had was that sometimes the string read from the arduino would contain backspaces that would delete the first part of the string. I’m not sure why that happened because my arduino sketch definitely didn’t include them. It was simple to fix by inserting the strip() function from here:
http://rosettacode.org/wiki/Strip_control_codes_and_extended_characters_from_a_string#C
Hi Eva,
You can just include `arduino-serial-lib.h` and `arduino-serial-lib.c` in your C project and then do something like:
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
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.
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);
^
Thanks, i use it for my meterN project
cheers
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.
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!