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.
Primeiro passo é iniciar um novo projeto Windows Forms Application em C#:
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:
Inserir um comboBox logo a frente do botão btConectar, inserido anteriormente:
Inserir outro botão, logo abaixo do btConectar, e mudar a sua propriedade Text para “Enviar” e Name para btEnviar:
Agora vamos inserir um textBox, que receberá os dados a serem enviados. Após ser inserido, mudar a sua propriedade Name para “textBoxEnviar”:
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:
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:
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:
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:
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:
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:
É 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:
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
}
}
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:
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:
/*
* 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;
}
}
}
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:
O download da aplicação completa pode ser feito através do link: Aplicação C# para interface serial com Arduino.
Lembre-se, você deve ser registrado e estar logado no site para fazer o download.