如何与Java中的串口通信?
我正在做项目,我需要访问串口并从中读取数据,将数据写入其中。我怎么能用Java做这个。
请帮助我..
提前致谢
I am doing project in which i need to access serial port and read the data from , write data into it. How can i do this in Java.
Please Help Me ..
Thanks in advance
http://www.java2s.com/Code/Java/Development-Class/ReadfromaSerialportnotifyingwhendataarrives.htm [ ^ ]
http:// stackoverflow.com/questions/589334/how-to-read-and-write-from-the-serial-port-with-java [ ^ ]
http://henrypoon.wordpress.c om / 2011/01/01 / serial-communication-in-java-with-example-program / [ ^ ]
http://www.java-samples.com/showtutorial.php?tutorialid=11 [ ^ ]
http://www.java2s.com/Code/Java/Development-Class/ReadfromaSerialportnotifyingwhendataarrives.htm[^]
http://stackoverflow.com/questions/589334/how-to-read-and-write-from-the-serial-port-with-java[^]
http://henrypoon.wordpress.com/2011/01/01/serial-communication-in-java-with-example-program/[^]
http://www.java-samples.com/showtutorial.php?tutorialid=11[^]
javax.comm拥有您所需要的一切。
Peter
javax.comm has all you need.
Peter
简介
由于Java的平台 - 独立性,串行接口很难。串行接口需要标准化的API和特定于平台的实现,这对Java来说很难。
不幸的是,Sun并不太关注Java中的串行通信。 Sun已经定义了一个名为JavaComm的串行通信API,但API的实现不是Java标准版的一部分。 Sun为少数但不是所有Java平台提供了参考实现。特别是,在2005年底,Sun默默地撤回了JavaComm对Windows的支持。可以使用某些省略平台的第三方实现。 JavaComm在维护活动方面没有太多看法,Sun只执行了最低限度的维护,除了Sun显然已经回应了他们自己的Sun Ray瘦客户机的买家的压力,并且在放弃Windows的同时将JavaComm改编为这个平台支持。
这种情况,以及Sun最初没有为Linux提供JavaComm实现的事实(从2006年开始,他们现在这样做)导致了免费软件RxTx库。 RxTx适用于许多平台,而不仅仅是Linux。它可以与JavaComm(提供特定于硬件的驱动程序的RxTx)结合使用,也可以单独使用。当用作JavaComm驱动程序时,JavaComm API和RxTx之间的桥接由JCL(JavaComm for Linux)完成。 JCL是RxTx发行版的一部分。
Sun对JavaComm和JavaComm特定编程模型的疏忽使JavaComm失去了无法使用的声誉。幸运的是,事实并非如此。不幸的是,那些根本不了解串行编程基础知识的人会进一步传播声誉,并让JavaComm对他们缺乏理解负责。
RxTx - 如果不用作JavaComm驱动程序 - 提供更丰富的接口,但不是标准化的接口。 RxTx支持比现有JavaComm实现更多的平台。最近,RxTx已被采用提供与JavaComm相同的接口,只是包名与Sun的包名不匹配。
那么,哪个库应该在应用程序中使用一次?如果需要最大的可移植性(对于某些值maximum),那么JavaComm是一个不错的选择。如果没有可用的特定平台的JavaComm实现,但是RxTx实现是,那么RxTx可以用作JavaComm的该平台上的驱动程序。因此,通过使用JavaComm,可以支持Sun的参考实现或RxTx与JCL直接支持的所有平台。这样就不需要更改应用程序,并且可以只针对一个接口,标准化的JavaComm接口。
这个模块讨论了JavaComm和RxTx。它主要侧重于演示概念,而不是现成的代码。那些想盲目复制代码的人会参考软件包附带的示例代码。那些想知道自己在做什么的人可能会在这个模块中找到一些有用的信息。
入门
学习串行通信的基础知识和编程。
准备好要与之通信的设备的文档(例如调制解调器)。
设置所有硬件和测试环境
例如,使用终端程序手动与设备通信。这是为了确保测试环境设置正确并且您已了解设备的命令和响应。
下载要用于特定操作系统的API实现
阅读
JavaComm和/或RxTx安装说明(并遵循它)
API文档
示例源代码发货
安装
一般问题
JavaComm和RxTX都显示出一些安装怪癖。强烈建议逐字逐句地遵循安装说明。如果他们说jar文件或共享库必须进入特定目录,那么这是严肃的意思!如果指示说某个特定文件或设备需要具有特定的所有权或访问权限,那么这也是非常重要的。许多安装问题只是因为没有严格按照说明进行操作。
特别要注意的是,某些版本的JavaComm附带了两条安装说明。一个用于Java 1.2及更高版本,一个用于Java 1.1。使用错误的安装将导致无法正常工作。另一方面,RxTx的某些版本/构建/软件包带有不完整的指令。在这种情况下,需要获得相应的RxTx源代码分发,其中应包含完整的指令。
应该进一步注意到它也是Windows的典型代表JDK安装最多有三个虚拟机,因此有三个扩展目录。
一个作为JDK的一部分,
一个部分作为一部分JDK运行JDK工具的私有JRE,以及作为JDK运行应用程序的公共JRE的一部分的
一个
有些人甚至声称在\ Windows目录层次结构中的第四个JRE。
JavaComm至少应该作为JDK中的扩展安装所有公共JRE。
Webstart
JavaComm
JavaComm和RxTx的一般问题是,他们拒绝通过Java WebStart安装:
JavaComm是臭名昭着的,因为它需要一个文件调用ed javax.comm.properties放在JDK lib目录中,这是Java WebStart无法完成的。这尤其令人难过,因为对该文件的需求是JavaComm中一些不必要的设计/决策的结果,并且JavaComm设计人员可以很容易地避免这种情况。 Sun不断拒绝纠正这个错误,引用机制是必不可少的。也就是说,当涉及到JavaComm时,他们正在撒谎,特别是因为Java长期以来都有一个服务提供商架构,正是出于这种目的。
属性文件的内容通常只有一行,即具有本机驱动程序的java类的名称,例如:
driver = com.sun.comm.Win32Driver
以下是一个hack,允许通过Web Start部署JavaComm,忽略脑死文件属性文件。它有严重的缺点,并且可能会因较新的JavaComm版本而失败 - 应该是Sun出现并制作新版本。
首先,关闭安全管理器。 Sun的一些doofus程序员决定,即使在最初加载之后,一次又一次地检查可怕的javax.comm.properties文件的存在会很酷,除了检查文件之外没有其他明显的原因。 />
System.setSecurityManager(null);
然后,在初始化JavaComm API时,手动初始化驱动程序:
String driverName =com.sun.comm.Win32Driver; //或作为JNLP属性获取
CommDriver commDriver =(CommDriver)Class.forName(driverName).newInstance();
commDriver.initialize();
RxTx
某些平台上的RxTx需要更改串行设备的所有权和访问权限。这也是无法通过WebStart完成的事情。
启动程序时,您可以要求用户以超级用户身份执行必要的设置。 />
此外,RxTx具有用于识别有效串行设备名称的模式匹配算法。当人们想要使用非标准设备(如USB转串口转换器)时,这通常会破坏事物。系统属性可以覆盖此机制。有关详细信息,请参阅RxTx安装说明。
JavaComm API
简介
用于串行通信的官方API Java是JavaComm API。此API不是标准Java 2版本的一部分。相反,必须单独下载API的实现。不幸的是,JavaComm并没有得到Sun的太多关注,并且长期以来一直没有得到真正的维护。 Sun不时会做一些微不足道的错误修复,但是不会进行早该进行的主要大修。
本节介绍JavaComm API的基本操作。提供的源代码保持简单,以证明重要的一点。在实际应用程序中使用时需要增强它。
本章中的源代码不是唯一可用的示例代码。 JavaComm下载有几个例子。这些示例几乎包含有关使用API的更多信息,而不是API文档。不幸的是,Sun没有提供任何真正的教程或一些介绍性文本。因此,有必要研究一下示例代码来理解API的机制。尽管如此,还应该研究API文档。但最好的方法是研究这些例子并与它们一起玩。由于缺乏易于使用的应用程序以及人们难以理解API编程模型,API通常是错误的。 API优于其声誉和功能。但不多了。
API使用回调机制向程序员通知新到达的数据。研究这种机制而不是依赖于轮询端口也是一个好主意。与Java中的其他回调接口(例如在GUI中)不同,这个接口只允许一个监听器监听事件。如果多个侦听器需要侦听串行事件,则必须以将其信息发送给其他辅助侦听器的方式实现一个主侦听器。
下载&安装
下载
Sun的JavaComm网页指向下载位置。在此位置,Sun目前(2007年)为Solaris / SPARC,Solaris / x86和Linux x86提供JavaComm 3.0实现。下载需要注册Sun在线帐户。下载页面提供了注册页面的链接。此注册的目的不明确。可以在没有注册的情况下下载JDK和JRE,但是对于几乎无足轻重的JavaComm Sun引用了法律和政府对软件分发和输出的限制。
Windows版JavaComm已不再正式提供,而且Sun已经 - 在他们自己的产品生产结束政策中 - 没有在Java产品档案中提供。但是,2.0版本(javacom 2.0)仍然可以从这里下载。
安装
按照下载附带的安装说明进行操作。某些版本的JavaComm 2.0附带两条安装说明。不幸的是,这两条指令中最明显的是用于古代Java 1.1环境的错误指令。参考古老的Java 1.2(jdk1.2.html)的信息是正确的。
特别是Windows用户通常不知道他们有副本相同的VM安装在多个位置(通常为三到四个)。有些IDE也喜欢自带的私有JRE / JDK安装,就像一些Java应用程序一样。每个VM安装(JDK和JRE)都需要重复安装,这应该与开发和执行串行应用程序一起使用。
IDE通常有IDE特有的IDE如何知道新库(类和文档)的方法。通常像JavaComm这样的库不仅需要知道IDE本身,而且还需要知道每个应该使用该库的项目。阅读IDE的文档。应该注意的是,旧的JavaComm 2.0版本附带了JavaDoc API文档,该文档是在历史悠久的Java 1.0 JavaDoc布局中构建的。一些现代IDE不再了解这种结构,也无法将JavaComm 2.0文档集成到他们的帮助系统中。在这种情况下,需要外部浏览器来阅读文档(建议的活动......)。
安装软件后,建议检查样本和JavaDoc目录。构建和运行其中一个示例应用程序以验证安装是否正确是有意义的。示例应用程序通常需要一些小的调整才能在特定平台上运行(例如,对硬编码的com端口标识符的更改)。在尝试示例应用程序时,最好使用一些串行硬件,如布线,零调制解调器,分线盒,真实调制解调器,PABX等。 Serial_Programming:RS-232连接和Serial_Programming:调制解调器和AT命令提供有关如何设置串行应用程序开发环境的硬件部分的一些信息。
查找所需的串行端口
使用JavaComm编程串行线路时要做的前三件事通常是枚举可用的所有串行端口(端口标识符)
到JavaComm,
从可用的端口标识符中选择所需的端口标识符,
通过端口标识符获取端口。
枚举和选择所需的端口标识符通常在一个循环中完成:
Introduction
Because of Java's platform-independence, serial interfacing is difficult. Serial interfacing requires a standardized API with platform-specific implementations, which is difficult for Java.
Unfortunately, Sun doesn't pay much attention to serial communication in Java. Sun has defined a serial communication API, called JavaComm, but an implementation of the API is not part of the Java standard edition. Sun provides a reference implementation for a few, but not all Java platforms. Particularly, at the end of 2005 Sun silently withdrew JavaComm support for Windows. Third party implementations for some of the omitted platforms are available. JavaComm hasn't seen much in the way of maintenance activities, only the bare minimum maintenance is performed by Sun, except that Sun has apparently responded to pressure from buyers of their own Sun Ray thin clients and has adapted JavaComm to this platform while dropping Windows support.
This situation, and the fact that Sun originally did not provide a JavaComm implementation for Linux (starting in 2006, they now do) led to the development of the free-software RxTx library. RxTx is available for a number of platforms, not only Linux. It can be used in conjunction with JavaComm (RxTx providing the hardware-specific drivers), or it can be used stand-alone. When used as a JavaComm driver the bridging between the JavaComm API and RxTx is done by JCL (JavaComm for Linux). JCL is part of the RxTx distribution.
Sun's negligence of JavaComm and JavaComm's particular programming model gained JavaComm the reputation of being unusable. Fortunately, this is not the case. Unfortunately, the reputation is further spread by people who don't know the basics of serial programming at all and make JavaComm responsible for their lack of understanding.
RxTx - if not used as a JavaComm driver - provides a richer interface, but one which is not standardized. RxTx supports more platforms than the existing JavaComm implementations. Recently, RxTx has been adopted to provide the same interface as JavaComm, only that the package names don't match Sun's package names.
So, which of the libraries should one use in an application? If maximum portability (for some value of "maximum") is desired, then JavaComm is a good choice. If there is no JavaComm implementation for a particular platform available, but an RxTx implementation is, then RxTx could be used as a driver on that platform for JavaComm. So, by using JavaComm one can support all platforms which are either directly supported by Sun's reference implementation or by RxTx with JCL. This way the application doesn't need to be changed, and can work against just one interface, the standardized JavaComm interface.
This module discusses both JavaComm and RxTx. It mainly focuses on demonstrating concepts, not ready-to-run code. Those who want to blindly copy code are referred to the sample code that comes with the packages. Those who want to know what they are doing might find some useful information in this module.
Getting started
Learn the basics of serial communication and programming.
Have the documentation of the device you want to communicate with (e.g. the modem) ready.
Set up all hardware and a test environment
Use, for example, a terminal program to manually communicate with the device. This is to be sure the test environment is set up correctly and you have understood the commands and responses from the device.
Download the API implementation you want to use for your particular operating system
Read
the JavaComm and/or RxTx installation instruction (and follow it)
the API documentation
the example source code shipped
Installation
General Issues
Both JavaComm and RxTX show some installation quirks. It is highly recommended to follow the installation instructions word-for-word. If they say that a jar file or a shared library has to go into a particular directory, then this is meant seriously! If the instructions say that a particular file or device needs to have a specific ownership or access rights, this is also meant seriously. Many installation troubles simply come from not following the instructions precisely.
It should especially be noted that some versions of JavaComm come with two installation instructions. One for Java 1.2 and newer, one for Java 1.1. Using the wrong one will result in a non-working installation. On the other hand, some versions/builds/packages of RxTx come with incomplete instructions. In such a case the corresponding source code distribution of RxTx needs to be obtained, which should contain complete instructions.
It should be further noticed that it is also typical for Windows JDK installations to come with up to three VMs, and thus three extension directories.
One as part of the JDK,
one as part of the private JRE which comes with the JDK to run JDK tools, and
one as part of the public JRE which comes with the JDK to run applications
Some even claim to have a fourth JRE somewhere in the \Windows directory hierarchy.
JavaComm should at least be installed as extension in the JDK and in all public JREs.
Webstart
JavaComm
A general problem, both for JavaComm and RxTx is, that they resist installation via Java WebStart:
JavaComm is notorious, because it requires a file called javax.comm.properties to be placed in the JDK lib directory, something which can't be done with Java WebStart. This is particularly sad, because the need for that file is the result of some unnecessary design/decision in JavaComm and could have easily been avoided by the JavaComm designers. Sun constantly refuses to correct this error, citing the mechanism is essential. Which is, they are lying through their teeth when it comes to JavaComm, particular, because Java for a long time has a service provider architecture exactly intended for such purposes.
The contents of the properties file is typically just one line, the name of the java class with the native driver, e.g.:
driver=com.sun.comm.Win32Driver
The following is a hack which allows to deploy JavaComm via Web Start ignoring that brain-dead properties file. It has serious drawbacks, and might fail with newer JavaComm releases - should Sun ever come around and make a new version.
First, turn off the security manager. Some doofus programmer at Sun decided that it would be cool to again and again check for the existence of the dreaded javax.comm.properties file, even after it has been loaded initially, for no other apparent reason than checking for the file.
System.setSecurityManager(null);
Then, when initializing the JavaComm API, initialize the driver manually:
String driverName = "com.sun.comm.Win32Driver"; // or get as a JNLP property
CommDriver commDriver = (CommDriver)Class.forName(driverName).newInstance();
commDriver.initialize();
RxTx
RxTx on some platforms requires changing ownership and access rights of serial devices. This is also something which can't be done via WebStart.
At startup of your program you could ask the user to perform the necessary setup as super user.
Further, RxTx has a pattern matching algorithm for identifying "valid" serial device names. This often breaks things when one wants to use non-standard devices, like USB-to-serial converters. This mechanism can be overridden by system properties. See the RxTx installation instruction for details.
JavaComm API
Introduction
The official API for serial communication in Java is the JavaComm API. This API is not part of the standard Java 2 version. Instead, an implementation of the API has to be downloaded separately. Unfortunately, JavaComm has not received much attention from Sun, and hasn't been really maintained for a long time. From time to time Sun does trivial bug-fixes, but doesn't do the long overdue main overhaul.
This section explains the basic operation of the JavaComm API. The provided source code is kept simple to demonstrate important point. It needs to be enhanced when used in a real application.
The source code in this chapter is not the only available example code. The JavaComm download comes with several examples. These examples almost contain more information about using the API than the API documentation. Unfortunately, Sun does not provide any real tutorial or some introductory text. Therefore, it is worth studying the example code to understand the mechanisms of the API. Still, the API documentation should be studied, too. But the best way is to study the examples and play with them. Due to the lack of easy-to-use application and people's difficulty in understanding the APIs programming model, the API is often bad-mouthed. The API is better than its reputation, and functional. But no more.
The API uses a callback mechanism to inform the programmer about newly arriving data. It is also a good idea to study this mechanism instead of relying on polling the port. Unlike other callback interfaces in Java (e.g. in the GUI), this one only allows one listener listening to events. If multiple listeners require to listen to serial events, the one primary listener has to be implemented in a way that it dispatches the information to other secondary listeners.
Download & Installation
Download
Sun's JavaComm web page points to a download location. Under this location Sun currently (2007) provides JavaComm 3.0 implementations for Solaris/SPARC, Solaris/x86, and Linux x86. Downloading requires to have registered for a Sun Online Account. The download page provides a link to the registration page. The purpose of this registration is unclear. One can download JDKs and JREs without registration, but for the almost trivial JavaComm Sun cites legal and governmental restrictions on the distribution and exportation of software.
The Windows version of JavaComm is no longer officially available, and Sun has - against their own product end-of-live policy - not made it available in the Java products archive. However, the 2.0 Windows version (javacom 2.0) is still downloadable from here.
Installation
Follow the installation instructions that come with the download. Some versions of JavaComm 2.0 come with two installation instructions. The most obvious of the two instructions is unfortunately the wrong one, intended for ancient Java 1.1 environments. The information referring to the also ancient Java 1.2 (jdk1.2.html) is the right one.
Particularly Windows users are typically not aware that they have copies of the same VM installed in several locations (typically three to four). Some IDEs also like to come with own, private JRE/JDK installations, as do some Java applications. The installation needs to be repeated for every VM installation (JDKs and JREs) which should be used in conjunction with the development and execution of a serial application.
IDEs typically have IDE-specific ways of how a new library (classes and documentation) is made known to the IDE. Often a library like JavaComm not only needs to be made known to the IDE as such, but also to each project that is supposed to use the library. Read the IDE's documentation. It should be noted that the old JavaComm 2.0 version comes with JavaDoc API documentation that is structured in the historic Java 1.0 JavaDoc layout. Some modern IDEs are no longer aware of this structure and can't integrate the JavaComm 2.0 documentation into their help system. In such a case an external browser is needed to read the documentation (a recommended activity ...).
Once the software is installed it is recommended to examine the samples and JavaDoc directories. It makes sense to build and run one of the sample applications to verify that the installation is correct. The sample applications typically need some minor adaptations in order to run on a particular platform (e.g. changes to the hard-coded com port identifiers). It is a good idea to have some serial hardware, like cabling, a null modem, a breakout box, a real modem, PABX and others available when trying out a sample application. Serial_Programming:RS-232 Connections and Serial_Programming:Modems and AT Commands provide some information on how to set up the hardware part of a serial application development environment.
Finding the desired serial Port
The first three things to do when programming serial lines with JavaComm are typically
to enumerate all serial ports (port identifiers) available to JavaComm,
to select the desired port identifier from the available ones, and
to acquire the port via the port identifier.
Enumerating and selecting the desired port identifier is typically done in one loop:
import javax.comm.*;
import java.util.*;
...
//
// Platform specific port name, here a Unix name
//
// NOTE: On at least one Unix JavaComm implementation JavaComm
// enumerates the ports as "COM1" ... "COMx", too, and not
// by their Unix device names "/dev/tty...".
// Yet another good reason to not hard-code the wanted
// port, but instead make it user configurable.
//
String wantedPortName = "/dev/ttya";
//
// Get an enumeration of all ports known to JavaComm
//
Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();
//
// Check each port identifier if
// (a) it indicates a serial (not a parallel) port, and
// (b) matches the desired name.
//
CommPortIdentifier portId = null; // will be set if port found
while (portIdentifiers.hasMoreElements())
{
CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers.nextElement();
if(pid.getPortType() == CommPortIdentifier.PORT_SERIAL &&
pid.getName().equals(wantedPortName))
{
portId = pid;
break;
}
}
if(portId == null)
{
System.err.println("Could not find serial port " + wantedPortName);
System.exit(1);
}
//
// Use port identifier for acquiring the port
//
...
注意:
JavaComm本身从其特定于平台的驱动程序获取可用串行端口标识符的默认列表。该列表不能通过JavaComm进行配置。 CommPortIdentifier.addPortName()方法具有误导性,因为驱动程序类是特定于平台的,并且它们的实现不是公共API的一部分。根据驱动程序,驱动程序中的端口列表可能是可配置/可消耗的。因此,如果在JavaComm中找不到特定的端口,有时一些摆弄驱动程序的人可以提供帮助。
一旦找到端口标识符,它就可以用来获取所需的端口:
...
Note:
JavaComm itself obtains the default list of available serial port identifiers from its platform-specific driver. The list is not really configurable via JavaComm. The method CommPortIdentifier.addPortName() is misleading, since driver classes are platform specific and their implementations are not part of the public API. Depending on the driver, the list of ports might be configurable / expendable in the driver. So if a particular port is not found in JavaComm, sometimes some fiddling with the driver can help.
Once a port identifier has been found, it can be used to acquire the desired port:
//
// Use port identifier for acquiring the port
//
SerialPort port = null;
try {
port = (SerialPort) portId.open(
"name", // Name of the application asking for the port
10000 // Wait max. 10 sec. to acquire port
);
} catch(PortInUseException e) {
System.err.println("Port already in use: " + e);
System.exit(1);
}
//
// Now we are granted exclusive access to the particular serial
// port. We can configure it and obtain input and output streams.
//
...
初始化串口
串口的初始化是直截了当的。使用setSerialPortParams(...)便捷方法单独设置通信首选项(波特率,数据位,停止位,奇偶校验)或一次性设置它们。
作为初始化过程的一部分,将在示例中配置用于通信的输入和输出流。
import java.io. *;
...
...
Initialize a Serial Port
The initialization of a serial port is straight forward. Either individually set the communication preferences (baud rate, data bits, stop bits, parity) or set them all at once using the setSerialPortParams(...) convenience method.
As part of the initialization process the Input and Output streams for communication will be configured in the example.
import java.io.*;
...
//
// Set all the params.
// This may need to go in a try/catch block which throws UnsupportedCommOperationException
//
port.setSerialPortParams(
115200,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
//
// Open the input Reader and output stream. The choice of a
// Reader and Stream are arbitrary and need to be adapted to
// the actual application. Typically one would use Streams in
// both directions, since they allow for binary data transfer,
// not only character data transfer.
//
BufferedReader is = null; // for demo purposes only. A stream would be more typical.
PrintStream os = null;
try {
is = new BufferedReader(new InputStreamReader(port.getInputStream()));
} catch (IOException e) {
System.err.println("Can't open input stream: write-only");
is = null;
}
//
// New Linux systems rely on Unicode, so it might be necessary to
// specify the encoding scheme to be used. Typically this should
// be US-ASCII (7 bit communication), or ISO Latin 1 (8 bit
// communication), as there is likely no modem out there accepting
// Unicode for its commands. An example to specify the encoding
// would look like:
//
// os = new PrintStream(port.getOutputStream(), true, "ISO-8859-1");
//
os = new PrintStream(port.getOutputStream(), true);
//
// Actual data communication would happen here
// performReadWriteCode();
//
//
// It is very important to close input and output streams as well
// as the port. Otherwise Java, driver and OS resources are not released.
//
if (is != null) is.close();
if (os != null) os.close();
if (port != null) port.close();
简单的数据传输
简单的数据写入
写入串口就像基本的Java IO一样简单。但是,如果您使用AT Hayes协议,需要注意几点:
不要使用println(或其他方法自动添加\\ \\ n)在OutputStream上。用于调制解调器的AT Hayes协议期望\\\\ n作为分隔符(无论底层操作系统如何)。
写入OutputStream后,InputStream缓冲区将包含重复的如果调制解调器设置为回显命令行,则发送给它的命令(带换行符)和另一个换行符(AT命令的答案)。因此,作为写入操作的一部分,请确保清除此信息的InputStream(实际上可用于错误检测)。
使用读者/写入器(不是一个非常好的主意)时,至少将字符编码设置为US-ASCII而不是使用平台的默认编码,这可能会也可能不起作用。
由于使用调制解调器时的主要操作是不加改变地传输数据,所以与调制解调器应该通过InputStream / OutputStream来处理,而不是读者/写入器。
剪贴板
Simple Data Transfer
Simple Writing of Data
Writing to a serial port is as simple as basic Java IO. However there are a couple of caveats to look out for if you are using the AT Hayes protocol:
Don't use println (or other methods that automatically append "\n") on the OutputStream. The AT Hayes protocol for modems expects a "\r\n" as the delimiter (regardless of underlying operating system).
After writing to the OutputStream, the InputStream buffer will contain a repeat of the command that was sent to it (with line feed), if the modem is set to echoing the command line, and another line feed (the answer to the "AT" command). So as part of the write operation make sure to clean the InputStream of this information (which can actually be used for error detection).
When using a Reader/Writer (not a really good idea), at least set the character encoding to US-ASCII instead of using the platform's default encoding, which might or might not work.
Since the main operation when using a modem is to transfer data unaltered, the communication with the modem should be handled via InputStream/OutputStream, and not a Reader/Writer.
Clipboard
To do:
Explain how to mix binary and character I/O over the same stream
Fix the example to use streams
// Write to the output
os.print("AT");
os.print("\r\n"); // Append a carriage return with a line feed
is.readLine(); // First read will contain the echoed command you sent to it. In this case: "AT"
is.readLine(); // Second read will remove the extra line feed that AT generates as output
Simple Reading of Data (Polling)
If you correctly carried out the write operation (see above) then the read operation is as simple as one command:
// Read the response
String response = is.readLine(); // if you sent "AT" then response == "OK"
简单阅读/写作的问题
从串口读取和/或写入串口的简单方法,如前面几节有严重的缺点。这两项活动都是通过阻止I / O完成的。这意味着,当有
没有数据可供阅读时,或者
写入的输出缓冲区已满(设备不接受) (更多)数据),
读取或写入方法(前一个例子中的os.print()或is.readLine())不返回,该应用程序停止了。更确切地说,完成读取或写入的线程被阻止。如果该线程是主应用程序线程,则应用程序冻结直到阻塞条件得到解决(数据可用于读取或设备再次接受数据)。
除非应用程序是一个非常原始的,冻结应用程序是不可接受的。例如,至少应该可以进行一些取消通信的用户交互。需要的是非阻塞I / O或异步I / O.但是,JavaComm基于Java的标准阻塞I / O系统(InputStream,OutputStream),但有一个扭曲,如下所示。
提到的扭曲是JavaComm通过事件通知机制为异步I / O提供一些有限的支持。但是Java中用于在阻塞I / O系统之上实现非阻塞I / O的一般解决方案是使用线程。实际上,这是串行写入的可行解决方案,强烈建议使用单独的线程写入串行端口 - 即使使用事件通知机制,如后面所述。
阅读也可以在一个单独的线程中处理。但是,如果使用JavaComm事件通知机制,则这不是必需的。总结一下:
活动架构
阅读使用事件通知和/或单独的线程
写作总是使用单独的线程,可选择使用事件通知
以下部分提供了一些细节。
事件驱动的串行通信
简介
JavaComm API提供了一个事件通知机制来克服阻塞I / O的问题。 However, in the typical Sun manner this mechanism is not without problems.
In principle an application can register event listeners with a particular SerialPort to be kept informed about important events happening on that port. The two most interesting event types for reading and writing data are
javax.comm.SerialPortEvent.DATA_AVAILABLE and
javax.comm.SerialPortEvent.OUTPUT_BUFFER_EMPTY.
But there are also two problems:
Only one single event listener per SerialPort can be registered. This forces the programmer to write \"monster\" listeners, discriminating according to the event type.
OUTPUT_BUFFER_EMPTY is an optional event type. Well hidden in the documentation Sun states that not all JavaComm implementations support generating events of this type.
Before going into details, the next section will present the principal way of implementing and registering a serial event handler. Remember, there can only be one handler at all, and it will have to handle all possible events.
Setting up a serial Event Handler
Problems with the simple Reading / Writing
The simple way of reading and/or writing from/to a serial port as demonstrated in the previous sections has serious drawbacks. Both activities are done with blocking I/O. That means, when there is
no data available for reading, or
the output buffer for writing is full (the device does not accept (any more) data),
the read or write method (os.print() or is.readLine() in the previous example) do not return, and the application comes to a halt. More precisely, the thread from which the read or write is done gets blocked. If that thread is the main application thread, the application freezes until the blocking condition is resolved (data becomes available for reading or device accepts data again).
Unless the application is a very primitive one, freezing of the application is not acceptable. For example, as a minimum some user interaction to cancel the communication should still be possible. What is needed is non-blocking I/O or asynchronous I/O. However, JavaComm is based on Java's standard blocking I/O system (InputStream, OutputStream), but with a twist, as shown later.
The mentioned "twist" is that JavaComm provides some limited support for asynchronous I/O via an event notification mechanism. But the general solution in Java to achieve non-blocking I/O on top of the blocking I/O system is to use threads. Indeed, this is a viable solution for serial writing, and it is strongly recommended to use a separate thread to write to the serial port - even if the event notification mechanism is used, as explained later.
Reading could also be handled in a separate thread. However, this is not strictly necessary if the JavaComm event notification mechanism is used. So summarize:
Activity Architecture
reading use event notification and/or separate thread
writing always use separate thread, optionally use event notification
The following sections provide some details.
Event Driven Serial Communication
Introduction
The JavaComm API provides an event notification mechanism to overcome the problems with blocking I/O. However, in the typical Sun manner this mechanism is not without problems.
In principle an application can register event listeners with a particular SerialPort to be kept informed about important events happening on that port. The two most interesting event types for reading and writing data are
javax.comm.SerialPortEvent.DATA_AVAILABLE and
javax.comm.SerialPortEvent.OUTPUT_BUFFER_EMPTY.
But there are also two problems:
Only one single event listener per SerialPort can be registered. This forces the programmer to write "monster" listeners, discriminating according to the event type.
OUTPUT_BUFFER_EMPTY is an optional event type. Well hidden in the documentation Sun states that not all JavaComm implementations support generating events of this type.
Before going into details, the next section will present the principal way of implementing and registering a serial event handler. Remember, there can only be one handler at all, and it will have to handle all possible events.
Setting up a serial Event Handler
import javax.comm.*;
/**
* Listener to handle all serial port events.
*
* NOTE: It is typical that the SerialPortEventListener is implemented
* in the main class that is supposed to communicate with the
* device. That way the listener has easy access to state information
* about the communication, e.g. when a particular communication
* protocol needs to be followed.
*
* However, for demonstration purposes this example implements a
* separate class.
*/
class SerialListener implements SerialPortEventListener {
/**
* Handle serial events. Dispatches the event to event-specific
* methods.
* @param event The serial event
*/
@Override
public void serialEvent(SerialPortEvent event){
//
// Dispatch event to individual methods. This keeps this ugly
// switch/case statement as short as possible.
//
switch(event.getEventType()) {
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
outputBufferEmpty(event);
break;
case SerialPortEvent.DATA_AVAILABLE:
dataAvailable(event);
break;
/* Other events, not implemented here ->
case SerialPortEvent.BI:
breakInterrupt(event);
break;
case SerialPortEvent.CD:
carrierDetect(event);
break;
case SerialPortEvent.CTS:
clearToSend(event);
break;
case SerialPortEvent.DSR:
dataSetReady(event);
break;
case SerialPortEvent.FE:
framingError(event);
break;
case SerialPortEvent.OE:
overrunError(event);
break;
case SerialPortEvent.PE:
parityError(event);
break;
case SerialPortEvent.RI:
ringIndicator(event);
break;
}
}
/**
* Handle output buffer empty events.
* NOTE: The reception of this event is optional and not
* guaranteed by the API specification.
* @param event The output buffer empty event
*/
protected void outputBufferEmpty(SerialPortEvent event) {
// Implement writing more data here
}
/**
* Handle data available events.
*
* @param event The data available event
*/
protected void dataAvailable(SerialPortEvent event) {
// implement reading from the serial port here
}
}
Once the listener is implemented, it can be used to listen to particular serial port events. To do so, an instance of the listener needs to be added to the serial port. Further, the reception of each event type needs to be requested individually.
SerialPort port = ...;
...
Once the listener is implemented, it can be used to listen to particular serial port events. To do so, an instance of the listener needs to be added to the serial port. Further, the reception of each event type needs to be requested individually.
SerialPort port = ...;
...
//
// Configure port parameters here. Only after the port is configured it
// makes sense to enable events. The event handler might be called immediately
// after an event is enabled.
...
//
// Typically, if the current class implements the SerialEventListener interface
// one would call
//
// port.addEventListener(this);
//
// but for our example a new instance of SerialListener is created:
//
port.addEventListener(new SerialListener());
//
// Enable the events we are interested in
//
port.notifyOnDataAvailable(true);
port.notifyOnOutputEmpty(true);
/* other events not used in this example ->
port.notifyOnBreakInterrupt(true);
port.notifyOnCarrierDetect(true);
port.notifyOnCTS(true);
port.notifyOnDSR(true);
port.notifyOnFramingError(true);
port.notifyOnOverrunError(true);
port.notifyOnParityError(true);
port.notifyOnRingIndicator(true);