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ção Express 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 menu FILE > 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:
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:
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;
}
}
}
}
}
É 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: [wpdm_asset id=’3′].
Lembre-se, você deve ser registrado e estar logado no site para fazer o download.
Conclusão sobre a Comunicação Serial C# e Arduino
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
Referências





Olá Fabio!
Parabéns pelo conteúdo, sou adm de redes e estou entrando mundo IOT. Gostaria de saber se é possível a operação inversa, ou seja, mandar comandos do Arduino UNO para o pc, por exemplo desligar, reiniciar ou ainda executar uma bat.
Abraço
Olá, Fabio Fiz os procedimentos porém o dado enviado ao arduino não está retornando no forms. Segue os codigos do arduino e do forms. codigo do forms: 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 TMTHdeskApp { public partial class testeComArduino : Form { string RxString; public testeComArduino() { 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… Leia mais »
Olá,
Estou o mesmo problema, você conseguiu solucionar o seu?
Att,
Pamela
Pessoal, sugiro que criem os componentes de serial e timer, pois apenas clonando o códigos eles não funcionam.
Olá Fábio, boa noite.
Tenho lido seus artigos, e tem me ajudado muito. Estou com um problema entre uma aplicação C# e o o arduino, onde estou monitorando quatro temperaturas. O fato é que, ao passar alguns minutos monitorando, a aplicação começa a apresentar uma atraso do sinal, e este vai aumentado cada vez mais. Já tentei de tudo para sincronizar e nada. Será que vc tem uma ideia do pode estar causando isso?
Alexandre, como seria esse atraso? Atraso na exibição? Se possível, compartilha o código para avaliarmos.
Olá Fábio, desculpe a demora em explicar. Na aplicação estou fazendo a leitura assim……. private void timer1_Tick(object sender, EventArgs e) { t += 1; if (serialPort1.IsOpen) { try { serialPort1.Write(“VA0”); string A0 = serialPort1.ReadLine(); labelTensao.Text = A0; V = Convert.ToDouble(A0); chartV.Series[0].Points.AddXY(t, V); serialPort1.Write(“CA1”); string A1 = serialPort1.ReadLine(); labelCorrente.Text = A1; I = Convert.ToDouble(A1) / 10; chartA.Series[0].Points.AddXY(t, I); serialPort1.Write(“TAmbA2”); string A2 = serialPort1.ReadLine(); labelTAmb.Text = A2; TAmb = Convert.ToDouble(A2) / 10; chartT.Series[0].Points.AddXY(t, TAmb); serialPort1.Write(“TMTA3”); string A3 = serialPort1.ReadLine(); labelTMotor.Text = A3; TMT = Convert.ToDouble(A3) / 10; chartT.Series[1].Points.AddXY(t, TMT); serialPort1.Write(“TLCA4”); string A4 = serialPort1.ReadLine(); labelTLC.Text = A4; TLC = Convert.ToDouble(A4) / 10;… Leia mais »
Opa tudo bem?
Consegui rodar todo o programa, porém ele não mostra as COM disponíveis.
O que poderia ser?
Roberto, Talvez você tenha que criar o componente da comunicação e o evento novamente para indexar.
Muito Bom,
Obrigado pelas dicas, tive que fazer algumas alterações mas tudo 100% agora.
Olá Bruno, se precisar de ajuda ou quiser contribuir com as alteração feitas, é só falar.
Sucesso!
Olá, a função private void AtualizaListaCOMs() não está sendo chamada no meu código. Coloquei uma MessageBox para teste e ela não aparece. Como posso fazer para o código chamar as funções?
Você precisa verificar o timer
Olá, só tenho Mac… e não rola instalar Visual Studio. Tem alguma outra IDE com ambiente parecido, mas no Mac?
Ola Fabio, estou com uma dificuldade aqui, meu arduino manda uma série variável de prints. A ideia é receber essa informação no programa e baseado no primeiro código dar split e tratar em um switch. Porem as vezes o programa recebe os dados pela metade, no exemplo
Serial.print(“130”);
Serial.print(“,”);
Serial.print(hour);
Serial.print(“,”);
Serial.print(minute);
Serial.print(“,11111”);
As vezes ele recebe a string toda, eventualmente ele quebra a string em duas. Como eu posso fazer pra isso nao ocorrer?? De preferencia sem alterar o programa do arduino.
Atualizando minha propria duvida, eu acabei mudando no arduino para println no ultimo print e mudei no programa para o comando readline 😉 resolvido
Olá Roberto, desculpe pela demora na resposta. Mas é isso mesmo, colocando o readline você consegue ler a string inteira de uam vez. Abraços
Fábio eu só tenho a agradecer a rápida resposta. se puder me ajudar de novo, quando resolvi o problema mudando pra println arrumei outra dor de cabeça. eu fiz um programa no app inventor 2 pra se comunicar com o mesmo dispositivo. ocorre que ele não tem uma opção pra ler linhas inteiras por bluetooth. pode me ajudar nisso? ??
Eu não conheço os detalhes do app inventor. Teria que ver como ele recebe os dados e se é possível descartar esses caracteres. Veja como chegam os dados no android. Outra opção seria identificar de alguma forma de está comunicando com o pc ou com o android, aí a formatar a string para cada caso. abraços
Oi,
Tem alguma noção de porque quando coloco pra rodar em .Net 2.0 o programa roda mas não lê
desconfiava do lixo que aparece junto do “COM1”, até coloquei pra pegar o nome de uma textbox, mas tem alguma coisa com as Threads q n to descobrindo
Obrigado
Guilherme, tem que testar o funcionamento do componentes nessa versão. Pode ser feita uma aplicação simples e testar o envio e recebimento. Vou testar e ver ser percebo alguma diferença.
Abraços
Olá Fabio!!
Segui os passos porém estou usando o visual c# 2010 e o mesmo não reconhece serialPort1 alguma dica do que pode ser?
Abraços
Olá Diogo, verifique se o componente foi criado corretamente e se foi incluida a linha using System.IO.Ports;
Abraços
Fabio estou conseguindo enviar comando do C# para o pic,porêm não estou conseguindo receber comando do pic para o C# para acender uma figura de led na tela do C#.Obs. a programação do pic esta correta. segue o código em C# : 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; using System.IO; namespace LER_ENTRADA_SERIAL { public partial class Form1 : Form { string Entrada; SerialPort recepcao = new SerialPort(); public Form1() { InitializeComponent(); recepcao.PortName = “COM3”; recepcao.BaudRate = 9600; } private void BT_LIGA_Click(object sender, EventArgs e) { recepcao.Open();… Leia mais »
Lin, verifique através do debug se a informação está chegando no PC. Caso contrario, recomendo que crie novamente o componente Serial.
Opa, resolveu!
kkkk
obrigado e parabéns pelo Post
abraços