Você está na página 1de 14

Applied Mechatronics

Lab: Serial port communication in C


Sven Gestegard Robertz <sven@cs.lth.se>
September 2013

1 Introduction
The overall goal of this (self study) lab is to get aquainted with serial port
programming. The specific task is to write a small program for writing to and
reading from the serial port, and to get two computers to communicate via a
null modem cable. This is a preliminary step towards communication between
the host (PC) and the embedded system (AVR) in the lab project.

2 Preliminaries
2.1 Test serial port and cable
A first step is to verify that the serial port and the cable works, using tools
provided with the system. On Windows, there is a terminal program called
Hyperterminal.
We will use this program to test the port and cabling using a method called
loopback testing. By connecting the Rx and Tx pins directly, the computer
can talk to itself via the serial port. This is a common technique for testing
communication links. It is also a good example of the testing practise of starting
from a working system and extending in small steps.
Do the following:
Start Hyperterminal, and configure the connection for 2400 8N1 with no
flow control
With nothing plugged into the serial port, type on your keyboard. Nothing
should appear in the terminal. Please note that this step is important.
Verifying that it doesnt work makes sure that you are actually testing
what you think you are testing. In this case, you may have Local echo
turned on, giving you a false positive.
Plug in your null modem cable, and connect the Rx and Tx pins (pins 2
and 3), or use a loopback test plug.
Type on your keyboard, and verify that you receive what you type.

With that test completed, we can be confident that the hardware and operating
system is working correctly, and that we are using the right serial port (if there

1
are multiple ports on the computer). Now, we can go on to write a program for
accessing the serial port.

2.2 Serial port access in C (POSIX1 )


For terminal I/O such as the serial port, and many other forms of I/O, the
operating system exposes the device as a file, and accessing it involves two
important concepts,
file descriptors are integers representing (opened) files as operating system
resources, and are used for opening, closing, low-level configuration and
access to the files or devices.
streams are high level channels that allows sending/receiving a stream of char-
acters. Streams provide a richer interface with formatted I/O, buffering,
etc., and also abstracts the underlying resources and provides a unified
interface. In POSIX, a stream is represented as a FILE *.
This section describes both ways to read and write a file, but as a general
hint it is recommended to use structured functions for doing output (i.e., the
different fprintf() variants), but for simple tasks, it is often less error prone
to use the low-level function for input (i.e, read()). One reason for that, in
addition to buffering and blocking, is that the structured input functions retain
the end-of-line character(s)2 and add a terminating null to the string, whereas
read() just gives the actual characters received.

2.2.1 Opening and closing the serial port device


To get you started, example functions for opening, closing, and configuring a
serial port device are provided in serialport.{h,c}, available on the course
web page. It provides two functions:
int serial_init ( char * modemdevice ) ;
void serial_ cleanup ( int fd ) ;

that open and close the serial device. The serial_init function configures the
port for 2400 bps, 8 bits, no parity, 1 stop bit (commonly abbreviated 2400-8N1)
and opens it for reading and writing. For the interested, the low-level operations
involved are described in Appendix A.
An example main program that only opens and closes the first serial port is:
# include " serialport . h "
main ()
{
int fd = serial_init ( " / dev / ttyS0 " ) ;

seria l_clean up ( fd ) ;
}

where /dev/ttyS0 is the device file for the first serial port, i.e., equal to COM1
in the Windows naming scheme.
1 POSIX, an acronym for Portable Operating System Interface, is a family of standards

specified by the IEEE for maintaining compatibility between operating systems. For Microsoft
Windows, Cygwin provides a largely POSIX-compliant environment.
2 Depending on system configuration, the end-of-line (EOL) marker may be either a single

newline character (\n) or a newline and a carriage return (\n\r). If you dont understand
this, dont worry, just use read() and write your own input parsing.

2
2.2.2 Raw file access using file descriptors
The low-level functions for reading and writing a file are read() and write(),
which read(write) at most nbyte bytes from(to) the resource behind file descrip-
tor fd. The prototypes are:
ssize_t read ( int fd , void * buf , size_t nbyte ) ;
ssize_t write ( int fd , const void * buf , size_t nbyte ) ;

Please note that neither the C compiler, nor the runtime system, does any
range checks, so its up to the programmer to ensure that the size of buf is
at least nbyte, or memory used by other variables may get overwritten with
undefined consequences.
We let examples from the manual pages illustrate the usage. The first exam-
ple reads data from the file associated with the file descriptor fd into the buffer
pointed to by buf.

# include < sys / types .h >


# include < unistd .h >
...
char buf [20];
size_t nbytes ;
ssize_t bytes_read ;
int fd ;
...
nbytes = sizeof ( buf ) ;
bytes_read = read ( fd , buf , nbytes ) ;
...

The second example writes data from the buffer pointed to by buf to the
file associated with the file descriptor fd.

# include < sys / types .h >


# include < string .h >
...
char buf [20];
size_t nbytes ;
ssize_t bytes_written ;
int fd ;
...
strncpy ( buf , " This is a test \ n " , sizeof ( buf ) ) ;
nbytes = strlen ( buf ) ;

bytes_written = write ( fd , buf , nbytes ) ;


...

Note the operator sizeof() for getting the size (in bytes) of a data type or
statically known3 array, and the convenient functions strcpy() and strlen()
(from string.h) for copying and determining the length (the lenght of the string
itself, not the size of the array containing it) of a null-terminated string.
To make the code portable, the integer types size_t (from sys/types.h)
for unsigned sizes and ssize_t (from unistd.h) for signed sizes (used since
read() and write() can return a negative value on error) are used.

3 That means that is works for a variable defined with a statically known size (like
char buf[20]) but not objects refereced by pointers or dynamically allocated buffers. For
this course, it is recommended to stick to the former method of allocating buffers.

3
2.3 Structured stream I/O
Above the low-level read() and write() functions, the standard library stdio.h
provides powerful functions (e.g., fprintf() and fscanf())for formatted I/O,
including formatting integers numbers as decimal or hexadecimal strings, real
numbers, etc. It also includes the function getc() which reads the next (un-
signed) character from a stream and casts it to an int, and the function fgets()
which reads a string from the stream until a newline or end of file (EOF) is en-
countered and stores it in a buffer.

2.3.1 Function overview


The function for creating a stream from a file descriptor is
FILE * fdopen ( int fd , const char * mode ) ;

where fd is the file descriptor and mode is a string specifying the mode of access.
In this course we will typically use "r+", meaning reading and writing.
There is also a function
int fclose ( FILE * fp ) ;

for flushing and closing the stream fp and then closing the underlying file de-
scriptor. Upon successful completion 0 is returned. Otherwise, EOF is returned
and errno is set to indicate the error.
Then we have the functions for reading one character and a line, respectively,
from stream;
int fgetc ( FILE * stream )
char * fgets ( char *s , int size , FILE * stream )

fgetc() reads the next character from stream and returns it as an unsigned
char cast to an int, or EOF on end of file or error.
fgets() reads at most one less than size characters from stream and stores
them into the buffer pointed to by s. Reading stops after an EOF or a
newline. If a newline is read, it is stored into the buffer. A terminating null
byte (\0) is stored after the last character in the buffer. fgets returns s on
success, and NULL on error or when end of file occurs while no characters
have been read.

EOF is a constant (or macro) defined in stdio.h, typically 1.


Finally, we have the functions for formatted output(input) to(from) streams,
int fprintf ( FILE * stream , const char * format , ...) ;
int fscanf ( FILE * stream , const char * format , ...) ;

where stream is the stream to access, format is a string specifying the format
of the data, and the remaining arguments (...) depend on the format string.

2.3.2 Formatted output example


Before going through the format specification in detail, we will first give some ex-
amples of conversion using printf() (which writes to standard out, the stream
connected to the terminal the program is executed in. I.e., printf(...) is a
shorthand for fprintf(stdout, ...)).

4
The program
# include < stdio .h >

int main ()
{
int x =17;
int y =428;
int z =1;
double f = 13.37;
double g = 1234.56789;
char * s1 = " hej " ;
char * s2 = " hopp " ;
char b = 0 b10100101 ;
unsigned char ub = 0 b10100101 ;

printf ( " x == % d \ n " , x ) ;


printf ( " %x , %# X , %.4 X , % d % u \ n " ,x ,x ,x ,x , x ) ;
printf ( " %x , %# X , %.4 X , % d % u \ n " ,y ,y ,y ,y , y ) ;
printf ( " %#10 x , %10.2 x , % s \ n " ,x ,x , s1 ) ;
printf ( " %#10 x , %10.2 x , % s \ n " ,y ,y , s2 ) ;
printf ( " %#10 x , %10.2 x , % s \ n " ,z ,z , " literal " ) ;
printf ( " %f , %.2 f , %2.2 f \ n " ,f ,f , f ) ;
printf ( " %f , %.9 f , %2.2 f \ n " ,g ,g , g ) ;

// note the d i f f e r e n c e between signed and u n s i g n e d .


printf ( " %x , %d , % u \ n " , b ,b , b ) ;
printf ( " %x , %d , % u \ n " , ub , ub , ub ) ;
printf ( " %x , %d , % u \ n " , ( unsigned char ) b ,( unsigned char ) b
,( unsigned char ) b ) ;
}

produces the output


x == 17
11, 0X11, 0011, 17 17
1ac, 0X1AC, 01AC, 428 428
0x11, 11, hej
0x1ac, 1ac, hopp
0x1, 01, literal
13.370000, 13.37, 13.37
1234.567890, 1234.567890000, 1234.57
ffffffa5, -91, 4294967205
a5, 165, 165
a5, 165, 165
Note the difference between signed and unsigned integer types as indicated in
the code, and how sign extension changes the value of a variable that is assigned
a constant with the most significant bit set. If the reader doesnt understand the
details of that it is fine (but do make sure to use unsigned types when dealing
with bit patterns etc.). However, we encourage the interested student to work
out the details as it gives valuable insight into twos complement arithmetics.
That is quite valuable to the Java programmer, as Java unfortunately does
not have unsigned data types and therefore requires great care when doing bit
operations.

5
2.3.3 The format specification
The format string is made up of ordinary characters, which are copied as is,
and conversion specifications, which causes conversion of the corresponding ar-
gument (defined by the order). A conversion specification begins with a % and
ends with a characher, where the most common ones are

character argument type, convert to/from


d,i signed decimal notation
x,X unsigned hexadecimal notation
u unsigned decimal notation
c single character (converted to unsigned char)
f double in decimal notation [-]mmmm.dddd
s char* (null terminated string)

The conversion specifications also take optional arguments, placed between the
% and the conversion character. They are, in order
A minus sign, making the converted argument left adjusted.

#, specifying an alternate form. For x,X it means printing the 0x prefix,


for f it means always printing the decimal point even if no digits follow
it. For others, refer to the manual page.
A number specifying the minimum field width.
A period, separating the width from the precision

A number specifying the precision. The meaning depends on the con-


version type; for floating point values it sets the number of decimals, for
integer the minimum number of digits, and for strings the maximum num-
ber of characters to be printed.

An h if an interger is to be printed as a short or an l if as a long.


Please note that fprintf() and fscanf() uses the format string to decide the
number of arguments that follow and their types. If they dont match the result
is undefined.
There are also similar functions for doing the same formatting operations on
a string rather than a stream, that may be quite convenient:
int sprintf ( char * str , const char * format , ...) ;
int snprintf ( char * str , size_t size , const char * format , ...) ;
int sscanf ( FILE * stream , const char * format , ...) ;

The difference between sprintf() and snprintf() is that the latter takes the
size of the destination, str, as an argument and writes at most size bytes (in-
cluding the terminating null byte (\0))to str. As C has no range checking,
in order to avoid buffer overflows and hard to find errors sprintf() should be
avoided in favour of snprintf().

6
3 Lab task: serial communication
The goal of the exercise is to build a simple chat system, where you can com-
municate between two computers via a serial cable. To reach that goal, and to
get a good, modular, design of your program, it is recommended that you do
this in small steps.

1. Write a function that sends a string of characters on the serial port.


2. Write a function that receives characters from the serial port and then
print the received characters on System.out.
3. Combine the two into a program that alternates between reading and
writing a line.

As a general advice, always aim for a modular design, and keep the modules
small and simple, to facilitate debugging and reuse. In this particular case,
implement input and output in separate functions and then use those functions
to build the main program.
That also applies to the development methodology. For the first two steps,
you can test your functions independently by using Hyperterminal on the remote
side. Then verify that they work together by running the sender program (1)
at one end and the receiver program (2) at the other.
Of course, if one only has access to a single computer, one can also use
loopback testing. The advantage of using two computers and a program that is
known to work at one end is that it reduces uncertainty. If a loopback test fails,
there is in general no way to determine if it is the sending or the receiving
routine that does not work.
Following that reasoning, the careful programmer would add one more step
at the top of the above list:

0. Run Hyperterminal on both sides to verify that the serial link is working.

7
A More on serial port configuration
For completeness, this section explains the provided code for opening, closing,
and configuring the serial port device in more detail.

A.1 Opening and closing a file


In order to access a file, it must first be opened by calling a function open, with
the prototype
int open(const char *path, int oflag);

where path is a string with the path to the file, and oflag is an integer whose
bits represent different modes of opening,creation and access to the file. The
return value is the file descriptor or -1 if an error occurred.
Values for oflag are constructed by a bitwise-inclusive OR of flags defined
in i header file (<fcntl.h>).
Applications shall specify exactly one of these three values (file access modes)

O_RDONLY Open for reading only.


O_WRONLY Open for writing only.
O_RDWR Open for reading and writing.

In addition to that, more flags can be set. Most of them define how files
shall be created, if existing files shall be overwritten or appended to, etc., but
one flag is interesting for serial ports:
O_NOCTTY If set and path identifies a terminal device, open() shall not
cause the device to become the controlling terminal for the process.

If not set, a Ctrl-C received over the serial line can kill the process. Sometimes,
that is desirable, but often not, as a Ctrl-C may be generated by line noise or
sent in error by the remote end.
When a program no longer needs to use a file, or before exiting, the file must
be closed, in order to release any operating system resources, locks, etc.. If a
serial port file descriptor is not properly closed before exiting, the next time a
program tries to open the serial port it may get an error that the port is in use.
A file is closed with a call to
int close(int fd);
where fd is the file descriptor returned by open, and the return value is 0 on
success, or -1 otherwise.

A.1.1 Example
The following snippet shows how a serial port (/dev/ttyS0 is the device file
representing the first serial port) is opened for reading and writing, and closed.

8
int fd ;

fd = open ( " / dev / ttyS0 " , O_RDWR | O_NOCTTY ) ;


if ( fd <0) { // an error o c c u r r e d . Print message and exit program
perror ( " Trying to open / dev / ttyS0 " ) ;
exit ( -1) ;
}

// ...

if ( close ( fd ) <0 ) {
perror ( " closing serial port " ) ;
}

If the return value of open or close is negative, an error has occurred. perror()
is a function printing a message on the standard error output, describing the
last error encountered during a call to a system or library function4 .

A.2 Configuring and controlling the serial port


The terminal I/O library provides functions (declared in termios.h) for config-
uring and controlling serial port lines.
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int opt, const struct termios *termios_p);
int tcdrain(int fd);
int tcflush(int fd, int queue_selector);
The first two read and write configuration flags, respectively. The configuration
flags are stored in a termios structure that contains at least the following
members
tcflag_t c_iflag; /* input modes */
tcflag_t c_oflag; /* output modes */
tcflag_t c_cflag; /* control modes */
tcflag_t c_lflag; /* local modes */
cc_t c_cc[NCCS]; /* special characters */
The mode flags are
tcdrain() waits until all output written to the object referred to by fd has
been transmitted.
tcflush() discards data written to the object referred to by fd but not
transmitted, or data received but not read, depending on the value of
queue_selector (TCIFLUSH flushes data received but not read, TCOFLUSH flushes
data written but not transmitted, and TCIOFLUSH flushes both data received but
not read, and data written but not transmitted).

A.3 Example
As a complete example of what has been described in this section we give the
serial port setup code provided serialport.c.
4 The mechanism behind this is that when an error occurs, the failing function writes an

error code to the global variable errno, and perror() reads that variable and prints a human-
readable message corresponding to the error code. The error codes are defined in errno.h.

9
# include < sys / types .h >
# include < sys / stat .h >
# include < fcntl .h >
# include < termios .h >
# include < stdlib .h >
# include < strings .h >
# include < stdio .h >

/* b a u d r a t e s e t t i n g s are defined in < asm / t e r m b i t s .h > , which is


* i n c l u d e d by < termios .h > */
# ifndef BAUDRATE
# define BAUDRATE B2400
# endif

# define _POSIX_SOURCE 1 /* POSIX c o m p l i a n t source */

static int fd , c , res ;


static struct termios oldtio , newtio ;
static char * device ;

int serial_init ( char * modemdevice )


{
/*
* Open modem device for reading and writing and
* don t make it a c o n t r o l l i n g tty because we don t want
* the process to * get killed if l i n e n o i s e sends CTRL - C .
* */
device = modemdevice ;
fd = open ( device , O_RDWR | O_NOCTTY ) ;
if ( fd < 0)
{
perror ( device ) ;
exit ( -1) ;
}

tcgetattr ( fd , & oldtio ) ; /* save current serial port


s e t t i n g s */
bzero (& newtio , sizeof ( newtio ) ) ; /* clear struct for new
port s e t t i n g s */

/*
* B A U D R A T E : Set bps rate . You could also use c f s e t i s p e e d and
cfsetospeed .
* CRTSCTS : output h a r d w a r e flow control ( only used if the
cable has
* all n e c e s s a r y lines . )
* CS8 : 8 n1 (8 bit , no parity ,1 stopbit )
* CLOCAL : local connection , no modem contol
* CREAD : enable r e c e i v i n g c h a r a c t e r s
* */
newtio . c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ;

/*
* IGNPAR : ignore bytes with parity errors
* ICRNL : map CR to NL ( o t h e r w i s e a CR input on the other
computer
* will not t e r m i n a t e input )
* o t h e r w i s e make device raw ( no other input
processing )
* */
newtio . c_iflag = IGNPAR | ICRNL ;

10
/*
* Raw output .
* */
newtio . c_oflag = 0;

/*
* ICANON : enable c a n o n i c a l input
* disable all echo functionality , and don t send
signals to calling program
* */
newtio . c_lflag = ICANON ;

/*
* i n i t i a l i z e all control c h a r a c t e r s
* default values can be found in / usr / include / termios .h , and
are given
* in the comments , but we don t need them here
* */
newtio . c_cc [ VINTR ] = 0; /* Ctrl - c */
newtio . c_cc [ VQUIT ] = 0; /* Ctrl -\ */
newtio . c_cc [ VERASE ] = 0; /* del */
newtio . c_cc [ VKILL ] = 0; /* @ */
newtio . c_cc [ VEOF ] = 4; /* Ctrl - d */
newtio . c_cc [ VTIME ] = 0; /* inter - c h a r a c t e r timer unused */
newtio . c_cc [ VMIN ] = 1; /* b l o c k i n g read until 1 c h a r a c t e r
arrives */
newtio . c_cc [ VSWTC ] = 0; /* \0 */
newtio . c_cc [ VSTART ] = 0; /* Ctrl - q */
newtio . c_cc [ VSTOP ] = 0; /* Ctrl - s */
newtio . c_cc [ VSUSP ] = 0; /* Ctrl - z */
newtio . c_cc [ VEOL ] = 0; /* \0 */
newtio . c_cc [ VREPRINT ] = 0; /* Ctrl - r */
newtio . c_cc [ VDISCARD ] = 0; /* Ctrl - u */
newtio . c_cc [ VWERASE ] = 0; /* Ctrl - w */
newtio . c_cc [ VLNEXT ] = 0; /* Ctrl - v */
newtio . c_cc [ VEOL2 ] = 0; /* \0 */

/*
* now clean the modem line and a c t i v a t e the s e t t i n g s for the
port
* */
tcflush ( fd , TCIFLUSH ) ;
tcsetattr ( fd , TCSANOW , & newtio ) ;

/*
* t e r m i n a l s e t t i n g s done , return file d e s c r i p t o r
* */

return fd ;
}

void serial_ cleanup ( int ifd ) {


if ( ifd != fd ) {
fprintf ( stderr , " WARNING ! file descriptor != the one
returned by serial_init () \ n " ) ;
}
/* restore the old port s e t t i n g s */
tcsetattr ( ifd , TCSANOW , & oldtio ) ;
}

11
B Serial port communication in Java
In previous years, we have used Java for the host programming. After opening
and configuring the device file, stream I/O is more straight-forward in C, and
using the same language on both the host and the embedded system may allow
some communication code reuse, but if you want to use Java, the instructions
from previous years are included here as a reference. It will not be directly
supported by the lab teachers, though.

B.1 Java libraries


For the serial programming, we will use the package gnu.io, which is an open
source implementation of the javax.comm API for serial and parallel port com-
munication. The gnu.io package and documentation is available on the web at
http://rxtx.qbang.org. The class documentation for javax.comm is available
at http://java.sun.com/products/javacomm/reference/index.html.

B.1.1 Project setup in Eclipse


This section explains how to get the serial port library and how to set up an
eclipse project using it.
1. You will need the gnu.io implementation from rxtx.org. At IEA, the
java library is available on the file server. Alternatively, it can be down-
loaded from the web. The current version is rxtx 2.2pre2.
2. Create a new eclipse project for the lab. (File New Project . . .
Java project)
3. Import the serial port library into your project. (File Import General
Archive file), click Next, and then browse to rxtx-2.2pre2-bins.zip
4. Now we should add RXTXcomm.jar to the build path. Right click your
project and do Build path Configure build path . . . . Go to the
Libraries tab and click Add JARs. . . . Navigate to rxtx-2.2pre2-bins
and select RXTXcomm.jar. Click OK.
5. Finally, we need to add the native libraries. Go to the Source tab, select
Native library location: and click Edit . . . . A new dialog opens. Click
Workspace . . . . Select the win32 folder (in rxtx-2.2pre2-bins). Click
OK to confirm your changes (three times in all).
6. Your project should now be configured for using the serial communication
library.

B.1.2 A brief overview of the classes


There are two classes that are of main importance:
gnu.io.SerialPort is the class that handles setup of, and access to, the serial
port. It includes methods for setting communication parameters like data
rate, character length, parity, etc., as well as methods for getting input
and output streams for the port, and reading and setting the hand shaking
signals.

12
gnu.io.CommPortIdentifier is the central class for controlling access to com-
munications ports. An application first uses methods in CommPortIden-
tifier to negotiate with the driver to discover which communication ports
are available and then select a port for opening.

B.1.3 Example: listing available serial ports


As an example of how the CommPortIdentifier class can be used, we show how
to list the available serial ports in the system
public static void listPorts() {
CommPortIdentifier cpi;
Enumeration ports;

ports = CommPortIdentifier.getPortIdentifiers();
while ( ports.hasMoreElements() ) {
cpi = (CommPortIdentifier) ports.nextElement();
if ( cpi.getPortType() == CommPortIdentifier.PORT_SERIAL ) {
System.out.println(cpi.getName());
}
}
}
Some remarks:
The static method CommPortIdentifier.getPortIdentifiers() returns
an enumeration containing all ports in the system.
Since we are only interested in serial ports, we iterate over the enumeration
and select thos ports where the port type is CommPortIdentifier.PORT_SERIAL.
Note the typical iterable idiom: while(x.hasMoreX() {... x.nextX() }.
In a more useful program, you would want to open a particular serial port
(e.g. COM1 or /dev/ttyS0), rather than just printing the port names.
Selecting the proper port can be done by comparing the name to the de-
sired string (e.g., if(cpi.getName().equals("COM1")). Then, the port
is opened by calling cpi.open(String appName, int timeout). Please
see the documentation for details.

B.1.4 Setting serial port parameters


When you have selected which port to use, and created a SerialPort object
using the open(...) method, it must be configured for the desired communi-
cation parameters. The different communication parameters are set by using
constants in the SerialPort class. A typical setup is
SerialPort port;

// find and create port

port.setSerialPortParams(9600,
SerialPort.DATABITS_8,

13
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);

B.1.5 Example: raw read from an InputStream


One way of reading from a stream is to allocate a buffer of suitable size and
then use InputStream.read(), as in the following examples. The most basic
way is to read from the stream one byte at the time:
public static void readChars(InputStream in) {
int ch= -1;
try {
while ( ( ch = in.read()) != -1 ) {
System.out.print( (char) ch);
}
}catch ( IOException e ){
e.printStackTrace();
}
}
A little more advanced is to read up to BUF SIZE bytes using a buffer:
static final int BUF_SIZE=80;
public static void readChars(InputStream in) {
byte[] buffer = new byte[BUF_SIZE];
int len = -1;
try {
while ( ( len = in.read(buffer)) > -1 ) {
String s = new String(buffer,0,len);
System.out.print(s);
}
}catch ( IOException e ){
e.printStackTrace();
}
}
Using a buffer is a bit more efficient, but reading one byte at a time leaves you
with more control of what is actually happening. Also, if you send a sequence
of n bytes over the serial line, there are no guarantees that read(buffer) will
read n bytes, that is entirely up to the lower level buffering. Thus, you will still
need to paste together the n byte sequence yourself, and reading one byte at a
time makes that a bit easier.
Note that read() reads raw bytes, without trying to interpret them as
characters, integers, or floats. It also doesnt wait for an entire line to be read
before returning. read(buffer) returns the number of bytes read, or 1 if the
end of the stream was reached.
As a higher level alternative, you can read structrured data (E.g., integers,
floats, strings, etc.) using e.g., java.util.Scanner. Which option is the best
or easiest depends on the application.

14

Você também pode gostar