Reading data from an Arduino's USB/serial port using Debian Linux

Updated: 3 December 2013

Home... Help... Search... Computers... Science...


Reading data from an Arduino's USB/serial port using Debian Linux

Here is a later approach using Minicom, from a reader. Thanks Martin, I've learned something new!


In case you don't know, the Arduino is a family of micro-controllers, of open source design, ideal for interfacing environmental sensors, etc. They are also very cheap, popular, easy to connect to external sensors and easy to program. But how do I connect one to a computer to read the data it gets from sensors?

As the Arduino has a built in USB port this ought to be the easiest way (other ways are to add Ethernet hardware, or even wireless networking, but both add cost, and complexity in the programming side). The USB's +5volt supply from the computer can also provide all the power the Arduino needs.

Here is how I got it all working with a home server running Linux Mint 17.1 (or any Linux based on Debian, such as Ubuntu and Mint):-

Getting the Arduino to send some data

I wanted to read a temperature and humidity sensor (and later, 3 of the same), using the cheap DHT11 or DHT22 chips. Here is the "sketch" (Arduino-speak for source code) I adapted from samples I found on the web. I compiled it and uploaded it via USB to the Arduino Nano clone I bought off eBay for $12.

  // Example testing sketch for various DHT humidity/temperature sensors
  // Written by ladyada, public domain
  
  #include "DHT.h"
  #define DHTPIN 2     // what pin we're connected to
  
  // Pin 13 has an LED connected on most Arduino boards.
  // give it a name:
  int led = 13;
  unsigned counter;
  
  // Uncomment whatever type you're using!
  #define DHTTYPE DHT11   // DHT 11 
  //#define DHTTYPE DHT22   // DHT 22  (AM2302)
  //#define DHTTYPE DHT21   // DHT 21 (AM2301)
  
  // Connect pin 1 (on the left) of the sensor to +5V
  // Connect pin 2 of the sensor to whatever your DHTPIN is
  // Connect pin 4 (on the right) of the sensor to GROUND
  // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
  
  DHT dht(DHTPIN, DHTTYPE);
  
  void setup() {
    delay(20000);
    Serial.begin(9600); 
    Serial.println("DHTxx test!");
   
    dht.begin();
  
    // initialize the digital pin as an output.
    pinMode(led, OUTPUT);
  }
  
  void loop() {
    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    counter = counter + 1;
    
    // check if returns are valid, if they are NaN (not a number) then something went wrong!
    if (isnan(t) || isnan(h)) {
      Serial.println("Failed to read from DHT");
    } else {
      Serial.print(counter);
      Serial.print(" Humidity: "); 
      Serial.print(h);
      Serial.print(" %\t");
      Serial.print("Temperature: "); 
      Serial.print(t);
      Serial.println(" *C");
    }
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);               // wait for a second
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);               // wait for a second
  
    delay(15000);
  }

The blinking of the on-board LED is just so that I know it is working.

Here is a sample of the data the Nano sends out:-

  
  DHTxx test!^M
  1 Humidity: 49.00 % Temperature: 19.00 *C^M
  2 Humidity: 49.00 % Temperature: 19.00 *C^M
  3 Humidity: 49.00 % Temperature: 19.00 *C^M
  4 Humidity: 49.00 % Temperature: 19.00 *C^M
  5 Humidity: 49.00 % Temperature: 19.00 *C^M
  6 Humidity: 49.00 % Temperature: 19.00 *C^M
  7 Humidity: 49.00 % Temperature: 20.00 *C^M
  8 Humidity: 49.00 % Temperature: 19.00 *C^M
  9 Humidity: 49.00 % Temperature: 19.00 *C^M
  10 Humidity: 49.00 %    Temperature: 19.00 *C^M
  11 Humidity: 49.00 %    Temperature: 19.00 *C^M
  12 Humidity: 49.00 %    Temperature: 19.00 *C^M

the "^M" is a carriage return (CR) character (ASCII 13) that the Arduino adds to each line. Linux/UNIX only uses a line feed (LF - ASCII 10) for line termination, but the scripts here can handle the CR without error.)

Gathering the data on the Linux box

Using minicom

I tried using the terminal program minicom, setting its serial port to the one used by the Nano, typically /dev/ttyUSB0, with the speed set to 9600 bits per second. This worked, and I could capture the output to a file, but was messy to run in the background, rather than on-screen. Still, it was a good first step to verify that the Nano was in fact sending data. To install minicom, log in as the root (superuser) account and

apt-get install minicom

Using a simple cat command

I then tried just putting a "cat" command into a shell script and running it in background

cat /dev/ttyUSB0 > arduino.dat

but getting the serial port settings to stick, say through a reboot of the server, was tricky. Might be possible using the "stty" command, but it was not obvious.

Using a PERL script - my preferred solution

Having used a PERL script for a similar function (reading the USB/serial port of a CurrentCost ENVI power monitor) I made a variant of it to read the Nano as well. The PERL script has been very reliable. Here it is

  #!/usr/bin/perl -w
  # getArduino.pl
  #
  # Based on Original Perl code by Paul Mutton.
  # See https://www.jibble.org/currentcost/
  # which was then Updated for CC128 by Mark E Taylor. April 2009.
  # https://metphoto.homeunix.net/met_current/
  # https://metphoto.net
  #Adapted to read the USB serial port on an Arduino Nano clone
  # by Tardus, November 2013, http://tardus.net
  
  use strict;
  use Device::SerialPort qw( :PARAM :STAT 0.07 );
      # port now passed via argument to this script, typically ttyUSB0 or ttyUSB1
  print "$ARGV[0]\n";
  #my $PORT = "/dev/ttyUSB0";
  my $PORT = "$ARGV[0]";
  my $ob = Device::SerialPort->new($PORT);
  $ob->baudrate(9600); # default speed for Arduino serial over USB
      #$ob->databits(8);
      #$ob->parity("none");
      #$ob->stopbits(1);
      $ob->write_settings;
  open(SERIAL, "+>$PORT");
  while (my $line = <SERIAL>) {
      open (DATFILE, '>>arduino.dat');
      print DATFILE  "$line" ;
      close (DATFILE);
  }

You will need the PERL serial module to run this script, so as root #apt-get install libdevice-serialport-perl

You will probably need to set the serial port device to read-write for all users (unless you run the script as root). To do this, as root,

chmod a+rw /dev/ttyUSB0 (or whatever your port is)

You might want to run this at each reboot of the server, so put the command in /etc/rc.local, or wherever your variant of Linux puts it.

To find out what port you have, look in the output of the dmesg command, for the "FTDI" device, the Nano's USB to serial convertor.

dmesg | grep FTDI

Note: I pass the port device to the script as an argument, since I have more than one USB serial device, and cannot be sure which will get assigned at boot time to the Nano. If you prefer to hard code it, remove the comment character "#" from the line "#my $PORT = "/dev/ttyUSB0";" and comment out the line that follows instead.

You also need to make the PERL script executable

chmod u+x getArduino.pl

To test

getArduino.pl /dev/ttyUSB0

You will not see any output to screen, but if you open a second terminal session and look at "arduino.dat" you should see some data. Try this:-

tail -f arduino.dat (CTRL-C to exit it)

Using the data to create graphplots with MRTG

Here is my bash shell script that runs every 10 minutes to create plots using MRTG. My brief guide to BASH might help if you are not familiar with BASH

  #!/bin/sh
  # plotArduino.sh
  # get the latest data from the Arduino's USB serial port to plot with mrtg
  # Tardus 29 Nov 2013, https://tardus.net
  
  cd ~/monitor
  
  #check to see if the data receiving script is running in the background
  if [ ! $(pidof -x getArduino.pl) ]; then
      echo "getArduino.pl is NOT running"
      echo "Starting getArduino.pl"
          # find what port the Arduino's FTDI usb serial convertor is on
          # we search the output of dmesg for "ttyUSB" 
          # then for "FTDI" which is the USB to serial convertor on board
          # the Arduino. awk prints the last field
          # which is the actual port name
      PORT=`dmesg|grep ttyUSB|awk '/FTDI/ {print $NF}'`
      echo "PORT=$PORT" # echo to screen for debug info only
          # run getArduino.pl in the background, passing it the port to use
          # note we have to prepend "/dev/" to the port name
      nohup ./getArduino.pl "/dev/"$PORT > ./getArduino.log 2>&1 &
          # as this PERL script runs in the background, 
          # we spit any output to a log file, including any errors.
          # The "2>&1" bit captures any errors to the log file.
  else
      echo "getArduino.pl is running"
          # we use tail to get the last line of the data file
      tail -1 arduino.dat > latestArduino.dat
  
          # plot using MRTG
      env LANG=C mrtg arduino.cfg
          # the cfg file includes an inline script to extract the necessary
          # data for plotting from the "latestArduino.dat" file
  fi
  exit

The script stores the latest data in a file latestArduino.dat

  14996 Humidity: 47.00 %	Temperature: 22.00 *C

Here is the MRTG config file, "arduino.cfg"

  Interval: 10
  WorkDir: /home/steve/monitor/mrtg/
  
  Target[ambient]: `~/monitor/ambient2mrtg.sh`
  Options[ambient]: growright,integer,gauge,pngdate
  Colours[ambient]: springgreen#00ee76,color2#000000,Color3#121212,Color4#191970
  MaxBytes[ambient]: 100
  AbsMax[ambient]: 100
  WithPeak[ambient]: ymw
  UnScaled[ambient]: ymwd
  YLegend[ambient]: C %
  ShortLegend[ambient]: x
  Legend1[ambient]: T
  LegendI[ambient]: Temperature
  LegendO[ambient]: Humidity
  PNGTitle[ambient]: Ambient Temperature and Humidity
  Title[ambient]: Ambient Temperature and Humidity
  PageTop[ambient]: <h1>Ambient Temperature and Humidity</h1> 
  Note: time is Australian Eastern Standard Time<br>

Here is the script that the mrtg calls to get the subfloor temperature and humidity:-

  #!/bin/sh
  # extracts temperature & humidity from the outside ambient.
  # ambient2mrtg.sh
  
  TEMP=`awk '{print int($6+0.5)}' ~/monitor/latestArduino.dat`
  RH=`awk '{print int($3+0.5)}' ~/monitor/latestArduino.dat`
  
  UPTIME=0
  LABEL="Ambient Temperature (C) & Humidity (%)"
  echo $TEMP
  echo $RH
  echo $UPTIME
  echo $LABEL

Finally, here is the crontab entry that runs the whole thing every 10 minutes.

  crontab.arduino
  */10 * * * * ~/monitor/plotArduino.sh > ~/monitor/plotArduino.log 2>&1 =
  

Live Plotted Output

https://tardus.net/mrtg/ambient.html

Another Approach

Martin contacted me about this - his Debian box uses ACM0 as the USB port, and he was having problems with port permissions using "cat" to get data. Here is how he solved it:-

"I finally got it working and with plotting of the temperature values.

I ended up using the minicom utility.

If you're interested or want to add this to your webpage, here are the steps I did.

1) I did not run the Arduino Serial Monitor. 2) I ran minicom utility:

sudo minicom -D /dev/ttyACM0 -b 9600 -C aaa.txt

3) run the Linux gnuplot utility:

set xdata time set timefmt "%H:%M:%S" plot 'aaa.txt' using 1:2

1:2 - display the data from the file found in columns 1 (time lapse) and 2 (temperature reading from DHT)

4) click the refresh button on the plotter page every now and again to display all the new data values that were added to the aaa.txt file since the last plotter page refresh.

I've attached the aaa.txt data file that the minicom utility captured and outputed to the aaa.txt file. I've also attached a copy of the gnu plotter page charting the data points.

The ardunio sketch is attached as well."

//This is neat - I had not realised you could use minicom in this way - I'd only ever used it as an interactive, menu-driven program.

Here are Martin's files, mentioned above:-//

iaaa.txt Arduino sketch


Home... Help... Search... Computers... Science...


This page tardus.net/readNano.html Last refreshed: 04 Oct 2023

About Tardus

Contact me, "Tardus" Copyright powered by txt2tags

Search tardus.net

Search...