Interface de Linha de Comando (CLI) para Arduino UNO: Rápida e Confiável
Componentes e Materiais
| Componente | Quantidade |
|---|---|
| Arduino UNO | 1 |
Sobre o Projeto
Chega um momento em que você precisa ler o valor de um sensor ou comandar seu robô a "mover para a esquerda". Talvez precise consultar o valor de uma variável em tempo de execução ou ajustar um potenciômetro digital. O que você precisa é uma interface de linha de comando (CLI). Sim, é fácil fazer o Arduino responder a comandos de texto.
Apresentamos uma CLI simples, rápida e extremamente eficiente em memória que você pode copiar e colar em seu código em poucos minutos. Os comandos consistem em um nome seguido de múltiplos argumentos. Suporta até backspace durante a digitação.
Visão Rápida
Todo sketch possui a função loop(). A sua pode ser tão simples quanto a abaixo. Ela chama duas rotinas em uma aba separada chamada CommandLine.h.
#include "CommandLine.h"
void setup() {
Serial.begin(115200);
}
void loop() {
bool received = getCommandLineFromSerialPort(CommandLine);
if (received) DoMyCommand(CommandLine);
}
Analisando o CommandLine.h
O arquivo CommandLine.h contém toda a implementação. Cada linha que você precisa modificar está marcada com //Modify here. Inclui dois comandos de exemplo: add e sub, demonstrando como são chamados dentro de DoMyCommand.
Detalhes da Implementação
Utiliza a biblioteca C padrão <string.h> para tokenização via strtok(), e <stdlib.h> para conversão ASCII-para-inteiro via atoi(). Um macro auxiliar print2 facilita depuração:
#define print2(x,y) (Serial.print(x), Serial.println(y))
// Uso: print2("minhaVar = ", minhaVar); // imprime: minhaVar = 25
A função getCommandLineFromSerialPort() monta a linha de comando a partir da porta serial, lidando com backspace e convertendo tudo para minúsculas. Retorna true quando um comando completo (terminado por \n) é recebido.
DoMyCommand() analisa o buffer, identifica o comando via strcmp() e executa a função correspondente. Para muitos comandos, uma tabela hash ou switch-case com caracteres únicos otimizaria, mas para Arduino a abordagem if-else costuma ser suficiente.
Comandos de Exemplo: Add e Sub
Cada função de comando parseia seus próprios argumentos usando readNumber() (baseado em strtok + atoi) e readWord(). Fácil de estender para novos comandos.
Como Começar Rapidamente
- Crie um sketch com o
loop()mostrado acima - Crie uma nova aba no Arduino IDE (triângulo no canto superior direito)
- Nomeie a aba
CommandLine.he cole o código completo - Faça upload e abra o Monitor Serial
- Digite:
add 5, 10ousub 10, 5seguidos de Enter
Código Completo (CommandLine.h)
/*****************************************************************************
* Simple Command Line Interpreter for Arduino
* Como usar: Crie sketch, cole loop() acima, crie aba CommandLine.h, cole isto.
* Teste: add 5, 10 | subtract 10, 5
*****************************************************************************/
#include <string.h>
#include <stdlib.h>
#define print2(x,y) (Serial.print(x), Serial.println(y))
#define CR '\r'
#define LF '\n'
#define BS '\b'
#define NULLCHAR '\0'
#define SPACE ' '
#define COMMAND_BUFFER_LENGTH 25
char CommandLine[COMMAND_BUFFER_LENGTH + 1];
const char *delimiters = " ,\n";
const char *addCommandToken = "add"; //Modify here
const char *subtractCommandToken = "sub"; //Modify here
bool getCommandLineFromSerialPort(char * commandLine) {
static uint8_t charsRead = 0;
while (Serial.available()) {
char c = Serial.read();
switch (c) {
case CR: case LF:
commandLine[charsRead] = NULLCHAR;
if (charsRead > 0) {
charsRead = 0;
Serial.println(commandLine);
return true;
}
break;
case BS:
if (charsRead > 0) {
commandLine[--charsRead] = NULLCHAR;
Serial.write(BS); Serial.write(SPACE); Serial.write(BS);
}
break;
default:
c = tolower(c);
if (charsRead < COMMAND_BUFFER_LENGTH) {
commandLine[charsRead++] = c;
}
commandLine[charsRead] = NULLCHAR;
break;
}
}
return false;
}
int readNumber() {
char *numTextPtr = strtok(NULL, delimiters);
return atoi(numTextPtr);
}
char *readWord() {
return strtok(NULL, delimiters);
}
void nullCommand(char *ptrToCommandName) {
print2("Command not found: ", ptrToCommandName);
}
int addCommand() { //Modify here
int firstOperand = readNumber();
int secondOperand = readNumber();
return firstOperand + secondOperand;
}
int subtractCommand() { //Modify here
int firstOperand = readNumber();
int secondOperand = readNumber();
return firstOperand - secondOperand;
}
bool DoMyCommand(char *commandLine) {
int result;
char *ptrToCommandName = strtok(commandLine, delimiters);
if (strcmp(ptrToCommandName, addCommandToken) == 0) {
result = addCommand();
print2("> The sum is = ", result);
} else if (strcmp(ptrToCommandName, subtractCommandToken) == 0) {
result = subtractCommand();
print2("> The difference is = ", result);
} else {
nullCommand(ptrToCommandName);
}
return true;
}
Processo de manufatura
- Dominando Comentários em C#: Tipos, Boas Práticas e Documentação XML
- Dominando Interfaces em C#: Definição, Implementação e Casos Práticos
- Dominando Interfaces em Java: Conceitos, Implementação e Boas Práticas
- Sensor Sem Fio Confiável para Garagens Longas – Integração Fácil com Raspberry Pi
- Sensor de Proximidade Infravermelho de Baixo Custo para Raspberry Pi Usando Apenas Pinos Digitais
- ANDY – O Robô Humanoide Multipropósito
- Simple Pi Robot – Construa Rover Autônomo 2-WD com Raspberry Pi
- Dominando Argumentos de Linha de Comando em C: Guia Prático
- Dominando Interfaces em C#: Contratos de Design para POO Robusta
- Entendendo Linhas de Corte Longitudinal de Bobinas de Aço: Transformando Bobinas em Tiras de Precisão