Macademia'nın robotik ve mühendislik programı · ücretsiz seviye tespiti ve kişisel öğrenme yolu

2. hafta / 14

Arduino ile gömülü programlama

Araçlar: Bu platform (Uno laboratuvarı), Wokwi (ESP32)

Bu haftanın sonunda

  • setup/loop yürütme modelini açıklamak
  • Dijital ve analog girişleri okumak, çıkışları sürmek
  • millis() ve sonlu durum makineleri (finite state machines) ile bloklamayan (non-blocking) kod yazmak

Ders notları

Mikrodenetleyici, tek bir yonga üzerindeki küçük bir bilgisayardır: CPU, programınız için flash bellek, RAM ve zamanlayıcılar, ADC'ler, seri portlar gibi çevre birimleri. Arduino Uno, 16 MHz'te çalışan, 32 KB flash ve 2 KB RAM'e sahip bir ATmega328P kullanır. İşletim sistemi yoktur: loop() fonksiyonunuz sonsuza dek çalışır.

Girişler ve çıkışlar

  • digitalRead / digitalWrite: mantık seviyeleri, 0 V veya 5 V.
  • analogRead: 10 bitlik ADC 0 ile 1023 arasında değer döndürür, dolayısıyla gerilim raw × 5 / 1023 olur.
  • analogWrite: PWM çıkışı, 0 ile 255 arası (5. hafta).
  • Serial: ana hata ayıklama (debugging) aracınızdır; Serial Plotter canlı grafikler çizer.

Döngüyü asla bloklamayın

delay(1000) tüm robotu bir saniyeliğine dondurur: sensör okuyamaz, tepki veremez. Robotlar aynı anda pek çok iş yapar; bu yüzden geçen süreyi millis() ile kontrol eder, yalnızca zamanı geldiğinde işlem yaparız.

Sonlu durum makineleri

Robot davranışlarının çoğu bir durum makinesidir (state machine): bir durumlar kümesi, her durumda yapılan eylem ve geçişlere yol açan olaylar. Davranışı açık bir enum ve bir switch ile yazmak, onu okunabilir, test edilebilir ve kolay genişletilebilir kılar. Aynı kalıp dronlardaki görev mantığına kadar ölçeklenir.

Derinlemesine

Bloklamadan zaman: millis() aritmetiği

millis(), 32 bitlik bir unsigned long döndürür ve bu değer 232 ms, yani yaklaşık 49,7 gün sonra sıfıra döner; micros() ise yaklaşık 71,6 dakikada başa döner. if (millis() - last >= period) testi bu taşmadan sonra da doğru kalır, çünkü işaretsiz çıkarma 232 modunda yapılır. Cazip görünen if (millis() >= last + period) ise tam taşma anında bozulur, çünkü önce last + period taşar. Depo AGV'si gibi haftalarca çalışan robotlar bu hataya gerçekten yakalanır.

Çözümlü örnek: gerilim bölücüyle batarya izleme

2S bir LiPo (nominal 7,4 V, dolu 8,4 V) 5 V'luk ADC için fazla yüksektir; bu yüzden üstte R1 = 20 kΩ, toprağa R2 = 10 kΩ olan bir bölücü kullanırız:

VA0 = Vbat · R2 / (R1 + R2) = 7.4 × 10 / 30 = 2.467 V

ADC ham değer olarak 2,467 × 1023 / 5 ≈ 505 döndürür. Geri dönersek 505 × 5 / 1023 × 3 = 7,40 V. Bir adım (LSB) pinde 5 / 1023 ≈ 4,89 mV, batarya tarafında 14,7 mV'tur; hücre başına 3,5 V'luk kesme eşiğini (7,0 V) yakalamak için fazlasıyla yeterlidir. Tam dolulukta pin 2,8 V görür, 5 V'un güvenle altındadır. 3,3 V'luk bir ESP32'de aynı bölücü yine güvenlidir, ancak ADC'si aralığının uçlarında daha az doğrusaldır; bu yüzden bir multimetreye karşı kalibre edin.

Seri port bedava değildir

115200 baud'da UART her bayt için 10 bit (başlangıç, 8 veri, bitiş) gönderir, yani saniyede yaklaşık 11 520 bayt. 100 Hz'de 40 karakterlik bir hata ayıklama satırı saniyede 4 000 bayt ister: sorun yok. 9600 baud'da sınır saniyede 960 bayttır; 64 baytlık gönderme tamponu dolduğunda Serial.print bloklar ve döngü periyodunuzu sessizce uzatır. Döngüyü ölçün: loop() başında micros() değerini kaydedin ve en kötü periyodu saniyede bir yazdırın.

Durum makinesi tasarlamak

  • Giriş eylemlerini (entry actions) (duruma girildiğinde bir kez çalışır: zamanlayıcıyı başlat, motorları ayarla) süregelen eylemlerden (during actions) (her geçişte çalışır) ayırın.
  • Bir geçişin bir olayı (event) ve isteğe bağlı bir koşulu (guard) vardır: "butona basıldı ve en az 5 s yeşil yandı".
  • Durum sayısı beşi aşınca bir geçiş tablosu (durum, olay, sonraki durum) iç içe if ifadelerinden daha kolay incelenir ve test edilir.
  • Her durumdan ulaşılabilen bir hata (fault) durumu mutlaka ekleyin, örneğin düşük batarya için.

Kıt kaynak RAM'dir

Uno'da yalnızca 2 KB SRAM vardır. Metin sabitleri F("...") içine alınmadıkça RAM'e kopyalanır ve Arduino String sınıfı yığını (heap) parçalayarak kartın rastgele görünen anlarda sıfırlanmasına yol açar. Sabit boyutlu char tamponları tercih edin ve kod büyüdükçe boş belleği izleyin.

Simülasyon

Engelleyen delay() ile millis() durum makinesi

İki trafik ışığı aynı döngüyü çalıştırır. Soldaki delay() kullanır ve yaya butonunu yalnız bir durum bittiğinde okuyabilir; sağdaki her döngüde okuyan engellemeyen bir durum makinesidir. Butona bas ve tepki sürelerini karşılaştır.

Kod

Bloklamayan trafik ışığı durum makinesi
enum State { GREEN, YELLOW, RED };
State state = GREEN;
unsigned long t0 = 0;
const unsigned long DUR[] = {4000, 1000, 3000};   // durum başına ms
const int PIN[] = {11, 12, 13};

void setup() {
  for (int i = 0; i < 3; i++) pinMode(PIN[i], OUTPUT);
  Serial.begin(115200);
}

void loop() {
  if (millis() - t0 >= DUR[state]) {          // taşmaya (overflow) karşı güvenli karşılaştırma
    t0 = millis();
    state = (State)((state + 1) % 3);
    Serial.print("state -> "); Serial.println(state);
  }
  for (int i = 0; i < 3; i++) digitalWrite(PIN[i], i == state);
  // diğer görevler burada beklemeden çalışabilir
}

Sık yapılan hatalar

  • if (millis() >= last + period) yazmak; millis() yaklaşık 49,7 gün sonra başa döndüğünde bozulur.
  • millis() değerini unsigned long yerine int içinde saklamak; zamanlayıcı 32,8 s sonra negatife düşer.
  • 9600 baud'da uzun hata ayıklama satırları yazdırmak; Serial.print bloklar ve döngü sessizce yavaşlar.
  • Butonu sıçrama giderme (debounce) olmadan okumak; tek basış birkaç olay olarak sayılır.
  • Giriş eylemlerini (zamanlayıcı başlatma, sayaç sıfırlama) durumun her geçişte çalışan gövdesine koymak; her döngüde tekrarlanırlar.
  • Uzun süre çalışan bir programda String sınıfını kullanıp Uno'nun 2 KB RAM'ini tüketmek.

Yazılım mühendisliği bağlantısı

Sonlu durum makinesi robotiğin dışında da en kullanışlı tasarım kalıplarından biridir: kullanıcı arayüzü akışları, ağ protokolleri ve sipariş iş akışları birer durum makinesidir. Onu açıkça yazmak, davranışı gözden geçirilebilir (review) ve test edilebilir hâle getirir.

  • next = step(state, event, now) fonksiyonunu saf (pure) yapın; böylece geçiş tablosunun her satırı bir birim testine dönüşür.
  • Zamanı dışarıdan verin: mantığın içinde millis() çağırmak yerine şimdiki zamanı parametre olarak geçirin; böylece bir test 50 günü bir milisaniyede simüle edebilir.
  • Bir kod inceleme kuralı ekleyin: ana döngüde delay() ve sınırsız while döngüsü olmayacak.

Dönem projesi uygulaması: robotunuzun üst düzey davranışını bir durum diyagramı olarak çizin (Mermaid veya PlantUML, depoda saklanır), onu bir enum ve geçiş tablosuyla gerçekleyin ve hata durumuna giden geçişler dahil her geçiş için bir test yazın.

Laboratuvarda: Trafik ışığı durum makinesi (millis) + potansiyometre

Işıklar 4 / 1 / 3 saniyelik döngüde değişir; aynı anda potansiyometre gerilimi 4 Hz'de yazılır. delay() olmadığı için ikisi birbirini beklemez.

Laboratuvarda aç

Laboratuvar görevi

Wokwi'de trafik ışığı durum makinesini kurun. A0'a bir potansiyometre ekleyin ve ışıklar çalışmaya devam ederken gerilimini Serial Plotter'a yazdırın.

Kendini sına

  1. millis() başa döndüğünde hangi zamanlama testi doğru kalır?

  2. 12 V'luk bir batarya, üstte 30 kΩ ve toprağa 10 kΩ olan bir bölücü üzerinden Uno'nun A0 pinine bağlı. analogRead ne döndürür?

  3. 9600 baud'da loop() her geçişte 48 bayt yazdırıyor. Seri portun bloklamadan kaldırabileceği en yüksek döngü hızı nedir?

  4. Işık yeşile döndüğünde 5 s'lik geri sayımı başlatan kod nereye konmalıdır?

Ödev

Kırmızı ışık talep eden bir yaya düğmesi ekleyin. Düğmenin arkını (debounce) yazılımla giderin ve programın hiçbir yerinde delay() kullanmayın.

Bu haftanın donanımı (Hyvex)

Esc
  • PanelAna sayfaSayfa
  • ModüllerTüm modüllerSayfa
  • ParçalarParça ansiklopedisiSayfa
  • Mühendislik yoluBüyükler için robotik: 128 parça, 7 seviyeSayfa
  • Robotik dersi (ESOF 323)Üniversite robotik dersi, 14 haftaSayfa
  • TestlerimMini testler ve puanlarSayfa
  • Blok atölyesiSerbest blok programlamaAraç
  • Devre laboratuvarıSanal Arduino ve devreAraç
  • Robot arenasıSanal robot sürüşüAraç
  • OyunlarBloklarla 8 öğretici oyunAraç
  • Mikro Kart atölyesiSanal LED kartı bloklarla programlaAraç
  • Robot atölyesiMikro Kart'ı robota tak, bloklarla görev yapAraç