[R] Reading data from a serial port

(Ted Harding) Ted.Harding at nessie.mcc.ac.uk
Wed Sep 14 02:13:26 CEST 2005


On 13-Sep-05 vittorio wrote:
> Alle 21:26, martedì 13 settembre 2005, Ted Harding ha scritto:
> ...................................>
>> Have a look at "?scan".
>>
>> You will have to do several things which depend on your hardware
>> setup, your operating system, the behaviour of your medical
>> applicance,
>> and how you want to handle the data as it comes through. Some of this
>> can be worked out from "?scan"; the rest is up to you!
>>
>> Example: On my Linux machine here, the serial port at the back is
>> /dev/ttyS0. I have used 'minicom' to set its data parameters to
>> 4800 baud, 7-bit data, space parity, 1 stop bit ("4800 7S1").
>>
>> Next, I have connected my GPS gadget (which has a serial output
>> in ASCII text format at the above characteristics) to the serial
>> port.
>>
>> Then, in R (with permissions on /dev/ttyS0 set to allow user
>> read/write,
>> namely "rw-rw-rw-"), in R I have executed, for example,
>>
>> scan(file="/dev/ttyS0",n=1,what="character")
>> Read 1 items
>> [1] "@050913192752N5228545E00023023G007-00004E0000N0000D0000"
>>
>> [...]
>> 
>> Hoping this helps,
> 
> Thanks, it helps! But Ted,
> how do you let R know the parameters of the serial connection
>  (e.g. "4800 7S1") ?
> 
> Ciao
> Vittorio

I didn't, as it happens -- I used the 'minicom' program to set
the serial port to these characteristics before starting the
business with 'scan' in R, so it was an "external" operation.
In fact I have a ready-made configuration file for this purpose
called "/etc/minirc.etrex" which contains

# Machine-generated file - use "minicom -s" to change parameters.
pr port             /dev/ttyS0
pu baudrate         4800
pu bits             7
pu parity           S
pu stopbits         1

so when I run

  mincom etrex

the serial port is set to those parameters. Unfortunately, minicom
does not have an option causing it to quit after initialising
the serial port, so it has to be killed explicitly, since otherwise
it will continue reading the serial port and thereby "steal" data
from R! This I do by pressing Ctrl-C on the keyboard, but it could
be wrapped in a script which identified minicom's process ID and
then sent a 'kill -15 <pid>' to it.

If I did it that way, say the script were called "set.etrex".
Then, using the 'system' command

  system("set.etrex")

I could set it up from within an R session.

On Unix/Linux systems there is a program 'setserial' which can be
used to set up the serial port, but it seems it can only be used
to set baud rate (and in a somewhat obscure way); I've seen nothing
which indicates how to use it to set data bits, parity, and stop
bits. So as far as I know 'minicom' is the only program I have
available which can do the lot (short of getting out a serial port
manual and writing a C program which will talk directly to the
hardware port!).

If other readers know better, I'd be very grateful to hear of it!

(Come to think of it, that's just the sort of question to ask my
mates on Linux lists ... )

Once that was done (and checked by reading a few lines of output
to screen by 'cat /dev/ttyS0', closed by Ctrl-C) I then went into
R, and did the things like

  scan(file="/dev/ttyS0",n=1,what="character")

Since the serial port was already set up, and receiving the output
from the instrument, all R was doing was reading this from the
serial port. 

Now all this of course is written in terms of a Linux system,
and we don't know yet what sort of system you are using. On Windows,
I find one can navigate by hand through

  My Computer -> Control Panel -> System -> Device Manager
  -> Ports -> Communications Port (COM1) -> Port settings

where again one can manually set "Bits per second", "Data bits",
"Stop bits" and "Flow control" but, again, I don't know of a
program which can be used to set these "non-manually".

Anyway, the summary of the way I did it is to set the serial
port parameters independently of R, connect the device and get
it sending data, and then within R use 'scan' to read the port.

Initially one may capture an incomplete first line from the
port, since R will start with whatever is sitting on the serial
port's data lines when 'scan' is invoked. However, I have rarely
seen this and indeed, even at 4800 baud, it is not very likely.
The GPS device sends a line of data for every second of time,
consisting of 57 ASCII characters including the terminal
CRLF, which is a total of 9*57 = 413 bits at "7S1" taking
513/4800 approx= 1/10 seconds, so it is only about 1/10 chance
of catching an incomplete first line.

However, there are various ways you can arrange to avoid such
incomplete data being processed:

a) Get R reading the serial port before the device starts
   sending data. The R will already be looking for data before
   the first line starts to come through;
b) Simply dump the first line read by R;
c) Read it anyway, but if its length (as a character string) is
   too short, then dump it (use the R function 'nchar' for this);

This is always the issue when dealing with hardware interfaces:
mere logic is not enough -- you have to plan how to cope with
unruly behaviour as well!

Of course, the data string output from your device may already
consist of distinct fields separated by whitespace or by a separator
character such as "," (as opposed to my GPS data I used as an
example, where the fields are defined by position). In that case,
since 'scan' does have the capability to split the string into
fields by the separator, you can use the "what=list(...)" option.
E.g.

> E<-scan(what=list(Name="",Num=as.integer(1),Wt=1),n=1,sep=",")
1: "Ted",1,70.1
2: 
Read 1 records
> E
$Name
[1] "Ted"

$Num
[1] 1

$Wt
[1] 70.1

> typeof(E$Name)
[1] "character"
> typeof(E$Num)
[1] "integer"
> typeof(E$Wt)
[1] "double"

I'm a bit unsure from the documentation "?scan" exactly how this
works, but emprically it seems that if in

  "what=list(name1=A,name2=B,name3=C)"

you put instances of types (in this example A is "" (string),
B is as,integer(1) (integer), C is 1 (number)) then the result
of 'scan' is a list whose elements have these types, as verified
above by 'typeof'.

But I confess I have few general ideas about how to handle the
situation where the output of the device is lines which are
variable-lengh records, or even worse variable-type. Here, I think,
you have to roll up your sleeves and do "intelligent programming"!

Feel free to ask further questions -- it's an interesting topic,
not often discussed, and I hope that R experts in this field will
intervene!

Best wishes,
Ted.


--------------------------------------------------------------------
E-Mail: (Ted Harding) <Ted.Harding at nessie.mcc.ac.uk>
Fax-to-email: +44 (0)870 094 0861
Date: 14-Sep-05                                       Time: 01:11:39
------------------------------ XFMail ------------------------------




More information about the R-help mailing list