I need to read data from from a medical appliance via the serial port. This medical appliance produces streams of set data at regular intervals. What commands, packages are available for this purpose? Vittorio
Reading data from a serial port
5 messages · v.demart@libero.it, (Ted Harding)
On 13-Sep-05 vittorio wrote:
I need to read data from from a medical appliance via the serial port. This medical appliance produces streams of set data at regular intervals. What commands, packages are available for this purpose? Vittorio
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"
which tells me that it is 2005/09/13 at 19:27:52 UTC, that I
am at 52deg 28.545minN and 000deg 23.023min E, that I have a
Good stellite fix, have a potential horizontal position error
of 007 metres, am at 0000.4 metres below sea level, and am
moving at 000.0m/s Eastwards, 000.0m/s Northwards, and 00.00m/s
Downwards. (Illustrating that such data is parsed by position;
'scan' does not seem to have a mechanism for splitting a line
into fields by position, but it can be done after reading by
using 'substr').
That command read just one line, so by repeating the command
I can read a line at a time, do something with it, read the next ...
Again,
scan(file="/dev/ttyS0",n=5,what="character")
Read 5 items
[1] "@050913191942N5228544E00023023G010-00001E0000N0000U0001"
[2] "@050913191943N5228544E00023023G010-00001E0000N0000U0001"
[3] "@050913191944N5228544E00023023G010-00001E0000N0000U0001"
[4] "@050913191945N5228544E00023023G010-00001E0000N0000U0001"
[5] "@050913191946N5228544E00023023G010-00001E0000N0000U0001"
reads a batch of lines, which can be assigned to a vector.
Variants on this depend on what data format your apparatus
puts out, and on what you want to do. In particular, if you
want to process the output in real time, then probably you
are best off reading a line at a time. But if you simply
want to store a batch of lines for later processing, then
set a (possibly large) number of lines to be read at a time.
An so on.
Hoping this helps,
Ted.
--------------------------------------------------------------------
E-Mail: (Ted Harding) <Ted.Harding at nessie.mcc.ac.uk>
Fax-to-email: +44 (0)870 094 0861
Date: 13-Sep-05 Time: 20:26:09
------------------------------ XFMail ------------------------------
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"
which tells me that it is 2005/09/13 at 19:27:52 UTC, that I
am at 52deg 28.545minN and 000deg 23.023min E, that I have a
Good stellite fix, have a potential horizontal position error
of 007 metres, am at 0000.4 metres below sea level, and am
moving at 000.0m/s Eastwards, 000.0m/s Northwards, and 00.00m/s
Downwards. (Illustrating that such data is parsed by position;
'scan' does not seem to have a mechanism for splitting a line
into fields by position, but it can be done after reading by
using 'substr').
That command read just one line, so by repeating the command
I can read a line at a time, do something with it, read the next ...
.
Again,
scan(file="/dev/ttyS0",n=5,what="character")
Read 5 items
[1] "@050913191942N5228544E00023023G010-00001E0000N0000U0001"
[2] "@050913191943N5228544E00023023G010-00001E0000N0000U0001"
[3] "@050913191944N5228544E00023023G010-00001E0000N0000U0001"
[4] "@050913191945N5228544E00023023G010-00001E0000N0000U0001"
[5] "@050913191946N5228544E00023023G010-00001E0000N0000U0001"
reads a batch of lines, which can be assigned to a vector.
Variants on this depend on what data format your apparatus
puts out, and on what you want to do. In particular, if you
want to process the output in real time, then probably you
are best off reading a line at a time. But if you simply
want to store a batch of lines for later processing, then
set a (possibly large) number of lines to be read at a time.
An so on.
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
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 ------------------------------
On 13-Sep-05 vittorio wrote:
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
Following up, I've now had info from people pointing out the following. For Windows, there's a simple DOS utility which, for a serial port, is one the lines of MODE COM1:<speed>,<parity>,<databits>,<stopbuts>[,P] where the optional "P" is to allow infinite retries to send data to a non-responding device. This shouldn't be necessary when passively reading data being output from external equipment. Any of the above can be omitted (in which case the corresponding setting is not changed) provided the requisite commas are present. Example: MODE COM1:4800,E,7,1 will set 4800 baud, Even parity, 7 databits and 1 stop bit. Thanks to Tom Mulholland for reminding me of this! For Linux, there is the 'stty' command (which can set far more things as well, since it is designed for terminal consoles connected via serial lines). Something like stty -F /dev/ttyS0 4800 parenb -parodd cs7 -cstopb would have the same effect as the above. See "man stty" for more details. So, since there is a simple command foreither Windows or Linux, this can be sent from within an R session using the 'system' command, which will set up the serial port. After this, 'scan' should simply read the incoming data (as discussed earlier). Best wishes to all, 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: 13:09:55 ------------------------------ XFMail ------------------------------