Sie sind auf Seite 1von 31

um anseio entre os iniciantes na rea de programao em C, a criao de programas que tenham uma interface amigvel, com botes, menus

entre outros componentes do windows. Aqueles que ja usam o arduino ou outros microcontroladores j deve ter sentido falta de um aplicativo com essas caractersticas e que se comunicasse com o PC via serial. Nesse tutorial, como o primeiro e em um nvel bem bsico, ser mostrado passo a passo como criar uma GUI que ser capaz de enviar dados para o arduino, afim de comandar um LED(ligar e desligar), e um servomotor. O aplicativo ser feito no ambiente do Visual Studio, na linguagem C#. recomendvel o Visual C# no minimo 2010, pois contem um componente novo, que um tanto interessante para quem deseja plotar de grficos, o Chart. Nesse e nos prximos tutoriais,no ser exigido grande conhecimento da linguagem C#, basta que se tenha um pouco de conhecimento em C++, e caso, seja necessrio o uso de alguma estrutura exclusiva do C#, ser feita uma breve explicao. 1 PASSO: Criar um novo projeto em linguagem C#

Aps criar um novo projeto,devero aparecer 3 elementos muito importantes da IDE: A aba Design onde podemos editar a aparncia do programa (formulrio). A caixa de ferramentas (ou Toolbox) que contem os elementos que faro parte do nosso programa.Ex:Botes, caixas de rolagem, menus, etc.

A aba de propriedades (ou properties) onde podemos predefinir alguns comportamentos que os elementos (controles) tero.Ex:O texto que o formulrio exibe, se um boto apresenta alguma imagem, etc.

2 PASSO: Design. Iremos usar osa seguintes componentes:


button hScrollBar SerialPort

Aps achar cada um na caixa de ferramentas, arrastem-os e soltem no formulrio. Redimensionem-os de modo que fique parecido com a figura abaixo. Naturalmente, para cada controle adicionado atribuido a ele um nome que corresponde a classe a que pertencem e a um numero.Ex: button1, button2,hScrollBar1,etc. Neste tutorial, mudei somente o nome do objeto SerialPort1 para Porta, na aba de propriedades.

Mudem tambm outras propriedades selecionando cada controle no formulrio : Porta:


PortName - "COMX"(X = N da porta em que o microcontrolador estar conectado) BaudRate - baud rate que est configurado no seu microcontrolador

button1:

Text - Off

hScrollBar1:

Maximum - 189 (180 +LargeChange-1)

Form1(formulrio):

FormBorderStyle - FixedSingle (no permite redimencionamento do formulario) MaximizeBox - false (desabilita boto de maximizar)

Alguns objetos tem propriedades em comum, por exemplo experimente trocar a forma que o cursor mostrado quando esta sobre o button1 e o hScrollBar1, selecione os dois ao mesmo tempo e mude a propriedade Cursor para hand. Aps isso apertem F5 para rodar o programa.Ele deve estar com essa cara.

3 PASSO: Programao.

A programao para GUI geralmente orientada a eventos o que significa que o controle de fluxo guiado por indicaes externas.Ex:Clique do mouse, tecla digitada, um novo dado no buffer serial,etc. O evento principal de cada controle, acessado quando se da um duplo clique no elemento.Porm existem vrios outros eventos que podem ser vinculados a cada controle, por exemplo nesse projeto usaremos dois eventos do formulrio que so: load() e o FormClosing().

Nesse projeto sero usadas trs funes do objeto Porta:


Open(); Write(); Close();

O Porta.Open() foi colocado dentro do evento de load() do formulrio, o que significa que ser estabelecida conexo serial a partir do momento que o programa for carregado. O Porta.Close() foi colocado dentro do evento FormClosing(), para que seja encerrada a conexo com a porta serial assim que o programa for fechado. O Porta.Write() ser usado no boto e na barra de rolagem, para enviar pro microcontrolador a informao que queremos. Ao dar um duplo clique no formulrio dever aparecer uma nova aba contendo alguns cdigos, e o evento de Load.

No Load deve ficar assim: private void Form1_Load(object sender, EventArgs e) { try { Porta.Open(); } catch { MessageBox.Show("Cabo desconectado","Erro"); Close();

} } O try e catch so usados para tratar excees(erros), que iro aparecer se o programa for executado sem que a porta exista, no caso do arduino a porta s existir se o cabo estiver conectado. Funciona da seguinte forma,"tente executar o que estiver dentro do try, caso ocorra a exceo faa o que estiver no catch" Nesse programa, "caso no consiga abrir comunicao com a serial, exiba uma mensagem ( MessageBox.Show("Cabo desconectado","Erro") ) e feche o programa usando o mtodo Close().(No confundir com Porta.Close()).

Indo no evento de FormClosing() devemos encerrar a comunicao: private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { if (Porta.IsOpen) { Porta.Close(); } } catch {} } A estrutura try e catch se faz presente novamente,sempre que houver risco de desconexo do cabo de comunicao ela deve ser usada, sob pena de ver seu programa travar. Nesse evento uma condio simples, caso a porta esteja "aberta", feche-a. Dando um duplo clique no boto abrir o evento de click private void button1_Click(object sender, EventArgs e) { try { if (button1.Text == "Off")

{ Porta.Write(comando, 1, 1); button1.Text = "On"; } else { Porta.Write(comando, 2, 1); button1.Text = "Off"; } } catch { MessageBox.Show("Cabo desconectado", "Erro"); Close(); } } Antes de falar deste cdigo, um array do tipo byte deve ser declarado pois a funo Porta.Write() exige um array de bytes ou chars, ento faam a declarao da seguinte forma: byte[] comando = { 0, 200, 201 }; antes de public Form1() O primeiro elemento iremos usar para comandar o angulo do servo, de 0 a 180. Como o byte chega at 255, temos um intervalo entre 181 e 255 livre para arbitrarmos um byte para acender e outro para apagar o led. No caso arbitrei 200 para acender e 2001 para apagar; Neste evento iremos acender ou apagar o led," Se o boto estiver como titulo Off mande pela serial o 200" pois o elemento 1 do array comando. o terceiro parmetro da Porta.Write(comando, 1, 1) se refere a quantos bytes sero enviados, no caso s um o 200.Com isso o led dever acender. Depois de enviar reescreva o Texto do boto para "On"; Quando o usurio clicar de novo ser enviado um byte equivalente a 201 desligando o led e reescrevendo o texto do boto para "Off". No evento Scroll da barra de rolagem ocorre o seguinte: private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) { try { comando[0] = (byte)hScrollBar1.Value; Porta.Write(comando, 0, 1); Text = Convert.ToString(hScrollBar1.Value);

} catch { MessageBox.Show("Cabo desconectado", "Erro"); Close(); } } A linha comando[0] = (byte)hScrollBar1.Value atribui ao primeiro elemento do array o valor atual da barra de rolagem.Logo em seguida o valor transmitido para o microcontrolador que controla o servomotor. Depois esse valor convertido para string e passado para o titulo do formulrio para poder ser visualizado pelo usurio. O programa do arduino este: #include <Servo.h> Servo servo1; void setup(){ Serial.begin(9600); pinMode(13,OUTPUT); servo1.attach(9); } void loop(){ if(Serial.available()){ byte dado = Serial.read();

if(dado <= 180) servo1.write(dado);

if(dado == 200) digitalWrite(13, HIGH);

if(dado == 201) digitalWrite(13, LOW); } } E o do aplicativo deve ficar assim: 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;

namespace Exemplo1 { public partial class Form1 : Form { byte[] comando = { 0, 200, 201 };

public Form1() { InitializeComponent(); }

private void Form1_Load(object sender, EventArgs e) { try { Porta.Open(); } catch { MessageBox.Show("Cabo desconectado","Erro"); Close(); } }

private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { if (Porta.IsOpen) { Porta.Close(); } } catch {}

private void button1_Click(object sender, EventArgs e) { try { if (button1.Text == "Off") { Porta.Write(comando, 1, 1); button1.Text = "On"; } else { Porta.Write(comando, 2, 1); button1.Text = "Off"; } } catch { MessageBox.Show("Cabo desconectado", "Erro"); Close(); } }

private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)

{ try { comando[0] = (byte)hScrollBar1.Value; Porta.Write(comando, 0, 1); Text = Convert.ToString(hScrollBar1.Value); } catch { MessageBox.Show("Cabo desconectado", "Erro"); Close(); } } } } Com isso conclumos o nosso aplicativo, adaptem a suas necessidades.At a prxima. uma pergunta recorrente aqui no frum, principalmente entre os que esto fazendo teste com carrinhos, ou robs que precisem de orientao sobre o uso das teclas direcionais do PC. Neste tutorial, estarei mostrando como identificar que essas teclas foram pressionadas. Mostrarei dois exemplos, o primeiro mais rpido e fcil de criar, e o outro mais geral. Primeiro exemplo: Formulrio limpo, sem outros componentes grficos.

Nesse exemplo o nico componente extra ser o SerialPort usado pra a comunicao com o arduino. Ajustem nas propriedades o nome da porta e o baudRate.No meu caso alterei tambm o nome de serialPort1 para Porta.

Na lista de eventos do formulrio existe um evento chamado KeyDown que chamado quando uma tecla pressionada quando o formulrio tem foco.

Clicando nele, ser aberto o cdigo para a edio desse evento, modifique de forma que fique semelhante a isso: private void Form1_KeyDown(object sender, KeyEventArgs e) { try { Porta.Open(); if (e.KeyCode == Keys.Up) { Porta.Write("U"); Text = "Up"; }

if (e.KeyCode == Keys.Down) { Porta.Write("D"); Text = "Down"; } if (e.KeyCode == Keys.Right) { Porta.Write("R"); Text = "Right"; } if (e.KeyCode == Keys.Left) { Porta.Write("L"); Text = "Left"; } Porta.Close(); } catch { Text = "Erro na comunicao"; } } Pegarei para a explicao o seguinte trecho: if (e.KeyCode == Keys.Right) {

Porta.Write("R"); Text = "Right"; } Trata-se de uma condio simples, o objeto e guarda entre outras coisas, a informao de qual tecla disparou esse evento. O que foi feito foi comparar essa informao com Keys.Right que representa o cdigo da tecla direcional, nesse casodireita.Como resultado afirmativo podemos enviar o dado que nos for conveniente para o arduino, nesse caso enviei a um carter R e escrevi no titulo do formulrio Right. Porta.Open(),Porta.Close() e try ... catch j expliquei no tutorial passado. Todo o programa se resume a isso. Segundo exemplo: Formulrio que contenha outros componentes grficos. Para casos onde temos que usar outros elementos grficos como botes, listas, caixas de texto no formulrio, o mtodo passado no funciona porque ele no receber mais foco, e sim algum outro elemento j listado. Nesses casos devemos escolher quais dos elementos vo gerar o evento de KeyDown para usarmos o mtodo passado. Nesse segundo exemplo adicionei 4 botes.

Pra quem j escreveu o Form1_KeyDown primeiro exemplo, podemos usa-lo como KeyDown de todos os quatro botes, aqui renomeei para teclado. Selecionando todos e relacionando aos eventos de KeyDown a mesma rotina teclado.

At aqui alguem poderia pensar que no h diferena entre o primeiro exemplo e o segundo. De fato se fossemos usar outras teclas, por exemplo, as de caracteres, isso resolveria nosso caso. Porm queremos usar as direcionais que por padro so usadas para navegar(mudar o foco) entre os controles, nesse caso botes. Sendo assim a priori no geram um evento de KeyDown. Isso pode ser revertido usando um evento que ocorre antes do KeyDown, o PreviewKeyDown. Acesse esse evento de qualquer controle edite o cdigo da seguinte forma: private void detectar_tecla(object sender, PreviewKeyDownEventArgs e) { if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right) { e.IsInputKey = true;

} } Aqui troquei o nome da funo para detectar_tecla mas no obrigatrio.O mais importante entender como ela funciona. Foi feita uma comparao para saber se a tecla que gerou o evento foi uma direcional, se sim, atribua a e.IsInputKey o valortrue. O que significa que ser gerado tambm um evento KeyDown logo em seguida. Repito, para teclas, como as alfabticas, no se precisa usar o evento PreviewKeyDown que por padro iram gerare.IsInputKey = true para essas teclas, desencadeando assim o KeyDown automaticamente . Depois atribua detectar_tecla aos PreviewKeyDown de todos os botes.

No evento do Click de cada boto coloquem o comando correspondente a sua tecla direcional, por exemplo: private void button3_Click(object sender, EventArgs e) {

try { Porta.Open(); Porta.Write("R"); Text = "Right"; Porta.Close(); } catch { Text = "Erro na comunicao"; } } Assim o evento do Click faz a mesma coisa do teclado, com uma pequena diferena. Caso o usurio mantenha o teclado pressionado o programa enviar informaes de forma continua da mesma forma de quando se mantm pressionada uma tecla alfanumrica num editor de texto. Assim conseguimos enviar informaes para o arduino, atravs de comando por teclado, adaptem a suas necessidades. At a prxima. Cdigo completo: http://pastebin.com/HNSwwJ9v At agora sempre definamos as propriedades como o nome da porta e BaudRate no projeto, para fins de testes funciona muito bem. Porm isso deixa o programa pouco flexvel, j que a porta que o arduino usa pode mudar dependendo do PC onde ele tem os drives instalados. Neste tutorial estarei mostrando como dar o poder de escolha ao usurio de qual porta e baud rate usar, e para aqueles que usam o arduino, a escolha de habilitar ou no o reset via serial. Vou aproveitar e mostrar tambm o como salvar essas configuraes para que da prxima vez que o programa for executado ele lembre delas e as tome como default. Primeiro passo: adicionar novos itens.

Depois de criar um novo projeto, no menu projeto escolham a opo adicionar novo item. Adicionem um novo formulrio e coloquem o nome de Config_Serial.cs.

Depois adicionem um novo arquivo de configuraes e coloquem o nome de Minhas_Config.settings.

Antes trabalhar com esses novos itens adicionem os seguintes controles no formulario1(Form1).

MenuStrip SerialPort

Alterei o nome de serialPort1 para Porta fiz algumas modificaes no menuStrip1 para que ficasse assim:

Apenas troquei a cor atribuindo a propriedade BackColor a cor GradientActiveCaption e criei um menu conexo adicionei as opes configurar e conectar. Dando um duplo click na opo configurar teremos acesso ao evento dele (Click). Qual a ideia? Configurar qual porta e velocidade que vamos usar. S que eu preferi fazer esse procedimento em outro formulrio pra ganhar mais espao no principal, pra isso adicionamos o formulrio Config_Serial anteriormente. Bom, precisamos chamar esse novo formulrio atravs do menu ento faam o seguinte: private void configurarToolStripMenuItem_Click(object sender, EventArgs e) { Config_Serial janela2 = new Config_Serial(this); janela2.ShowDialog(); } Primeiro declaramos uma nova varivel do tipo Config_serial, que corresponde ao nome que demos ao segundo formulrio, com o nome janela2 e inicializamos ela passando este formulrio (Form1) como parmetro usando a palavra this. Na segunda linha o segundo formulrio exibido.

importante entender porque passar o Form1 como parmetro neste caso. O que acontece que as variveis de um formulrio no podem ser acessadas por outro. Como vamos mudar duas propriedade do objeto Porta atravs do segundo formulrio, e aquele esta no primeiro, com esse procedimento poderemos mudar campos desse objeto. Tambm importante que mudem a propriedade Modifiers do objeto Porta para public. No evento da opo Conectar do menu deixem parecido com isso: private void conectarToolStripMenuItem_Click(object sender, EventArgs e) { try { if (Porta.IsOpen) { Porta.Close(); conectarToolStripMenuItem.Text = "Conectar"; } else { Porta.Open(); if (reset) { Porta.DtrEnable = true; Porta.DtrEnable = false; } conectarToolStripMenuItem.Text = "Desconectar"; } } catch(SystemException erro)

{ MessageBox.Show(erro.Message, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); } } Traduzindo: se estiver conectado, desconecta e muda o texto do menu para Conectar.

Seno, conecta e muda o texto para Desconectar.

Se ocorrer algum erro mostra a descrio dele, que esta na varivel erro.Message num formulrio com titulo Erro, que tem um boto de ok e um cone de Error.

Assim podemos iniciar e encerrar a comunicao quando quisermos. Abro um parntese para falar do seguinte trecho: if (reset) { Porta.DtrEnable = true; Porta.DtrEnable = false; } Aqueles que usam o arduino, devem ter notado que o Serial Monitor capaz de resetar o arduino assim que aberto. Esse trecho de cdigo faz o mesmo, se a varivel reset for verdadeira, reseta o arduino ao conectar. Declarem essa varivel com antecedncia: public bool reset = false; public porque vamos acessa-la tambm no segundo formulrio. 2 passo: segundo formulrio. Agora que terminamos o formulrio principal, vamos trabalhar o segundo que onde faremos as configuraes da porta. Adicionem os seguintes controles:

1 x GroupBox 2 x Label: 2 x ComboBox

1 x Checkbox 1 x Timer

Redimencionem e mudem os text para que fique dessa forma:

Mudem a propriedade FormBorderStyle para FixedToolWindow para impedir redimensionamento em tempo de execuo, e retirar os botes de maximizar e minimizar. Mudem tambm para DropDownList da propriedade DropDownStyle dos dois comboBoxs No timer1 deixem um intervalo de 1000 ms e deixem a propriedade enabled em true.

Esse timer vai servir para atualizar a lista de portas que o PC tem disponvel. No comboBox correspondente ao baud rate, no meu caso o comboBox2, coloquei as velocidades padro do Serial Monitor na propriedade items.

Vejam o resultado:

No meu caso s existe a porta que o arduino est, mas quem tiver mais de uma, pode acontecer de os nomes ficarem fora de ordem. Pra corrigir isso basta mudar a propriedade Sorted do comboBox1 para true. No evento checkBox1_CheckedChanged faam o mesmo que o exemplo abaixo: private void checkBox1_CheckedChanged(object sender, EventArgs e)

{ if(checkBox1.Checked) { reset2 = true; } else { reset2 = false; } } Declarem antes a varivel bool reset2 = false; que ser responsvel por habilitar o reset do arduino ao conectar. Ok, o usurio escolheu, qual porta, qual velocidade, e at se deseja resetar o arduino. Agora resta passar essa informaes pro Form1 j que nele que est o objeto Porta. Arbitrei que iria passar esses dados quando fechasse as configuraes. Lembrem se que passamos o Form1 como parmetro quando fizemos: Config_Serial janela2 = new Config_Serial(this); Para usar esse parmetro faremos o seguinte: Form1 janela1; public Config_Serial(Form1 x) { InitializeComponent(); janela1 = x; } Logo que clicamos em algum controle para ter acesso a algum evento esse mtodo j automaticamente gerada para ns logo no comeo do cdigo. Ele originalmente vem sem parmetros. O que fizemos aqui foi declarar um objeto do tipo Form1com nome janela1 e recebeu o valor x passado pelo formulrio anterior.

Agora podemos passar as informaes para o formulrio anterior fazendo: private void Config_Serial_FormClosing(object sender, FormClosingEventArgs e) { try { janela1.Porta.BaudRate = Convert.ToInt32(comboBox2.Text); janela1.Porta.PortName = comboBox1.Text; janela1.Text = comboBox1.Text + "/" + comboBox2.Text; janela1.reset = reset2; } catch { DialogResult escolha; escolha = MessageBox.Show("Prencha todos os campos ", "Aviso", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation); if (escolha == System.Windows.Forms.DialogResult.OK) { e.Cancel = true; } } } O cdigo acima tenta passar os dados para o formulrio anterior, caso no consiga ele mostra uma mensagem que diz:"Prencha todos os campos corretamente", e d duas opes ao usurio:

Ok: significa que o evento ser cancelado(e.cancel) para correo das informaes Cancel: segue com o evento, fechando o formulrio.

Passo 3: Configurar definies. Ufa! Conseguimos, por aqui termina para quem se deu por satisfeito com o resultado. Porm um pouco chato sempre ter que ir no menu e escolher de novo os parmetros para configurar a conexo serial, seria interessante que o programa guardasse essas informaes mesmo depois de fechado. Pra isso mesmo que adicionamos no comeo do projeto um arquivo de configuraes com o nome de minhas_config.settings. Ele vai guardar dados que sero usados na prxima vez que o programa for executado. Ele deve ter essa cara:

Aqui j defini alguns valores que eu desejo guardar. A ideia atualizar e salvar esses valores quando fecharmos o programa, logo devemos usar o evento FormClosing do Form1 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { Minhas_Config.Default.Nome_Porta = Porta.PortName; Minhas_Config.Default.Baud_Rate = Porta.BaudRate; Minhas_Config.Default.restart_arduino = reset; Minhas_Config.Default.Save(); } catch { MessageBox.Show("Erro ao salvar!Reconfigure quando executar novamente","Erro"); }

} E quando abrirmos novamente o programa, carregar os valores guardados: private void Form1_Load(object sender, EventArgs e) { try { Porta.PortName = Minhas_Config.Default.Nome_Porta; Porta.BaudRate = Minhas_Config.Default.Baud_Rate; reset = Minhas_Config.Default.restart_arduino; Text = Porta.PortName + "/" + Convert.ToString(Porta.BaudRate); //Mostra no titulo do formulrio o nome da porta seguido de "/" e o baudrate. } catch { MessageBox.Show("Erro ao salvar!Reconfigure quando executar novamente", "Erro"); } } Pronto, com isso termino mais um tutorial, adaptem a suas necessidades. At a prxima.