Utilizando a API RXTX para manipulação da serial – Parte III
Instalando e utilizando a API de comunicação serial RXTX para leitura e escrita em portas seriais para Windows e Linux.
Escrevendo o código da leitura/escrita serial
Chegamos à parte mais importante de todo o artigo, agora iremos escrever o código de leitura serial, o primeiro passo é criar os imports dentro da classe SerialCom, iremos codificar primeiro essa classe, para isso basta inserir o seguinte código:
import gnu.io.CommPortIdentifier;
import java.util.Enumeration;
Agora vamos criar um array e uma variável do tipo Enumeration para trabalharmos nos métodos que serão criados:
protected String[] portas;
protected Enumeration listaDePortas;
O construtor da classe deverá ser da seguinte forma, já com a identificação das portas serias disponíveis:
public SerialCom(){
listaDePortas = CommPortIdentifier.getPortIdentifiers();
}
Agora iremos montar o método para retornar as portas disponíveis:
public String[] ObterPortas(){
return portas;
}
Até agora o processo foi bem simples, a seguir iremos construir dois métodos, o primeiro deles terá a função de armazenar uma lista das portas seriais do seu sistema disponíveis para a comunicação e o segundo irá identificar se a porta seleciona existe e está tudo em funcionamento com ela:
protected void ListarPortas(){
int i = 0;
portas = new String[10];
while (listaDePortas.hasMoreElements()) {
CommPortIdentifier ips =
(CommPortIdentifier)listaDePortas.nextElement();
portas[i] = ips.getName();
i++;
}
}
public boolean PortaExiste(String COMp){
String temp;
boolean e = false;
while (listaDePortas.hasMoreElements()) {
CommPortIdentifier ips = (CommPortIdentifier)listaDePortas.nextElement();
temp = ips.getName();
if (temp.equals(COMp)== true) {
e = true;
}
}
return e;
}
Assim finalizamos a construção da classe SerialCom, vamos agora iniciar o desenvolvimento da classe SerialComLeitura onde vamos implementar a interface que vai ficar “ouvindo” a porta para a captura de dados ou ainda via mandar dados para a porta serial.
Como na classe anterior o primeiro passo é colocar os imports:
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;
Como pode ser observado vamos também utilizar alguns recursos de IO implementado na própria API nativa do Java. Iremos utilizar o recurso de implementação para nesse classe implementar os métodos abstratos de listagem da serial e o método Run() da Thread.
public class SComm implements Runnable, SerialPortEventListener {
}
Agora vamos criar as seguintes variáveis que serão utilizadas durante o desenvolvimento:
public String Dadoslidos;
public int nodeBytes;
private int baudrate;
private int timeout;
private CommPortIdentifier cp;
private SerialPort porta;
private OutputStream saida;
private InputStream entrada;
private Thread threadLeitura;
private boolean IDPortaOK;
private boolean PortaOK;
private boolean Leitura;
private boolean Escrita;
private String Porta;
protected String peso;
Vamos definir os métodos para setar e resgatar os valores de peso para o processo de leitura da serial:
public void setPeso(String peso){
this.peso = peso;
}
public String getPeso(){
return peso;
}
Vamos definir também o construtor da classe recebendo os parâmetros do seu hardware serial (é de suma importância que esses parâmetros estejam corretos, caso contrário a leitura/escrita serial não terá sucesso):
public SerialComLeitura( String p , int b , int t ){
this.Porta = p;
this.baudrate = b;
this.timeout = t;
}
Os dois métodos a seguir servem para habilitar hora a escrita hora a leitura serial, eles serão muito utilizados no decorrer desse artigo para habilitar um ou outro recurso já que a interface serial não suporta leitura e escrita de forma simultânea:
public void HabilitarEscrita(){
Escrita = true;
Leitura = false;
}
public void HabilitarLeitura(){
Escrita = false;
Leitura = true;
}
O método seguinte irá obter o ID da porta para ser utilizado na identificação da mesma:
public void ObterIdDaPorta(){
try {
cp = CommPortIdentifier.getPortIdentifier(Porta);
if ( cp == null ) {
System.out.println(“Erro na porta”);
IDPortaOK = false;
System.exit(1);
}
IDPortaOK = true;
} catch (Exception e) {
System.out.println(“Erro obtendo ID da porta: ” + e);
IDPortaOK = false;
System.exit(1);
}
}
O método a seguir irá abrir a comunicação com a porta serial, após essa porta aberta podemos ou enviar ou ler a serial, é muito importante que os dados de DATABITS, STOPBITS, PARITY e FLOWCONTROL sejam exatos ao do seu equipamento serial, pois são parâmetros que vão estabelecer a velocidade e modo de leitura e escrita serial do computador com o seu equipamento:
public void AbrirPorta(){
try {
porta = (SerialPort)cp.open("SerialComLeitura", timeout);
PortaOK = true;
//configurar parâmetros
porta.setSerialPortParams(baudrate,
porta.DATABITS_8,
porta.STOPBITS_1,
porta.PARITY_NONE);
porta.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
}catch(Exception e){
PortaOK = false;
System.out.println(“Erro abrindo comunicação: ” + e);
System.exit(1);
}
}
O método abaixo irá habilitar a leitura da porta serial, vale lembrar que o “pulso” continuo de recebimento da serial será controlado pelo método abstrato serialEvent, o método então monitora a porta e obtém as entradas de dados pelo fluxo:
public void LerDados(){
if (Escrita == false){
try {
entrada = porta.getInputStream();
} catch (Exception e) {
System.out.println(“Erro de stream: ” + e);
System.exit(1);
}
try {
porta.addEventListener(this);
} catch (Exception e) {
System.out.println(“Erro de listener: ” + e);
System.exit(1);
}
porta.notifyOnDataAvailable(true);
try {
threadLeitura = new Thread(this);
threadLeitura.start();
run();
} catch (Exception e) {
System.out.println(“Erro de Thred: ” + e);
}
}
}
O método abaixo irá enviar uma string para a porta serial quando invocado:
public void EnviarUmaString(String msg){
if (Escrita==true) {
try {
saida = porta.getOutputStream();
System.out.println("FLUXO OK!");
} catch (Exception e) {
System.out.println("Erro.STATUS: " + e );
}
try {
System.out.println("Enviando um byte para " + Porta );
System.out.println("Enviando : " + msg );
saida.write(msg.getBytes());
Thread.sleep(100);
saida.flush();
} catch (Exception e) {
System.out.println("Houve um erro durante o envio. ");
System.out.println("STATUS: " + e );
System.exit(1);
}
} else {
System.exit(1);
}
}
Pronto, agora falta pouco, iremos agora construir um método abstrato run() esse método será o responsável por manter e controlar a execução da Thread de leitura da nossa aplicação serial:
public void run(){
try {
Thread.sleep(5);
} catch (Exception e) {
System.out.println(“Erro de Thred: ” + e);
}
}
O método abstrato serialEvent() vai gerenciar todos os dados recebidos pela serial, note que o nosso maior interesse é no evento DATA_AVAILABLE que irá dizer se tem informação disponível para a leitura na serial, esse trecho abaixo como toda a aplicação foi testada em inúmeras balanças de diversos fabricantes, dando uma margem de erro bem pequena, contudo se a sua aplicação ao ler dados enviados pela serial exibir dados quebrados, ou seja, se a string a ser recebida fosse “esse é um teste geral” recebesse da seguinte forma:
“esse é”
“um teste”
“ geral”
Isso significa que esse trecho precisa ser revisto, pois é ele que controla a informação e alguma incompatibilidade poderá ter ocorrido.
public void serialEvent(SerialPortEvent ev){
StringBuffer bufferLeitura = new StringBuffer();
int novoDado = 0;
switch (ev.getEventType()) {
case SerialPortEvent.BI:
case SerialPortEvent.OE:
case SerialPortEvent.FE:
case SerialPortEvent.PE:
case SerialPortEvent.CD:
case SerialPortEvent.CTS:
case SerialPortEvent.DSR:
case SerialPortEvent.RI:
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
break;
case SerialPortEvent.DATA_AVAILABLE:
//Novo algoritmo de leitura.
while(novoDado != -1){
try{
novoDado = entrada.read();
if(novoDado == -1){
break;
}
if('\r' == (char)novoDado){
bufferLeitura.append('\n');
}else{
bufferLeitura.append((char)novoDado);
}
}catch(IOException ioe){
System.out.println(“Erro de leitura serial: ” + ioe);
}
}
setPeso(new String(bufferLeitura));
System.out.println(getPeso());
break;
}
}
Agora somente os métodos para fechar a serial, obter a porta e obter o baurate do seu equipamento e assim essa classe está finalizada.
public void FecharCom(){
try {
porta.close();
} catch (Exception e) {
System.out.println(“Erro fechando porta: ” + e);
System.exit(0);
}
}
public String obterPorta(){
return Porta;
}
public int obterBaudrate(){
return baudrate;
}
}
Implementando a leitura/escrita serial
Podemos finalmente dizer que o pior já passou, vamos trabalhar com a classe Main para aproveitarmos os recursos das outras duas classes e efetuarmos a leitura e escrita na porta serial.
O primeiro passo é cuidar dos imports:
import net.viamais.serial.SerialCom;
import net.viamais.serial.SerialComLeitura;
Vamos extender a classe para herdar suas características:
public class Main extends SerialCom
Agora é preciso deixar o construtor padrão para a classe Main com o array de erros, nesse exemplo vamos implementar uma leitura simples da porta serial por um período de 1000 milessegundos, após esse tempo a comunicação é automaticamente finalizada.
public static void Main(String[] args){
//Iniciando leitura serial
SerialComLeitura leitura = new SerialComLeitura("COM1", 9600, 0);
leitura.HabilitarLeitura();
leitura.ObterIdDaPorta();
leitura.AbrirPorta();
leitura.LerDados();
//Controle de tempo da leitura aberta na serial
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("Erro na Thread: " + ex);
}
leitura.FecharCom();
}
}
Não fiquei me atendo a demasiados comentários no código, pois acredito que o código é simples e auto-explicativo, apenas dei algumas orientações basta estudar o código que rapidamente será assimilado.
Agora o seu projeto pode ser testado diretamente do Netbeans, para isso basta clicar com o botão direito do mouse em cima do nome do projeto e escolher a opção executar, pronto a comunicação (leitura neste caso) irá ser iniciada. No próximo artigo irei mostrar o processo para a escrita na serial aproveitando esse mesmo código e como compilar e distribuir a sua aplicação.