Comunicação Serial com C# e Arduino – Parte 1

Uma interface entre o arduino e um computador muitas vezes é importante durante o desenvolvimento de projeto ou para controle de dispositivos através de uma interface gráfica. Pode-se usar uma aplicação no computador para Aquisição e exibição de dados em forma de gráfico durante algum experimento em laboratório ou estudo. Como já foi visto aqui no artigo sobre o Arduino Uno e sobre comunicação Serial com Arduino, a comunicação entre a placa e o computador é feira através de uma porta serial emulada através do driver da USB. Já foi exibido aqui uma aplicação de comunicação serial desenvolvida com a plataforma JAVA. Neste artigo vamos ensinar como desenvolver uma aplicação para Windows usando a plataforma .Net usando o ambiente Visual Studio da Microsoft, com a linguagem C#. Será desenvolvido um terminal simples, onde se poderá enviar e receber caracteres através de uma interface gráfica. Esse artigo também servirá de base para desenvolvermos uma aplicação envolvendo botões e outros elementos visuais.

Você pode baixar a versão express dessa ferramenta diretamente no site da plataforma. Selecione a opçãoExpress 2013 for Windows Desktop. Após o download faça a instalação, que é um processo bem simples porém um pouco demorado, basta seguir os passos de instalação.

Ao Iniciar o Visual Studio será exibida sua tela inicial e para iniciar um novo projeto deve-se acessar o menuFILE > New Project. Como vamos trabalhar com a linguagem C#, deve-se selecionar a opção Visual C# no menu lateral. Agora vamos iniciar o passo a passo pra criar nossa aplicação:

01

Primeiro passo é iniciar um novo projeto Windows Forms Application em C#:

02

Agora vamos inserir os componentes no Form. O primeiro a ser inserido será um botão e deve-se mudar a sua propriedade Name para “btConectar” e a sua propriedade Text para “Conectar”, conforme exibido a seguir:

03

Inserir um comboBox logo a frente do botão btConectar, inserido anteriormente:

04

Inserir outro botão, logo abaixo do btConectar, e mudar a sua propriedade Text para “Enviar” e Name para btEnviar:

05

Agora vamos inserir um textBox, que receberá os dados a serem enviados. Após ser inserido, mudar a sua propriedade Name para “textBoxEnviar”:

06

Agora vamos inserir um textBox maior, que exibirá os dados recebidos. Mudar as propriedades Name para “textBoxReceber”, Multiline para “True” e ScrollBar para “Vertical”. A aparência do Form ficará da seguinte forma:

07

Próximo passo é inserir um componente timer que será responsável pela atualização das portas COM disponíveis no PC. Selecione o componente timer e clique dentro do Form. Será exibido logo abaixo o componente timer1. Troque a propriedade Name para “timerCOM” e Interval para 1000, conforme exibido a seguir:

08

Por último vamos inserir o componente de comunicação serial, o SerialPort. Selecione o componente SerialPort e depois clique dentro do Form. Será exibido este componente ao lado do timerCOM:

09

Com os componentes inseridos no Form, vamos para a codificação.

Antes de conectar a porta Serial, é necessário verificar as portas COMs disponíveis para uso, e qual a porta o usuário deseja conectar. Para isso vamos atualizar a cada segundo a ComboBox com as portas disponíveis. Vamos criar um método privado dentro da classe Form1, que será chamado de atualizaListaCOMs. Clique com o botão direito no Form e selecione a opção View code. Insira o método atualizaListaCOMs(), conforme exibido no código a seguir:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports; 	// necessário para ter acesso as portas
namespace interfaceArduinoVS2013
{
    public partial class Form1 : Form
    {
    	public Form1()
    	{
        	InitializeComponent();
    	}
    	private void atualizaListaCOMs()
    	{
        	int i;
        	bool quantDiferente;	//flag para sinalizar que a quantidade de portas mudou
        	i = 0;
        	quantDiferente = false;
        	//se a quantidade de portas mudou
        	if (comboBox1.Items.Count == SerialPort.GetPortNames().Length)
        	{
            	foreach (string s in SerialPort.GetPortNames())
            	{
                	if (comboBox1.Items[i++].Equals(s) == false)
                	{
                    	quantDiferente = true;
                	}
            	}
        	}
        	else
        	{
            	quantDiferente = true;
        	}
        	//Se não foi detectado diferença
        	if (quantDiferente == false)
        	{
            	return;                     //retorna
        	}
        	//limpa comboBox
        	comboBox1.Items.Clear();
        	//adiciona todas as COM diponíveis na lista
        	foreach (string s in SerialPort.GetPortNames())
        	{
            	comboBox1.Items.Add(s);
        	}
        	//seleciona a primeira posição da lista
        	comboBox1.SelectedIndex = 0;
    	}
	}
}

Para testar a aplicação é necessário clicar no botão Start ou pressionar a tecla F5. Se tiver alguma porta disponível para comunicação, esta será listada dentro da comBox, conforme exibido a seguir:

08testeAtualizaCOM

 

 

Na imagem acima nota-se que apenas a COM5 estava disponível. Caso uma placa Arduino seja inserida, é necessário que atualize automaticamente a lista. Para isso vamos usar o timerCOM que está configurado para gerar um evento a cada segundo. Inicialmente deve-se habilitar o timer logo após a inicialização do Form e colocar o método de atualização dentro do evento timerCOM_tick, conforme exibido a seguir:

Obs.: Para gerar o evento timerCOM_tick basta dar duplo clique no componente timerCOM na aba design.

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;  // necessário para ter acesso as portas
namespace interfaceArduinoVS2013
{
    public partial class Form1 : Form
    {
    	public Form1()
    	{
        	InitializeComponent();
        	timerCOM.Enabled = true;
    	}
    	private void atualizaListaCOMs()
    	{
        	int i;
        	bool quantDiferente;	//flag para sinalizar que a quantidade de portas mudou
        	i = 0;
        	quantDiferente = false;
        	//se a quantidade de portas mudou
        	if (comboBox1.Items.Count == SerialPort.GetPortNames().Length)
        	{
      	      foreach (string s in SerialPort.GetPortNames())
            	{
                	if (comboBox1.Items[i++].Equals(s) == false)
                	{
                    	quantDiferente = true;
                	}
            	}
      	  }
        	else
        	{
            	quantDiferente = true;
        	}
        	//Se não foi detectado diferença
        	if (quantDiferente == false)
        	{
            	return;                     //retorna
        	}
        	//limpa comboBox
        	comboBox1.Items.Clear();
        	//adiciona todas as COM diponíveis na lista
        	foreach (string s in SerialPort.GetPortNames())
        	{
            	comboBox1.Items.Add(s);
        	}
            //seleciona a primeira posição da lista
        	comboBox1.SelectedIndex = 0;
    	}
    	private void timerCOM_Tick(object sender, EventArgs e)
    	{
        	atualizaListaCOMs();
    	}
	}
}

Insira outro Arduino ou crie uma porta COM virtual para verificar que é atualizado automaticamente o comboBox:

09timerAtualizaCOMs-660x325

No exemplo acima foi criada uma COM virtual com o auxílio do programa VSPE, que pode ser baixado aqui.

Agora já se pode escolher em qual porta a aplicação vai conectar. O evento click do btConectar será usado para  fazer a conexão. Para criar esse evento, selecione a aba de design do Form e dê um duplo clique no botão conectar. Será gerado o evento e agora deve-se  inserir o código para conexão. O botão conectar também servirá para desconectar quando a porta já estiver conectada, confira o código a seguir:

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;  // necessário para ter acesso as portas
namespace interfaceArduinoVS2013
{
    public partial class Form1 : Form
    {
    	public Form1()
    	{
        	InitializeComponent();
        	timerCOM.Enabled = true;
    	}
    	private void atualizaListaCOMs()
    	{
        	int i;
        	bool quantDiferente;	//flag para sinalizar que a quantidade de portas mudou
        	i = 0;
        	quantDiferente = false;
        	//se a quantidade de portas mudou
        	if (comboBox1.Items.Count == SerialPort.GetPortNames().Length)
        	{
            	foreach (string s in SerialPort.GetPortNames())
            	{
                	if (comboBox1.Items[i++].Equals(s) == false)
                	{
                    	quantDiferente = true;
                	}
            	}
        	}
        	else
        	{
            	quantDiferente = true;
        	}
        	//Se não foi detectado diferença
        	if (quantDiferente == false)
        	{
            	return;                     //retorna
        	}
        	//limpa comboBox
            comboBox1.Items.Clear();
        	//adiciona todas as COM diponíveis na lista
        	foreach (string s in SerialPort.GetPortNames())
        	{
            	comboBox1.Items.Add(s);
        	}
        	//seleciona a primeira posição da lista
        	comboBox1.SelectedIndex = 0;
    	}
    	private void timerCOM_Tick(object sender, EventArgs e)
    	{
        	atualizaListaCOMs();
    	}
    	private void btConectar_Click(object sender, EventArgs e)
 	   {
        	if (serialPort1.IsOpen == false)
        	{
            	try
            	{
                	serialPort1.PortName = comboBox1.Items[comboBox1.SelectedIndex].ToString();
                	serialPort1.Open();
            	}
            	catch
            	{
                	return;
            	}
            	if (serialPort1.IsOpen)
            	{
                	btConectar.Text = "Desconectar";
                	comboBox1.Enabled = false;
     	       }
        	}
        	else
        	{
            	try
            	{
                	serialPort1.Close();
                	comboBox1.Enabled = true;
                	btConectar.Text = "Conectar";
            	}
    	        catch
            	{
                	return;
            	}
        	}
    	}
	}
}

10testConectar

 

É necessário colocar uma proteção para que o programa não seja fechado e deixe a porta COM aberta, dessa forma impedindo que outros programas possam usá-la. Para isso vamos fechar a porta dentro do evento Form1_FormClosed:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;  // necessário para ter acesso as portas
namespace interfaceArduinoVS2013
{
    public partial class Form1 : Form
    {
    	public Form1()
    	{
        	InitializeComponent();
 	       timerCOM.Enabled = true;
    	}
    	private void atualizaListaCOMs()
    	{
        	int i;
        	bool quantDiferente;	//flag para sinalizar que a quantidade de portas mudou
        	i = 0;
        	quantDiferente = false;
        	//se a quantidade de portas mudou
        	if (comboBox1.Items.Count == SerialPort.GetPortNames().Length)
        	{
            	foreach (string s in SerialPort.GetPortNames())
            	{
                	if (comboBox1.Items[i++].Equals(s) == false)
                	{
                    	quantDiferente = true;
                	}
            	}
        	}
        	else
        	{
            	quantDiferente = true;
        	}
        	//Se não foi detectado diferença
        	if (quantDiferente == false)
        	{
            	return;                     //retorna
        	}
        	//limpa comboBox
        	comboBox1.Items.Clear();
        	//adiciona todas as COM diponíveis na lista
        	foreach (string s in SerialPort.GetPortNames())
        	{
            	comboBox1.Items.Add(s);
        	}
        	//seleciona a primeira posição da lista
        	comboBox1.SelectedIndex = 0;
    	}
 	   private void timerCOM_Tick(object sender, EventArgs e)
    	{
        	atualizaListaCOMs();
    	}
    	private void btConectar_Click(object sender, EventArgs e)
    	{
        	if (serialPort1.IsOpen == false)
        	{
     	       try
            	{
                	serialPort1.PortName = comboBox1.Items[comboBox1.SelectedIndex].ToString();
                	serialPort1.Open();
            	}
            	catch
            	{
                	return;
                }
            	if (serialPort1.IsOpen)
            	{
                	btConectar.Text = "Desconectar";
                	comboBox1.Enabled = false;
            	}
        	}
        	else
        	{
            	try
            	{
                	serialPort1.Close();
                	comboBox1.Enabled = true;
                	btConectar.Text = "Conectar";
            	}
            	catch
            	{
                	return;
         	   }
        	}
    	}
    	private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    	{
        	if(serialPort1.IsOpen == true)  // se porta aberta
         	serialPort1.Close();        	//fecha a porta
    	}
	}
}

O processo para conexão e fechamento da porta serial já está feito, e o próximo passo é fazer o programa enviar para o Arduino o que for digitado dentro do textBoxEnviar. Para isso, dentro do evento btEnviar_Click, deve-se inserir o seguinte código:

private void btEnviar_Click(object sender, EventArgs e)
{
if(serialPort1.IsOpen == true) //porta está aberta
serialPort1.Write(textBoxEnviar.Text); //envia o texto presente no textbox Enviar
}

A recepção de dados requer um pouco mais de atenção. Inicialmente deve-se criar um evento serialPort1_DataReceived e uma variável global do tipo String. O processo de recepção acontece em uma Thread diferente da atualização dos componentes. A atualização do textBoxRebecer deve ser feita fora do evento de recepção da serial. Para isso criamos uma função trataDadoRecebido. Confira como ficará o código completo da aplicação:

 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Forms;
 using System.IO.Ports; // necessário para ter acesso as portas
 namespace interfaceArduinoVS2013
 {
 public partial class Form1 : Form
 {
 string RxString;
 public Form1()
 {
 InitializeComponent();
 timerCOM.Enabled = true;
 }
 private void atualizaListaCOMs()
 {
 int i;
 bool quantDiferente; //flag para sinalizar que a quantidade de portas mudou
 i = 0;
 quantDiferente = false;
 //se a quantidade de portas mudou
 if (comboBox1.Items.Count == SerialPort.GetPortNames().Length)
 {
 foreach (string s in SerialPort.GetPortNames())
 {
 if (comboBox1.Items[i++].Equals(s) == false)
 {
 quantDiferente = true;
 }
 }
 }
 else
 {
 quantDiferente = true;
 }
 //Se não foi detectado diferença
 if (quantDiferente == false)
 {
 return; //retorna
 }
 //limpa comboBox
 comboBox1.Items.Clear();
 //adiciona todas as COM diponíveis na lista
 foreach (string s in SerialPort.GetPortNames())
 {
 comboBox1.Items.Add(s);
 }
 //seleciona a primeira posição da lista
 comboBox1.SelectedIndex = 0;
 }
 private void timerCOM_Tick(object sender, EventArgs e)
 {
 atualizaListaCOMs();
 }
 private void btConectar_Click(object sender, EventArgs e)
 {
 if (serialPort1.IsOpen == false)
 {
 try
 {
 serialPort1.PortName = comboBox1.Items[comboBox1.SelectedIndex].ToString();
 serialPort1.Open();
 }
 catch
 {
 return;
 }
 if (serialPort1.IsOpen)
 {
 btConectar.Text = "Desconectar";
 comboBox1.Enabled = false;
 }
 }
 else
 {
 try
 {
 serialPort1.Close();
 comboBox1.Enabled = true;
 btConectar.Text = "Conectar";
 }
 catch
 {
 return;
 }
 }
 }
 private void Form1_FormClosed(object sender, FormClosedEventArgs e)
 {
 if(serialPort1.IsOpen == true) // se porta aberta
 serialPort1.Close(); //fecha a porta
 }
 private void btEnviar_Click(object sender, EventArgs e)
 {
 if(serialPort1.IsOpen == true) //porta está aberta
 serialPort1.Write(textBoxEnviar.Text); //envia o texto presente no textbox Enviar
 }
 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
 {
 RxString = serialPort1.ReadExisting(); //le o dado disponível na serial
 this.Invoke(new EventHandler(trataDadoRecebido)); //chama outra thread para escrever o dado no text box
 }
 private void trataDadoRecebido(object sender, EventArgs e)
 {
 textBoxReceber.AppendText(RxString);
 }
 }
 }
 

Para testar a aplicação junto ao Arduino, vamos fazer o upload do seguinte sketch:

[language code=”cpp”]
void setup()
{
Serial.begin(9600); //inicia comunicação serial com 9600
}
void loop()
{
if(Serial.available()) //se algum dado disponível
{
char c = Serial.read(); //le o byte disponivel
Serial.write(c); //retorna o que foi lido
}
}
[/code]

Nesse programa o Arduino simplesmente retornará o dado que ele receber. Dessa forma, quando enviarmos dados pelo programa, estes serão exibidos no computador por meio do textBoxRecebe. A figura abaixo exibe os dados enviados e recebidos pela aplicação:

11testeComunicação

 

Agora que a aplicação está completa, ou seja, já conseguimos enviar e receber dados, vamos a um exemplo funcional. Conforme foi exibido no Artigo sobre comunicação serial no Arduino, vamos aproveitar o exemplo que acenderá o led através do comando vindo pela serial. Carregue o seguinte exemplo no Arduino:

[language code="cpp"]
 /*
 * comandos via serial
 * inverte o estado do led conctado a saída 13 do arduino quando recebe o caracter 'A' pela serial
 */
 const int LED = 13;
 void setup() {
 Serial.begin(9600); //configura comunicação serial com 9600 bps
 pinMode(LED,OUTPUT); //configura pino do led como saída
 }
 void loop() {
 if (Serial.available()) //se byte pronto para leitura
 {
 switch(Serial.read()) //verifica qual caracter recebido
 {
 case 'A': //caso 'A'
 digitalWrite(LED,!digitalRead(LED)); //inverte estado do LED
 break;
 }
 }
 }
 [/code]

Execute a aplicação, conectando a porta na qual o Arduino está ligado e envie o caractere ‘A’. Verifique o resultado no LED conectado ao pino 13 da placa Arduino:

 

12testeArduino-660x315

O download da aplicação completa pode ser feito através do link: Aplicação C# para interface serial com Arduino.

 

 Conclusão

 

A ferramenta Visual Studio da Microsoft permite criar facilmente uma interface de comunicação Serial entre o Arduino e um computador (com sistema operacional Windows). A partir do exemplo apresentado, pode-se fazer aplicações para enviar e receber comandos para o Arduino ou outro dispositivo conectado a uma porta serial. No próximo artigo vamos fazer uma aplicação envolvendo botões e outros componentes que deixarão nossa interface mais elegante.

 

 

Para aprender mais

 

Comunicação Serial Java + Arduino

 

 

Referências

 

Comunicação serial Arduino
SerialPort Class
Classe SerialPort

 

Esse artigo foi publicado originalmente no site Embarcados e é de autoria de Fábio Souza.

Licença Creative Commons
Comunicação Serial com C# e Arduino – Parte 1 por Fábio Souza. Esta obra está sob a licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Anúncios
Marcado com: , ,
Publicado em ARDUINO
%d blogueiros gostam disto: