Tecnologia e Outros
[Link]
Comunicao Serial Utilizando a API da SUN
Daniel Vasconcelos Gomes
Uma introduo a programao serial em Java utilizando a API de comunicao serial da SUN.
Introduo
Programar uma rotina em Assembly para comunicar dados de um teclado at um motor de passo uma tarefa bastante complicada, mas exemplifica o uso de comunicao serial. complicado, porque, Assembly no uma linguagem muito acessvel. E tambm, porque preciso lidar com mscaras de interrupo, sincronizar clock, programar uma porta paralela, reconhecer stop bits, os dados, paridade e ainda lidar com o cdigo ASCII gerado pelo teclado. Com o padro atual de microprocessadores para PCs, no preciso esse trabalho, basta usar os recursos de alto nvel. Para WIN32 temos as opes das APIs e Visual Basic. E a melhor delas todas, por ser um meio termo, a API Java Communications da Sun. A mquina virtual Java faz o trabalho sujo para voc.
Veja abaixo a pinagem de um conector DB9:
Pinagem do conector DB9
1- DCC (Sinal de controle) 2- Dados recebidos 3- Dados transmitidos 4- DTR (Terminal de dados pronto) Sinal de controle 5- Terra 6- DSR (Sinal de controle) 7- RTS( Requisio para enviar) Sinal de controle 8- CTS (Pronto para enviar) Sinal de controle 9- Indicador de Anel
A tenso lida entre os terminais 3 e 5 fornece o sinal de sada/transmitido. O sinal lido entre 2 e 5 nos fornece o sinal recebido. Os outros pinos nos fornecem sinais de controle (todos acessveis atravs da API da SUN claro) conhecidos como handshaking.
Grupo de Usurios Java [Link] Pgina 1
Tecnologia e Outros USB?
[Link]
No futuro, as portas seriais e os protocolos de comunicao serial sero substitudos pelo padro USB (Universal Serial Bus). H um link bem interessante com detalhes de como ampliar a API de comunicao Java para que ela possa suportar a USB: [Link]
O Padro EIA-232
Tambm incorretamente chamado de RS-232, o padro EIA-232 especfica o meio fsico de comunicao serial. Acrescido ao nmero, h uma letra que indica a reviso atual do padro. Se no me engano, a verso atual EIA-232-F. O nvel de tenso varia de -12 Volts -3 Volts para o nvel lgico 1 e de +3 Volts +12 Volts para nvel lgico 0. Como meu objetivo mostrar o uso da API, no vou tentar descrever em maiores detalhes o padro. S devo deixar claro, que o programador no precisa converter os dados seriais para paralelo diretamente no cdigo. Isso quem faz a UART do PC.
Algumas Definies:
DTE: (Data terminal equipament), o equipamento que envia os dados . DCE: (Data communication equipament) o equipamento que o recebe, Baudrate: taxa de bits por segundo que transmitida. Assim, quando falarmos em um baudrate de 9600 bps, queremos dizer 9600 bits por segundo, ou seja, cada bit dura 1/9600 segundos. Timeout: o tempo que a CPU espera para obter uma resposta do DCE. Caso esse tempo seja excedido, a comunicao no estabelecida. Parity : Paridade. Os bits de paridade so bits de controle. Paridade par significa que o total de nveis 1 lgicos transmitidos juntamente com a paridade associada deve ser um nmero par.
A API de Comunicao da SUN
A SUN fornece como download gratuito a API de comunicao serial e paralela na URL: [Link] Basta baixar a API e realizar os procedimentos de instalao. Aps baixar a API, descompact-la, voc ter: o o o o Copiar o arquivo [Link] para o diretrio C:\JavaSDK\BIN (isto , o diretrio onde o J2SDK foi instalado no seu PC). Copiar o arquivo [Link] para o diretrio C:\JavaSDK\BIN\LIB. Copiar o arquivo [Link] para o diretrio C:\JavaSDK\BIN\LIB. Em seguida configure o CLASSPATH para que ele reconhea o arquivo [Link].
Reconhecendo As Portas
Antes de iniciarmos a comunicao com a porta serial, precisamos reconhecer as portas existentes em sua estao de trabalho. A API de comunicao nos fornece o mtodo getPortIdentifiers() integrante da classe CommPortIdentifier que retorna em uma estrutura Enumeration, as portas disponveis. A Classe CommPortIdentifier pode ser instanciada e representar uma porta. Para isso, precisamos varrer a
Grupo de Usurios Java [Link] Pgina 2
Tecnologia e Outros
[Link]
estrutura retornada por getPortIdentifiers() e instanciando cada porta atravs de uma converso (casting) simples:
//.... Enumeration listaDePortas; listaDePortas = [Link](); //... //.. int i = 0; portas = new String[10]; while ([Link]()) { CommPortIdentifier ips = (CommPortIdentifier)[Link](); portas[i] = [Link](); i++; } //..
O mtodo hasMoreElements() retorna o prximo elemento da estrutura listaDePortas, mas o loop while garante que todos os elementos sejam passados ao Array portas atravs do mtodo getName().
Abrindo portas para comunicar dados
O mtodo getPortIdentifier(String porta) da classe CommPortIdentifier retorna um identificador da porta escolhida. Precisamos instanciar um objeto para receber esse identificador:
CommIdentifier cp = [Link](minhaPortaEscolhida);
Em seguida criamos uma instncia da classe SerialPort utilizando o identificador. Note que uma converso dever ser feita. A porta s pode ser instanciada atravs desse casting e ao mesmo tempo abrimos a porta para comunicao:
SerialPort porta = (SerialPort)[Link]("SComm",timeout);
O mtodo open() tem como parmetros o nome da classe principal (faa isso para no gerar conflitos) e o valor desejado para timeout. Em seguida, precisamos atribuir fluxos de entrada e sada. Basta utilizar as classes Abstratas OutputStream e InputStream, j que a classe SerialPort implementa os mtodos de entrada e sada dessas classes para comunicao serial. Para ler dados na porta serial:
InputStream entrada = [Link]();
E para escrever dados na porta serial:
OutputStream saida = [Link]();
Em seguida precisamos configurar os parmetros de comunicao serial, para isso utilizamos o mtodo setSerialPortParams:
[Link](baudrate, porta.DATABITS_8, porta.STOPBITS_2, porta.PARITY_NONE);
Enviando bytes para a porta serial
Depois de configurar a porta para comunicar e definido o fluxo de sada, podemos comunicar os dados. Isso bem simples:
String msg = Ol Mundo!; [Link]([Link]()); [Link](100); [Link]();
Grupo de Usurios Java [Link] Pgina 3
Tecnologia e Outros
[Link]
Recebendo dados na porta serial
At agora tudo foi relativamente simples, e ento entramos na rea mais complicada: ler dados! A API de comunicaes Java facilita bastante o trabalho, mas mesmo assim, so vrias linhas de cdigo. Basicamente o que deve ser feito : o o o o Criar um fluxo de entrada. Adicionar um gerenciador de eventos para dados na porta serial. Instanciar uma Thread para aguardar os eventos Tratar o evento e receber os dados
A etapa i. j foi detalhada. Para adicionarmos um gerenciador de eventos para a porta serial basta fazer:
[Link](this);
Em seguida precisamos notificar o objeto porta criado de que podem exister dados para serem lidos:
[Link](true);
Agora falta apenas tratar o evento. Infelizmente, essa a parte mais complicada. Primeiro instanciamos um Array de bytes. Esse Array ser nosso buffer de dados.
public void serialEvent(SerialPortEvent ev){ switch ([Link]()) { // case SerialPortEvent.DATA_AVAILABLE: byte[] bufferLeitura = new byte[20];
J definimos entrada como nosso fluxo de entrada de dados. O mtodo available() retorna sempre 0 se InputStream (nesse caso entrada) classe da qual ele invocado.
try { while ( [Link]() > 0 ) { nodeBytes = [Link](bufferLeitura); }
O metodo read(byte[] b) faz toda a leitura. Ele copia os bytes lidos para o Array bufferLeitura e retorna um inteiro representando o nmero de bytes lidos. Podemos converter esses valores para uma String como mostrado abaixo:
String Dadoslidos = new String(bufferLeitura);
Se a dimenso do buffer for igual zero, isso nos dir que nenhum byte foi lido. Se a dimenso do buffer for igual a 1, saberemos que apenas um byte foi lido. Caso contrrio, a estrutura bufferLeitura recebe os bytes lidos. O primerio byte lido armazenado em bufferLeitura[0], o segundo em bufferLeitura[1] e assim por diante.
if ([Link] == 0) { [Link]("Nada lido!"); } else if ([Link] == 1 ){ [Link]("Apenas um byte foi lido!"); } else { [Link](Dadoslidos); } } catch (Exception e) { [Link]("Erro durante a leitura: " + e ); } [Link]("n.o de bytes lidos : " + nodeBytes ); break; } } Grupo de Usurios Java [Link] Pgina 4
Tecnologia e Outros
[Link]
O Package SrCom
Eu criei um package SrCom com duas classes: SerialComm e SComm. SerialComm reconhece as portas disponveis e SComm, envia um mensagem para a porta especificada. Tambm pode ler uma mensagem. Para isso a classe SComm implementa as interfaces Runnable e o listener de eventos da porta serial.
Classe SerialComm
//***************************************************************************** //COMUNICAO SERIAL UTILIZANDO A API DA SUN //***************************************************************************** // CLASSE [Link] PARTE DO PACKAGE SrCom //***************************************************************************** //AUTOR : Daniel V. Gomes //EMAIL: dansovg@[Link] //DATA INICIAL: 29/04/04 //DATA ATUAL: 03/05/04 //***************************************************************************** //NOTA: Voc pode utilizar este cdigo a vontade mas no me responsabilizo por //erros durante sua execuo. Quaisquer dvidas ou sugestes, //envie-me por email. //***************************************************************************** package SrCom; import [Link].*; import [Link].*; import [Link].*; public class SerialCom { //********************************* //Variveis //********************************* //variveis para identificar portas protected String[] portas; protected Enumeration listaDePortas; //construtor public SerialCom(){ listaDePortas = [Link](); } //retorna as portas disponveis public String[] ObterPortas(){ return portas; } //Copia portas para um Array protected void ListarPortas(){ int i = 0; portas = new String[10]; while ([Link]()) { CommPortIdentifier ips = (CommPortIdentifier)[Link](); portas[i] = [Link](); i++; } } //pesquisa se a Porta existe public boolean PortaExiste(String COMp){ String temp; boolean e = false; while ([Link]()) { CommPortIdentifier ips = (CommPortIdentifier)[Link](); temp = [Link](); if ([Link](COMp)== true) { e = true; }; } return e; } //imprime as portas disponveis protected void ImprimePortas(){ for (int i = 0 ; i < [Link] ; i ++ ) { if (portas[i] != null ) { [Link](portas[i] + " "); Grupo de Usurios Java [Link] Pgina 5
Tecnologia e Outros
[Link] } } [Link](" "); } }//FIM DA CLASSE
Classe SComm
//***************************************************************************** //COMUNICAO SERIAL UTILIZANDO A API DA SUN //***************************************************************************** // CLASSE [Link] PARTE DO PACKAGE SrCom //***************************************************************************** //AUTOR : Daniel V. Gomes //EMAIL: dansovg@[Link] //DATA INICIAL: 29/04/04 //DATA ATUAL: 03/05/04 //***************************************************************************** //NOTA: Voc pode utilizar este cdigo a vontade mas no me responsabilizo por //erros durante sua execuo. Quaisquer dvidas ou sugestes, //envie-me por email. //***************************************************************************** package SrCom; import [Link].*; import [Link].*; //classe Principal public class SComm implements Runnable, SerialPortEventListener { //propriedades private String Porta; 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; //indicadores private boolean IDPortaOK; //true porta EXISTE private boolean PortaOK;// true porta aberta private boolean Leitura; private boolean Escrita; //construtor default paridade : par //baudrate: 9600 bps stopbits: 2 COM 1 public SComm() { Porta = "COM1"; baudrate = 9600; timeout = 1000; }; //um Objeto ComObj passado ao construtor //com detalhes de qual porta abrir //e informaes sobre configuraes public SComm( String p , int b , int t ){ [Link] = p; [Link] = b; [Link] = t; }; //habilita escrita de dados public void HabilitarEscrita(){ Escrita = true; Leitura = false; } //habilita leitura de dados public void HabilitarLeitura(){ Escrita = false; Leitura = true; } //Obtm o ID da PORTA public void ObterIdDaPorta(){ try { cp = [Link](Porta); if ( cp == null ) { [Link]("A " + Porta + " nao existe!" ); [Link]("ERRO!Abortando..." ); Grupo de Usurios Java [Link] Pgina 6
Tecnologia e Outros
[Link] IDPortaOK = false; [Link](1); } IDPortaOK = true; } catch (Exception e) { [Link]("Erro durante o procedimento. STATUS" + e ); IDPortaOK = false; [Link](1); } } //Abre a comunicao da porta public void AbrirPorta(){ try { porta = (SerialPort)[Link]("SComm",timeout); PortaOK = true; [Link]("Porta aberta com sucesso!"); //configurar parmetros [Link](baudrate, porta.DATABITS_8, porta.STOPBITS_2, porta.PARITY_NONE); } catch (Exception e) { PortaOK = false; [Link]("Erro ao abrir a porta! STATUS: " + e ); [Link](1); } } //funo que envie um bit para a porta serial public void EnviarUmaString(String msg){ if (Escrita==true) { try { saida = [Link](); [Link]("FLUXO OK!"); } catch (Exception e) { [Link]("[Link]: " + e ); } try { [Link]("Enviando um byte para " + Porta ); [Link]("Enviando : " + msg ); [Link]([Link]()); [Link](100); [Link](); } catch (Exception e) { [Link]("Houve um erro durante o envio. "); [Link]("STATUS: " + e ); [Link](1); } } else { [Link](1); } } //leitura de dados na serial public void LerDados(){ if (Escrita == true){ try { entrada = [Link](); [Link]("FLUXO OK!"); } catch (Exception e) { [Link]("[Link]: " + e ); [Link](1); } try { [Link](this); [Link]("SUCESSO. Porta aguardando..."); } catch (Exception e) { [Link]("Erro ao criar listener: "); [Link]("STATUS: " + e); [Link](1); } [Link](true); try { threadLeitura = new Thread(this); [Link](); } catch (Exception e) { [Link]("Erro ao iniciar leitura: " + e ); } } }
Grupo de Usurios Java [Link] Pgina 7
Tecnologia e Outros
[Link] //mtodo RUN da thread de leitura public void run(){ try { [Link](5000); } catch (Exception e) { [Link]("Erro. Status = " + e ); } } //gerenciador de eventos de leitura na serial public void serialEvent(SerialPortEvent ev){ switch ([Link]()) { case [Link]: case [Link]: case [Link]: case [Link]: case [Link]: case [Link]: case [Link]: case [Link]: case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break; case SerialPortEvent.DATA_AVAILABLE: byte[] bufferLeitura = new byte[20]; try { while ( [Link]() > 0 ) { nodeBytes = [Link](bufferLeitura); } String Dadoslidos = new String(bufferLeitura); if ([Link] == 0) { [Link]("Nada lido!"); } else if ([Link] == 1 ){ [Link]("Apenas um byte foi lido!"); } else { [Link](Dadoslidos); } } catch (Exception e) { [Link]("Erro durante a leitura: " + e ); } [Link]("n.o de bytes lidos : " + nodeBytes ); break; } } //funo que fecha a conexo public void FecharCom(){ try { [Link](); [Link]("CONEXAO FECHADA>>FIM.."); } catch (Exception e) { [Link]("ERRO AO FECHAR. STATUS: " + e ); [Link](0); } } //Acessores public String obterPorta(){ return Porta; } public int obterBaudrate(){ return baudrate; } }
Extendendo a capacidade das classes
Nesses exemplos, eu utilizei o modo console. Assim, vrias mensagens de erro e de notificao de status so enviadas tela. No entanto, o leitor/a poderia converter essas mensagens para strings e pass-las para JLabels, que informam as mensagens pertinentes. Abaixo, apresento um programa simples que tenta escrever e depois ler um dado na porta serial COM2, se ela existir.
//***************************************************************************** //COMUNICAO SERIAL UTILIZANDO A API DA SUN //***************************************************************************** //AUTOR : Daniel V. Gomes //EMAIL: dansovg@[Link] //***************************************************************************** //NOTA: Voc pode utilizar este cdigo a vontade mas no me responsabilizo por //erros durante sua execuo. Quaisquer dvidas ou sugestes, Grupo de Usurios Java [Link] Pgina 8
Tecnologia e Outros
[Link] //envie-me por email. //***************************************************************************** import SrCom.*; public class Stest2 extends SerialCom { public Stest2(){ super(); } public static void main(String[] args){ Stest2 st = new Stest2(); if ( [Link]("COM2") == true) { [Link]("Iniciando comunicao!"); SComm sc = new SComm("COM2",9600,2000); [Link](); [Link](); [Link](); [Link]("Ol mundo!"); [Link](); } Stest2 st2 = new Stest2(); if ( [Link]("COM2") == true) { [Link]("Iniciando comunicao!"); SComm sc2 = new SComm("COM2",9600,2000); [Link](); [Link](); [Link](); [Link](); [Link](); } } }
Concluso
Utilizar a API de comunicao da SUN uma alternativa utilizar as APIs do Windows ou mesmo utilizar um componente do Visual Basic. A vantagem que orientado a objeto, rapidamente assimilvel e possui tima performance.
Referncias
Sun Communications API: - [Link] Java Cook Book, Captulo 11, disponvel para download gratuito . - [Link] Outro sample gratuito. - [Link] Artigo sobre o uso da API de comunicao elaborado por Rick Proctor da Borland. - [Link]
Site oficial da SUN
Comunicao Serial EIA 232: - [Link] Uma descrio bem completa do padro serial - [Link] IDC. Eles tm alguns tutoriais gratuitos em pdf muito bons sobre conversores A/D, D/A, automao industrial e comunicaes (serial, TCP-IP). - [Link]
Daniel Vasconcelos Gomes (dansovg@[Link]) um Engenheiro Eletricista bastante interessado em Java e C++. Ele elabora manuais tcnicos de PLCs, mas preferiria trabalhar com desenvolvimento. Entusiasta da tecnologia Java, est sempre disposto a mostrar que a linguagem pode ser utilizada no somente para aplicativos web.
Grupo de Usurios Java [Link] Pgina 9