- 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
Introdução
Fiz um post há um tempo sobre como realizar comunicação serial utilizando o Python e para facilitar utilizei um Arduino UNO. Desta vez vamos conhecer o Qt e verificar o que precisamos e as opções disponíveis para comunicarmos. Podemos fazer uma aplicação simples, para interação via console apenas, ou com interface gráfica.
Para familiarizarmos com o Qt, tem um post que escrevi há um tempo que aborda com bastante detalhes essa ferramenta. Para quem nunca teve contato e quer instalar o Qt, pode usar este outro post, com passo a passo para instalar o QtCreator no Linux. Mesmo quem já instalou é interessante ver o post para entender as opções que podem ser habilitadas antes da instalação.
Configuração do ambiente host
Para este artigo será utilizada a distribuição Linux Mint 17 Qiana 64 bits, mas sem problemas em utilizar um Ubuntu ou Debian. 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.
Qt5 Console Application
A abordagem neste post e na série que está por vir, o Qt5 será utilizado em ambiente Linux e com desenvolvimento de aplicação para Linux (x86 ou arm), porém Qt5 pode ser instalado tanto em Windows, Linux ou MacOS, além de poder ser compilado para outras plataformas.
Sem mais delongas, se você já instalou o Qt5 pode abrir o Qt Creator, e caso ainda não tenha instalado confira o procedimento nesse link.
Com a IDE do Qt aberta, click em File > New File or Project… e uma janela como na Figura 1 surgirá e você deverá escolher Applications > Qt Console Application e clicar em Choose .
Na janela seguinte devemos dar um nome ao projeto (Name:) e dizer onde deverá ser salvo (Create in:) como na Figura 2. No meu caso escolhi salvar na pasta /tmp/ apenas para este artigo, mas você poderá criar uma pasta no seu /home e salvar nela.
Na etapa seguinte vamos apenas confirmar se estão selecionadas as opções como a da Figura 3, não precisa ser igual mas atente sobre o GCC. Quando criar o post de projetos Qt para embarcados esta etapa será abordada com mais detalhes. Para avançar clique em Next.
Na janela seguinte da Figura 4 será onde você deve ou não escolher o software de versionamento (Git, Bazaar, Mercurial, CVS, Subversion, entre outros). No caso do artigo não estarei usando, mas quando for utilizar um projeto do Qt, por favor utilize uma das opções, quem for dar manutenção no futuro ira agradecer. Logo abaixo será exibido um resumo sobre o nome do projeto e o local que será salvo. Se estiver de acordo é só clicar em Finish.
Uma estrutura como na Figura 5 é para ser criada, onde temos o nome do projeto em primeiro nível, logo em seguida algumas diretivas que podemos passar para o Qt, e Sources e Headers, que são os diretórios dos arquivos fontes. Neste caso apenas Sources com o main.cpp já criado pelo Qt.
Para entender melhor sobre esta estrutura e as opções que surgiram na coluna ao lado, veja o post.
Maravilha, estamos com a IDE instalada, projeto criado e o passo seguinte será configurar para comunicação serial e criar nosso primeiro programa.
Plugin QtSerialPort
Caso você tenha realizado a instalação do Qt5 conforme mencionei no link, você selecionou qtserialport e estará tudo certo. Você pode obter informações no link QtSerialPort. Primeiro iremos adicionar no arquivo .pro a seguinte entrada.
Qt5
1 |
QT += serialport |
Qt4
1 |
CONFIG += serialport |
Como sei que muitos desenvolvedores ainda utilizam o Qt4 (4.8) tentarei guiar o post para as duas versões. No caso do Qt5 pode ser visto na Figura 6.
O próximo passo é incluir as bibliotecas básicas para trabalhar com o QtSerialPort, como pode ser visto na Figura 7, linhas 2 e 3.
Vamos construir o projeto, clicando em Build Project (Ctrl + B), o ícone com um martelo desenhado, e devemos ter uma saída Compile Output como da Figura 7.
Caso na saída Compile Output apareça algo como “error: QtSerialPort/QtSerialport: No such file or directory” você não possui o QtSerialPort instalado ou configurado corretamente.
Caso você não tenha tido problemas com o Build Project ignore a etapa a seguir, Instalando o QtSerialPort, visto que apenas quem encontrar erros deve realizá-la.
Instalando o QtSerialPort
A única exigência é possuir Perl instalado, do mais vamos resolver nas etapas a seguir.
Primeiro, clone o repositório do qtserialport:
1 2 3 4 5 6 7 8 |
$ cd /tmp && git clone git://gitorious.org/qt/qtserialport.git Cloning into 'qtserialport'... remote: Counting objects: 6869, done. remote: Compressing objects: 100% (4103/4103), done. remote: Total 6869 (delta 4667), reused 3755 (delta 2719) Receiving objects: 100% (6869/6869), 1.74 MiB | 462.00 KiB/s, done. Resolving deltas: 100% (4667/4667), done. Checking connectivity... done. |
Como já possuímos o Qt5 instalado, agora é só compilar:
1 |
$ cd qtserialport && qmake qtserialport.pro |
Caso aparecer o erro “qmake: could not exec ‘/usr/lib/x86_64-linux-gnu/qt4/bin/qmake’: No such file or directory”, pode ser que você teve outro Qt instalado, ou já instalou algo com o gerenciador de pacotes do Linux e teve que utilizar o qmake. Neste caso é só usar o mesmo caminho onde o Qt5 está instalado.
No meu caso está em /home/bueno/Qt/5.4/gcc_64/bin/qmake.
1 2 3 |
$ /home/bueno/Qt/5.4/gcc_64/bin/qmake qtserialport.pro Info: creating cache file /tmp/qtserialport/.qmake.cache |
Em seguida termine a instalação:
1 |
$ sudo make install |
Pronto. Você esta com o QtSerialPort instalado. Teste o projeto novamente clicando em Build Project.
Listando Portas
Vamos agora adicionar ao nosso main.cpp, um código que deve listar as portas seriais do computador.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include <QCoreApplication> #include <QtCore/QDebug> #include <iostream> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> QTextStream cinput(stdin); QTextStream coutput(stdout); /** * @brief LoadPorts * @return QStringList * */ QStringList LoadPorts(void); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QStringList portSerial; int myPortSerial; portSerial = LoadPorts(); uint numPortSerial = portSerial.size(); coutput << "INDICE" << "\t\tDISPOSITIVO" << endl; if (numPortSerial > 0) { for(uint indice=0; indice<numPortSerial; indice++) { qDebug() << "[" << indice << "]\t\t" << portSerial.value(indice); } } else { qDebug() << "Nenhuma porta serial detectada!\n"; exit(1); } exit(0); return a.exec(); } QStringList LoadPorts(void) { QStringList device; foreach (const QSerialPortInfo &devinfo, QSerialPortInfo::availablePorts()) { QSerialPort devserial; devserial.setPort(devinfo); if (devserial.open(QIODevice::ReadWrite)) { qDebug() << "\nPorta\t\t:" << devinfo.portName(); qDebug() << "Descrição\t:" << devinfo.description(); qDebug() << "Fabricante\t:" << devinfo.manufacturer() << "\n"; device << devinfo.portName(); devserial.close(); } } return device; } |
No código criado acima temos a função LoadPorts(), cuja implementação é baseada no exemplo da própria Qt, onde utilizando QtSerialPortInfo vamos obter informações detalhadas da porta serial, como as portas disponíveis e informações como o nome da porta, fabricante e descrição. Cada porta será adicionada à variável device, que é uma StringList.
Voltando ao main(), nele temos outra QStringList portSerial que irá receber o retorno de LoadPorts(), e a sequência é muito simples. Verificamos o número do portSerial que será a quantidade de devices. Se for 0 nenhuma porta serial foi detectada, caso seja maior que 0 as portas são impressas na tela com os seus nomes e suas informações.
Clicando no botão Run ou o atalho (Ctrl + R), vamos obter uma saída como abaixo:
1 2 3 |
Starting /tmp/build-comunicacao_serial_qt_console-Desktop_Qt_Qt_Version_GCC_64bit-Debug/comunicacao_serial_qt_console... INDICE DISPOSITIVO Nenhuma porta serial detectada! |
Agora conectando um dispositivo serial, no meu caso o Arduino UNO:
1 2 3 4 5 6 7 8 |
Starting /tmp/build-comunicacao_serial_qt_console-Desktop_Qt_Qt_Version_GCC_64bit-Debug/comunicacao_serial_qt_console... Porta : "ttyACM0" Descrição : "Arduino Uno" Fabricante : "Arduino www.arduino.cc " INDICE DISPOSITIVO [ 0 ] "ttyACM0" |
Caso mais algum dispositivo que emule através da porta USB a serial seria acrescentado índice acima.
Comunicando com o Arduino
Que tal agora fazer uma comunicação de verdade? Para isso vamos contar com a ajuda do Arduino UNO, onde através da sua porta USB iremos comunicar via serial com o Qt. Caso não conheça o Arduino ou teve pouco contato pode acessar os posts do Fábio Souza, Arduino UNO e Arduino – Primeiros Passos.
Segue abaixo o código-fonte do firmware do Arduino. Poderia ser uma aplicação simples, como receber e imprimir o caractere recebido ou enviá-lo de volta, mas pensei, vamos fazer algo mais legal, porque não um simples prompt? Onde recebe um comando e trata o mesmo reportado a saída ou um erro, e no nosso caso o Qt será nosso prompt.
Vamos começar com o código-fonte do Arduino.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#define FW_VERSAO 1.0 #define HW_MODELO "ARDUINO UNO" String serialCmd = ""; bool flagControlRxSerial = false; void setup() { Serial.begin(9600); /* Limitando a 10 bytes para nossos comandos */ serialCmd.reserve(10); } void loop() { /* Estarou sempre verificando se acabou a recepcao da serial, esta variavel flagControlRxSerial se tornara true para isso. */ if (flagControlRxSerial) { if( serialCmd == "versao\n" ) { Serial.print("\tFirmware: "); Serial.print(FW_VERSAO); } else if ( serialCmd == "hardware\n") { Serial.print("\tPlaca : "); Serial.print(HW_MODELO); } else if ( serialCmd == "help\n" || serialCmd == "?\n") { Serial.print("\tComandos : \n\thardware - Modelo atual de placa\n\tversao - Versao atual do firmware"); } else if (serialCmd == "\n") { Serial.print("\n"); } else { Serial.print("\t* Comando invalido *"); } /* Resetando valores para nova recepcao */ serialCmd = ""; flagControlRxSerial = false; } } void serialEvent() { /* Loop verificando se algum byte esta disponivel na serial */ while (Serial.available()) { /* O que chegar pela serial feito um typecast de char para a variavel caractere */ char caractere = (char)Serial.read(); /* Nossa variavel caractere eh concatenada a nossa variavel serialCmd que eh uma String */ serialCmd += caractere; /* Se chegar um CR, nova linha, nossa flag de controle (flagsControlRxSerial) passa para true e no loop principal ja podera ser utilizada */ if (caractere == '\n') { flagControlRxSerial = true; } } } |
O código está bem comentado para ajudar a compreensão, o foco é atender os comandos hardware, versao e help que são recebidos pela serial e imprimir estas informações na tela e tratar a comunicação como um prompt.
A novidade aqui é o serialEvent(), que antes da interação de cada loop() verifica se algum byte foi recebido, e eventualmente indica a recepção de um comando. Para compreender melhor o serialEvent() veja o artigo Entendendo e usando serialEvent().
Agora a implementação no lado do Qt5, você pode editar o main.cpp do projeto atual ou criar outro projeto para isso e adicionar o código abaixo ao seu main.cpp. Lembrando, é claro, de editar o arquivo .pro, para criar um novo projeto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
#include <QCoreApplication> #include <QtCore/QDebug> #include <iostream> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> /* Vou criar cinput e couput como globais para utilizar em Debugs * nas funções de leitura e escrita da porta serial * * ReadPort(QSerialPort *port); * WritePort(QSerialPort *port, const QByteArray &writeData) * */ QTextStream cinput(stdin); QTextStream coutput(stdout); /** * @brief ReadPort * @param port * @return */ QByteArray ReadPort(QSerialPort *port); /** * @brief WritePort * @param port * @param writeData */ void WritePort(QSerialPort *port, const QByteArray &writeData); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); /* Estrutura de variaveis locais */ QByteArray saidaCmdSerial; // Dados recebidos pela serial QByteArray enviarCmdSerial; // Comando a ser enviado pela serial QStringList argumentList; // Parametros passados na execucao da apicação int argumentCount; // Numero de argumentos passados QSerialPort serialPort; // Novo objecto QSerialPort QString serialPortName; // Variavel que ira receber o caminho da porta serial int serialPortBaudRate; // Variavel que ira receber o baudrate da conexão /* Capturando o numero de parametro passado */ argumentCount = QCoreApplication::arguments().size(); /* Carregando em um StringList os parametros passados */ argumentList = QCoreApplication::arguments(); /* Verifica se pelo um parametro foi passado, no caso o tty da serial, por exemplo: * ttyUSB0, ttyACM0 em caso de duvidas verifique no /dev/tty* */ if (argumentCount == 1) { coutput << QObject::tr("Uso: %1 <portaserial> [baudrate]").arg(argumentList.first()) << endl; return 1; } /* Se passou na etapa acima então a porta serial foi passada e a mesma * será carregada em serialPortName e logo em seguida configurada no * serialPort.setPortName(); */ serialPortName = argumentList.at(1); serialPort.setPortName(serialPortName); /* O segundo parametro esperado é o BaudRate, caso não seja passado nenhum automaticamente ira receber * 9600, caso contrario ira configurar serialPort.setBaudRate() com o valor passado, e sera armazenado * na variavel serialPortBaudRate */ serialPortBaudRate = (argumentCount > 2) ? argumentList.at(2).toInt() : QSerialPort::Baud9600; serialPort.setBaudRate(serialPortBaudRate, QSerialPort::AllDirections); /* Configuração Padrão e que pode ser configurada de acordo com sua necessidade, no caso estou usando: * Databits 8 (8) * FlowControl NoFlowControl ( Sem controle de fluxo) * Parity NoParity (Sem Paridade) * StopBits OneStop (1) */ serialPort.setDataBits(QSerialPort::Data8); serialPort.setFlowControl(QSerialPort::NoFlowControl); serialPort.setParity(QSerialPort::NoParity); serialPort.setStopBits(QSerialPort::OneStop); /* Proximo passo é realizado a conexão, onde irei abrir o device especificado com as devidas configurações * e no caso abaixo, estou dizendo que para Leitura+Escrita, e as opções são: * * QIODevice::WriteOnly : Apenas escrita no device * QIODevice::ReadOnly : Apenas leitura do device * QIODevice::ReadWrite : Leitura e Escrita do device * * Existem outras flags mas nesta aplicação não serão abordadas. * Em caso de falha, um erro é reportado na tela e a aplicação encerrada. */ if (!serialPort.open(QIODevice::ReadWrite)) { coutput << QObject::tr("Falha ao abrir porta %1, erro: %2").arg(serialPortName).arg(serialPort.errorString()) << endl; return 1; } /* LOOP PRINCIPAL DA APLICAÇÂO */ while(true) { /* Mensagem para aparecer no terminal, dando cara de prompt */ coutput << "prompt :> " << flush; /* O que for digitar no stdin sera capturado e guardado em enviarCmdSerial*/ enviarCmdSerial = cinput.readLine().toUtf8(); /* Verifica se foi digitado algum comando e ignora \n e o exit */ if( (enviarCmdSerial != "exit") && (enviarCmdSerial.length() > 0) ) { /* Atravez da função WritePort(); envia o endereço do objeto serialPort * e o comando a ser escrito na serial */ WritePort(&serialPort,enviarCmdSerial+"\n"); /* Atravez da função ReadPort() é passado o endereço do objecto serialPort * e a variavel saidaCmdSerial aguarda o retorno da resposta ao comando enviado */ saidaCmdSerial = ReadPort(&serialPort); /* Imprimo na tela o comando recebido */ coutput << saidaCmdSerial << endl; /* Limpo o conteudo do variavel dos comandos enviados */ /* Agora é um if() que faz o controle nao mais um while() e a * funcao de clear() nao é mais necessaria */ //enviarCmdSerial.clear(); } /* Caso digou exit entra aqui */ if(enviarCmdSerial == "exit") { /* Mensagem padrao que ira aparecer no terminal quando digitar exit */ coutput << "Encerrando prompt Qt <> Arduino :)" << endl; return false; } } /* Fechando a conexão serial */ serialPort.close(); /* Saindo com sucesso da aplicação */ exit(0); return a.exec(); } /** * @brief ReadPort * @param port * @return Bytes com os dados da comunicação serial * * Deve receber apenas o endereço do objeto QSerialPort * e ira devolver os dados recebidos da comunicação serial */ QByteArray ReadPort(QSerialPort *port) { QByteArray readData; /* Vou aguardar X ms, no caso 75, para ver os dados disponiveis e começar a leitura (-1) não possuira timeout. */ while (port->waitForReadyRead(75)) { readData.append(port->readAll()); } /* Descomentar a linha abaixo apenas para debug da recepçao serial */ //coutput << "RX data :> " << readData << endl; return readData; } /** * @brief WritePort * @param port * @param writeData * * Recebe o endereço do objeto QSerialPort usado e o ByteArray do comandos * a ser enviado e escreve na porta serial. * */ void WritePort(QSerialPort *port, const QByteArray &writeData) { /* Descomentar a linha abaixo apenas para debug da transmissão serial */ //coutput << "TX data :>" << writeData << endl; /* Baseado no endereço do QSerialPort recebido em *port, eu vou escrever na serial * com port->write() e passando como parametro o QByteArray writeData e como retorno * ele irá me dar o numero de bytes enviados */ qint64 bytesWritten = port->write(writeData); /* Faço apenas uma checagem basica onde se retornou -1 deu erro ao enviar os dados e se retornar um numero * diferente do retornado alguma informação foi perdida durante a escrita */ if (bytesWritten == -1) { coutput << QObject::tr("Falha ao escrever os dados na porta %1, error: %2").arg(port->portName()).arg(port->errorString()) << endl; QCoreApplication::exit(1); } else if (bytesWritten != writeData.length()) { coutput << QObject::tr("Falha ao escrever uma parte dos dados na porta %1, error: %2").arg(port->portName()).arg(port->errorString()) << endl; QCoreApplication::exit(1); } } |
No código acima, acho que posso ser bem breve pois em cada etapa eu adicionei um comentário. As funções de ler e escrever na porta serial usando o QtSerialPort é baseado na implementação exemplo no site do Qt, e mais detalhes sobre manipulação de IO no Qt pode ser visto na sessão QIODevice, recomendo este link para conhecer melhor as opções e flags que podem ser utilizadas.
Vamos construir nossa aplicação e executa-la. Pode clicar em Build Project (Ctrl + B), e como usei o diretório /tmp para o projeto vou acessá-lo por esse caminho. Você deverá trocar pelo caminho que escolheu. Vamos executar a aplicação:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/tmp/build-comunicacao_serial_qt_console $ ls -1 comunicacao_serial_qt_console main.o Makefile /tmp/build-comunicacao_serial_qt_console $ ./comunicacao_serial_qt_console Uso: ./comunicacao_serial_qt_console <portaserial> [baudrate] /tmp/build-comunicacao_serial_qt_console $ ./comunicacao_serial_qt_console /dev/ttyACM1 Falha ao abrir porta /dev/ttyACM1, erro: No such file or directory /tmp/build-comunicacao_serial_qt_console $ ./comunicacao_serial_qt_console /dev/ttyACM0 prompt :> prompt :> prompt :> info * Comando invalido * prompt :> help Comandos : hardware - Modelo atual de placa versao - Versao atual do firmware prompt :> versao Firmware: 1.00 prompt :> hardware Placa : ARDUINO UNO prompt :> prompt :> clear * Comando invalido * prompt :> exit Encerrando prompt Qt <> Arduino :) /tmp/build-comunicacao_serial_qt_console $ |
Que legal, na saída dos comandos acima podemos ver que quando executei sem o Arduino conectado deu um aviso na tela da maneira correta de execução. Logo em seguida passei uma porta incorreta e foi dado o erro de que não foi possível abrir o dispositivo. Logo em seguida, com a porta correta, foi realizada a conexão e já apareceu a entrada do nosso prompt, aguardando os comandos. Note que se não passar um segundo parâmetro, que é o baudrate, é assumido por padrão 9600 bps.
Com os comandos corretos help, hardware e versao foram retornadas as devidas informações e os comandos inexistentes uma mensagem de exceção. Se gostou da ideia, você pode estender as funções do Qt agora, como por exemplo no que eu digitei o comando clear e não aconteceu nada, poderia fazer de limpar a tela do terminal, poderia adicionar um timeout caso depois de algum tempo nenhum comando for digitado.
O Qt é muito poderoso e usei neste post apenas uma pequena parcela de seu poder e também do QtSerialPort, que podemos trabalhar com conexões assíncronas e síncronas. E se você já trabalhou com termios do Linux em aplicações C para comunicação serial, fique mais confortável, é em cima dele que o QtSerialPort foi escrito para manipular o device.
No próximo artigo, com o Qt5 vamos ver como realizar comunicação serial utilizando o QtSerialPort só que desta vez usando uma interface gráfica e o que mais podemos agregar à nossa aplicação.
Espero que tenham gostado, até a próxima!
Programa morre aqui: if (argumentCount == 1) {
coutput << QObject::tr("Uso: %1 [baudrate]”).arg(argumentList.first()) << endl;
return 1;
}
Olá Ricardo, ao compilar ou executar? Caso seja ao executar você esta passando o nome do device serial como argumento?
Muito bom o artigo! Utilizo Qt a anos, mas nunca pensei em usar ele para interagir com Arduino, já me rendeu algumas ideias 🙂
Que bom que gostou MDK. O Qt abre muitas oportunidades para comunicação, e isso foi apenas uma pequena demonstração, você não perde por esperar pelo que vem por ae 😉
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] post anterior mostrei como preparar o ambiente com Qt5 – Comunicação Serial com Arduino utilizando Qt5 Console Application para realizar comunicação serial via console e, para isso, utilizamos um Arduino. Desta vez […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]
[…] Comunicação serial com Arduino utilizando Qt5 Console Application […]