Fundamentos · 20 min de leitura

Fundamentos da programação Arduino: variáveis, laços e funções

Aprenda programação Arduino do zero: setup e loop, int, long, byte, estouro, const, #define, if-else, switch-case, laços, arrays e funções com LEDs.

Programar o Arduino é dizer a uma placa Arduino o que fazer na linguagem C/C++: você escreve qual LED acende e quando, o que acontece quando um botão é pressionado e como interpretar um número que vem de um sensor, e a placa repete essas instruções milhares de vezes por segundo. A boa notícia é que quase todo sketch que você vai ver, de piscar um LED a um carrinho robô, é feito do mesmo conjunto de uns dez blocos de construção. São eles setup() e loop(), variáveis, constantes, operadores, if-else, switch-case, laços for e while, arrays e funções.

Neste guia você vai aprender esses blocos um por um, cada um com um exemplo real de LED ou botão. Todos os exemplos rodam no mesmo circuito simples e foram compilados e testados no Arduino Uno. Os pontos em que os iniciantes mais travam, ou seja, o estouro de variáveis, a confusão entre = e ==, o break esquecido e o contador que zera a cada ciclo, são explicados à parte. Nas últimas seções você também vai ver como a mesma lógica funciona no ESP32 e como ela fica em MicroPython no Raspberry Pi Pico.

Placa Arduino Uno e seus pinos digitais
Arduino Uno: todos os exemplos deste artigo usam um botão no pino D2 e seis LEDs nos pinos D8–D13. Graças ao LED embutido no D13, os primeiros exemplos nem precisam de fios.

O que é programação Arduino e qual linguagem ela usa?

O código do Arduino é baseado em C++; não existe uma “linguagem Arduino” separada. O núcleo (core) do Arduino acrescenta ao C++ funções prontas como pinMode(), digitalWrite() e delay(). O arquivo que você escreve se chama sketch e tem a extensão .ino. Quando você clica em Verificar, a Arduino IDE adiciona o arquivo de cabeçalho Arduino.h no início do arquivo e gera as declarações das funções para você, e o compilador transforma o código em linguagem de máquina, que o processador entende. Depois, o botão Carregar envia esse resultado pelo USB para o bootloader da placa. Se você ainda não instalou a IDE, comece pelo guia Como instalar a Arduino IDE.

Há algumas regras básicas a seguir ao escrever código. Aprendê-las logo de início elimina metade dos erros de compilação que você veria nos primeiros dias:

RegraForma corretaSe você não seguir
Toda instrução termina com ponto e vírguladigitalWrite(13, HIGH);expected ';' before '}' token (falta ponto e vírgula)
Maiúsculas e minúsculas fazem diferençapinMode(13, OUTPUT);'pinmode' was not declared in this scope (não declarado neste escopo)
Blocos de código ficam entre chavesvoid loop() { ... }Toda { precisa de uma } correspondente
Comentário de uma linha// esta linha não é executadaComentários não são compilados, servem para explicar
Comentário de várias linhas/* ... */Se você não fechar, tudo o que vem abaixo vira comentário
Texto entre aspas duplas, um único caractere entre aspas simples"Olá" e 'A'Trocar os tipos de aspas dá um valor errado ou um erro

O circuito usado nos exemplos

Todos os exemplos rodam em um único circuito. Ligue seis LEDs aos pinos D8–D13, cada um com um resistor de 220 Ω; a perna curta (cátodo) de cada LED vai ao GND. Você pode conferir o valor do resistor para o seu LED com a calculadora de resistor para LED. Ligue uma perna do botão ao D2 e a perna diagonalmente oposta ao GND. Como o código usa INPUT_PULLUP, não é preciso resistor externo: o pino lê HIGH quando o botão está solto e LOW quando está pressionado. Para mais detalhes, veja Como ligar um botão no Arduino e O que é LED?.

PeçaArduino UnoESP32Raspberry Pi Pico
6 LEDs (cada um com 220 Ω, cátodo no GND)D8, D9, D10, D11, D12, D13GPIO 18, 19, 21, 22, 23, 25GP14, GP13, GP12, GP11, GP10, GP9
Botão (outra perna no GND)D2GPIO 4GP2
LED da placaD13 (o LED marcado com L)GPIO 2 (na maioria dos DevKits)GP25 (“LED” no Pico W)
Velocidade serial9600 baud115200 baudShell do Thonny
Não ligue um LED sem resistor: tanto o LED quanto o pino do Arduino podem ser danificados. Se você usar duas pernas do mesmo lado ao encaixar o botão na protoboard, essas pernas podem já estar ligadas por dentro e o botão vai se comportar como se nunca fosse pressionado; por isso, usar as pernas na diagonal é a escolha mais segura. Você pode consultar os números dos pinos da sua placa na ferramenta Pinagem.

setup() e loop(): o esqueleto de todo sketch

Todo sketch Arduino tem duas funções obrigatórias. setup() roda só uma vez, quando a placa é ligada ou quando o botão de reset é pressionado; preparativos como a configuração dos pinos e Serial.begin() ficam aqui. Depois que setup() termina, loop() se repete para sempre. Nos bastidores, o núcleo do Arduino tem uma função main() oculta: primeiro ela prepara o hardware, depois chama setup() uma vez e chama loop() de novo e de novo dentro de um laço infinito. A área no topo do arquivo, fora dessas duas funções, é a área global; ali são definidas as constantes e as variáveis que precisam ser acessíveis de qualquer lugar.

C++ (Arduino IDE)
1// 1) Global alan: sabitler ve değişkenler / Global area: constants and variables
2const int LED_PIN = 13;        // dahili LED / built-in LED
3unsigned long passCount = 0;   // loop() kaç kez döndü / how many times loop() has run
4
5// 2) setup(): açılışta bir kez çalışır / runs once at power-up or reset
6void setup() {
7  pinMode(LED_PIN, OUTPUT);
8  Serial.begin(9600);
9  Serial.println("setup() bitti / setup() done");
10}
11
12// 3) loop(): sonsuza kadar tekrar eder / repeats forever
13void loop() {
14  passCount++;                       // her turda 1 artır / add 1 on every pass
15  Serial.print("Tur / Pass: ");
16  Serial.println(passCount);
17
18  digitalWrite(LED_PIN, HIGH);       // LED'i yak / LED on
19  delay(500);                        // 500 ms bekle / wait 500 ms
20  digitalWrite(LED_PIN, LOW);        // LED'i söndür / LED off
21  delay(500);
22}

Carregue o código e abra o Monitor Serial a 9600 baud: você verá “setup() done” uma vez, seguido de um número de ciclo que aumenta a cada segundo, enquanto o LED embutido pisca no mesmo ritmo. As duas chamadas delay(500) dentro de loop() esticam cada ciclo para cerca de um segundo; um loop() sem nenhuma espera e sem impressão serial pode rodar centenas de milhares de vezes por segundo em um Uno. Para tirar o máximo do Monitor Serial, dê uma olhada no guia Monitor Serial do Arduino.

Variáveis e tipos de dados: int, long, float, bool, byte

Uma variável é uma caixa com nome na memória que guarda um valor. Ao defini-la, você escreve primeiro o tipo, depois o nome e, se quiser, um valor inicial, por exemplo int counter = 0;. O tipo decide quantos bytes a caixa ocupa e que faixa de números ela consegue guardar. O Arduino Uno tem só 2 KB de RAM, então escolher o tipo certo importa. Nomes de variáveis começam com uma letra ou um sublinhado e não podem ter espaços nem letras acentuadas como é, ã ou ç.

TipoTamanho no UnoFaixaUso típico
bool1 bytetrue ou falseO LED está aceso? O botão foi pressionado?
byte1 byte0 – 255Número de pino, brilho PWM
char1 byte-128 – 127Um único caractere: 'A'
int2 bytes-32.768 – 32.767Contadores, resultados de analogRead()
unsigned int2 bytes0 – 65.535Só números positivos
long4 bytes-2.147.483.648 – 2.147.483.647Números inteiros grandes
unsigned long4 bytes0 – 4.294.967.295Medir tempo com millis()
float4 bytesCerca de ±3,4 × 10^38, 6–7 dígitos de precisãoTemperatura, tensão, médias
double4 bytes (igual ao float no Uno)Igual ao float8 bytes no ESP32

Algumas regras práticas vão poupar você de muita dor de cabeça. analogRead() devolve um número de 0 a 1023, então um int basta. Já millis() devolve um unsigned long; se você guardar o tempo em uma variável int, o valor estoura depois de 32,7 segundos e a sua temporização quebra. No Uno, as contas com float são feitas por software, por isso são mais lentas que as contas com inteiros, e nada além de 6–7 dígitos é confiável; é por isso que você não deve comparar dois floats com ==. Se quiser um tipo cujo tamanho seja o mesmo em qualquer placa, use tipos que trazem o tamanho no nome, como uint8_t, int16_t e uint32_t.

Estouro (overflow): o que acontece quando uma variável passa do limite?

Se você tentar guardar um número maior do que o tipo comporta, o Arduino não acusa erro; o número dá a volta, como o hodômetro de um carro. No Uno, um int guarda no máximo 32.767, e somar 1 a ele o transforma em -32.768. Um byte volta de 255 para 0. Uma armadilha mais traiçoeira são as contas com constantes: uma expressão como 60 * 1000 é calculada primeiro como int e estoura, mesmo que o resultado seja guardado em uma variável long. O sketch abaixo imprime todos esses casos no Monitor Serial, e o seu loop() mostra o estouro de um byte em um LED:

C++ (Arduino IDE)
1const int LED_PIN = 9;        // PWM destekli pin (~9) / PWM-capable pin (~9)
2byte brightness = 0;          // byte: 0-255
3
4void setup() {
5  pinMode(LED_PIN, OUTPUT);
6  Serial.begin(9600);
7
8  int a = 32767;              // Uno'da int'in en büyük değeri / largest int on an Uno
9  a = a + 1;                  // taşma: -32768 olur / overflow: becomes -32768
10  Serial.println(a);
11
12  long wrong = 60 * 1000;     // int ile hesaplanır, taşar: -5536 / computed as int, overflows: -5536
13  long right = 60 * 1000L;    // L eki hesabı long yapar: 60000 / the L suffix makes it long math: 60000
14  Serial.println(wrong);
15  Serial.println(right);
16
17  float half = 5 / 2;         // tam sayı bölmesi: 2.00 / integer division: 2.00
18  float half2 = 5 / 2.0;      // ondalıklı bölme: 2.50 / floating-point division: 2.50
19  Serial.println(half);
20  Serial.println(half2);
21}
22
23void loop() {
24  analogWrite(LED_PIN, brightness);   // parlaklığı uygula / apply the brightness
25  brightness++;                       // 255'ten sonra 0'a döner / wraps to 0 after 255
26  delay(10);
27}
Linha de códigoSaída no UnoSaída no ESP32 e no PicoPor quê?
a = 32767 + 1-3276832768O int tem 2 bytes no Uno e 4 bytes nos outros
60 * 1000-553660000Constantes são calculadas primeiro como int
60 * 1000L6000060000O sufixo L faz a conta em long
5 / 22.002.00Dividir dois inteiros dá um inteiro
5 / 2.02.502.50Se um dos números tem ponto decimal, o resultado também tem

Observe o LED no D9: o brilho sobe devagar de 0 a 255, então a linha brightness++ transforma o valor em 0 em vez de 256 e o LED apaga de repente. É o jeito mais fácil de ver um estouro com os próprios olhos. O D9 é um dos pinos PWM do Uno marcados com ~, por isso analogWrite() funciona nele; o PWM é explicado em detalhes em O que é PWM?. O fato de millis() voltar a zero depois de cerca de 49,7 dias também é um estouro, e isso não causa nenhum problema em um código bem escrito; você vai ver como escrevê-lo no guia millis() ou delay().

Na Arduino IDE, abra Arquivo > Preferências e ajuste Avisos do compilador para “Mais” ou “Todos”. Assim, quando você escrever uma expressão que estoura, como 60 * 1000, o compilador mostra o aviso integer overflow in expression e você pega o bug antes que o código chegue à placa.

const e #define: valores que nunca mudam

Há duas maneiras de dar nome a números que não vão mudar enquanto o programa roda, como números de pino e limites. Uma constante definida com const tem tipo, segue as regras de escopo e, se você tentar alterá-la sem querer, o compilador para você com assignment of read-only variable 'LED_PIN'. Já #define é uma diretiva do pré-processador: antes de a compilação começar, ela troca cada BUTTON_PIN do código por 2. Ela não tem tipo, e a linha não leva nem ponto e vírgula nem sinal de igual. Para números simples, nenhuma das duas usa RAM, porque o compilador coloca o valor direto no código. Hoje o const é o preferido, e escrever os nomes das constantes em maiúsculas (UPPER_CASE) é um costume comum. O exemplo abaixo usa as duas: o LED fica aceso enquanto o botão estiver pressionado.

C++ (Arduino IDE)
1#define BUTTON_PIN 2          // önişlemci: derlemeden önce metin değişimi / preprocessor: text swap before compiling
2const int LED_PIN = 13;       // tipi olan, salt okunur sabit / typed, read-only constant
3
4void setup() {
5  pinMode(LED_PIN, OUTPUT);
6  pinMode(BUTTON_PIN, INPUT_PULLUP);   // dahili pull-up: basılınca LOW / internal pull-up: LOW when pressed
7}
8
9void loop() {
10  if (digitalRead(BUTTON_PIN) == LOW) {   // buton basılı mı? / is the button pressed?
11    digitalWrite(LED_PIN, HIGH);          // evet: LED yanar / yes: LED on
12  } else {
13    digitalWrite(LED_PIN, LOW);           // hayır: LED söner / no: LED off
14  }
15}
Se você escrever #define LED_PIN 13;, o ponto e vírgula vira parte do valor, e a linha pinMode(LED_PIN, OUTPUT) se transforma em pinMode(13;, OUTPUT). O compilador acusa expected ')' before ';' token; a nota in expansion of macro 'LED_PIN' logo abaixo mostra que o problema aparece onde a constante é usada, mas a linha que você precisa corrigir é a do #define. Escrever #define LED_PIN = 13 também dá o erro expected primary-expression before '=' token.

Operadores: aritméticos, de comparação e lógicos

Os operadores permitem fazer contas com variáveis, compará-las e combinar condições. Quase todos os operadores que você vai usar nos projetos do dia a dia estão nesta tabela:

GrupoOperadorSignificadoExemplo
Aritméticos+ - * /As quatro operações básicasx = a * 2;
Resto%Resto de uma divisão (só com inteiros)7 % 3 dá 1
Atribuição= += -= *= /=Definir, somar, subtrair…total += 5;
Incremento, decremento++ --Somar 1 ou subtrair 1i++;
Comparação== != < > <= >=O resultado é true ou falseif (x >= 10)
Lógicos&& || !E, OU, NÃOif (a && !b)
Condicional? :if-else curtox > 0 ? HIGH : LOW
Bit a bit& | ^ ~ << >>Opera sobre os bits de um número1 << 3 dá 8

Os dois pares que mais se confundem são = e == e & e &&. Um sinal de igual simples atribui um valor; o duplo compara. Um & simples opera sobre os bits dos números, enquanto && pergunta se as duas condições são verdadeiras. Você pode testar as tabelas-verdade de E, OU e NÃO na ferramenta Portas lógicas, e os operadores bit a bit são explicados em detalhes no guia sistemas de numeração e operações bit a bit. O exemplo abaixo conta quantas vezes o botão foi pressionado e acende o LED depois de um número ímpar de toques:

C++ (Arduino IDE)
1const int LED_PIN = 13;
2const int BUTTON_PIN = 2;
3
4int presses = 0;               // toplam basış sayısı / total number of presses
5bool lastPressed = false;      // önceki turdaki durum / state on the previous pass
6
7void setup() {
8  pinMode(LED_PIN, OUTPUT);
9  pinMode(BUTTON_PIN, INPUT_PULLUP);
10  Serial.begin(9600);
11}
12
13void loop() {
14  bool pressed = (digitalRead(BUTTON_PIN) == LOW);   // karşılaştırma: true ya da false / comparison: true or false
15
16  if (pressed && !lastPressed) {     // şimdi basılı VE önceden değildi: yeni basış / pressed now AND not before: new press
17    presses++;                       // 1 artır / increment by 1
18    Serial.print("Basış / Presses: ");
19    Serial.println(presses);
20    delay(50);                       // basit debounce / simple debounce
21  }
22  lastPressed = pressed;
23
24  // Kalan (%) ile tek mi çift mi? / odd or even using the remainder (%)
25  digitalWrite(LED_PIN, (presses % 2 == 1) ? HIGH : LOW);
26}

O segredo aqui é a condição pressed && !lastPressed: ela significa “pressionado agora E não pressionado no ciclo anterior”. Assim o número não fica subindo enquanto você segura o botão, e cada toque é contado só uma vez. presses % 2 dá o resto da divisão do número por 2: 1 para números ímpares, 0 para os pares. Na última linha, o operador ? : escolhe HIGH ou LOW com base nesse resultado.

if, else if e else: tomando decisões

if executa o código entre chaves quando a condição entre parênteses é verdadeira. Se a condição for falsa, o bloco else é executado, se existir. Quando você precisa de mais opções, acrescenta else if no meio. As condições são verificadas de cima para baixo e, assim que o bloco da primeira verdadeira é executado, as demais são ignoradas. Em C++, todo valor diferente de zero conta como verdadeiro, então if (counter) significa “se o contador não for zero”. Já vimos um exemplo simples de if-else na seção do const. Agora vamos escrever um que diferencia um toque curto de um toque longo:

C++ (Arduino IDE)
1const int LED_PIN = 13;
2const int BUTTON_PIN = 2;
3
4void setup() {
5  pinMode(LED_PIN, OUTPUT);
6  pinMode(BUTTON_PIN, INPUT_PULLUP);
7  Serial.begin(9600);
8}
9
10void loop() {
11  if (digitalRead(BUTTON_PIN) == LOW) {             // basıldı / pressed
12    unsigned long start = millis();
13    while (digitalRead(BUTTON_PIN) == LOW) {         // bırakılana kadar bekle / wait until released
14    }
15    unsigned long held = millis() - start;          // basılı kalma süresi (ms) / hold time (ms)
16
17    if (held < 50) {
18      return;                                       // 50 ms'den kısa: parazit, yok say / under 50 ms: bounce, ignore
19    } else if (held < 1000) {
20      digitalWrite(LED_PIN, HIGH);                  // kısa basış: LED yak / short press: LED on
21      Serial.println("Kısa basış / Short press");
22    } else {
23      digitalWrite(LED_PIN, LOW);                   // uzun basış: LED söndür / long press: LED off
24      Serial.println("Uzun basış / Long press");
25    }
26  }
27}

No momento em que o botão é pressionado, o código registra o tempo com millis(), espera em um laço while até o botão ser solto e calcula o tempo entre os dois. Toques com menos de 50 ms são tratados como ruído do repique mecânico do botão, e return encerra mais cedo aquele ciclo do loop(). Um toque com menos de 1 segundo acende o LED; um mais longo o apaga. A ordem das condições importa: se você tivesse colocado a verificação held < 1000 primeiro, uma falha de 30 ms também contaria como “toque curto”.

Escrever if (mode = 2) é o erro clássico: essa linha não compara nada, ela atribui 2 a mode e, como o resultado não é zero, a condição é sempre verdadeira. Outro bug traiçoeiro é um ponto e vírgula logo depois dos parênteses, como em if (x == 5);: o if termina com uma instrução vazia e o bloco de baixo roda sempre, qualquer que seja a condição.

switch-case: escolhendo entre vários casos

Se você precisa fazer muitas coisas diferentes dependendo do valor de uma variável, switch-case é mais legível que uma longa cadeia de else if. O switch compara o valor entre seus parênteses com cada rótulo case e começa a executar a partir do que coincidir. Você deve terminar cada case com break; caso contrário, o programa continua no código do case seguinte. Se nenhum case coincidir, o bloco default é executado. O switch só funciona com tipos inteiros, como int, byte, char e enum; para String, float ou faixas como “entre 10 e 20”, você precisa de if. No exemplo abaixo, cada toque leva a lâmpada ao modo seguinte: apagada, acesa, piscando devagar, piscando rápido.

C++ (Arduino IDE)
1const int LED_PIN = 13;
2const int BUTTON_PIN = 2;
3
4byte mode = 0;                 // 0 kapalı, 1 açık, 2 yavaş, 3 hızlı / 0 off, 1 on, 2 slow, 3 fast
5bool lastPressed = false;
6
7void setup() {
8  pinMode(LED_PIN, OUTPUT);
9  pinMode(BUTTON_PIN, INPUT_PULLUP);
10}
11
12void loop() {
13  bool pressed = (digitalRead(BUTTON_PIN) == LOW);
14  if (pressed && !lastPressed) {
15    mode = (mode + 1) % 4;      // 0 → 1 → 2 → 3 → 0
16    delay(50);                  // basit debounce / simple debounce
17  }
18  lastPressed = pressed;
19
20  switch (mode) {
21    case 0:                                        // kapalı / off
22      digitalWrite(LED_PIN, LOW);
23      break;
24    case 1:                                        // sürekli açık / always on
25      digitalWrite(LED_PIN, HIGH);
26      break;
27    case 2:                                        // 500 ms'de bir değiş / toggle every 500 ms
28      digitalWrite(LED_PIN, (millis() / 500) % 2 ? HIGH : LOW);
29      break;
30    case 3:                                        // 100 ms'de bir değiş / toggle every 100 ms
31      digitalWrite(LED_PIN, (millis() / 100) % 2 ? HIGH : LOW);
32      break;
33    default:                                       // beklenmeyen değer: başa dön / unexpected value: reset
34      mode = 0;
35      break;
36  }
37}

A linha mode = (mode + 1) % 4 faz o modo passar por 0, 1, 2, 3 e, graças ao operador %, voltar a 0 quando chega a 4. Repare que não usamos delay() para piscar: a expressão (millis() / 500) % 2 alterna entre 0 e 1 a cada 500 ms, então o botão responde na hora enquanto o LED pisca. A versão “adulta” dessa abordagem de modos se chama máquina de estados; você pode ver como ela é usada em projetos como semáforos e menus no guia Máquina de estados no Arduino.

Se você definir uma variável dentro de um case, coloque o código desse case entre chaves: case 1: { byte step = 3; ... break; }. Sem as chaves, o Uno compila com apenas um aviso, mas o ESP32 dá o erro jump to case label e o código simplesmente não compila.

Laços for, while e do-while

Os laços (loops) executam um bloco de código várias e várias vezes. Os parênteses de um laço for têm três partes: o início (int i = 0), a condição para continuar (i < 6) e o passo depois de cada volta (i++). O for é ideal quando você sabe quantas vezes repetir. O while continua enquanto sua condição for verdadeira e verifica a condição antes de cada volta, por isso serve para esperas cuja duração você não conhece de antemão. O do-while verifica a condição no fim, então sempre roda pelo menos uma vez. Dentro de um laço, break sai do laço na hora e continue pula o resto da volta atual e passa para a próxima. Como loop() já é um laço infinito, normalmente você não precisa escrever while (true) dentro dele.

LaçoQuando a condição é verificada?Quando usarExemplo
forAntes de cada voltaQuando o número de repetições é conhecidofor (int i = 0; i < 6; i++)
whileAntes de cada voltaAo esperar que algo aconteçawhile (digitalRead(2) == HIGH)
do-whileDepois de cada voltaQuando precisa rodar pelo menos uma vezdo { ... } while (x < 10);

O exemplo abaixo cria um efeito Knight Rider com seis LEDs. O laço for em setup() transforma os seis pinos em saídas em uma única linha, e o laço while em loop() espera até o botão ser pressionado. Enquanto você segura o botão, a luz corre do D8 ao D13 e volta:

C++ (Arduino IDE)
1const int FIRST_LED = 8;       // ilk LED pini / first LED pin
2const int LAST_LED = 13;       // son LED pini / last LED pin
3const int BUTTON_PIN = 2;
4
5void setup() {
6  for (int pin = FIRST_LED; pin <= LAST_LED; pin++) {
7    pinMode(pin, OUTPUT);      // 6 pini tek döngüyle çıkış yap / make 6 pins outputs with one loop
8  }
9  pinMode(BUTTON_PIN, INPUT_PULLUP);
10}
11
12void loop() {
13  while (digitalRead(BUTTON_PIN) == HIGH) {
14    // butona basılana kadar burada bekle / wait here until the button is pressed
15  }
16
17  for (int pin = FIRST_LED; pin <= LAST_LED; pin++) {      // ileri: 8 → 13 / forward: 8 → 13
18    digitalWrite(pin, HIGH);
19    delay(80);
20    digitalWrite(pin, LOW);
21  }
22  for (int pin = LAST_LED - 1; pin > FIRST_LED; pin--) {   // geri: 12 → 9 / back: 12 → 9
23    digitalWrite(pin, HIGH);
24    delay(80);
25    digitalWrite(pin, LOW);
26  }
27}

Começar o laço de volta em LAST_LED - 1 e rodá-lo só enquanto o pino for maior que FIRST_LED é uma escolha proposital: os LEDs das pontas não acendem duas vezes seguidas, então o movimento fica suave. Para controle de velocidade, um efeito de enchimento e mais padrões, veja o guia Knight Rider e LED sequencial no Arduino.

O laço for (byte i = 0; i < 256; i++) nunca termina: um byte guarda no máximo 255, então i volta a 0 antes de chegar a 256 e a condição continua verdadeira para sempre. Do mesmo jeito, um laço while que espera uma condição que nunca acontece, como um sensor cujo fio se soltou, trava o programa de vez.

Arrays: guardando vários valores com um só nome

Um array (vetor) guarda vários valores do mesmo tipo, em ordem, com um único nome. A linha const byte LEDS[] = {8, 9, 10, 11, 12, 13}; cria um array com seis elementos. Você acessa os elementos com colchetes, e a contagem começa em 0: LEDS[0] é 8 e LEDS[5] é 13. O tamanho de um array não pode mudar depois de definido. Se você deixar sizeof(LEDS) / sizeof(LEDS[0]) calcular o número de elementos em vez de digitá-lo à mão, seus laços se ajustam sozinhos quando você acrescenta um novo pino ao array. Um índice fora do array, como LEDS[6], não gera erro; ele lê ou corrompe alguma outra variável na memória. Por isso, você precisa garantir que o índice fique entre 0 e o número de elementos menos um. O exemplo abaixo transforma os seis LEDs em um dado eletrônico:

C++ (Arduino IDE)
1const byte LEDS[] = {8, 9, 10, 11, 12, 13};               // LED pinleri / LED pins
2const byte LED_COUNT = sizeof(LEDS) / sizeof(LEDS[0]);    // eleman sayısı: 6 / number of elements: 6
3const byte BUTTON_PIN = 2;
4
5void setup() {
6  for (byte i = 0; i < LED_COUNT; i++) {     // indeks 0'dan başlar / index starts at 0
7    pinMode(LEDS[i], OUTPUT);
8  }
9  pinMode(BUTTON_PIN, INPUT_PULLUP);
10  randomSeed(analogRead(A0));                // boştaki A0'ın gürültüsüyle tohumla / seed from noise on the unconnected A0
11}
12
13void loop() {
14  if (digitalRead(BUTTON_PIN) == LOW) {
15    byte dice = random(1, 7);                // 1 ile 6 arası / between 1 and 6
16    for (byte i = 0; i < LED_COUNT; i++) {
17      digitalWrite(LEDS[i], (i < dice) ? HIGH : LOW);   // ilk 'dice' kadar LED yanar / the first 'dice' LEDs light up
18    }
19    delay(300);                              // basılı tutarsan zar dönmeye devam eder / keeps rolling while held
20  }
21}

random(1, 7) gera um número de 1 a 6, porque o limite superior não entra no resultado. Acendem tantos LEDs quanto o número sorteado: se o dado der 4, os quatro LEDs de LEDS[0] a LEDS[3] acendem. A linha randomSeed(analogRead(A0)) usa o ruído elétrico do pino A0, que está desconectado, como valor inicial; sem ela, a placa geraria a mesma sequência de números toda vez que fosse ligada. Outra vantagem dos arrays: se você mudar os LEDs para outros pinos, só precisa alterar a primeira linha.

Escrevendo funções: dividindo o código em partes

Se você faz a mesma tarefa em vários pontos do programa, transforme essa tarefa em uma função. Uma função é formada por um tipo de retorno, um nome, parâmetros entre parênteses e um corpo entre chaves. Funções que não devolvem valor têm o tipo void; as que devolvem entregam o resultado com return. Os parâmetros são passados como cópias, então alterar um parâmetro dentro da função não altera a variável no lugar onde ela foi chamada. A Arduino IDE gera sozinha as declarações das funções em arquivos .ino, por isso você também pode escrever uma função abaixo de loop(). Em arquivos .cpp ou no PlatformIO, porém, é preciso declarar a função antes de usá-la. Dê às funções nomes que digam o que elas fazem, como blinkLed ou isPressed.

C++ (Arduino IDE)
1const byte LED_PIN = 13;
2const byte BUTTON_PIN = 2;
3
4// Değer döndürmeyen (void) fonksiyon / function that returns nothing (void)
5void blinkLed(byte pin, byte times, int ms) {
6  for (byte i = 0; i < times; i++) {
7    digitalWrite(pin, HIGH);
8    delay(ms);
9    digitalWrite(pin, LOW);
10    delay(ms);
11  }
12}
13
14// bool döndüren fonksiyon / function that returns a bool
15bool isPressed(byte pin) {
16  return digitalRead(pin) == LOW;      // INPUT_PULLUP: basılı = LOW / pressed = LOW
17}
18
19void setup() {
20  pinMode(LED_PIN, OUTPUT);
21  pinMode(BUTTON_PIN, INPUT_PULLUP);
22  blinkLed(LED_PIN, 3, 100);           // açılışta 3 hızlı yanıp sönme / 3 quick blinks at start-up
23}
24
25void loop() {
26  if (isPressed(BUTTON_PIN)) {
27    blinkLed(LED_PIN, 2, 250);         // basınca 2 yavaş yanıp sönme / 2 slow blinks per press
28  }
29}

blinkLed() recebe o pino, o número de piscadas e a velocidade como parâmetros, então você pode reaproveitar a mesma função para outro LED e outra velocidade. isPressed() devolve um bool e mantém em um só lugar a lógica “pressionado = LOW” do INPUT_PULLUP; se um dia você ligar o botão ao contrário, só muda essa linha. Mas o delay() dentro da função ainda para o programa. Para funções que fazem várias coisas ao mesmo tempo, você precisa de temporização baseada em millis().

Variáveis globais, locais e static

O lugar onde uma variável pode ser vista se chama escopo. As variáveis globais, definidas fora das funções, vivem durante todo o programa e podem ser lidas e alteradas de qualquer lugar. As variáveis locais, definidas dentro de uma função ou de um bloco entre chaves, são criadas de novo toda vez que esse bloco roda e desaparecem quando ele termina. É daqui que vem a queixa mais comum dos iniciantes: “meu contador fica sempre em 1”. Se você colocar static na frente de uma variável local, ela recebe o valor inicial só na primeira vez e mantém o valor entre os ciclos, mas continua visível apenas dentro daquela função.

C++ (Arduino IDE)
1const byte LED_PIN = 13;
2const byte BUTTON_PIN = 2;
3
4int globalCount = 0;            // global: her yerden görünür, değerini korur / global: visible everywhere, keeps its value
5
6void setup() {
7  pinMode(LED_PIN, OUTPUT);
8  pinMode(BUTTON_PIN, INPUT_PULLUP);
9  Serial.begin(9600);
10}
11
12void loop() {
13  int localCount = 0;           // yerel: her turda yeniden 0 olur / local: back to 0 on every pass
14  static int staticCount = 0;   // static yerel: değerini korur, yalnız burada görünür / static local: keeps its value, visible only here
15
16  if (digitalRead(BUTTON_PIN) == LOW) {
17    globalCount++;
18    localCount++;
19    staticCount++;
20    Serial.print(globalCount);
21    Serial.print(' ');
22    Serial.print(localCount);
23    Serial.print(' ');
24    Serial.println(staticCount);                   // 3. basışta: 3 1 3 / on the 3rd press: 3 1 3
25    digitalWrite(LED_PIN, (staticCount % 2) ? HIGH : LOW);
26    delay(300);
27  }
28}

Quando você pressiona o botão três vezes, o Monitor Serial mostra as linhas 1 1 1, 2 1 2 e 3 1 3: os contadores global e static continuam subindo, enquanto o contador local fica sempre em 1, porque recomeça do 0 a cada ciclo do loop(). Se você definir uma variável local com o mesmo nome de uma global, a local esconde a global, e dentro daquela função você nunca mexe na variável global. Variáveis globais alteradas por uma função de interrupção (ISR) também precisam ser declaradas como volatile; os detalhes estão no guia interrupções no Arduino.

Mantenha as variáveis o mais locais possível: use static para contadores de que só uma função precisa e global para valores que várias funções compartilham. A linha Variáveis globais usam … bytes de memória dinâmica, que a IDE imprime depois de compilar, mostra quanta RAM as suas variáveis globais ocupam. No Uno, um comportamento instável pode começar quando isso passa de 75 por cento; você encontra soluções no guia memória insuficiente no Arduino.

Programação Arduino no ESP32: mesma linguagem, tamanhos diferentes

Quando você programa o ESP32 com a Arduino IDE, usa a mesma linguagem C++ e os mesmos comandos, mas há algumas diferenças. Como o ESP32 tem um processador de 32 bits, o int tem 4 bytes e guarda valores de -2.147.483.648 a 2.147.483.647, então 32767 + 1 no exemplo de estouro dá 32768. O double tem 8 bytes, analogRead() devolve valores de 0 a 4095 e os pinos trabalham com 3,3 V. Você pode fazer impressão formatada com Serial.printf(). random() usa por padrão o gerador de números aleatórios por hardware, então a linha randomSeed() não é necessária. O programa abaixo junta tudo o que você aprendeu neste artigo: um array, uma função, switch-case e um botão que troca de modo. Para as características gerais da placa, veja O que é o ESP32?.

C++ (Arduino IDE)
1// ESP32: aynı dil, farklı pinler / ESP32: same language, different pins
2const byte LEDS[] = {18, 19, 21, 22, 23, 25};            // güvenli GPIO'lar / safe GPIOs
3const byte LED_COUNT = sizeof(LEDS) / sizeof(LEDS[0]);
4const byte BUTTON_PIN = 4;                                // diğer bacağı GND'ye / other leg to GND
5
6byte mode = 0;                  // 0 kapalı, 1 yürüyen ışık, 2 hepsi yanıp söner / 0 off, 1 chaser, 2 all blink
7bool lastPressed = false;
8
9void setAll(bool on) {
10  for (byte i = 0; i < LED_COUNT; i++) {
11    digitalWrite(LEDS[i], on ? HIGH : LOW);
12  }
13}
14
15void setup() {
16  Serial.begin(115200);
17  for (byte i = 0; i < LED_COUNT; i++) {
18    pinMode(LEDS[i], OUTPUT);
19  }
20  pinMode(BUTTON_PIN, INPUT_PULLUP);
21  Serial.printf("int = %d bayt / bytes\n", (int)sizeof(int));   // ESP32'de 4 / 4 on the ESP32
22
23  int a = 32767;
24  a = a + 1;
25  Serial.println(a);                                          // ESP32'de 32768, taşma yok / 32768 on the ESP32, no overflow
26}
27
28void loop() {
29  bool pressed = (digitalRead(BUTTON_PIN) == LOW);
30  if (pressed && !lastPressed) {
31    mode = (mode + 1) % 3;      // 0 → 1 → 2 → 0
32    delay(50);
33  }
34  lastPressed = pressed;
35
36  switch (mode) {
37    case 0:
38      setAll(false);
39      break;
40    case 1: {                                                 // case içinde değişken için süslü parantez / braces for a variable inside a case
41      byte step = (millis() / 120) % LED_COUNT;               // sıradaki LED / LED whose turn it is
42      for (byte i = 0; i < LED_COUNT; i++) {
43        digitalWrite(LEDS[i], (i == step) ? HIGH : LOW);
44      }
45      break;
46    }
47    case 2:
48      setAll((millis() / 300) % 2);                           // 300 ms'de bir hepsi / all of them every 300 ms
49      break;
50  }
51}

Para rodar este programa em um Uno, basta mudar o array LEDS para {8, 9, 10, 11, 12, 13}, definir BUTTON_PIN como 2 e trocar a linha Serial.printf(), que o Uno não suporta, por Serial.print(). Essa é justamente a vantagem de definir os números dos pinos como constantes e arrays no topo. Repare também que o bloco do case 1 está entre chaves; como a variável step é definida dentro dele, essas chaves são obrigatórias no ESP32.

Raspberry Pi Pico: a mesma lógica em MicroPython

Você pode programar o Raspberry Pi Pico em C++ adicionando o pacote de placas RP2040 à Arduino IDE; nesse caso, o código deste artigo funciona assim que você muda os números dos pinos, e o int volta a ter 4 bytes. A forma mais comum de usar o Pico, porém, é com MicroPython. Em Python você não escreve o tipo das variáveis, os blocos são definidos pela indentação em vez de chaves, não há ponto e vírgula no fim da linha e os números inteiros não estouram: crescem o quanto for preciso. O código abaixo é a versão em MicroPython do exemplo do dado eletrônico. Para a configuração, veja O que é o Raspberry Pi Pico? e, para os detalhes da linguagem, Fundamentos de MicroPython.

MicroPython
1from machine import Pin
2import time, random
3
4leds = [Pin(n, Pin.OUT) for n in (14, 13, 12, 11, 10, 9)]   # liste = dizi / list = array
5button = Pin(2, Pin.IN, Pin.PULL_UP)                        # basılınca 0 / reads 0 when pressed
6
7def show(count):                                            # fonksiyon / function
8    for i, led in enumerate(leds):                          # for döngüsü / for loop
9        led.value(1 if i < count else 0)
10
11show(0)                                                     # setup() karşılığı: bir kez çalışır / like setup(): runs once
12while True:                                                 # loop() karşılığı / like loop()
13    if button.value() == 0:                                 # buton basılı mı? / is the button pressed?
14        show(random.randint(1, 6))                          # 1-6 arası zar at / roll a number from 1 to 6
15        time.sleep_ms(300)
ConceitoArduino (C++)MicroPython
Parte que roda uma vezsetup()O código antes da linha while True:
Parte que se repete para sempreloop()while True:
Variávelint x = 5;x = 5
Constanteconst int PIN = 2;PIN = const(2) (com from micropython import const)
Condiçãoif / else if / else (se / senão se / senão)if / elif / else
Escolha múltiplaswitch-caseCadeia de if / elif
Laço de contagemfor (int i = 0; i < 6; i++)for i in range(6):
Arraybyte a[] = {1, 2, 3};a = [1, 2, 3]
Funçãovoid blink(int pino) { ... }def blink(pino):
Lógica&& || !and or not
Comentário// comentário# comentário

Ideias de projetos que você pode montar com esse básico

  • Semáforo: monte um cruzamento com botão de pedestre usando valores const, delay() e if; o passo a passo está em Semáforo com Arduino.
  • Knight Rider e LED sequencial: crie dezenas de padrões de luz diferentes com um laço for e um array.
  • Dado eletrônico: leve o dado de 6 LEDs para um display de 7 segmentos com um array, random() e uma função; para isso, veja o guia display de 7 segmentos.
  • Teste de reação: acenda um LED em um momento aleatório e meça a rapidez com que você pressiona o botão usando um unsigned long e millis().
  • Lâmpada com vários modos: modos apagada, fixa, respiração e flash com um só botão usando switch-case; para uma versão colorida, dê uma olhada em Como usar um LED RGB.
  • Fechadura com senha: junte as teclas pressionadas em um array e compare com a senha guardada; os detalhes estão no guia do teclado matricial.

Erros comuns e como corrigi-los

ErroSintomaSolução
Usar = em uma comparaçãoO bloco if sempre é executadoUse == para comparar
Um ; ou = na linha do #defineexpected ')' before ';' token (falta parêntese antes do ponto e vírgula)Escreva #define LED_PIN 13
Comando com maiúsculas e minúsculas erradas'pinmode' was not declared in this scope (não declarado neste escopo)Preste atenção às maiúsculas e minúsculas, como em pinMode
Letra acentuada no nome de uma variávelstray '\303' in program (caractere inválido no programa)Use só letras sem acento, dígitos e _ nos nomes
Calcular 60 * 1000 como intO resultado é -5536Escreva 60 * 1000L ou 60000UL
Guardar millis() em um intA temporização quebra depois de 32 segundosUse unsigned long
Divisão de inteiros5 / 2 dá 2Escreva 5 / 2.0 ou converta para float
Esquecer o break no fim de um caseO case seguinte também é executadoTermine todo case com break
Escrever fora de um arrayValores aleatórios, travamentosMantenha o índice entre 0 e o número de elementos - 1
Deixar um contador localO contador fica sempre em 1Torne-o global ou static
Quando aparecer um erro de compilação, leia só a primeira linha de erro e corrija-a; a maioria dos erros nas linhas seguintes é efeito colateral do primeiro. Você encontra o significado das mensagens no guia erros de compilação do Arduino. Se não tiver uma placa à mão, também pode testar o código em uma simulação no Tinkercad.

Depois de aprender esses blocos de construção, o próximo passo é conhecer os comandos mais usados: a lista de comandos do Arduino cobre tudo, de pinMode() a attachInterrupt(), com exemplos. No app BlueGrays você também encontra projetos como piscar um LED, controlar um LED com um botão, Knight Rider, semáforo e dado, passo a passo, com esquemas do circuito e código pronto, para testar em um circuito real cada estrutura que aprendeu aqui.

Perguntas frequentes

Que linguagem de programação o Arduino usa?

O código do Arduino é baseado em C++. O núcleo do Arduino acrescenta à linguagem funções prontas como pinMode(), digitalWrite() e delay(), e o código é escrito em arquivos com a extensão .ino. Placas como o ESP32 e o Raspberry Pi Pico também podem ser programadas em MicroPython, se você preferir.

Por onde começar a aprender programação Arduino?

Primeiro, instale a Arduino IDE e carregue o exemplo Blink, que faz piscar o LED embutido. Depois, experimente nesta ordem setup() e loop(), variáveis, if-else, laços, arrays e funções, cada um com um circuito de LED ou botão. Usar cada assunto novo em um pequeno projeto fixa o aprendizado muito melhor do que só ler sobre ele.

Qual é a diferença entre int e long no Arduino?

No Arduino Uno, um int tem 2 bytes e guarda números de -32.768 a 32.767, enquanto um long tem 4 bytes e vai até cerca de ±2,1 bilhões. Um número que passa desse limite dá a volta sem nenhum erro, por isso long ou unsigned long são usados para valores grandes, como o de millis(). No ESP32 e no Pico, o int também tem 4 bytes.

Qual é a diferença entre const e #define no Arduino?

Uma constante definida com const tem tipo, segue as regras de escopo, e o compilador acusa erro se você tentar alterá-la. #define é uma substituição de texto feita antes da compilação; não tem tipo e causa erros inesperados se você terminar a linha com ponto e vírgula. Nenhum dos dois usa RAM para números simples, mas o const costuma ser preferido por ser mais seguro.

Qual é a diferença entre um laço for e um laço while no Arduino?

Um laço for reúne o início, a condição e o passo em uma só linha e é usado quando você sabe quantas vezes repetir, por exemplo para acender seis LEDs um após o outro. Um laço while só verifica uma condição e continua rodando enquanto ela for verdadeira, o que serve para situações de duração desconhecida, como esperar o toque em um botão.

Dá para programar o Arduino em Python?

Placas de 8 bits como o Arduino Uno não conseguem rodar Python, por isso são programadas em C++. Já o ESP32 e o Raspberry Pi Pico podem ser programados em MicroPython. Um programa em Python rodando no seu computador também pode controlar um Uno de forma indireta, conversando com ele pela porta serial.

Ver todos os projetos
LED piscante
#1Iniciante

LED piscante

O primeiro contato com a eletrônica: acender e apagar um único LED. O Arduino acende e apaga o LED em intervalos curtos, fazendo a luz piscar rapidamente. Você vê da forma mais simples como a placa co

4.6(188)
1127
Controle de LED com botão (momentâneo)
#9Iniciante

Controle de LED com botão (momentâneo)

É como uma campainha: a luz fica acesa enquanto você pressiona o botão e apaga quando você solta. O Arduino monitora o botão continuamente; enquanto ele está pressionado, o LED fica aceso e apaga assi

4.8(113)
229
Ligar e desligar um LED com botão
#10Iniciante

Ligar e desligar um LED com botão

Pressione o botão uma vez para acender a luz e outra vez para apagar, como o interruptor do seu quarto. O Arduino detecta cada toque e inverte o estado do LED, aguardando um breve instante para que as

4.8(100)
167
Efeito Knight Rider com LEDs
#6Intermediário

Efeito Knight Rider com LEDs

O clássico efeito Knight Rider: um ponto de luz percorre seis LEDs de uma ponta à outra e depois volta. O Arduino acende cada LED por um décimo de segundo antes de passar para o próximo, muda de direç

4.8(106)
202
Semáforo
#3Iniciante

Semáforo

Uma pequena versão do semáforo que você vê na rua todos os dias. O vermelho fica aceso por dois segundos, o amarelo acende por um instante, o verde fica aceso por dois segundos e depois volta ao verme

4.8(131)
397
Controle de LED com botão no ESP32 (momentâneo)
#104Iniciante

Controle de LED com botão no ESP32 (momentâneo)

O LED acende enquanto o botão está pressionado e apaga ao soltá-lo. Mostra como usar o pino de entrada digital e o resistor pull-up interno do ESP32. O modo INPUT_PULLUP dispensa um resistor externo.

4.6(36)
55
Raspberry Pi Pico: ligar e desligar LED com botão
#156Iniciante

Raspberry Pi Pico: ligar e desligar LED com botão

Botão + LED — próximo nível! Ao contrário do projeto básico de botão, não é preciso manter o botão pressionado. Pressione e solte → LED aceso. De novo → LED apagado (alternância). O circuito é o mesmo (LED no GP14, botão no GP2), mas o código muda bastante. Aprenda detecção de borda (o instante da queda 1→0), rastreamento de estado e debounce (filtro de repique). led.toggle() alterna o estado em uma linha. Base para projetos interativos com botões (seletor de modo, IRQ, clique duplo).

0.0(0)
9
Projeto de dado
#79Intermediário
Premium

Projeto de dado

Chega de perder o dado durante os jogos de tabuleiro: a cada toque no botão, um número aleatório de 1 a 6 aparece no display de 7 segmentos. Cada toque gera exatamente um resultado, que também aparece

5.0(48)
67