Início
/
Software
/
Dicas de Software
/
Entendendo Parâmetros por Referência no Arduino: Como Usar Objetos e Funções de Forma Inteligente
Entendendo Parâmetros por Referência no Arduino: Como Usar Objetos e Funções de Forma Inteligente
Angelo Luis Ferreira | 11/11/2025
Acessos: 58
Aprenda de forma simples e prática como usar referências em funções Arduino

Objetivo
O objetivo deste tutorial é mostrar, de forma clara e prática, o conceito de parâmetros por referência no Arduino, utilizando o operador & em funções. Você leitor entenderá a diferença entre passar variáveis e objetos por valor ou por referência, entendendo como isso impacta o desempenho, a memória e o comportamento do código.
Ao final do artigo, o leitor será capaz de:
-
Identificar quando usar referências (&) em funções;
-
Compreender como o Arduino manipula objetos e variáveis internamente;
-
Escrever códigos mais eficientes, modulares e profissionais;
-
Aplicar o conceito em projetos reais, como controle de servos, sensores, displays e bibliotecas.
Em resumo, este tutorial tem como propósito aprofundar o conhecimento de iniciantes e intermediários em Arduino na linguagem C++, tornando-os capazes de aproveitar todo o potencial das funções e objetos com passagem por referência.
Referências
Como criar e utilizar Funções para organizar seu código em Arduino
Projeto 48a - Como controlar um display LCD com o módulo I2C (LiquidCrystal_I2C)
Definições
Quando começamos a programar com Arduino, um dos primeiros conceitos que aprendemos é a criação de funções. Elas nos ajudam a organizar o código, reutilizar trechos e tornar os programas mais legíveis.
Com o tempo, nossos projetos vão ficando mais complexos — passamos a usar sensores, motores, displays LCD, módulos Wi-Fi e outras bibliotecas externas. É aí que surge uma dúvida muito comum entre desenvolvedores:
“Por que em algumas funções eu passo uma variável normalmente, como int x, e em outras vejo algo estranho como Servo &servo ou LiquidCrystal_I2C &lcd?”
Esse pequeno símbolo “&”, que aparece antes do nome do parâmetro, costuma causar confusão até mesmo entre quem já tem alguma experiência. Ele não significa endereço de memória, como muitos pensam ao associá-lo aos ponteiros, mas algo muito mais simples e prático: ele indica que o parâmetro está sendo passado por referência.
A passagem por referência é um conceito fundamental da linguagem C++, que está por trás da programação do Arduino. Ela define como os dados são transmitidos para dentro das funções — e entender isso muda completamente a forma como você escreve e otimiza seus programas.
Em vez de apenas enviar uma cópia do valor (como acontece normalmente), o Arduino pode permitir que uma função acesse diretamente a variável ou objeto original. Isso traz várias vantagens, como:
-
Maior eficiência, pois evita cópias desnecessárias;
-
Menor uso de memória, algo essencial nos microcontroladores;
-
E a possibilidade de alterar o valor original de dentro da função.
Compreender esse comportamento é o que separa o programador iniciante do programador consciente, capaz de escrever códigos otimizados e de entender como o Arduino lida com variáveis, objetos e memória
Entendendo como os parâmetros são passados
Toda vez que chamamos uma função no Arduino, podemos passar valores ou objetos de duas formas:
-
Por valor (cópia)
-
Por referência (acesso direto ao original)
1️⃣ Passagem por valor
Quando passamos um parâmetro por valor, o Arduino cria uma cópia da variável dentro da função. Isso significa que qualquer alteração feita ali não afeta o valor original.
Exemplo:
void dobrar(int numero) {
numero = numero * 2;
}
void setup() {
int valor = 10;
dobrar(valor);
Serial.begin(9600);
Serial.println(valor); // Continua sendo 10, não mudou!
}
Atenção: Aqui, a função dobrar() recebeu uma cópia de valor. Alterar numero não altera valor fora da função.
2️⃣ Passagem por referência
Agora veja o mesmo exemplo, mas usando referência com o operador &:
void dobrar(int &numero) {
numero = numero * 2;
}
void setup() {
Serial.begin(9600);
int valor = 10;
dobrar(valor);
Serial.println(valor); // Agora imprime 20!
}
Atenção: Nesse caso, numero não é uma cópia, mas um apelido (referência) para a mesma variável original. Qualquer modificação feita dentro da função reflete diretamente no valor real.
Passagem de objetos por referência
Quando lidamos com bibliotecas Arduino, como:
-
Servo
-
LiquidCrystal
-
SoftwareSerial
-
WiFiClient
-
Adafruit_Sensor
Esses objetos contêm dados internos e ponteiros de hardware. Se você tentasse copiá-los, o Arduino ficaria sem saber qual instância é a “verdadeira”.
Por isso, sempre que for manipular um objeto existente, prefira usar (exemplos):
void atualizarDisplay(LiquidCrystal_I2C &lcd);
void moverMotor(Servo &motor);
void enviarDados(WiFiClient &cliente);
Resumo prático
-
mostrarMensagem(LiquidCrystal_I2C lcd, ...) → Copia o objeto
-
mostrarMensagem(LiquidCrystal_I2C &lcd, ...) → Usa o mesmo objeto original
Por que isso é importante no Arduino
O Arduino tem memória muito limitada (apenas 2 KB de RAM no Uno).
Ao trabalhar com objetos grandes (como um display LCD, sensor ultrassônico ou conexão WiFi), copiar esses dados cada vez que chamamos uma função é custoso e pode travar o microcontrolador.
Passar por referência resolve isso:
-
Nenhuma cópia é feita;
-
O código roda mais rápido;
-
O consumo de memória é menor;
-
A função pode modificar o objeto original, se necessário.
Exemplos práticos com um objeto: LCD I2C
a) Quando o & não aparece, mas a referência existe (sem necessidade do símbolo &)
Neste exemplo, o objeto lcd é criado uma única vez e usado diretamente no loop().
Mesmo sem criar uma função, o conceito de “referência” ainda está presente — pois estamos manipulando o mesmo objeto (lcd) ao longo de todo o programa.
#include <LiquidCrystal_I2C.h>
// Cria um único objeto LCD no endereço 0x27 e com 16 colunas por 2 linhas
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
lcd.init(); // Inicializa o display
lcd.backlight(); // Liga a luz de fundo
lcd.clear(); // Limpa o conteúdo inicial
}
void loop() {
// --- Usa sempre o mesmo objeto LCD já criado ---
lcd.setBacklight(HIGH); // Liga a luz de fundo
lcd.setCursor(1, 0);
lcd.print("Squids Arduino");
lcd.setCursor(0, 1);
lcd.print("LCD e modulo I2C");
delay(1000); // Espera 1 segundo
lcd.setBacklight(LOW); // Desliga o backlight
delay(1000); // Espera mais 1 segundo
}
-
O objeto lcd é criado fora de qualquer função, no início do código.
-
Isso significa que ele é global, ou seja, pode ser usado por todo o programa.
-
Todas as instruções no loop() acessam exatamente o mesmo display.
-
É o método mais eficiente, pois o objeto é inicializado apenas uma vez.
b) Sem passagem por referência (Passagem por Valor)
Neste exemplo, a função mostrarMensagem() cria seu próprio objeto LCD dentro dela — um objeto independente.
/*******************************************************************************
*
* Projeto 48a: Ligando o Display LCD 16x2 (com passagem por referência)
* http://squids.com.br/arduino
*
*******************************************************************************/
#include <LiquidCrystal_I2C.h>
// --- Função que cria e controla o LCD local ---
void mostrarMensagem() {
LiquidCrystal_I2C display(0x27, 16, 2); // Cria novo objeto local
display.init();
display.backlight();
display.setCursor(1, 0);
display.print("Squids Arduino");
display.setCursor(0, 1);
display.print("LCD e modulo I2C");
delay(1000);
display.setBacklight(LOW);
delay(1000);
}
void setup() {
// Não há necessidade de inicializar o LCD globalmente
}
void loop() {
mostrarMensagem(); // Chama a função (novo LCD a cada execução)
}
-
O objeto LiquidCrystal_I2C display é criado toda vez que a função é chamada.
-
Quando a função termina, o objeto é destruído da memória.
-
O display ainda funciona, porque é inicializado e usado dentro da função — mas essa prática é pouco eficiente.
-
Cada chamada cria e destrói o objeto, ocupando mais memória e aumentando o tempo de inicialização.
c) Quando é necessário a passagem por referência com o símbolo (&)
Neste exemplo, a função mostrarMensagem() recebe o objeto lcd por referência — ou seja, a função trabalha diretamente com o mesmo objeto já criado no programa, sem a precisar que o objeto LiquidCrystal_I2C display seja criado toda vez que a função é chamada.
#include <LiquidCrystal_I2C.h>
// Define o endereço I2C e o tamanho do display
LiquidCrystal_I2C lcd(0x27, 16, 2); // Exemplo para display 16x2
// --- Função que controla o LCD ---
void mostrarMensagem(LiquidCrystal_I2C &display) {
display.setBacklight(HIGH);
display.setCursor(1, 0);
display.print("Squids Arduino");
display.setCursor(0, 1);
display.print("LCD e modulo I2C");
delay(1000);
display.setBacklight(LOW);
delay(1000);
}
void setup() {
lcd.init();
lcd.backlight();
lcd.clear();
}
void loop() {
mostrarMensagem(lcd); // Passa o objeto por referência
}
-
LiquidCrystal_I2C &display → o símbolo & indica referência, ou seja, a função não cria uma cópia do objeto.
-
A função mostrarMensagem() atua diretamente sobre o mesmo LCD declarado no início do código.
-
É eficiente: baixo consumo de memória e execução rápida.
-
Todas as alterações feitas dentro da função afetam o mesmo display.
d) Usando 2 displays LCD diferentes (PASSAGEM POR REFERÊNCIA)
Recomendado quando quiser operar o mesmo objeto:
Aqui os displays são objetos globais criados no topo. A função recebe uma referência ao objeto (LiquidCrystal_I2C &display), ou seja, ela trabalha diretamente com o mesmo objeto já inicializado.
/*******************************************************************************
*
* Projeto: Dois Displays LCD I2C - Passagem por Referência
* Descrição: Exemplo funcional com dois displays I2C de endereços diferentes.
* Autor: Arduino Maestro
*******************************************************************************/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Dois displays com endereços diferentes
LiquidCrystal_I2C lcd1(0x27, 16, 2);
LiquidCrystal_I2C lcd2(0x3F, 16, 2);
// Função que recebe o display por referência (sem cópia)
void mostrarMensagem(LiquidCrystal_I2C &display, String texto) {
display.clear();
display.setCursor(0, 0);
display.print("Display Teste:");
display.setCursor(0, 1);
display.print(texto);
}
void setup() {
lcd1.init();
lcd1.backlight();
lcd2.init();
lcd2.backlight();
// Agora a função manipula o mesmo objeto original
mostrarMensagem(lcd1, "Squids Arduino");
delay(2000);
mostrarMensagem(lcd2, "Bem Vindos");
}
void loop() {
// Nada aqui
}
-
A função não cria cópias; ela opera diretamente no objeto original.
-
Muito eficiente em memória e tempo — ideal para microcontroladores com pouca RAM.
-
Padrão recomendado ao trabalhar com LiquidCrystal_I2C, Servo, etc.
- Obs.: Para ligar os dois displays no mesmo barramento, e eles devem ter endereços diferentes para que sejam acionados de forma independente e correta.
Benefícios práticos do uso de referências
✅ Eficiência: evita cópias desnecessárias de variáveis e objetos.
✅ Velocidade: a função acessa o mesmo dado em memória.
✅ Flexibilidade: permite alterar valores sem precisar de return.
✅ Compatibilidade: algumas bibliotecas só aceitam objetos passados por referência.
✅ Organização: facilita criar funções genéricas reutilizáveis.
Quando não usar referência
Apesar de útil, há casos em que não vale a pena usar referências:
-
Quando a função só precisa ler o valor e não alterá-lo (a cópia é mais segura).
-
Quando a variável é muito pequena (ex: int, bool, byte) e o ganho de desempenho é insignificante.
-
Quando é importante manter o valor original intacto.
Nessas situações, a passagem por valor é mais simples e segura.
Conclusão
Compreender a passagem por referência (&) é um dos passos mais importantes para quem quer sair do nível básico e programar de forma profissional com Arduino.
Esse recurso:
-
Otimiza memória e desempenho;
-
Permite criar funções mais flexíveis e modulares;
-
É essencial ao trabalhar com objetos de bibliotecas externas.
Portanto, sempre que você vir algo como Servo &servo ou LiquidCrystal_I2C &lcd, lembre-se: Essa função não cria cópia — ela manipula o objeto real, com eficiência e precisão
O anúncio abaixo ajuda a manter o Squids Arduino funcionando
Comentários