Arduino Programlama Temelleri: Değişken, Döngü ve Fonksiyon
Arduino programlama sıfırdan: setup ve loop, int, long, byte ve taşma, const, #define, if-else, switch-case, for, while, dizi, fonksiyon; LED ve buton örnekli.
Arduino programlama, Arduino kartına ne yapacağını C/C++ diliyle anlatmaktır: hangi LED'in ne zaman yanacağını, butona basılınca ne olacağını, sensörden gelen sayının nasıl yorumlanacağını sen yazarsın, kart da bu talimatları saniyede binlerce kez tekrarlar. İyi haber şu: LED yakmaktan robot arabaya kadar gördüğün kodların neredeyse hepsi aynı on kadar yapı taşından oluşur. Bunlar setup() ve loop(), değişkenler, sabitler, operatörler, if-else, switch-case, for ve while döngüleri, diziler ve fonksiyonlardır.
Bu rehberde bu yapı taşlarını sırayla, her biri için gerçek bir LED ya da buton örneğiyle öğreneceksin. Bütün örnekler aynı basit devrede çalışır ve Arduino Uno için derlenip denenmiştir. Yeni başlayanların en çok takıldığı noktalar, yani değişken taşması, = ile == karışıklığı, unutulan break ve her turda sıfırlanan sayaç ayrıca anlatılıyor. Son bölümlerde aynı mantığın ESP32'de nasıl çalıştığını ve Raspberry Pi Pico'daki MicroPython karşılıklarını da göreceksin.

Arduino programlama nedir, hangi dil kullanılır?
Arduino kodu C++ dilinin üzerine kurulmuştur; ayrı bir "Arduino dili" yoktur. Arduino çekirdeği C++'a pinMode(), digitalWrite() ve delay() gibi hazır fonksiyonlar ekler. Yazdığın dosyaya sketch denir ve uzantısı .ino'dur. Doğrula düğmesine bastığında Arduino IDE dosyanın başına Arduino.h başlık dosyasını ekler, fonksiyonların bildirimlerini kendisi üretir ve derleyici kodu işlemcinin anladığı makine diline çevirir. Yükle düğmesi bu çıktıyı USB üzerinden kartın önyükleyicisine (bootloader) gönderir. IDE'yi henüz kurmadıysan Arduino IDE kurulumu yazısıyla başlayabilirsin.
Kod yazarken uyman gereken birkaç temel kural vardır. Bunları baştan öğrenmek, ilk günlerde göreceğin derleme hatalarının yarısını ortadan kaldırır:
| Kural | Doğru yazım | Uymazsan |
|---|---|---|
| Her komut noktalı virgülle biter | digitalWrite(13, HIGH); | expected ';' before '}' token |
| Büyük-küçük harf önemlidir | pinMode(13, OUTPUT); | 'pinmode' was not declared in this scope |
| Kod blokları süslü parantez içindedir | void loop() { ... } | Açılan her { için bir } gerekir |
| Tek satırlık yorum | // bu satır çalışmaz | Yorumlar derlenmez, açıklama içindir |
| Çok satırlık yorum | /* ... */ | Kapatılmazsa altındaki her şey yorum olur |
| Metin çift, tek karakter tek tırnak | "Merhaba" ve 'A' | Tırnak türü karışırsa yanlış değer ya da hata |
Örneklerde kullanacağımız devre
Bütün örnekler tek bir devrede çalışır. D8–D13 pinlerine birer 220 Ω dirençle altı LED bağla; LED'lerin kısa bacağı (katot) GND'ye gider. Direnç değerini kendi LED'ine göre LED direnci hesaplayıcı ile kontrol edebilirsin. Butonun bir bacağını D2'ye, çapraz karşısındaki bacağını GND'ye bağla. Kodda INPUT_PULLUP kullanacağımız için harici dirence gerek yoktur: buton bırakıldığında pin HIGH, basıldığında LOW okunur. Ayrıntılar için Arduino'ya buton nasıl bağlanır? ve LED nedir? yazılarına bakabilirsin.
| Parça | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| 6 LED (her biri 220 Ω ile, katot GND'ye) | D8, D9, D10, D11, D12, D13 | GPIO 18, 19, 21, 22, 23, 25 | GP14, GP13, GP12, GP11, GP10, GP9 |
| Buton (diğer bacağı GND'ye) | D2 | GPIO 4 | GP2 |
| Kart üzerindeki LED | D13 (L harfli LED) | GPIO 2 (çoğu DevKit) | GP25 (Pico W'de "LED") |
| Seri port hızı | 9600 baud | 115200 baud | Thonny Shell |
setup() ve loop(): her programın iskeleti
Her Arduino programında iki zorunlu fonksiyon vardır. setup() kart açıldığında ya da reset düğmesine basıldığında yalnızca bir kez çalışır; pin ayarları ve Serial.begin() gibi hazırlıklar buraya yazılır. loop() ise setup() bittikten sonra sonsuza kadar tekrar eder. Arka planda Arduino çekirdeğinin gizli bir main() fonksiyonu vardır: önce donanımı hazırlar, sonra setup()'ı bir kez çağırır ve loop()'u sonsuz bir döngü içinde tekrar tekrar çağırır. Bu iki fonksiyonun dışında, dosyanın en üstündeki bölge global alandır; sabitler ve her yerden erişilecek değişkenler burada tanımlanır.
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}Kodu yükleyip Seri Port Ekranı'nı 9600 baud hızında açtığında önce bir kez "setup() bitti" yazısını, ardından her saniye artan bir tur numarasını görürsün; dahili LED de aynı ritimde yanıp söner. loop() içindeki iki delay(500) her turu yaklaşık bir saniyeye uzatır; hiç bekleme ve seri yazdırma içermeyen bir loop() ise Uno'da saniyede yüz binlerce kez dönebilir. Seri port ekranını verimli kullanmak için Arduino Serial Monitor yazısına göz atabilirsin.
Değişkenler ve veri tipleri: int, long, float, bool, byte
Değişken, bir değeri saklamak için bellekte açılan ve adı olan bir kutudur. Tanımlarken önce tipini, sonra adını yazarsın, istersen başlangıç değeri de verirsin: int sayac = 0; gibi. Tip, kutunun kaç bayt yer kaplayacağını ve hangi aralıktaki sayıları tutabileceğini belirler. Arduino Uno'da yalnızca 2 KB RAM olduğu için doğru tipi seçmek önemlidir. Değişken adları harf ya da alt çizgiyle başlar; içinde boşluk ve ç, ğ, ı, ö, ş, ü gibi harfler olamaz.
| Tip | Uno'da boyut | Değer aralığı | Tipik kullanım |
|---|---|---|---|
| bool | 1 bayt | true ya da false | LED açık mı, buton basıldı mı |
| byte | 1 bayt | 0 – 255 | Pin numarası, PWM parlaklığı |
| char | 1 bayt | -128 – 127 | Tek karakter: 'A' |
| int | 2 bayt | -32.768 – 32.767 | Sayaçlar, analogRead() sonucu |
| unsigned int | 2 bayt | 0 – 65.535 | Yalnızca pozitif sayılar |
| long | 4 bayt | -2.147.483.648 – 2.147.483.647 | Büyük tam sayılar |
| unsigned long | 4 bayt | 0 – 4.294.967.295 | millis() ile zaman ölçümü |
| float | 4 bayt | Yaklaşık ±3,4 × 10^38, 6–7 basamak hassasiyet | Sıcaklık, gerilim, ortalama |
| double | 4 bayt (Uno'da float ile aynı) | float ile aynı | ESP32'de 8 bayttır |
Birkaç pratik kural seni çok uğraştırmaktan kurtarır. analogRead() 0–1023 arası bir sayı döndürür, bu yüzden int yeterlidir. millis() ise unsigned long döndürür; zamanı int bir değişkende tutarsan 32,7 saniye sonra değer taşar ve zamanlaman bozulur. Uno'da float işlemleri yazılımla yapıldığı için tam sayı işlemlerinden yavaştır ve 6–7 basamaktan sonrası güvenilir değildir; bu yüzden iki float sayıyı == ile karşılaştırma. Hangi kartta olursan ol boyutu aynı kalan bir tip istiyorsan uint8_t, int16_t ve uint32_t gibi boyutu adında yazan tipleri kullanabilirsin.
Taşma (overflow): değişken sınırını aşınca ne olur?
Bir değişkene tipinin alabileceğinden büyük bir sayı yazmaya çalışırsan Arduino hata vermez; sayı, arabanın kilometre sayacı gibi başa döner. Uno'da int en fazla 32.767 tutabilir, buna 1 eklersen -32.768 olur. byte 255'ten sonra 0'a döner. Daha sinsi bir tuzak da sabit hesaplarıdır: 60 * 1000 gibi bir işlem, sonucu long bir değişkene yazılsa bile önce int olarak hesaplanır ve taşar. Aşağıdaki kod bu durumların hepsini Seri Port Ekranı'na yazar, loop() kısmı da byte taşmasını bir LED üzerinde gösterir:
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}| Kod satırı | Uno'da çıktı | ESP32 ve Pico'da çıktı | Neden? |
|---|---|---|---|
| a = 32767 + 1 | -32768 | 32768 | Uno'da int 2 bayt, diğerlerinde 4 bayt |
| 60 * 1000 | -5536 | 60000 | Sabitler önce int olarak hesaplanır |
| 60 * 1000L | 60000 | 60000 | L eki işlemi long yapar |
| 5 / 2 | 2.00 | 2.00 | İki tam sayının bölümü tam sayıdır |
| 5 / 2.0 | 2.50 | 2.50 | Sayılardan biri ondalıklı olunca sonuç da ondalıklı olur |
D9'a bağlı LED'e bak: parlaklık 0'dan 255'e kadar yavaşça artar, sonra brightness++ satırı değeri 256 yerine 0 yapar ve LED aniden söner. Taşmayı gözle görmenin en kolay yolu budur. D9, Uno'da ~ işaretli PWM pinlerinden biri olduğu için analogWrite() burada çalışır; PWM'in ayrıntıları PWM nedir? yazısında. millis() değerinin yaklaşık 49,7 günde sıfırlanması da bir taşmadır ve doğru yazılmış kodda sorun çıkarmaz; nasıl yazılacağını millis() ve delay() yazısında bulabilirsin.
const ve #define: değişmeyen değerler
Pin numaraları ve eşik değerleri gibi program boyunca değişmeyecek sayılara isim vermenin iki yolu vardır. const ile tanımlanan bir sabitin tipi vardır, kapsam kurallarına uyar ve yanlışlıkla değiştirmeye çalışırsan derleyici assignment of read-only variable 'LED_PIN' hatasıyla seni durdurur. #define ise bir önişlemci komutudur: derleme başlamadan önce koddaki her BUTTON_PIN yazısını 2 ile değiştirir. Tipi yoktur, satır sonunda noktalı virgül ve eşittir işareti kullanılmaz. Basit sayılar için ikisi de RAM harcamaz, çünkü derleyici değeri doğrudan koda yerleştirir. Günümüzde const tercih edilir; sabit adlarını BÜYÜK_HARFLE yazmak da yaygın bir alışkanlıktır. Aşağıdaki örnekte ikisi birlikte kullanılıyor: buton basılıyken LED yanar.
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}Operatörler: aritmetik, karşılaştırma ve mantık
Operatörler değişkenlerle hesap yapmanı, onları karşılaştırmanı ve koşulları birleştirmeni sağlar. Günlük projelerde kullanacağın operatörlerin neredeyse tamamı bu tabloda:
| Grup | Operatör | Anlamı | Örnek |
|---|---|---|---|
| Aritmetik | + - * / | Dört işlem | x = a * 2; |
| Kalan | % | Bölümden kalan (yalnızca tam sayılarda) | 7 % 3 sonucu 1 |
| Atama | = += -= *= /= | Değer ver, üzerine ekle, çıkar… | toplam += 5; |
| Artırma, azaltma | ++ -- | 1 artır ya da 1 azalt | i++; |
| Karşılaştırma | == != < > <= >= | Sonuç true ya da false | if (x >= 10) |
| Mantık | && || ! | VE, VEYA, DEĞİL | if (a && !b) |
| Koşul | ? : | Kısa if-else | x > 0 ? HIGH : LOW |
| Bit | & | ^ ~ << >> | Sayının bitleri üzerinde işlem | 1 << 3 sonucu 8 |
En sık karıştırılan iki çift = ile == ve & ile &&'dir. Tek eşittir değer atar, çift eşittir karşılaştırır. Tek & sayıların bitlerine bakar, çift && ise iki koşulun ikisinin de doğru olup olmadığını sorar. VE, VEYA ve DEĞİL işlemlerinin doğruluk tablolarını Mantık kapıları aracında deneyebilirsin. Bit operatörlerinin ayrıntıları ise sayı sistemleri ve bit işlemleri yazısında. Aşağıdaki örnek butona kaç kez basıldığını sayar ve tek sayıda basışta LED'i yakar:
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}Buradaki püf noktası pressed && !lastPressed koşuludur: "şimdi basılı VE bir önceki turda basılı değildi" anlamına gelir. Böylece butonu basılı tuttuğun sürece sayı artmaz, her basış yalnızca bir kez sayılır. presses % 2 işlemi sayının 2'ye bölümünden kalanı verir: tek sayılarda 1, çiftlerde 0. Son satırdaki ? : operatörü de bu sonuca göre HIGH ya da LOW seçer.
if, else if ve else: karar vermek
if, parantez içindeki koşul doğruysa süslü parantezdeki kodu çalıştırır. Koşul yanlışsa varsa else bloğu çalışır. Birden çok seçenek gerektiğinde araya else if eklenir. Koşullar yukarıdan aşağıya kontrol edilir ve ilk doğru olanın bloğu çalıştıktan sonra geri kalanlara bakılmaz. C++'ta sıfır olmayan her değer doğru kabul edilir; bu yüzden if (sayac) yazımı "sayaç sıfır değilse" anlamına gelir. Basit if-else örneğini const bölümünde gördük. Şimdi kısa ve uzun basışı ayıran bir örnek yazalım:
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}Kod, buton basıldığı anda millis() ile zamanı kaydeder, while döngüsüyle buton bırakılana kadar bekler ve aradaki süreyi hesaplar. 50 ms'den kısa basışlar butonun mekanik titreşiminden (bounce) gelen parazit kabul edilir ve return ile loop()'un o turu erkenden bitirilir. 1 saniyeden kısa basış LED'i yakar, daha uzun basış söndürür. Koşulların sırası önemlidir: held < 1000 kontrolünü en başa yazsaydın 30 ms'lik bir parazit de "kısa basış" sayılırdı.
switch-case: birden çok durumu seçmek
Bir değişkenin değerine göre birçok farklı iş yapacaksan uzun bir else if zinciri yerine switch-case daha okunaklıdır. switch, parantezindeki değeri her case etiketiyle karşılaştırır ve eşleşen yerden çalışmaya başlar. Her case'in sonuna break koymalısın; aksi hâlde program bir sonraki case'in kodunu da çalıştırır. Hiçbir case eşleşmezse default bloğu çalışır. switch yalnızca int, byte, char ve enum gibi tam sayı türleriyle çalışır; String, float ya da "10 ile 20 arası" gibi aralıklar için if kullanman gerekir. Aşağıdaki örnekte her basış lambayı bir sonraki moda geçirir: kapalı, açık, yavaş yanıp sönme, hızlı yanıp sönme.
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}mode = (mode + 1) % 4 satırı modu 0, 1, 2, 3 diye artırır ve 4'e gelince % operatörü sayesinde yeniden 0 yapar. Yanıp sönme için delay() kullanmadığımıza dikkat et: (millis() / 500) % 2 ifadesi her 500 ms'de bir 0 ile 1 arasında değişir, böylece LED yanıp sönerken buton da her an basışa cevap verir. Bu "mod" yaklaşımının büyümüş hâline durum makinesi denir; trafik lambası ve menü gibi projelerde nasıl kullanıldığını Arduino'da durum makinesi yazısında görebilirsin.
for, while ve do-while döngüleri
Döngüler bir kod bloğunu tekrar tekrar çalıştırır. for döngüsünün parantezinde üç bölüm vardır: başlangıç (int i = 0), devam koşulu (i < 6) ve her turdan sonra yapılacak işlem (i++). Tekrar sayısı belli olduğunda for idealdir. while koşul doğru olduğu sürece döner ve koşulu her turdan önce kontrol eder; ne kadar süreceği belli olmayan beklemeler için uygundur. do-while ise koşulu sonda kontrol ettiği için en az bir kez çalışır. Döngünün içinde break döngüden hemen çıkar, continue o turun geri kalanını atlayıp bir sonraki tura geçer. loop() zaten sonsuz bir döngü olduğu için içine ayrıca while (true) yazmana genelde gerek yoktur.
| Döngü | Koşul ne zaman kontrol edilir? | Ne zaman kullanılır? | Örnek |
|---|---|---|---|
| for | Her turdan önce | Tekrar sayısı belliyse | for (int i = 0; i < 6; i++) |
| while | Her turdan önce | Bir şey olana kadar beklerken | while (digitalRead(2) == HIGH) |
| do-while | Her turdan sonra | En az bir kez çalışması gerekiyorsa | do { ... } while (x < 10); |
Aşağıdaki örnek altı LED'le bir karaşimşek efekti yapar. setup() içindeki for döngüsü altı pini tek satırda çıkış yapar; loop() içindeki while döngüsü butona basılana kadar bekler. Butonu basılı tuttuğun sürece ışık D8'den D13'e gider ve geri döner:
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}Geri dönüş döngüsünün LAST_LED - 1'den başlayıp FIRST_LED'den büyük olduğu sürece dönmesi bilinçli bir seçimdir: uçtaki LED'ler iki kez üst üste yanmaz ve hareket akıcı görünür. Hız ayarı, birikerek dolan efekt ve daha fazla desen için Arduino karaşimşek ve yürüyen LED yazısına bakabilirsin.
Diziler (array): birden çok değeri tek isimde tutmak
Dizi, aynı tipteki birden çok değeri tek bir isim altında sıralı olarak tutar. const byte LEDS[] = {8, 9, 10, 11, 12, 13}; satırı altı elemanlı bir dizi oluşturur. Elemanlara köşeli parantezle ulaşılır ve sayma 0'dan başlar: LEDS[0] değeri 8, LEDS[5] değeri 13'tür. Dizinin boyu tanımlandıktan sonra değişmez. Eleman sayısını elle yazmak yerine sizeof(LEDS) / sizeof(LEDS[0]) ile hesaplatırsan diziye yeni bir pin eklediğinde döngüler kendiliğinden uyum sağlar. Dizinin dışına taşan bir indeks, örneğin LEDS[6], hata vermez; bellekteki başka bir değişkeni okur ya da bozar. Bu yüzden indeksin 0 ile eleman sayısının bir eksiği arasında kaldığından emin olmalısın. Aşağıdaki örnek altı LED'i bir elektronik zara çevirir:
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) ifadesi 1 ile 6 arasında bir sayı üretir, çünkü üst sınır sonuca dahil değildir. Gelen sayı kadar LED yanar: zar 4 gelirse LEDS[0]'dan LEDS[3]'e kadar dört LED yanar. randomSeed(analogRead(A0)) satırı, hiçbir yere bağlı olmayan A0 pinindeki elektriksel gürültüyü başlangıç değeri olarak kullanır. Bu satır olmasaydı kart her açıldığında aynı sayı dizisini üretirdi. Dizilerin bir güzel yanı da şudur: LED'leri başka pinlere taşırsan yalnızca ilk satırı değiştirmen yeterlidir.
Fonksiyon yazmak: kodu parçalara bölmek
Aynı işi programın birkaç yerinde yapıyorsan o işi bir fonksiyona dönüştür. Bir fonksiyon dönüş tipi, adı, parantez içindeki parametreleri ve süslü parantez içindeki gövdesinden oluşur. Geriye değer döndürmeyen fonksiyonların tipi void'dir. Değer döndürenler sonucu return ile verir. Parametreler kopya olarak aktarılır, yani fonksiyon içinde parametreyi değiştirmek onu çağıran yerdeki değişkeni değiştirmez. Arduino IDE, .ino dosyalarındaki fonksiyonların bildirimlerini kendisi ürettiği için fonksiyonu loop()'un altına da yazabilirsin. Ancak .cpp dosyalarında ya da PlatformIO'da fonksiyonu kullanmadan önce bildirmen gerekir. Fonksiyonlara blinkLed ya da isPressed gibi ne yaptığını anlatan isimler ver.
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() hangi pinin, kaç kez ve hangi hızda yanıp söneceğini parametre olarak alır. Bu yüzden aynı fonksiyonu başka bir LED ve başka bir hız için de kullanabilirsin. isPressed() ise bool döndürür ve INPUT_PULLUP'ın "basılı = LOW" mantığını tek bir yerde saklar; ileride butonu ters bağlarsan yalnızca bu satırı değiştirirsin. Fonksiyonun içindeki delay() programı yine durdurur. Aynı anda birden çok iş yapan fonksiyonlar için millis() tabanlı zamanlama gerekir.
Global, yerel ve static değişkenler
Bir değişkenin nerede görülebildiğine kapsam denir. Fonksiyonların dışında tanımlanan global değişkenler program boyunca yaşar ve her yerden okunup değiştirilebilir. Bir fonksiyonun ya da süslü parantez bloğunun içinde tanımlanan yerel değişkenler o blok her çalıştığında yeniden oluşturulur, blok bitince de yok olur. Yeni başlayanların en sık şikâyeti olan "sayacım hep 1 kalıyor" sorunu buradan çıkar. Yerel bir değişkenin başına static yazarsan yalnızca ilk seferde başlangıç değerini alır ve değerini turlar arasında korur, ama yine yalnızca o fonksiyonun içinden görülür.
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}Butona üç kez bastığında Seri Port Ekranı'nda 1 1 1, 2 1 2 ve 3 1 3 satırlarını görürsün: global ve static sayaçlar artmaya devam eder, yerel sayaç ise loop()'un her turunda yeniden 0'dan başladığı için hep 1'de kalır. Global bir değişkenle aynı adı taşıyan yerel bir değişken tanımlarsan yerel olan globali gölgeler ve fonksiyon içinde global değişkene hiç dokunmazsın. Bir kesme fonksiyonunun (ISR) değiştirdiği global değişkenlerin ayrıca volatile olarak tanımlanması gerekir; bunu Arduino interrupt yazısında ayrıntılı olarak bulabilirsin.
ESP32'de Arduino programlama: aynı dil, farklı boyutlar
ESP32'yi Arduino IDE ile programladığında aynı C++ dilini ve aynı komutları kullanırsın, ama birkaç fark vardır. ESP32 32 bitlik bir işlemci olduğu için int 4 bayttır ve -2.147.483.648 ile 2.147.483.647 arasındaki değerleri tutar; bu yüzden taşma örneğindeki 32767 + 1 işlemi 32768 sonucunu verir. double 8 bayttır, analogRead() 0–4095 arası değer döndürür ve pinler 3,3 V ile çalışır. Serial.printf() ile biçimli yazdırma yapılabilir. random() varsayılan olarak donanımsal rastgele sayı üretecini kullandığı için randomSeed() satırına gerek yoktur. Aşağıdaki program yazıda öğrendiğin her şeyi birleştirir: dizi, fonksiyon, switch-case ve mod değiştiren buton. Kartın genel özellikleri için ESP32 nedir? yazısına bakabilirsin.
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}Bu programı Uno'da çalıştırmak için LEDS dizisini {8, 9, 10, 11, 12, 13}, BUTTON_PIN sabitini 2 yapman ve Uno'da bulunmayan Serial.printf() satırını Serial.print() ile değiştirmen yeterlidir. Pin numaralarını en başta sabit ve dizi olarak tanımlamanın faydası tam olarak budur. case 1 bloğunun süslü parantez içinde olduğuna da dikkat et; içinde step değişkeni tanımlandığı için ESP32'de bu parantezler zorunludur.
Raspberry Pi Pico: aynı mantık MicroPython ile
Raspberry Pi Pico'yu Arduino IDE'ye RP2040 kart paketini ekleyerek C++ ile programlayabilirsin; bu durumda yazıdaki kodlar pin numaralarını değiştirmen yeterli olacak şekilde çalışır ve int yine 4 bayttır. Pico'nun en yaygın kullanımı ise MicroPython'dır. Python'da değişken tipi yazılmaz, bloklar süslü parantez yerine girintiyle belirlenir, satır sonunda noktalı virgül yoktur ve tam sayılar taşmaz, gerektiği kadar büyür. Aşağıdaki kod elektronik zar örneğinin MicroPython karşılığıdır. Kurulum için Raspberry Pi Pico nedir?, dilin ayrıntıları için MicroPython temelleri yazılarına bakabilirsin.
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)| Kavram | Arduino (C++) | MicroPython |
|---|---|---|
| Bir kez çalışan kısım | setup() | while True: satırından önceki kod |
| Sürekli tekrar eden kısım | loop() | while True: |
| Değişken | int x = 5; | x = 5 |
| Sabit | const int PIN = 2; | PIN = const(2) (from micropython import const) |
| Koşul | if / else if / else | if / elif / else |
| Çoklu seçim | switch-case | if / elif zinciri |
| Sayma döngüsü | for (int i = 0; i < 6; i++) | for i in range(6): |
| Dizi | byte a[] = {1, 2, 3}; | a = [1, 2, 3] |
| Fonksiyon | void blink(int pin) { ... } | def blink(pin): |
| Mantık | && || ! | and or not |
| Yorum | // yorum | # yorum |
Bu temellerle yapabileceğin proje fikirleri
- Trafik lambası: const sabitler, delay() ve if ile yaya butonlu bir kavşak kur; adım adım anlatımı Arduino trafik lambası yazısında.
- Karaşimşek ve yürüyen LED: for döngüsü ve diziyle onlarca farklı ışık deseni üret.
- Elektronik zar: Dizi, random() ve fonksiyonla 6 LED'li zarı 7 segment bir göstergeye taşı; bunun için 7 segment display yazısına bak.
- Reaksiyon testi: LED rastgele bir anda yansın, butona ne kadar hızlı bastığını unsigned long ve millis() ile ölç.
- Mod seçmeli lamba: switch-case ile tek butondan kapalı, sabit, nefes ve flaş modları; renkli sürümü için RGB LED kullanımı yazısına göz at.
- Şifreli kilit: Girilen tuşları bir dizide topla ve kayıtlı şifreyle karşılaştır; ayrıntılar Keypad kullanımı yazısında.
Sık yapılan hatalar ve çözümleri
| Hata | Belirti | Çözüm |
|---|---|---|
| Karşılaştırmada = kullanmak | if bloğu her zaman çalışır | Karşılaştırmada == kullan |
| #define satırına ; ya da = koymak | expected ')' before ';' token | #define LED_PIN 13 biçiminde yaz |
| Komutu yanlış harfle yazmak | 'pinmode' was not declared in this scope | pinMode gibi büyük-küçük harfe dikkat et |
| Değişken adında Türkçe harf | stray '\303' in program | Adlarda yalnızca İngilizce harf, rakam ve _ kullan |
| 60 * 1000'i int ile hesaplamak | Sonuç -5536 çıkar | 60 * 1000L ya da 60000UL yaz |
| millis() değerini int'te tutmak | Zamanlama 32 saniye sonra bozulur | unsigned long kullan |
| Tam sayı bölmesi | 5 / 2 sonucu 2 çıkar | 5 / 2.0 yaz ya da float'a çevir |
| case sonunda break unutmak | Sonraki case de çalışır | Her case'i break ile bitir |
| Dizi dışına yazmak | Rastgele değerler, kilitlenme | İndeksi 0 ile eleman sayısı - 1 arasında tut |
| Sayacı yerel tanımlamak | Sayaç hep 1 kalır | Global ya da static yap |
Bu yapı taşlarını öğrendikten sonra sıradaki adım, en çok kullanılan komutları tanımaktır: Arduino komutları ve anlamları yazısı pinMode()'dan attachInterrupt()'a kadar hepsini örneklerle anlatıyor. BlueGrays uygulamasında da LED yakma, butonla LED kontrolü, karaşimşek, trafik lambası ve zar gibi projeleri devre şemaları ve hazır kodlarıyla adım adım bulabilir, burada öğrendiğin her yapıyı gerçek bir devrede deneyebilirsin.
Sıkça sorulan sorular
Arduino hangi programlama diliyle yazılır?
Arduino kodu C++ dilinin üzerine kurulmuştur. Arduino çekirdeği bu dile pinMode(), digitalWrite() ve delay() gibi hazır fonksiyonlar ekler, kodlar da .ino uzantılı dosyalara yazılır. ESP32 ve Raspberry Pi Pico gibi kartlar istersen MicroPython ile de programlanabilir.
Arduino programlamaya nereden başlamalıyım?
Önce Arduino IDE'yi kur ve dahili LED'i yakıp söndüren Blink örneğini yükle. Ardından sırasıyla setup() ve loop(), değişkenler, if-else, döngüler, diziler ve fonksiyonlar konularını birer LED ya da buton devresiyle dene. Her yeni konuyu küçük bir projede kullanmak, yalnızca okumaktan çok daha kalıcı öğrenmeni sağlar.
Arduino'da int ile long arasındaki fark nedir?
Arduino Uno'da int 2 bayttır ve -32.768 ile 32.767 arasındaki sayıları tutar. long ise 4 bayttır ve yaklaşık ±2,1 milyara kadar çıkar. Bu sınırı aşan bir sayı hata vermeden başa döner, bu yüzden millis() gibi büyük değerler için long ya da unsigned long kullanılır. ESP32 ve Pico'da int de 4 bayttır.
Arduino'da const ile #define arasındaki fark nedir?
const ile tanımlanan sabitin bir tipi vardır, kapsam kurallarına uyar ve değiştirilmeye çalışıldığında derleyici hata verir. #define ise derlemeden önce yapılan bir metin değiştirmedir, tipi yoktur ve sonuna noktalı virgül konursa beklenmedik hatalara yol açar. Basit sayılar için ikisi de RAM harcamaz, ancak güvenli olduğu için genellikle const tercih edilir.
Arduino'da for ile while döngüsü arasındaki fark nedir?
for döngüsü başlangıç, koşul ve artırma bölümlerini tek satırda toplar ve tekrar sayısı belli olduğunda kullanılır; örneğin altı LED'i sırayla yakmak için. while ise yalnızca bir koşula bakar ve koşul doğru olduğu sürece döner; butona basılmasını beklemek gibi ne kadar süreceği belli olmayan durumlara uygundur.
Arduino Python ile programlanabilir mi?
Arduino Uno gibi 8 bitlik kartlar Python çalıştıramaz, bu yüzden C++ ile programlanırlar. ESP32 ve Raspberry Pi Pico ise MicroPython ile programlanabilir. Ayrıca bilgisayarda çalışan bir Python programı, seri port üzerinden Uno ile haberleşerek onu dolaylı olarak kontrol edebilir.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
Led Yakma
Elektroniğin ilk merhabası: tek bir LED'i yakıp söndürmek. Arduino LED'i çok kısa aralıklarla yakıp söndürür, ışık hızlı hızlı göz kırpar. Kartın bir ışığı nasıl yönettiğini en sade haliyle görür, son

Buton İle Led Yakma (Push)
Kapı zili gibi: bastığın sürece ışık yanar, bırakınca söner. Arduino butonu sürekli dinler; butona basılıyken LED'i yakar, elini çektiğin an söndürür. Kartın dışarıdan gelen bir dokunuşu nasıl hissett

Buton İle Led Yakma (On Off)
Bir butona bir kez basınca ışık yanar, bir daha basınca söner; tıpkı odandaki lamba düğmesi gibi. Arduino her basışı fark edip LED'in durumunu tersine çevirir, düğme içindeki minik titreşimler ışığı k

Karaşimşek Led
Karaşimşek ışığı adıyla bilinen efekt: ışık altı LED'in üzerinde bir uçtan öbür uca kayar, sonra geri döner. Arduino her LED'i saniyenin onda biri kadar yakıp sıradakine geçer; uca varınca yön değişti

Trafik Lambası
Her gün yolda gördüğün trafik ışığının minik bir kopyası. Kırmızı iki saniye yanar, sarı kısa bir an parlar, yeşil iki saniye yanar, ardından yine sarı üzerinden kırmızıya dönülür ve döngü hiç durmaz.

ESP32 Buton ile LED Kontrol (Push)
Butona basılı tutulduğunda LED yanar, bırakıldığında söner. ESP32'nin dijital input pin'i ve dahili pull-up direncinin nasıl kullanıldığını gösterir. INPUT_PULLUP modu sayesinde harici direnç gerekmez.

Raspberry Pi Pico Buton ile LED Yakma (Toggle, GP14 + GP2)
Buton ile LED — bir üst seviye! 🔁 #155'in aksine burada butonu basılı tutmana gerek yok. Bir kez bas ve bırak → LED açılır. Tekrar bas ve bırak → LED söner (toggle). Devre #155 ile aynı (LED GP14, Buton GP2), ama kod büyük fark içeriyor. Yeni öğrenme: **edge detection** (kenar algılama — pinin 1'den 0'a düştüğü an), **state tracking** (önceki durumu hafızada tut), **debounce** (buton mekanik "bounce" sonrası yanlış tetikleme önleme). MicroPython'ın `led.toggle()` metodu ile tek satırda değişim.

Zar Projesi
Kutu oyunlarında kaybolan zara son: butona her basışta 7 segment ekranda 1 ile 6 arasında rastgele bir sayı çıkar. Tek basışta tek sonuç gelir ve sonuç bilgisayar ekranına da yazılır. Arkadaşlarınla o
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 160'tan fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

