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.

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:
| Regra | Forma correta | Se você não seguir |
|---|---|---|
| Toda instrução termina com ponto e vírgula | digitalWrite(13, HIGH); | expected ';' before '}' token (falta ponto e vírgula) |
| Maiúsculas e minúsculas fazem diferença | pinMode(13, OUTPUT); | 'pinmode' was not declared in this scope (não declarado neste escopo) |
| Blocos de código ficam entre chaves | void loop() { ... } | Toda { precisa de uma } correspondente |
| Comentário de uma linha | // esta linha não é executada | Comentá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ça | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| 6 LEDs (cada um com 220 Ω, cátodo no GND) | D8, D9, D10, D11, D12, D13 | GPIO 18, 19, 21, 22, 23, 25 | GP14, GP13, GP12, GP11, GP10, GP9 |
| Botão (outra perna no GND) | D2 | GPIO 4 | GP2 |
| LED da placa | D13 (o LED marcado com L) | GPIO 2 (na maioria dos DevKits) | GP25 (“LED” no Pico W) |
| Velocidade serial | 9600 baud | 115200 baud | Shell do Thonny |
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.
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 ç.
| Tipo | Tamanho no Uno | Faixa | Uso típico |
|---|---|---|---|
| bool | 1 byte | true ou false | O LED está aceso? O botão foi pressionado? |
| byte | 1 byte | 0 – 255 | Número de pino, brilho PWM |
| char | 1 byte | -128 – 127 | Um único caractere: 'A' |
| int | 2 bytes | -32.768 – 32.767 | Contadores, resultados de analogRead() |
| unsigned int | 2 bytes | 0 – 65.535 | Só números positivos |
| long | 4 bytes | -2.147.483.648 – 2.147.483.647 | Números inteiros grandes |
| unsigned long | 4 bytes | 0 – 4.294.967.295 | Medir tempo com millis() |
| float | 4 bytes | Cerca de ±3,4 × 10^38, 6–7 dígitos de precisão | Temperatura, tensão, médias |
| double | 4 bytes (igual ao float no Uno) | Igual ao float | 8 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:
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ódigo | Saída no Uno | Saída no ESP32 e no Pico | Por quê? |
|---|---|---|---|
| a = 32767 + 1 | -32768 | 32768 | O int tem 2 bytes no Uno e 4 bytes nos outros |
| 60 * 1000 | -5536 | 60000 | Constantes são calculadas primeiro como int |
| 60 * 1000L | 60000 | 60000 | O sufixo L faz a conta em long |
| 5 / 2 | 2.00 | 2.00 | Dividir dois inteiros dá um inteiro |
| 5 / 2.0 | 2.50 | 2.50 | Se 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().
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.
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}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:
| Grupo | Operador | Significado | Exemplo |
|---|---|---|---|
| Aritméticos | + - * / | As quatro operações básicas | x = 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 1 | i++; |
| Comparação | == != < > <= >= | O resultado é true ou false | if (x >= 10) |
| Lógicos | && || ! | E, OU, NÃO | if (a && !b) |
| Condicional | ? : | if-else curto | x > 0 ? HIGH : LOW |
| Bit a bit | & | ^ ~ << >> | Opera sobre os bits de um número | 1 << 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:
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:
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”.
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.
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.
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ço | Quando a condição é verificada? | Quando usar | Exemplo |
|---|---|---|---|
| for | Antes de cada volta | Quando o número de repetições é conhecido | for (int i = 0; i < 6; i++) |
| while | Antes de cada volta | Ao esperar que algo aconteça | while (digitalRead(2) == HIGH) |
| do-while | Depois de cada volta | Quando precisa rodar pelo menos uma vez | do { ... } 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:
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.
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:
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.
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.
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.
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?.
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.
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)| Conceito | Arduino (C++) | MicroPython |
|---|---|---|
| Parte que roda uma vez | setup() | O código antes da linha while True: |
| Parte que se repete para sempre | loop() | while True: |
| Variável | int x = 5; | x = 5 |
| Constante | const int PIN = 2; | PIN = const(2) (com from micropython import const) |
| Condição | if / else if / else (se / senão se / senão) | if / elif / else |
| Escolha múltipla | switch-case | Cadeia de if / elif |
| Laço de contagem | for (int i = 0; i < 6; i++) | for i in range(6): |
| Array | byte a[] = {1, 2, 3}; | a = [1, 2, 3] |
| Função | void 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
| Erro | Sintoma | Solução |
|---|---|---|
| Usar = em uma comparação | O bloco if sempre é executado | Use == para comparar |
| Um ; ou = na linha do #define | expected ')' 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ável | stray '\303' in program (caractere inválido no programa) | Use só letras sem acento, dígitos e _ nos nomes |
| Calcular 60 * 1000 como int | O resultado é -5536 | Escreva 60 * 1000L ou 60000UL |
| Guardar millis() em um int | A temporização quebra depois de 32 segundos | Use unsigned long |
| Divisão de inteiros | 5 / 2 dá 2 | Escreva 5 / 2.0 ou converta para float |
| Esquecer o break no fim de um case | O case seguinte também é executado | Termine todo case com break |
| Escrever fora de um array | Valores aleatórios, travamentos | Mantenha o índice entre 0 e o número de elementos - 1 |
| Deixar um contador local | O contador fica sempre em 1 | Torne-o global ou static |
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.
Projetos para montar depois desta leitura
Ver todos os projetos
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

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

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

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ç

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

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.

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).

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
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

