terça-feira, 1 de junho de 2010

Acessando porta serial com C#

Enviar uma string via porta serial.
Esse é o objetivo.

Em C# há um namespace próprio para trabalhar com portas seriais do computador:
System.IO.Ports

Ele fornece uma série de métodos e propriedades que podemos usar para trabalhar com porta serial.
O primeiro passo é incluir o namespace no início do arquivo de código fonte da classe onde você pretende acessar a porta serial, junto com os demais namespaces que estiver usando. Exemplo:

using System;
using System.Collection.Generics;
using System.Text;
using System.IO.Ports;

Dentro desse namespace há uma classe chamada SerialPort. O próximo passo é criar um atributo, que é um objeto da classe SerialPort, dentro da classe que estiver usando.

class Nome_da_classe
{
    static SerialPort _portaCOM;

.
.
.
}

"_portaCOM" é um nome para o atributo. Você pode escolher outro nome.

OBS.: Se estiver criando um programa diretamente no método Main(), a classe deve ser declarada antes do Main().

public class Nome_da_classe
{
    static SerialPort _portaCOM;

    public static void Main()
    {
        //linhas
        //de
        //código
    }
}

Feito isso vamos ao que interessa.

A inicialização do atributo pode ser feita na declaração dele ou no construtor da classe. No caso, fiz no construtor.

class Nome_da_classe
{
    static SerialPort _portaCOM;

    public Nome_da_classe()
    {
        _portaCOM = new SerialPort();
    }
.
.
.
}

A partir daí temos vários métodos e propriedades que podemos usar para nosso benefício. Por exemplo:

Propriedades:
    PortName: Armazena o nome da porta com a qual vai conectar
        Ex.: COM1, COM2 => para Windows
                /dev/ttyS0, /dev/ttyS1 => para Linux.
     BaudRate: Armazena o BaudRate que será utilizado.
    IsOpen: Retorna 'true' quando está conectado à porta e 'false' caso contrário.


Métodos:
    Open() => Conecta à porta serial.
    Close() => Desconecta.
    Write() => Envia uma string pela porta serial.
    WriteLine() => Envia uma string pela porta serial com caracter de nova linha no final.
    ReadLine() => Lê a última linha recebida na porta serial.


No meu caso criei um método para conectar e outro para desconectar serial.

{
.
.
.
    public string Conectar()
    {
        //Testa se a porta já está aberta e só conecta se ela ainda não estiver.
        if (!_portaCOM.IsOpen)
        {
            _portaCOM.Open();
            return "Conectado a porta " + _portaCOM.PortName;
        }
        else return "A porta " + _portaCOM.PortName + " já está sendo usada. Não foi possível conectar.";
    }

    public string Desconectar()
    {
        //Testa se a porta está aberta e só desconecta se ela estiver aberta.
        if (_portaCOM.IsOpen)
        {
            _portaCOM.Close();
            return "Desconectado da porta " + _portaCOM.PortName;
        }
        else return "A porta " + _portaCOM.PortName + " não está conectada.";
    }
.
.
.
}


Métodos de leitura e escrita.

{
.
.
.
    public string Read()
    {
        string resposta = "";

        try
        {
            resposta = _portaCOM.ReadLine();
        }
        catch (TimeoutException e)
        {
            resposta = "Não foi possível ler da serial. Erro: " + e.Message;
        }
        return resposta;
    }

    public string Write(string texto)
    {
        string resposta = "";
        try
        {
            _portaCOM.WriteLine(texto);
        }
        catch (TimeoutException e)
        {
            resposta = "Não foi possível enviar dados. Erro: " + e.Message;
        }
        return resposta;
    }
.
.
.
}

As strings que estão sendo retornadas podem ser usadas em um Label de uma StatusStrip de uma Form qualquer em que a porta serial esteja sendo usada.

Pode-se adicionar mais tratamentos de exceções com outros 'catch', conforme for necessário.Só estava exemplificando.

Uma coisa interessante é ter um método que retorne as portas disponíveis.
Isso pode ser usado para criar uma comboBox com as portas disponíveis.

{
.
.
.
    public List<string> PortasDisponiveis()
    {
        List<string> portasDisponiveis = new List<string>();
        foreach (string s in SerialPort.GetPortNames())
            portasDisponiveis.Add(s);
        return portasDisponiveis;
    }
.
.
.
}

SerialPort.GetPortNames() => retorna uma coleção com as portas disponíveis no computador.
Eu usei uma List para retornar no meu método, mas pode ser usado um vetor de string ou outro tipo de coleção, dependendo de pra que será utilizado.

Também criei um atributo com alguns baudrates usuais.

private int[] baudrate = {
                            4800,
                            9600,
                            14400,
                            19200,
                            28800,
                            38400,
                            57600,
                            115200
                         };

E um método para retornar os baudrates

{
.
.
.
    public int[] BaudRates()
    {
        return baudrate;
    }
.
.
.
}


E propriedades para editar o nome e o baudrate da porta:

{
.
.
.
    public string Name
    {
        get
        {
            return _portaCOM.Name;
        }
        set
        {
            if (value != "")
                _portaCOM.Name = value;
        }
    }

    public int BaudRate
    {
        get
        {
            return _portaCOM.BaudRate;
        }
        set
        {
            _portaCOM.BaudRate = value;
        }
    }
.
.
.
}

Já me estendi demais. Se algo não tiver ficado claro é só perguntar.

11 comentários:

Viajando nas NUVENS disse...
Este comentário foi removido por um administrador do blog.
Viajando nas NUVENS disse...

Ola Ivan.
Coloquei as 2 classes neste endereco.
Me da uma ajuda por favor.

http://ocaccy.blogspot.com/

Obrigado,
Ocaccy

giancorrea disse...

Olá Ivan, gostaria de saber como faço para receber dados da serial 'full time', isto é, a todo momento..
como uma interrupção de um microcontrolador.. tentei utilizar o evento 'DataReceiver' mas nao tive sucesso...
Obrigado

_ivan disse...

Oi Gian.
Poderia explicar melhor?

Basicamente, se estiver usando C#.NET o evento DataReceived (não DataReceiver, como tu escreveste) tem que funcionar.
Poderia explicar qual foi o problema?
Se estiver usando outras linguagens é possível que tu não tenhas esse recurso.
Nesse caso tu terias que implementar uma thread manualmente, que é basicamente o que acontece quando se usa o evento DataReceived.

Fica mais fácil eu te sugerir alguma coisa se tu explicares melhor.

giancorrea disse...

basicamente eu tenho que receber um caractere pela serial (esse é enviado de um microcontrolador).
quando eu pressiono um botão ligado ao microcontrolador ele envia um caractere. é isso! mas preciso receber isso a toda hora pra saber se houve a chegada desse caracter, estou trabalhando com o Visual Studio C# Express 2010.

o DataReceived não está funcionando

_ivan disse...

Seguinte cara.

Não sei se há alguma limitação em relação ao DataReceived por ser o Express, ou se algo foi mudado no Visual Studio 2010.

Tu verificou se não está fazendo nenhuma referência errada com a porta serial.

Sobre o evento:
O DataReceived não será disparado enquanto não houver bytes no buffer da serial então, no teu caso, ele só deverá ser disparado quando tu apertar o botão.
Uma possibilidade pra testar é tu programar o microcontrolador pra escrever algo quando o botão não está apertado também.
Se mesmo quando tu aperta o botão tu não recebe nada então meu primeiro palpite é de que tem alguma referência errada (direcionando pra porta errada, instanciando novamente uma porta serial em um local errado, coisas do gênero).
Se tu já checou e tem certeza que não é isso então tu vai ter que checar essa possibilidade de ser alguma limitação do Visual Studio Express (ainda acho pouco provável). A maneira de testar isso é criar um thread (um trecho de código que roda em paralelo com o resto do teu software) que fique lendo tua porta serial.
O namespace que tu precisa pra isso é o System.Threading

Tem bastante tutorial na internet sobre como criar threads, caso tu nunca tenha lidado com isso.
Se precisar de mais alguma coisa é só falar.

Unknown disse...

Ola!! Ivan . Eu preciso de um código que recebe o valor de um contador e mostra numa label. E também quando preciono um botão em auto o pc recebe e troca uma imagem!! E se botão ter no nível lógico baixo, o pc recebe e troca a imagem atual por outra!! Me ajuda cara!! Preciso mesmo é para o meu projeto final!! Sou angolano!!!

Unknown disse...

Ola!! Ivan . Eu preciso de um código que recebe o valor de um contador e mostra numa label. E também quando preciono um botão em auto o pc recebe e troca uma imagem!! E se botão ter no nível lógico baixo, o pc recebe e troca a imagem atual por outra!! Me ajuda cara!! Preciso mesmo é para o meu projeto final!! Sou angolano!!!

Unknown disse...

Ola!! Ivan . Eu preciso de um código que recebe o valor de um contador e mostra numa label. E também quando preciono um botão em auto o pc recebe e troca uma imagem!! E se botão ter no nível lógico baixo, o pc recebe e troca a imagem atual por outra!! Me ajuda cara!! Preciso mesmo é para o meu projeto final!! Sou angolano!!!

Unknown disse...

Ola!! Ivan . Eu preciso de um código que recebe o valor de um contador e mostra numa label. E também quando preciono um botão em auto o pc recebe e troca uma imagem!! E se botão ter no nível lógico baixo, o pc recebe e troca a imagem atual por outra!! Me ajuda cara!! Preciso mesmo é para o meu projeto final!! Sou angolano!!!

_ivan disse...

Olá Calembe.

Me desculpa. Como estou com outros trabalhos no momento não tenho conseguido acompanhar o blog e por isso não vi teu comentário.

Não entendi muito bem qual é o problema que tens, mas acredito que já seja tarde para ajudar.

Se precisar de alguma coisa específica mande e-mail.