Introdução
No post anterior Comunicação Serial com Arduino e Qt5 Widgets Application – Parte 2, finalizamos a nossa aplicação com Qt5 para comunicação serial com qualquer dispositivo embarcado, que para facilitar o post utilizamos um Arduino UNO.
Agora, avançando um pouco mais, vamos ver como criar ações, usando Qt5 para comunicação serial, para serem enviadas e/ou recebidas via serial e ocorrer eventos como acender/apagar um led, obter informações do microcontrolador e diversas possibilidades que você possa imaginar.
Novamente usarei um Arduino UNO para facilitar a prototipação e focar na aplicação alto nível, mas para o framework estamos usando Qt5 para comunicação serial. Então poderá aplicar o que faremos aqui em um 8051 montado na protoboard como em um ARM Cortex-M, desde que utilize comunicação serial.
Configuração do Host
Neste artigo utilizarei a distribuição Linux Mint 17 Qiana 64 bits, mas não há problemas em realizar o procedimento em um Ubuntu ou outra distribuição suportada pelo Qt5. A seguir são listados os seus pré-requisitos:
-
Qt5 Instalado com o Qt Creator;
-
IDE Arduino >=1.6.2;
-
Arduino UNO;
-
Plugin Qt Serial.
Criando o projeto
Devemos criar o nosso projeto. Para isso abra o Qt5 e em seguida vá em File → New File or Project…, na janela a seguir selecione Application e Qt Widgets Application e Choose…. No nome do projeto digite AppGuiArduino, na opção de Kit Selection deixe o padrão selecionado e clique em Next. Na próxima janela Class Information selecione QWidget em Base class e Next, na janela seguinte sobre opções de versionamento pode clicar em Finish.
Ficou confuso? Veja o artigo anterior Comunicação Serial com Arduino e Qt5 Widgets Application – Parte 2 que mostrei como fazer essa parte passo a passo com imagens.
É muito importante ver o artigo anterior porque neste projeto iremos novamente criar a classe comserial e anexar o mesmo código que já utilizamos, pois assim focamos em desenvolver nossa aplicação e a parte de tratar a comunicação serial utilizamos a que já foi feita anteriormente.
Desenhando a tela
Agora a parte divertida do artigo, onde o trabalho é achar os componentes em Widgets e montar a tela usando o Qt Design. No final do artigo anterior eu comentei como seria a aplicação deste post, houve algumas mudanças para trazer mais informações e ser mais didática. Segue na Figura 1 como ficará nossa aplicação com o posicionamento dos Widgets.
Agora vem uma etapa que pode agregar muito a aplicação e que muitos desenvolvedores não dão tanta importância. Muitas vezes a preocupação é o background ou o core da aplicação, segurança, estabilidade e outros fatores que ficam no nível mais abaixo, mas o cliente muitas vezes preza por uma aparência agradável ou um melhor layout, de fácil utilização.
E é onde focaremos agora, na “perfumaria” de nossa aplicação, adicionando ícones, colocando atalhos e informações para ajudar o operador.
Os ícones utilizados nesta aplicação foram todos obtidos no site IconsPedia, muitos possuem licenças free mas olhe as informações dos pacotes caso a ideia seja comercializar a aplicação.
No diretório do projeto criei um diretório chamado icons onde estão todos os ícones utilizados, e agora veremos como utilizar estes em nossa aplicação.
Vamos criar uma estrutura em nossa aplicação para anexar estes ícones, para isso deve-se clicar com o botão direito em cima do nome do projeto e clicar em Add New…, em seguida selecionar Qt e logo a frente Qt Resource File, como na Figura 2.
Prosseguindo agora no diretório Resources que surgir em nosso projeto, expanda-o, clique com o botão direito em cima de icons.qrc, selecione Add Existing Files… e selecione todos os ícones no diretório icons em nosso projeto. E o projeto ficará como a Figura 3.
Voltando a tela da aplicação, vamos selecionar o botão Sair e ir até o Property Editor do lado direito do QtCreator. Procure por QAbstractButton e clique na caixa de seleção na opção icon como na Figura 4.
Selecione Choose Resource…, na janela que abrir deve-se selecionar icon no lado esquerdo e no lado direito o ícone que deseja usar no botão, como podemos ver na Figura 5.
Voltando ao QAbstractButton expanda a opção iconSize e mude de 16 x 16 para 32 x 32, na sequência vamos criar um atalho para o botão Sair. Para isso clique em shortcut e pressione a tecla que será o atalho, no caso da aplicação eu pressionei F4 e veja como ficarão as propriedades do nosso botão na Figura 6.
Uma opção legal a ser utilizada é adicionar uma mensagem ao posicionar o mouse sobre o botão, por exemplo se o operador parar o cursor em cima de Sair. E se você desejar que apareça a mensagem ‘Sair da aplicação’, procure a propriedade toolTip e insira o texto, como na Figura 7.
Pronto, customizamos o widget PushButton do botão Sair, agora pode aplicar o mesmo procedimento nos demais botões, e por fim nossa aplicação ficará como a Figura 8.
Código Qt5 para comunicação serial
Chegando nesta etapa sua tela deverá estar pronta, já deverá ter criado a classe comserial com comserial.h e comserial.cpp idêntico ao artigo anterior, e agora iremos apenas criar as ações dos botões para tratar o que será enviado e recebido pela serial.
Recordando que para dar ação a um botão a maneira mais simples sem ter que lidar com Sinais e Slots neste momento é clicar com o botão direito do mouse no botão e selecionar Go to Slot… e em seguida selecionar clicked() e OK.
Segue o código utilizado em widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "comserial.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnAppClose_clicked();
void WriteData(const QByteArray data);
QString ReadData();
void on_btnSetLed_clicked();
void on_btnDevOpen_clicked();
void on_btnDevClose_clicked();
void on_btnGetVersion_clicked();
void on_btnGetinfoHW_clicked();
void on_btnHelp_clicked();
void on_btnAbout_clicked();
void on_btnBWTerminal_clicked();
void on_editSendCmd_returnPressed();
private:
Ui::Widget *ui;
bool PaletaLogBW;
QSerialPort *devserial;
comserial *procSerial;
void CarregarInfoDispSerial(void);
};
#endif // WIDGET_H
E agora o widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
/* Criando objeto da classe QSerialPort*/
devserial = new QSerialPort(this);
/* Criando objeto da classe comserial para manipular leitura/escrita */
procSerial = new comserial(devserial);
/* Carrega os dispositivos seriais disponiveis */
QStringList DispSeriais = procSerial->CarregarDispositivos();
/* Inseri os Dispositivos no ComboBox */
ui->cbDevList->addItems(DispSeriais);
/* Habilita PushButton "Conectar" se encontrou dispositivo seiral.
* Caso contrario, um erro é reportado no Log
*/
if(DispSeriais.length() > 0) {
ui->btnDevOpen->setEnabled(true);
ui->teLog->append("### Porta serial pronta para ser utilizada.");
}
else { ui->teLog->append("### Nenhuma porta serial foi detectada!"); }
connect(devserial, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnAppClose_clicked()
{
exit(0);
}
void Widget::WriteData(const QByteArray data)
{
procSerial->Write(data);
}
QString Widget::ReadData()
{
QString data = procSerial->Read();
qDebug() << "RX UART: " << data << endl;
return data;
}
void Widget::on_btnSetLed_clicked()
{
QString StatusLed;
/* PixMap com os icones de Led ON e Led OFF */
QPixmap LedON(":icons/Light-bulb-on-32.png");
QPixmap LedOFF(":icons/Light-bulb-off-32.png");
/* Escrevendo 11 no dispositivo da serial Arduino */
WriteData("11\n");
/* Pegando o retorno do comando e armazenando em uma QString */
StatusLed = ReadData();
/*
* Verifico se o que voltou foi ON ou OFF
* Baseado no retorno:
* ON -> Mudo o icone do botão e escrevo Ligar [F10]
* OFF -> Mudo o icone do botão e escrevo Desligar [F10]
*/
if (strcmp(StatusLed.toUtf8(),"ON")) {
ui->btnSetLed->setIcon(LedOFF);
ui->btnSetLed->setText("Ligar [F10]");
ui->btnSetLed->setShortcut(Qt::Key_F10);
}
else {
ui->btnSetLed->setIcon(LedON);
ui->btnSetLed->setText("Desligar [F10]");
ui->btnSetLed->setShortcut(Qt::Key_F10);
}
}
void Widget::on_btnDevOpen_clicked()
{
bool statusOpenSerial;
statusOpenSerial = procSerial->Conectar(ui->cbDevList->currentText(),
ui->cbDevBaudRate->currentText().toInt()
);
/*
* Se conectou com sucesso no disposito serial
* Desabilito o botão Conectar e Sair
* Habilito Desconectar, Versao, Hardware e Ligar [F10]
*/
if (statusOpenSerial) {
ui->btnDevClose->setEnabled(true);
ui->btnDevOpen->setEnabled(false);
ui->btnGetinfoHW->setEnabled(true);
ui->btnSetLed->setEnabled(true);
ui->btnGetVersion->setEnabled(true);
ui->btnAppClose->setEnabled(false);
ui->teLog->append("### Porta serial aberta com sucesso!");
}
else {
ui->teLog->append("Falha ao abrir conexão serial.");
}
}
void Widget::on_btnDevClose_clicked()
{
bool statusCloseSerial;
statusCloseSerial = procSerial->Desconectar();
/*
* Descontando a porta serial com sucesso
* Desabilito os botões Versao, Desconectar, Hardware, Ligar [F10]
* Habilito Sair e Conectar
*/
if (statusCloseSerial) {
ui->btnDevClose->setEnabled(false);
ui->btnDevOpen->setEnabled(true);
ui->btnAppClose->setEnabled(true);
ui->btnGetinfoHW->setEnabled(false);
ui->btnGetVersion->setEnabled(false);
ui->btnSetLed->setEnabled(false);
ui->teLog->append("### Porta serial fechada com sucesso!");
}
else {
ui->teLog->append("### Falha ao fechar conexão serial.");
}
}
void Widget::on_btnGetVersion_clicked()
{
QString GetVersionHw;
/* Envia o comando 13 para o Arduino */
WriteData("13\n");
/* A resposta é armazenada na variavel GetVersionHW */
GetVersionHw = ReadData();
/* É removida qualquer tabulação */
GetVersionHw = GetVersionHw.simplified();
/* E a saida é anexada a tela de Log de nossa aplicação */
ui->teLog->append(GetVersionHw);
}
void Widget::on_btnGetinfoHW_clicked()
{
QString GetInfoHw;
QStringList InfoHW;
/* Enviando comando para obter informações do Device */
WriteData("12\n");
/* Recebendo as informações */
GetInfoHw = ReadData();
/* Confirmando se recebeu os dados */
if( GetInfoHw.size() > 0 ) {
GetInfoHw = GetInfoHw.simplified();
/* Ex: 4.3.2|UNO
* O que chegou pela serial foi adicionado na variavel GetInfoHW
* então acima removemos qualquer tabulação e abaixo um split
* baseado no caractere |, então sera quebrado em 2 posicoes
* 0 - 4.3.2
* 1 - UNO
*/
InfoHW = GetInfoHw.split("|");
/* Inserindo nos devidos Edits */
ui->editDevCompiler->setText(InfoHW[0]);
ui->editDevModel->setText(InfoHW[1]);
}
else {
ui->teLog->append("### Erro ao obter informações do Dispositivo, tente novamente.");
}
}
void Widget::on_btnHelp_clicked()
{
/*
* Usando MessageBox para configurada uma simples mensagem
* para exibir ao operador ao cliente em Ajuda
* Neste exemplo usamos o tipo Information para estilo de MessageBox
*/
QMessageBox msgHelp;
msgHelp.setWindowTitle("Ajuda");
msgHelp.setInformativeText("Em desenvolvimento...");
msgHelp.setIcon(QMessageBox::Information);
msgHelp.exec();
}
void Widget::on_btnAbout_clicked()
{
/*
* Usando MessageBox para configurar uma simples mensagem
* para exibir ao operador ao clicar em Sobre
* O legal aqui é o uso de sintaxe HTML para isso!
*/
QMessageBox msgAbout;
msgAbout.setWindowTitle("Sobre");
msgAbout.setTextFormat(Qt::RichText);
msgAbout.setText(""
"<center><strong>Desenvolvido por</strong><br>Cleiton Bueno<br><br>"
"Mais em: <a href='https://embarcados.com.br'>Embarcados</a></center><br><br>"
"Contato: <a href='mailto:cleitonrbueno@gmail.com'>cleitonrbueno@gmail.com</a>"
"");
msgAbout.exec();
}
void Widget::on_btnBWTerminal_clicked()
{
QPalette paleta;
/*
* Verifica se PaletaBW é True ou False
* Se True: Fundo Preto, Fonte Branco
* Se False: Fundo Branco, Fonte Preto
*/
if(PaletaLogBW) {
paleta.setColor(QPalette::Base,Qt::black);
paleta.setColor(QPalette::Text,Qt::white);
ui->teLog->setPalette(paleta);
PaletaLogBW=false;
}
else {
paleta.setColor(QPalette::Base,Qt::white);
paleta.setColor(QPalette::Text,Qt::black);
ui->teLog->setPalette(paleta);
PaletaLogBW=true;
}
}
void Widget::on_editSendCmd_returnPressed()
{
QByteArray CmdSendUart;
//ui->teLog->append("> "+ui->editSendCmd->text());
/* Envia para stdout (Debug) o comando a ser escrito na Serial */
qDebug() << "TX UART:" << CmdSendUart << endl;
/*
* Armazena a string digitada em Comandos em CmdSendUart
* e remove qualquer tabulação
*/
CmdSendUart = ui->editSendCmd->text().toLatin1().simplified();
/*
* Escreve na serial o que foi digitado em Comandos
*/
WriteData(CmdSendUart+"\n");
/* Limpar o edit do Comandos */
ui->editSendCmd->clear();
/*
* A resposta é enviada para a tela de Log da aplicação
*/
ui->teLog->append("Resposta: "+ReadData());
}
Se copiar e colar o código acima irá funcionar, porém você deverá utilizar os nomes corretos para os PushButtons, QLineEdit e demais Widgets. Caso prefira, faça o download de todo o projeto no final do artigo, ou crie seus widgets com os nomes que achar melhor e apenas use os códigos acima.
Os botões Sobre, Sair, Ajuda e Cor Terminal é um bônus do que você pode usar criando estas ações. O foco agora é atacar o Conectar, Desconectar, Versão, Hardware e Ligar|Desligar.
A lógica é até simples, pois para ler e escrever na serial só usamos ReadData() e WriteData(), o resto é logica e estrutura do Qt5.
Código Arduino
Segue abaixo o código utilizado para o firmware do Arduino UNO, não comentarei muito aqui sobre o mesmo pois inseri bastante comentários nos principais pontos. Mas um resumo é que estou usando o Led que posso interagir com o pino 13, e enviando pela serial o comando ‘11‘ (Acender/Apagar Led), ‘12‘ (obter informações do microcontrolador) e ‘13‘ (a versão configurada no meu firmware).
Usaremos uma simples Máquina de Estado. Para mais informações sobre Máquina de Estado, confira o excelente artigo Máquina de Estado, do Pedro Betoleti.
/*
* Defines
*/
#define LED_ONBOARD 13
#define FW_VERSION "1.0"
/*
* Globais
*/
unsigned char FLAG_CONTROL_RX_UART_DATA = 0;
const unsigned int SIZE_BUF_RX = 10;
/*
* Estruturas
*/
typedef enum States{
NONE='10', //10
CALL_LED, //11
CALL_INFO, //12
CALL_VERSION //13
};
/*
* Prototipos das funcoes
*/
void ProcToggleLed(int);
void ProcInfo();
void ProcVersion();
void ProcStatesUARTRx(char *buffRx);
void CheckUARTRx(char *bufRx);
/*
* Funcoes
*/
void ProcInfo()
{
/* Retorna o versao do GCC utilizado para compilar */
Serial.write(__VERSION__);
Serial.write('|');
/* Retorna o Modelo do Arduino */
#if defined(__AVR_ATmega168__)
Serial.write("DIECIMILA");
#endif
#if defined(__AVR_ATmega328P__)
Serial.write("UNO");
#endif
#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
Serial.write("MEGA");
#endif
#if defined(__AVR_ATmega32U4__)
Serial.write("LEONARDO");
#endif
#if defined(__SAM3X8E__)
Serial.write("DUE");
#endif
Serial.flush();
}
void ProcVersion()
{
/* Retorna a versao atual deste Firmware */
Serial.write(FW_VERSION);
Serial.flush();
}
void ProcToggleLed(int)
{
/* Pegando o status atual do Led (Pino 13) */
volatile int led_status = digitalRead(LED_ONBOARD);
/* Enviando o outro estado para o Pino */
digitalWrite(LED_ONBOARD, !led_status);
/* Enviando na serial se o Led esta aceso ou apagado */
if(!led_status) { Serial.write("ON"); }
else { Serial.write("OFF"); }
Serial.flush();
}
void ProcStatesUARTRx(char *buffRx)
{
/* Flag para sinalizar quando tem dados no Buffer RX */
if(FLAG_CONTROL_RX_UART_DATA) {
/* Ira continuar apenas se o primeiro caractere
for 1 sendo do range do nosso States
*/
if (buffRx[0] != '1') return;
/*
Neste caso me interessa apenas o segundo
caractere do Buffer, ele seria o ID para
meus States
*/
switch(buffRx[1]) {
case NONE:
break;
case CALL_LED:
ProcToggleLed(LED_ONBOARD);
break;
case CALL_INFO:
ProcInfo();
break;
case CALL_VERSION:
ProcVersion();
break;
default:
break;
}
FLAG_CONTROL_RX_UART_DATA=0;
}
}
void CheckUARTRx(char *buffRx)
{
static unsigned int pos_buf_rx = 0;
/* Caso tenha algo na serial, ira tratar e/ou guardar no buffer */
if( Serial.available()>0 ) {
const byte RxDataUart = Serial.read();
switch (RxDataUart) {
case '\n':
/* Inseri terminador no final do buffer */
buffRx[pos_buf_rx] = 0;
/* Sinaliza que o Buffer esta pronto */
FLAG_CONTROL_RX_UART_DATA=1;
/* Reseta o valor de indice para o Buffer de Recepção */
pos_buf_rx = 0;
break;
case '\r':
/* Ignora Carriage Return ou pode colocar junto ao /n */
break;
default:
/* Inseri no buffer se ainda nao encher */
if (pos_buf_rx < (SIZE_BUF_RX - 1))
buffRx[pos_buf_rx++] = RxDataUart;
break;
}
Serial.flush();
}
}
/* Inicializando e configurando os perifericos usados */
void setup() {
/* Inicialização Serial 115200 bps */
Serial.begin(115200,SERIAL_8N1);
/* Inicialização do Led OUTPUT */
pinMode(LED_ONBOARD, OUTPUT);
}
/* Loop Principal Infinito */
void loop() {
/* Buffer para recepção dos dados vindos da Serial */
static char RxBuffer[SIZE_BUF_RX];
/*
Rotina para verificar se tem algo para receber na serial
e como parametro envia o buffer para RX
*/
CheckUARTRx(RxBuffer);
/*
Rotina para processar o que chegou pela serial
*/
ProcStatesUARTRx(RxBuffer);
}
Executando a aplicação no Linux
Resumidamente, a função da nossa aplicação é, se tiver porta serial no computador, listar em Portas e libera o botão Conectar. Caso contrario, não libera o botão e exibe uma mensagem que não encontrou portas seriais.
Conectando a porta serial, os botões são liberados e podemos executar as três ações Versão, Hardware e Ligar/Desligar, o legal que ligar e desligar o led é com o mesmo botão e o ícone do mesmo irá mudar conforme o status do led.
Em Comandos o que for digitado e a seguir pressionado ENTER é enviado ao dispositivo serial. No nosso caso podemos testar com 11, 12 e 13.
Vamos testar a aplicação e ver os resultados nas Figuras 9 e 10.
Todo o código-fonte utilizado na aplicação e o firmware do Arduino podem ser baixados no link abaixo. Contém todo o projeto para compilar e executar, fazer as suas modificações e adaptações para uso.
[wpdm_asset id=’17’]Saiba mais
– Comunicação serial com Arduino utilizando Qt5 Console Application
– Comunicação serial com Arduino e Qt5 Widgets Application – Parte 1
– Comunicação serial com Arduino e Qt5 Widgets Application – Parte 2
Referências
– https://doc.qt.io/qt-5/qserialport.html
– https://doc.qt.io/qt-5/qlineedit.html
– https://doc.qt.io/qt-5/qpixmap.html









Excelente Tutorial, muito bacana mesmo trabalhar com o Qt, eu que nunca tinha visto antes, começei a tentar algumas coisas aqui depois do primeiro tutorial, vi que tudo é muito simples mesmo.
Espero que esse não seja o último da série 🙂
Olá Pedro, fico feliz que tenha gostado desta série.
Tenho uma noticia boa e uma ruim, a ruim é que acabou esta serie de comunicação serial e a boa é que outros artigos com outros temas viram usando Qt5 😉
Abraço!
Belo Artigo Cleiton, Antes de alguém pensar em fazer em Java, dê uma olhada neste artigo que ficou muito bom! Aliás, em java, o trabalho será muito maior!
Olá Caio!
Fico feliz que tenha gostado, realmente Qt esta ganhando muita força em termos de desenvolver aplicações gráficas multiplataforma.
E o elenco da ferramenta é forte, mais artigos com Qt estão por vir.
Abraço
Sem comentários, muto bom mesmo! Aguardamos mais! 🙂
Bacana que gostou André, e realmente vem mais por ae 🙂
Excelente tutorial!
Olá Abraão!
Legal que gostou do post, e muita coisa legal esta por vir utilizando Qt.
Abraço.