Elektronik etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Elektronik etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

22 Ocak 2026 Perşembe

MAX7219 Kullanarak Raspberry Pico Üzerinde Performans Karşılaştırması: C vs Micropython


Merhaba. Bu yazıda Raspberry Pi Pico mikrodenetleyicisiyle yaptığım bir kaç deneyden ve elde ettiğim ilginç sonuçlardan söz ettim. Uzunca bir yazı oldu, o yüzden sonuçları ortalarda ele aldım. Kodları nasıl çalıştırdığımı ek okuma olarak sona bıraktım.


Raspberry Pi Pico Nedir?

Raspberry Pi Pico (kısaca Pico), Raspberry Pi Vakfı'nın (Raspberry Pi Foundation) Arduino'ya her ne kadar doğrudan cevabı olmasa da, çok güçlü bir alternatif mikrodenetleyici. Arduino, giriş seviyesi hobi kullanıcıları hedef alıyor. Pico bence biraz daha teknik bilgi gerektiriyor. Ben bu yazıda Pico üzerinde farklı programlama yaklaşımlarını gösterip bunların hızını karşılaştırdım. Elimde önceki yazıdan hazır kod var: SPI ve MAX7219 ile LED sürme, bu yazıda da bu konudan devam ettim. Ama daha önce bunu yalnız Arduino üzerinde uygulamıştım, şimdiyse yalnız Pico üzerinde.

Arduino 8-bitlik ATmega328P (16 MHz) mikrodenetleyicisine sahip. Pico'daysa 32-bitlik çift çekirdekli ARM Cortex mikrodenetleyici var ve dökümante edilen maksimum çalışma hızı 133 MHz. İşlemci, varsayılan 125 Mhz ile çalışmaya başlıyor. Saat hızı sonradan programla değiştirilebiliyor. Örn. pille beslenen cihazlarda pili idareli kullanmak için saat hızı yavaşlatılabiliyor. Sonradan dökümante edildi mi bilmiyorum ama sahada Pico'nun 200 MHz ile çalıştırıldığını okudum. Ben kendi kodumu paralelleştirmeye uğraşmadığımdan çift çekirdeği dikkate almıyorum. Pico'da RAM ve flash bellek miktarı Arduino'ya göre daha fazla ama benim uygulamam CPU'yu yoğun olarak kullandığından bunları da burada karşılaştırmayacağım.

Pico'ya yeni başlayanlar için "Get Started with MicroPython on Raspberry Pi Pico" adlı açık ekitap var. Son baktığımda bu sayfadaki bağlantı düzgün çalışmıyordu ama kitabın adını Google'da aratınca .pdf sürümü çıkıyor.

Pico için kod geliştirirken kullanılabilecek çeşitli alternatifler var. İlki Arduino IDE ve bunun güzel yanı Arduino'da çalışan C kodu Pico'da da aynı şekilde çalışıyor. pinMode() veya digitalWrite() gibi komutlar tamamen uyumlu. Eğer çok Arduino-native bir kod değilse Pico'da da çalışacaktır.

İkinci alternatif Micropython. Pico'yu (micro)python'la da programlamak mümkün. Örneğin aşağıdaki satır onüçüncü GPIO pinini çıkış olarak ayarlayıp set ediyor:

spi_CS = machine.Pin(13, machine.Pin.OUT, 1);

Micropython'da SPI protokolü için kullanılacak pinlerle bir SPI nesnesi oluşturup bunun write() metodunu çağırmak yeterli. Micropython basit, ama biraz yavaş. Pico için yazılmış 660 KB boyutunda bir Micropython yorumlayıcısı var. Pico'yu boot select modunda çalıştırıp bu dosyayı kopyalayınca (boot select modunda Pico bilgisayarda bir USB flash bellek gibi görünüyor) seri arabirimi (RS232) USB üzerinden emüle ederek bilgisayarla haberleşiyor. Bu yolla python çalıştırmak mümkün.

Üçüncü alternatif C/C++. Pico'nun geniş bir C desteği ve kütüphaneleri var. Platforma özgü çok fazla fonksiyon olması (get_absolute_time(), cyw43_arch_gpio_put(), stdio_init_all() vb.) en başta benim için kafa karıştırıcı oldu. Ancak her konu için Raspberry'nin resmi github hesabında sayısız örnek var. C için en iyi kaynak Getting started with Raspberry Pi Pico-series adlı kitap. Pico SDK'nın kullanım kılavuzu da oldukça detaylı ama bu okunacak bir kitap değil, 745 sayfa. SDK'yı ve örnekleri derlerken aşağıdaki videodan yararlandım.


Elbette Assembly de bir seçenek ama ben ne Arduino'da ne de Pico'da assembly yazmadım. C kodları içinden assembly çağırmak da bir olasılık.


Nasıl Test Ettim?

Bütün testler önceki yazıda kütüphane kullanmadan yazdığım sayaç kodunu temel alıyor. Elbette Micropython için algoritmayı python'a çevirdim. Sayaç kodu Arduino IDE'den çalıştırılınca hem Arduino'da hem de Pico'da sorunsuz ve aynı şekilde çalışıyor. İlginç şekilde bu kodun kütüphane kullanan sürümü Pico'da çalışmadı. IDE, farklı platformlar için header dosyalarını muhtemelen farklı dizinlerde arıyor. Kodun kütüphanesiz sürümü sorunsuz çalıştığından bunu önemsemedim.

Aslında kafamdaki plan a dizisinin (satır 67) ilk elemanında gerçekleşen her taşmada bir pini set/reset yapıp frekansmetreyle taşmaların periyodunu bulmaktı. Bu plan frekansmetremin düzgün çalışmaması nedeniyle en başta bozuldu. Pini osiloskopa bağlayıp test ettim, bu sefer de örnekleme hızından dolayı bazı atımları kaçırdı. Set ile reset arasına ufak bir gecikme eklediğimde sorunsuz ölçüm yapabildim. Yukarıdaki görselde her bir atımın çıkan ve inen kenarı arasında 10 ms gecikme görülüyor (ölçek sağ üstte). Burada atımların frekansı 2.182 Hz. Bu periyoda çevrildiğinde 458 ms ediyor. Bunun 10ms'si gecikme olduğu için her 448 ms.'de sayaç taşıyor.

Önceki yazıda onuncu LED'in yaklaşık bir saniye yanık kaldığını gözlemlemiş ve hesabı buna istinaden yapmıştım. Sekizinci LED'in periyodu 448 ms. ise dokuzuncu LED'in periyodu 892 ms. olur, dolayısıyla onuncu LED 892 ms boyunca yanık kalır. Gözleme dayalı kaba bir tahmin için fena değil.

Ancak süreye dayalı bir ölçüm yaparken araya gecikme eklemek mantıklı olmadığı için, paralelde a dizisinin ikinci elemanındaki iki taşma arasında geçen süreyi seri porttan yazdırdım. Bu süre ortalama 114.486 saniye. Birinci dizi elemanı 892 milisaniye aralıklarla 256 kere taşarsa (overflow) 114.176 saniye yapıyor. Hesapla deney arasında 310 ms'lik bir fark var. Bu da Arduino'nun osilatörünün yeterince hassas olmadığı için ölçümleri yaptığım günler arasında odadaki sıcaklık farkından kaynaklanıyor olabilir, veya seri port iletişiminin getirdiği yük (overhead) olabilir, ki bu daha olası. Arduino Serial.println()'yi asenkron olarak yapamıyor, üstelik seçtiğim baud hızı da yavaş. Diğer yandan ‰2.7 kabul edilebilir bir hata. Kısacası süreleri seri porttan okumaya karar verdim.

Seri portun başlatılması için sekizinci satırdan #ifdef'i kaldırdım. Yeni kod github'dan ulaşılabilir. Süre ölçümü için millis() fonksiyonunu kullandım (satır 69). Eski koda ek olarak sekseninci satırda bir if bloğu var. Burada StartTime1'in değeri güncel tick count'tan çıkarılıp aradan geçen zaman seri porta yazılıyor.

Bu arada Arduino IDE'de ilginç bir durum var. Nedense LED Matrix'e yazılan sayının paritesine bağlı olarak döngü süresi değişiyor. Üstelik değişim Arduino'da pozitif, Pico'da negatif yönlü. Aşağıdaki grafik Arduino'nun sürelerini gösteriyor.

Örneğin, ortadaki pik 127'de ortaya çıkıyor, 128'de birden süre aşağıya düşüyor. O pikle sıfır noktasının tam ortasında daha küçük bir sıçrama 63 ile 64 arasında, ve benzeri bir sıçrama 191 ile 192 arasında var. En büyük sıçramaysa 255 ile 256 arasında, sağda. Diğer küçük sıçramaların 2n deseninde olduğu görülebilir. Nedeni konusunda bir fikrim yok. Pico'nun grafiğine bakarsak:

Bu grafik sanki öncekinin simetriği gibi, sayıdaki birlerin adedi arttıkça süre azalıyor. İlk grafiktekine benzer sıçramalar ters yönde 64, 128, 192 ve 256 civarında görülebilir. Neden Pico'da tersi davranış gösterdiği konusunda yine bir fikrim yok.

Python kodunu, C'den birebir çevirdiğimi yazmıştım. spiwrite() MAX7219'a iki byte'lık komutlar gönderiyor. SPI için kullanacağım pinleri initialize ettikten sonra, bu pinlerle SPI metodununu çağırarak bir nesne oluşturdum (satır 19). Ardından Arduino'dakiyle aynı şekilde MAX7219'u initialize ettim. Sonsuz döngüye girmeden önce süreyi StartTime1'e yazdım ve Z[1]'deki her taşmada bilgisayara print() ile süreyi ilettim.

C kodunu büyük ölçüde pico-examples/spi/max7219_32x8_spi/max7219_32x8_spi.c kodundan sadeleştirerek aldım. Örnek kod, SPI pinleri için "hardware/spi.h"daki varsayılan tanımları (PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_CSN_PIN vb.) kullanıyor. Ben kendi tanımladığım pinleri kullandım (satır 12, 13, 14). MAX7219 komut tanımlamalarını, cs_select() ve write_register() fonksiyonlarını orjinal koddan aldım. Bu kodda StartTime1 (satır 48) değişkenine süre get_absolute_time() ile main()'in başında yazılıyor. Bunun aslında sonsuz döngüden hemen önce olması gerekirdi ancak sonuçlarda ciddi bir fark yaratmıyor. Burada dikkat edilmesi gereken bir konu, Pico'da denetleyicinin içinde iki tane SPI çevre donanımı (peripheral) var. Bu da demek oluyor ki (bit-banging ve Programmable IO gibi ileri konulara girmeden) aynı anda iki SPI bus bağlanabilir. Bu SPI donanımları birden fazla GPIO pinine bağlı. Demek istediğim Pico'nun pinout şemasından daha iyi görülebilir:


SPI0 devresini yalnız SPI0 etiketli pinlerle kullanabilirim. Örn. GP0, GP1, GP2 ve GP3 (Pin 1, 2, 4, 5). Eğer bir SPI cihazı SPI1'e bağlayacaksam, bunu da benim kodumda olduğu gibi GP10, GP11, GP12 ve GP13 (Pin 14, 15, 16, 17) üzerinden bağlayabilirim. Ve SPI'ı initialize ederken hangi donanımı kullandığımı bildirmem gerekir. Orjinal kodda spi_default, spi0. Ben spi1'i kullandığımdan elli dördüncü satırda spi_init()'i spi1 ile çağırmam gerekti. Sırf SPI için bile bunca detay bulunması, C'nin donanıma yakın olmasının bir sonucu ama bu durum hız olarak kullanıcıya geri dönüyor.

MAX7219 initialization kısmı kodun 64 ve 70. satırları arasında. Ondan sonraki blok zaten Arduino'da da çalışan kod bloğu. Tek fark, sayının değeri SPI'a write_register() fonksiyonu üzerinden yazılıyor.


Test Sonuçları

Kodları nasıl çalıştırdığıma, yazının asıl konusu olmadığı için, sonuçlardan sonra ayrıca değineceğim.

İlk olarak Arduino'dan elde ettiğim sonuçları, verim ve saat hızına temel olması için ele alacağım. Arduino'da gerçekleşen 282 taşma ortalama 114.486 sn sürmüş. Yani 216'ya kadar saymanın süresi ve diğer bir ifadeyle on yedinci LED'in periyodu. 64 tane LED olduğuna göre daha 64 - 17 = 47 bit var. Bunların tamamının yanması için gereken süre 114.486 sn * 247 = 1.611 * 1016 saniye = 5.106 * 108 yıl (511 milyon). Önceki yazıda göz kararı hesapladığım 5.709 * 108 yıldı.

Bu, satranç tahtasındaki buğday taneleri problemine benziyor. Hikayeye göre satrancın mucidi, icadını Hint hükümdarına sunar. Hükümdar bunu çok beğenir ve klasik olarak "dile benden ne dilersen" der. Mucit, tahtanın birinci karesine bir buğday tanesi ve takip eden karelere öncekinin iki katı olmak üzere tahtaya koyulacak miktarda buğday ister. Hükümdar başta basit bir istek gibi algılasa da, mucidin istediği buğday tam olarak

Bu kadar buğday tanesi günümüz koşullarındaki yıllık buğday üretiminin 1600 katına denk. Bu arada hikayenin çeşitli varyasyonları var, ben lise matematik hocamdan duyduğumu aklımda kaldığı kadarıyla aktardım. Ana fikir üstel sayıların akıl almaz şekilde büyüyebileceği. İlk LED'in periyodu 1.75 ms (aslında mikroişlemci için uzun bir süre) kısa gibi gelse de bunun 264 katı inanılmaz büyük.

Konuya geri döneyim. Aynı kodun Arduino IDE içinden Pico'da çalışması ortalama 14.658 saniye sürüyor. Pico, Arduino'dan 7.8125 kat hızlı (125 MHz / 16 MHz). 14.658 sn * 7.8125 = 114.516 sn ediyor. Saat hızına göre normalleştirildiğinde Arduino ile aralarında sadece 30 ms fark var. Demek ki verim olarak ikisi de neredeyse aynı. Tüm LED'lerin yanması için 14.658 sn * 247 = 2.063 * 1015 sn = 6.537 * 107 (65.3 milyon) yıl gerekiyor.

Aynı algoritma Micropython'la hepsinden yavaş çalışıyor. Bir çevrim ortalama 128.641 saniye. Arduino IDE'deki koddan 8.78 kat daha yavaş. Bu, Micropython'ın mikrodenetleyici gibi düşük seviye bir platformda çok verimsiz olduğunu gösteriyor. Ben bu sonuçları SPI hızını 1 MHz olarak ayarladıktan sonra aldım. İlk denemede yanlışlıkla hızı 400 KHz ayarladığımda aynı döngü ortalama 138.291 saniye sürmüştü, ve ertesi gün 142.800 saniye. Baud'un genel hıza etkisi var ama yavaşlık kesinlikle program kaynaklı. Aksi halde baud'u 2.5 katına çıkarmak %7'den fazla etkili olurdu. Bu sonuçların ne zaman 264'e varacağını hesaplama zahmetine girmeye gerek yok.

Benim açımdan en heyecan verici olan C'deki sürelerdi. C'de 65 536'ya kadar 1.656 saniyede sayabiliyor. Yani, Arduino IDE'den bile 8.85 kat hızlı çalışan bir kod üretiliyor. Bu arada çıktı için UART veyea USB seri port kullanmak arasında süre farkı yok. 1.656 sn * 247 = 2.331 * 1014 sn = 7.385 * 106 (7.385 milyon) yıl. Bu da Otostopçunun Galaksi Rehberi'ne göre yaşamın, evrenin ve her şeyin cevabı olarak 42'yi hesaplamak için süper bilgisayarın ihtiyaç duyduğu süreden sadece biraz az.


Ek: Raspberry Pico'da Kod Çalıştırmak

Yazıda bahsettiğim üç farklı yaklaşımın (Arduino IDE, Micropython, C) ortak noktası Pico'nun Bootsel butonu. Bu butona basılı tutarken Pico'yu USB'ye bağlayınca Bootloader modunda açılıyor ve bilgisayarda 128 MB'lik bir USB flash bellek gibi görünüyor. Bu diske Raspberry Pico için derlenmiş .uf2 uzantılı bir dosya kopyalayınca, Pico otomatik olarak bunu tanıyıp çalıştırıyor. UF2, Pico'ya özgü çalıştırılabilir dosya formatı. Çapraz derleyici çıktı olarak .uf2 dosyası üretiyor. Bu dosya bizim yazdığımız C kodu veya Micropython yorumlayıcısı olabilir.

Arduino IDE'de çalışmak için, Bootsel'e basılı tutarken Pico USB'ye bağlanmalı. Sonra IDE'de yukarıdaki drop-down menüden "Select other board and port..."u seçip, çıkan pencerede Boards altında Pico diye aratınca çıkanlardan "Deprecated" olmayan board'u seçmek gerekiyor. Pico bağlandığında /dev/ttyACM0 aygıtını oluşturuyor, port olarak bu seçilmeli. Bunu yaptıktan sonra IDE, Pico çapraz derleyicisini ve kütüphanelerini indireyim mi diye soruyor. Buna evet diyip kurulumu tamamladıktan sonra, Pico Blink'le test edilebilir (File -> Examples -> 01. Basics -> Blink). Bu çalışıyorsa Pico diğer sketch'leri çalıştırmaya hazır demektir.

Micropython ile çalışmak için ilkin Micropython yorumlayıcısına ihtiyaç var. Bu daha önce belirttiğim gibi .uf2 uzantılı bir dosya. Dosyanın bağlantısı Raspberry'nin resmi sayfasında var. Pico bootloader modundayken çıkan .htm dosyanın bu yorumlayıcının bulunduğu sayfaya yönlendirdiği ve .txt dosyada da model bilgisinin yer aldığı yazıyor ama bende düzgün çalışmadı. Doğru .uf2 dosyasını Pico'ya kopyalayınca disk ortadan kayboluyor, yanlış .uf2 dosyasını kopyalayınca hiç birşey olmuyor.

İkinci olarak bir python IDE gerekli. Genellikle, Linux ve Windows'ta Thonny IDE öneriliyor. Bu yazıyı hazırlarken paket yöneticisinin resmi reposunda 4.1.6 sürümü vardı ancak Thonny'i Linux'ta çalıştırmak çok basit değil. Ben önce paket yöneticisiyle kurdum, fakat aşağıdaki hatayı aldım:

RuntimeError: There is no current event loop in thread 'MainThread'.


Bu bir izin (permission) kaynaklı değil, çünkü daha sonra github'dan .tar.gz dosyasını indirip açtım ve çalıştırdığımda hiç bir hata almadım ve daha da garibi paket yöneticisiyle kurduğum thonny'i tekrar çalıştırdığımda aynı hatayı almama rağmen sorunsuz çalıştı. Bu arada .tar.gz'den çıkan dosyayla paket yöneticisinden gelen dosya binary olarak farklı. Muhtemelen derleme parametreleriyle ilgi bir durum veya paket yöneticisi bazı config dosyalarını düzgün oluşturamıyor.

Eğer Pico, Micropython yorumlayıcısını çalıştırmaya başlamışsa, thonny'nin sağ alt köşesinden doğru board'u ve portu seçince ekrana gelen python prompt'u seri port üzerinden Pico'da çalışan python'dan geliyor. Yani buraya bir komut yazarak doğrudan Pico üzerinde çalıştırılabilir. Komutun çıktısı yine USB üzerinden thonny'e gelecektir. Her ne kadar bir ekran bağlı olmasa da Pico'daki stdout seri port olduğu için, print komutu seri porta yazıyor ve bu thonny'le görülebiliyor. Yukarıda "doğru portu board'u seçince" yazdım, çünkü thonny aynı zamanda bilgisayardaki python yorumlayıcısını da kullanabiliyor. Eğer bu seçiliyse aynı çıktı yerel python'dan gelecektir. Üst tarafta python betiği yazılıp çalıştırılabiliyor ve kaydedilebiliyor. Bir dosya açarken veya kaydederken, thonny kullanıcıya bilgisayarı mı yoksa Pico'yu mu kullanacağını soruyor. Yani board'a dosya da kaydedilebiliyor. Üstelik eğer dosya main.py olarak adlandırılırsa, board'a elektrik verilir verilmez bu dosya Micropython tarafından otomatik çalıştırılıyor. Yazılan betik IDE'deki yeşil play tuşu ile seçilen platformda çalıştırılıp kırmızı stop tuşu ile durdurulabiliyor. thonny herhangi bir örnek içermediğinden basit bir blink kodunu aşağıya ekledim:

from machine import Pin
import time

led = Pin(25, Pin.OUT)

while True:
    led.on()
    time.sleep(0.5)
    led.off()
    time.sleep(0.5)

Bu kadar thonny anlattıktan sonra şunu da ekleyeyim ki, aslında Pico'da python kodu çalıştırmak için thonny'e gerek yok. İletişimin RS232 protokolüyle gerçekleştiğini yazmıştım. Dolayısıyla minicom'da (veya Windows'ta hala var mı bilmiyorum ama Hyperterminal veya putty'de) sudo minicom -D /dev/ttyACM0 -b 115200 komutuyla bir oturum açıp, Pico üzerindeki python yorumlayıcısına ulaşmak mümkün. Yanılmıyorsam kodları kaydetmek ve kayıtlı dosyaları çalıştırmak için de klavye kısayolları mevcut ancak tüm bunları akılda tutmamak için thonny ile çalışmayı tercih ediyorum. thonny için oldukça detaylı bir videoyu buraya bırakayım:



Son olarak C/C++ kodlarını çalıştırmak aslında hepsinden daha kolay. Çapraz derleyicinin çıktısı olan .uf2 dosyayı Pico'ya atınca çalışıyor. Zor kısım SDK'nın ön koşullarını yükleyip derlemek. Ben bunun için

  1. arm-none-eabi-gcc-cs.x86_64
  2. arm-none-eabi-gcc-cs-c++.x86_64
  3. arm-none-eabi-newlib.noarch
  4. arm-image-installer.noarch

paketlerini yükledim. Ancak ben yıllardır kendi makinamda türlü çeşit paket derlediğim için zaten bir sürü devel paketi yüklü. Dolayısıyla yukarıdaki dört paket minimal gereklilik. Örn. cmake de gerekli ama bu benim bilgisayarımda zaten vardı.

Pico SDK'yı ve Pico Examples'ı github'dan klonladım. PICO_SDK_PATH çevre değişkenine SDK'yı indirdiğim yolu girip pico-examples dizininde cmake -S . && make -j4 dedim. Küçük küçük bir sürü örnek olduğu için paralel make derlemeyi çok hızlandırıyor. Bu arada bir build/ dizini oluşturup derlenen dosyaların oraya yazılması tavsiye ediliyor. Ben biraz üstünkörü derledim. Derleme bittikten sonra her örneğe karşılık bir .uf2 dosyası üretiliyor. Bunu denemek için blink/blink.uf2 Pico'ya kopyalanabilir.


7 Aralık 2025 Pazar

Arduino ve MAX7219 ile 8x8 LED Matrix Display Nasıl Kullanılır?


Merhaba. Bu yazıda MAX7219 entegresini ve bununla 8x8 LED Matrix'in nasıl sürüleceğini anlatacağım. Entegrenin, Arduino için yazılmış bir kütüphanesi olduğu için örnekleri Arduino ile yapacağım ama seri veri yolu protokolü mikrodenetleyiciden bağımsız olduğundan, başka denetleyicilere de kolayca uygulanabilir. MAX7219 da oldukça basit bir entegre, kütüphane olmadan doğrudan erişmek de kolay. Örneklerden birinin kütüphane olmadan nasıl yapılacağını da göstereceğim.


MAX7219 ve MAX7221 Entegreleri

MAX7219 ve MAX7221 entegreleri, SPI ile 7 segment display veya 8x8 LED matrix sürmek için arayüz sunan entegreler. Pin ve komut uyumlular, datasheet'leri ve yaptıkları iş hemen hemen aynıysa da MAX7221, SPI dışında başka seri protokolleri de destekliyor ve daha kararlı çalışıyor. Dolayısıyla daha pahalı.

Entegre, ondalık noktalı (decimal point) sekiz adet 7 segment display'i, bar-graph display'leri ve 8x8 LED matrixlerini destekliyor. Bunlar için farklı kod çözme (decode) seçenekleri var. 64 LED, 8 tane ortak katot üzerinden sürülebiliyor. Ben hazır kitlerden aldığım için entegre pin bağlantılarına değinmeyeceğim.

Entegrelerin güzel bir özelliği de sekiz taneye kadar kaskadlanabiliyor olmaları. Yani bu modülden sekiz tane alıp birinin DOUT bacağını diğerinin DIN bacağına zincirleme bağlayarak daha fazla sayıda LED display'i de sürebilirim. Kitin arka tarafındaki 5-pin konektör kaskadlama için. Elbette farklı display türleri de kaskadlanabiliyor.

Benzer şekilde dört tane 8x8 LED matrix display de kaskadlanmış hazır kit olarak satılıyor. Kaskadlamanın nasıl çalıştığını anlamak için bir tane de 4lü LED matrix display aldım. Bunu entegrenin yazmaçlarına değindiğim zaman ayrıntılı açıklayacağım.

Baskı devreye arkadan bakıldığı zaman kaskadlamanın nasıl ve ne kadar basitçe yapıldığı kolayca anlaşılıyor:

Modülün beş tane bacağı var. Vcc ve GND bacakları açıklama gerektirmiyor. DIN (Data In) verinin yazıldığı bacak. CS (Chip Select) bacağı active low. Bu bacak aktif olduğunda DIN'e bağlı shift register latch'ları açılıp veri okunuyor. CLK, DIN'den gelen veriye eş zamanlı saat sinyalini taşıyor.

Buradaki Türkçe'den ötürü bir parantez açma ihtiyacı hissettim. Normalde kullandığım dile özen göstermeye çalıştığımdan yukarıda cümle beni rahatsız ediyor. Diğer yandan yukarıdaki terimlerin oturmuş Türkçe karşılığını bulamıyorum. Shift register = kaydırma yazmacı, evet anlaşılabiliyor ama anlamı tam karşılamıyor gibi. Peki, Latch = Mandal(?). Türkçe Wikipedia'da Flip flop maddesinde parantez içinde "yaz-boz" verilmiş. Bazı yazarlar terimleri kendi Türkçeleştirmeye çalışıyor ama bu terimler kullanılmayıp yaygınlaşmadığında, yazılar sadece yazarının anlayabildiği anlamsız bir metinden öteye gitmiyor. O yüzden bu terimler beni rahatsız etse de olduğu gibi bırakacağım.

Yazmaçlar

Yazının başında MAX7219'un oldukça basit bir entegre olduğunu söylemiştim. Bütün işlemler toplam 14 yazmaçla yapılıyor. Bunların sekizi LED'leri kontrol eden veri yazmaçları. Yazmaçların listesini kolaylık olsun diye datasheet'ten alıp yana ekledim.

Bu adreslerin (veya başka bir bakış açısıyla komutların) hepsi 8-bit ve hepsinden sonra 8-bitlik bir veri alanı geliyor. Başka bir deyişle DIN'e (Data IN) gelen her paket 16-bit olmalı. Veri göndermek için CS' (nChipSelect) sinyalini lojik 0 yapmak ve veriyi DIN'den CLK ile senkron şekilde göndermek gerekiyor. CS'´yi tekrar lojik 1 yaparak veri gönderimi sonlandırılıyor. Datasheet'in "Serial Addressing Modes" bölümünde iletişim ayrıntılı anlatılıyor.

İlk yazmaç olan No-Op'u en son ele alacağım, çünkü No-Op aslında tam olarak No-Op işlemi yapmıyor.

Digit 0..7 yazmaçları, 8x8 LED Matrix'in bir LED satırını veya 7-segment display'in bir basamağını tutuyor. Örn. Digit 0'a 0x0F değerini göndermekle, yani veriyoluna 0x01 0x0F verisini sürmekle ilk satırın en sağdaki dört LED'in yakıp, en sol dörtlüyü söndürmüş oluyorum. Digit 1 için bu ikinci satır, Digit 2 için üçüncü satır vb.

Decode Mode (0x09) yazmacı entegrenin içindeki 7-segment decoder birimini kontrol ediyor. Buraya 0xFF değeri yazılırsa, Digit X yazmaçlarına gelen verilerin yalnızca düşük dört biti dikkate alınıp, 7-segment display için çözümleniyor, 0x00 yazılırsa herhangi bir kod çözme işlemi yapılmıyor. 8x8 LED Matrix için doğru olan mod bu.

Intensity (0x0A) yazmacı PWM ile LED'lerin parlaklığını ayarlamak için kullanılır.

Scan Limit (0x0B) yazmacı, bütün LED'ler kullanılmayacaksa (örn. ondalık noktası olmayan 7 segment display) bağlı olmayan pinleri deaktive ederek, kullanılan LED'in tarama hızını arttırmak için kullanılır. LED Matrix'te bütün LED'ler kullanılacaktır.

Shutdown (0x0C) yazmacına 0x0 değeri yazılırsa entegre kendini kapatır. Entegrenin tarama osilatörü kapanır ve LED'ler söner. Çekilen akım 150µA'e düşer. Normal çalışmada çekilen akım mA mertebesinde olup tüm LED'ler yanık olduğu zaman 300 mA kadardır. Entegreye elektrik geldiği zaman shutdown modunda başlar. Dolayısıyla ilk iş, buraya 0x1 değeri yazılmalıdır.

Display test (0x0F) yazmacına 0x1 değeri yazıldığı zaman tüm LED'ler yanar. Böylelikle bozuk LED olup olmadığı kontrol edilebilir. Test sırasında Digit yazmaçlarının değerine dokunulmaz. Buraya 0x0 değeri yazıldığında entegre normal çalışma moduna döner.

No-Op işlemi (0x0) entegrelerin kaskadlanmasında kullanılır. Bu komutun, komutu alan display üzerinde etkisi yoktur. No-Op işlemini alan entegre, bu işlemden sonra gelen işlemi DOUT bacağından gönderir. Örn. dört display'in zincirleme bağlandığı durumda, dördüncü display'e erişmek için önden üç tane No-Op ve ardından asıl işlem gönderilir. Bu durumda, birinci display ilk No-Op'u aldıktan sonra DOUT bacağından önce iki No-Op ardından asıl işlemi ikinci display'e gönderir. İkinci display aynı şekilde DOUT bacağından bir No-Op ve asıl işlemi üçüncü display'e gönderir. Üçüncü display kalan No-Op'u alır ve dördüncü display'e asıl işlem böylece gönderilmiş olur. Aşağıda bu işleme dair bir çizim var.



Datasheet'teki blok diyagramda bu adda bir yazmaç gösterilmiyor, fakat onuncu sayfada "No-Op Register" başlığı altında anlatıldığı için bu bir işlem mi yoksa bir yazmaç mı ben anlayamadım. Bu arada yukarıda görüldüğü üzere, No-Op'un anlamsız bile olsa, kendisini takip eden 8-bitlik veriyle birlikte gönderilmesi gerekmektedir.


Arduino LedControl Kütüphanesi

Aslında yazmaçları bu kadar ayrıntılı açıkladıktan sonra kütüphane kullanmak mantıksız gelebilir, ama LedControl kütüphanesi bazı işleri gerçekten kolaylaştırıyor. Örn. Digit yazmaçları LED'lere satır satır erişim sağlıyorken, kütüphanede setRow() yanısıra setColumn(), setLed() ve setChar() gibi fonksiyonlar var.

Kütüphaneyi yüklemek için Arduino IDE'de Tools -> Manage Libraries'i seçip oradan LedControl aratmak gerekiyor. Sonrasında kodlarda LedControl.h header dosyasını ekleyip bir LedControl nesnesi oluşturuluyor. Bunu oluştururken constructor fonksiyona hangi bacağın Arduino'nun hangi bacağına bağlı olduğunu ve kaç tane cihazın kaskadlandığı giriliyor. Örn.

#include "LedControl.h"
LedControl lc = LedControl(data = 12, clk = 11, chipSel = 10, 4);

Ben bütün örneklerde DIN'i Arduino'nun 12. bacağına, CLK'yı 11 ve CS'i 10. bacağına bağladım. Görseli aşağıda:

Fritzing'deki LED Matrix display görseli altı pinli. En üstte boşta duran pin, ikinci Vcc, dolayısıyla bağlı olmamasının bir önemi yok.


Kod Örnekleri

İlk örnekte basit bir karakter kaydırma işlemi var. Burada karakterler modül modül kaydırılıyor. Herhangi bir bit operasyonu yok. Kodu github hesabıma yükledim. MAX7219 entegresinin başlangıçta shutdown modunda açıldığını yazmıştım. setup() rutininin 61. satırındaki döngüde her display tek tek shutdown modundan çıkarılıyor, LED'ler en düşük parlaklığa ayarlanıyor. "dizi" dizisi display'de gösterilecek karakterlerin sıralarını tutuyor. En sonda döngüsellik için son 3 karakter tekrar ediyor.

table dizisi karakterlerin bitmap'lerini içeriyor. Bu bitmap'leri şuradan topluca indirip BIOS.F08 fontunu kullandım. Bu dosya klasik 8x8 BIOS fontunu içeriyor. Bunu GIMP'te açıp C source code veya C header olarak export ettim. Elbette tüm karakter tablosu 2 KB olduğu için (256 * 8), Arduino UNO'nun 2KB'lık RAM'ine hepsini aktarmak olanaksız, zaten gerek de yok. Yalnız kullanılacak karakterler yeterli. loop() rutinindeki for döngüsünde değerler setRow() fonksiyonuyla satır satır yazılıyor. Karakterler 20 tane oldukları halde, pointer'in değeri 16 olduğunda ekranda 16, 17, 18 ve 19. karakterler görünecekleri için, pointer'in 20-4'ü aşmaması gerekiyor.


İkinci örnek (counter) ilkinden daha da basit bir sayaç. Arduino, "a" dizisindeki değerleri sıfırıncı elemandan başlayarak işlemci saat hızında arttırıyor. 25. satırdaki for döngüsünde dizi elemanları byte büyüklüğünde bir taşmaya (overflow) karşı kontrol ediliyor. Eğer taşma varsa bu dizi elemanı sıfırlanıp, elde bir sonraki byte'a aktarılıyor (satır 29), ve bu ikili sayaç 32. satırda LED'lerle görselleştiriliyor.

Sıfırdan 256'ya bir sayma işlemi bir saniyenin altında tamamlanıyor. Onuncu bite karşılık gelen LED yaklaşık saniyede bir yanıp sönüyor. Dolayısıyla daha küçük bitleri görmezden gelirsek, geri kalan LED'lerin yanması için kabaca 2(64-10)=254 saniye gerekiyor, ki bu da 5.709 * 108 (571 milyon) yıl ediyor.


Üçüncü örnek, ikincinin aynısı ama bunu kütüphane kullanmadan yazdım. Bu kodda, önce pinler OUTPUT olarak ayarlanıyor. Ardından sırayla entegre shutdown modundan çıkarılıp (satır 32), tarama limiti 7'ye ayarlanıyor (satır 37), decoding devreden çıkarılıyor (satır 43) çünkü 8x8 LED display bağlı. 51. satırda parlaklık en düşüğe ayarlanıyor ve tüm LED'ler sıfırlanıyor (satır 60). loop() prosedüründeki mantık aynı. Tek fark, her dizi elemanı bir LED satırına karşılık gelecek şekilde Digit yazmaçlarına yazılıyor olması (satır 77).

Görüleceği gibi entegre kütüphane kullanmadan da kolayca programlanıyor. Burada amaç kütüphaneli veya kütüphanesiz hız testi yapmak değil. Zaten muhtemelen yakın sonuçlar alınacaktır. Hıza odaklanacak olsaydım zaten Assembly kullanıyor olurdum.


Dördüncü ve son örnekte yazıyı bit kaydırma işlemleri kullanarak yumuşak geçişlerle kaydırdım. Bunun için iki tane uzun kelime alıp bunu karakter dizisi haline getirdim (satır 16 veya 19). İlk örnekteki gibi bir karakter tablosu oluşturdum, elbette bu sefer daha fazla karakterle. setup() rutininde her zamanki gibi entegre başlatılıyor ve karakterler bitmap olarak "kayanyazi" dizisine aktarılıyor.

loop()'ta "kayanyazi" dizisinin ilk 4 karakteri display'e gönderiliyor. Yazıyı sola kaydıracağımdan, her satırın en soldaki bit'inin (MSB) carry_old değişkenine atıyorum (yani carry_old sıfırıncı sütunu içeriyor). Sonra tüm karakterler bir bit kaydırılıyor (satır 108) ama önce her karakterin ilk sütunu carry_new'e aktarılıyor (satır 106) ki, bu taşacak bitler sonraki karakterin en düşük anlamlı (LSB) bitine aktarılabilsin.

18 Mart 2021 Perşembe

Hantek 6022BL USB Osiloskop: OpenHantek+PulseView vs. Hantek


Merhaba. Bir buçuk yıl kadar önce Hantek 6022BL USB osiloskop satın aldım. Daha önce Windows'ta Hantek'in kendi yazılımını kullanıyordum ama çok memnun değildim. Önceki yazıdan anlaşılacağı gibi yakın zamanda bilgisayarımı tekrar kurdum. Bu fırsatla Hantek için alternatif programlara gözatma şansım oldu. Osiloskop için OpenHantek'i ve logic analyzer için PulseView'ı inceledim. Bu yazıda cihaz sürücülerinin nasıl kurulacağını anlatıp yazılımları karşılaştıracağım, ayrıca sürücü kurulumu için kendime not gibi olacak.

Hantek 6022BL, iki kanal USB osiloskopla 16 kanal logic analyzer'ı birleştiriyor. 20 MHz bant genişliği ve 48MS/s örnekleme oranıyla ortalama bir kullanıcının ihtiyacını karşılıyor. Özelliklerinden anlaşılacağı gibi, profesyonel bir tezgah üstü (benchtop) model değil. Benim için iki yanı çekiciydi, küçük ve ucuz olması.

USB osiloskoplar çıktığından beri bunların işe yararlığı konusunda sürekli bir tartışma var, öyle ki bir çok forumda USB'ciler ve benchtop'çılar, Android'ciler ve iOS'cular gibi ikiye bölünmüş durumda. Açık konuşmam gerekirse Hantek'ten çok daha kaliteli USB cihazlar var. Picotech firması açık ara kaliteli cihazlar üretiyor ancak Hantek'e yakın özellikli 2205A MSO modeli Hantek'in 4 katı fiyata satılıyor. Bu arada, amazon yorumlarına göre Hantek'lerde USB izolasyonu yok. Bu, eğer probdan kazara yüksek voltaj gelirse anakarta kadar iletilir demek. Picotech'lerde ve bazı Owon modellerindeyse girişler izole. Benim görüşüm, USB modeller fiyatlarıyla benchtop modellere giriş seviyesinde ciddi rakipler. Ama lab seviyesinde USB osiloskoplar bir elin parmakları kadar.

Bu cihazın Fedora'da ayarlanması gerçekten çocuk oyuncağı. Bu yüzden konuya Fedora'yla gireceğim. Osiloskobun arkasında, USB bağlantılarının olduğu yerde bir düğme var. İlk olarak bu düğmenin basılı, yani H konumunda olması gerek. Düğme H konumundayken osiloskobun USB ID'si 04B4:602A oluyor.


Yukarıda görülen USBXI portu, sanırım Hantek'e özgü modifiye bir USB portu ve birden fazla cihazı kaskadlamaya yarıyor. Yani iki cihaz alıp 4 kanal osiloskop veya 32 kanal logic analyzer olarak kullanmak mümkün.

Cihazla birlikte görseldeki Y şekilli USB kablo geliyor. Kablonun iki girişinin olması, bilgisayarda tek porttan çekilen akımın yetmemesi durumunda, ikinci porttan da akım çekilebilmesi için. Ben iki uçlu standart B-tipi kabloyla da bağladım, sorun çıkmadı. Kırmızı uç yalnız güç için, veri bağlantısı yok. Dolayısıyla bu USB adaptöre de bağlanabilir.

Hantek'in kendi yazılımının linux sürümü yok. Linux için OpenHantek iyi bir alternatif çünkü Fedora'nın standart reposunda rpm'i var. Yani kurmak için sudo dnf install openhantek komutu yeterli. Aynı şekilde Ubuntu reposunda da deb dosyası var. Bu arada OpenHantek açık kaynak kodluysa da derlemeyi denemedim.

Osiloskop bilgisayara bağlıysa OpenHantek çalıştırıldığında hemen açılıyor. Aksi halde kullanıcıdan sinyalleri okuyabileceği bir aygıt seçmesi için bir pencere geliyor. Aşağıda osiloskop arayüzünün ekran görüntüsünü ekledim.


Standart bir osiloskoptan beklenecek birçok özellik var. Ekran görüntüsü için kalibrasyon çıkışını 1kHz'ye ayarlayıp birinci kanala, sinyal üretecini ikinci kanala bağladım, trigger'ı birinci kanal olarak seçtim. Marker'ları kare dalgaya göre ayarladığımda, ekranın üst tarafında frekansı 1 kHz/div olarak okunuyor. En alttaki koyu mavi çizgi sinüs sinyalinin Fourier (spektrum) analizi. 6 ve 8 kHz'deki minik harmonikler görülebilir. Aşağıda basit bir RC devresiyle elde ettiğim Lissajous eğrisinin ekran görüntüsü var:


Soldaki menüden sinyal histogramı eklenebiliyor ve zoom düğmesiyle oradaki iki kareyi ayrı bir bölmede yakınlaştırılıyor. Matematik modunda toplama, çıkarma ve çarpma işlemleri var ve son olarak sinyal değerleri .csv olarak alınabiliyor.

OpenHantek'in logic analyzer özelliği bulunmadığından logic analyzer için sigrok'un PulseView (PV) yazılımını kurdum. Aslında PV analog sinyalleri de gösterebiliyor ama bu konuda OpenHantek kadar yetenekli değil. PV'nin kurulumu da görece kolay. Downloads sayfasında Fedora için rpm paketleri var. PV'yi derlemeyi de denemiştim ama beklediğimden uzun sürdüğü için vazgeçtim. Yüklemeye başlamadan şu paketlerin yüklü olması gerek: libzip, qt5-qtbase, qt5-qtbase-gui, boost-serialization, qt5-qtsvg. Fedora 33'te bunlardan yalnız boost-serialization yüklü değildi. Bunlar yüklendikten sonra Downloads'taki paketlerin sayfadaki sırayla yüklenmesi gerek, ancak bazı paketler birden fazla rpm içeriyor. Aşağıdaki komut bloğu ile hepsi yüklenebilir:

sudo dnf install libzip qt5-qtbase  qt5-qtbase-gui  boost-serialization  qt5-qtsvg

sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/libserialport/0.1.1/9.fc33/x86_64/libserialport-0.1.1-9.fc33.x86_64.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/libsigrok/0.5.2/4.fc33/x86_64/libsigrok-0.5.2-4.fc33.x86_64.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/libsigrokdecode/0.5.3/6.fc33/x86_64/libsigrokdecode-0.5.3-6.fc33.x86_64.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/sigrok-cli/0.7.1/5.fc33/x86_64/sigrok-cli-0.7.1-5.fc33.x86_64.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/libsigrok/0.5.2/4.fc33/x86_64/libsigrok-cxx-0.5.2-4.fc33.x86_64.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/sigrok-firmware/0.1.0/16.20151211gitb2daf81.fc33/noarch/sigrok-firmware-filesystem-0.1.0-16.20151211gitb2daf81.fc33.noarch.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/sigrok-firmware-fx2lafw/0.1.7/3.fc33/noarch/sigrok-firmware-fx2lafw-0.1.7-3.fc33.noarch.rpm
sudo rpm -ivh  https://kojipkgs.fedoraproject.org//packages/pulseview/0.4.2/4.fc33/x86_64/pulseview-0.4.2-4.fc33.x86_64.rpm

PV için cihazın USB bağlantısını söküp H/P düğmesini P (basılı olmayan) konumuna getirmek gerek (Cihaz çalışırken düğmeye basılırsa ID değişmiyor). P konumunda cihazın VID/PID'si 0925:3881 oluyor ve Saleae Logic çipiyle (veya uyumlusuyla) çalışıyor. OpenHantek gibi, PV de cihaz bağlıysa otomatik olarak tanıyıp uygun sürücüyü seçiyor. Eğer "<No Device>" yazıyorsa, bunun üstüne tıklayıp "fx2lafw" sürücüsünü seçmek gerekiyor. Listede hantek-6xxx sürücüsü olsa da cihazın üstündeki düğme P konumunda olduğundan hantek olarak çalışmıyor. Doğru sürücü seçildikten sonra "Scan for devices..."a basınca en altta "Saleae Logic with 8 channels" bulunyor. Bu noktada PV küçük bir hayal kırıklığı yaratıyor çünkü sürücü yalnız 8 kanalı destekliyor (Kaynak: Hantek 6022BL) umarım bu ileride geliştirilir. PV'nin en güzel özelliğiyse içinde bir protokol çözücü olması.

Logic analyzer ile gösterecek hazır birşeyim yoktu. Internette Z. X. Lee tarafından yazılmış bir Arduino projesi buldum. O da kendi blogunda Saleae'nin ürününü test etmiş. Projenin ileride silinmesi olasılığına karşı .ino dosyası buradan da indirilebilir. Dosyayı Arduino'ya yükleyip bağlantıları yaptıktan sonra (Arduino Pin 5..12) örnekleme parametrelerini 24 MHz ve 100M örneğe ayarlayıp örneklemeyi başlatıyorum (sol üstte "Run") ve aynı zamanda en sağdaki yeşil-sarı butona basıp paralel protokol çözücü ekliyorum.


Cihaz seçiminin hemen solundaki lacivert butonla (marker) sinyal geçişlerinin araları ve sinyal frekansı ölçülebiliyor. PV'nin bundan başka fazla bir özelliği yok.


Şimdi bunları Windows'ta yapacağım. Öncelikle Windows'ta iki seçeneğim var: Biri OpenHantek+PV'yi Windows'ta da kullanırım ama böyle olursa logic analyzer'ın 16 kanalını hiçbir zaman kullanamam. İkincisi Hantek'in kendi yazılımını kullanırım. Bu arada, bu yazılımın özel bir adı yok, kısayolunun adı "Hantek6022BL". Yazının geri kalanında karışıklık olmaması için buna HKY (Hantek'in kendi yazılımı) diyeceğim. Hiç yaratıcı değil ama yaratıcılığı Hantek'in yapması gerekiyordu. Ayrıca ilginç bir durum da, OpenHantek'le HKY farklı USB sürücülerini kullanıyorlar. Yani ikisini de kurup kullanma şansım yok. Ben Windows'ta yine HKY'yle devam etmeye karar verdim çünkü diğerlerini istediğim zaman diğer makinamda kullanabilirim.

Cihazı HKY ile kullanırken düğmenin H konumunda olması gerek. Bunu kontrol edip bilgisayarıma bağladım ve beraberinde gelen CD'den Windows sürücülerini yükledim. Burada ben bir sorun yaşamadım ama eğer sürücüler imzalanmamış olduğu için Win10'da yüklenemezse, sorunun geçici (ve sevimsiz) bir çözümü var. Bu arada Hantek'in sayfasında "HT6022BL Software" dosyası "HT6022BL Driver"dan daha yeni sürücüler içeriyor ve Şubat '21 itibariyle, ürün CD'sindeki içerik "HT6022BL Software" ile aynı. Sürücüleri kurduktan sonra CD'deki kurulumu çalıştırıp HKY'yi başlattım. Açık konuşmak gerekirse, arayüz OpenHantek'le karşılaştırıldığında çok çakma olduğu izlenimini uyandırıyor.

Osiloskop için 6022BE'ye tıkladım. Kişisel fikrim arayüzün hiç kullanışlı olmadığı.


İlk göze çarpan, sağ panelin olması gerekenden sade olması. Sıkça kullanılan "marker"lar için üst alandaki düğmelere tıklamak gerekiyor. Kullanışlı bulduğum tek özellik yukarıdaki "Auto" düğmesinin kanallar için uygun gerilimi, trigger ve zaman aralıklarını otomatik ayarlaması. Math modunda fazladan A/B işlemi var. Fourier analizini ise anlayamadım, 2 kHz sinyal için ekranın kenarında yalnız bir tepe çıkardı ve OpenHantek gibi harmonikleri yakalayamadı. Ayrıca zoom veya histogram da bulunmyor. Ctrl+M kısayoluyla açılan "Measure" panelindeki (aşağıdaki görsel) sinyal bilgileri güzel ama yine arayüz eksikleri var. Örn. bilgileri iki kanaldan birden alırken alt alta değil de yan yana sıralasaydı hem pencereye daha çok veri sığar hem de karşılaştırmak daha kolay olurdu.


Son olarak arayüzde bulduğum bir hatayı da yukarıda göstermeye çalıştım. Sağ panel küçültüldükten sonra kaydırmaksızın büyütülünce kontroller kayboluyor.

Osiloskop arayüzünü kapatıp, logic analyzer için 6022BL ikonuna tıkladım ve program açılır açılmaz örneklemeyi başlattı. Oysa ne örnekleme hızı ne de sayısını ayarlamamıştım! Görüldüğü gibi HKY kullanışlılık konusunda oldukça başarısız.


Sağ panelde, örnekleme oranını 100K/s yaptım. Ancak örnekleme sayısı değiştirilmiyor. Bu maalesef programın eksikliği. Bunu cihazı aldıktan çok sonra farkettim. Kullanım kılavuzunun 66. sayfasında "Buffer Size: Max. 1M Samples" diyor. PV, ara belleği sürekli okurken, HKY'yi yazanlar anladığım kadarıyla ara bellek dolunca örneklemeyi durdurmayı tercih etmişler. Örnekleme üst sağdaki "Play" benzeri düğmeyle başlatılıyor. Her kanalın renginin aynı olması ve bunun değiştirilememesi de okunaklılığı düşürüyor.

Peki HKY'nin bu kadar eksiği varsa neden kullanalım? Yukarıda açıkladığım gibi linux'ta HKY'yi istesem de kullanamıyorum ama yalnız Windows bilgisayarım olsaydı yine OpenHantek+PV kullanıyor olurdum. İkisini daha önce Windows'ta denedim ve şu an değiştirme konusunda tek çekincem sürücüler. Daha önce dediğim gibi OpenHantek ve PV, HKY'den farklı olarak WinUSB sürücüsünü kullanıyor. OpenHantek'e geçmek için Hantek'in sürücüsünü silmeye gerek yok. zadig adlı program eski sürücüleri etkisizleştirip WinUSB'yi yüklüyor. PV'yi Windows'ta kurunca zadig de kuruluyor. Zadig'in kullanımı OpenHantek'in kurulum dökümanında "Windows USB Driver Install" başlığı altında anlatılıyor.

Kısaca anlatırsam, zadig'i başlatıp yukarıdaki listede Hantek6022BL'i seçtim. Listede bu yoksa, Options altında "List All Devices"ı aktifleştirmek gerekiyor. Soldaki görseli WinUSB yüklü bir makinadan aldım, normalde "Driver" kısmında WinUSB yazmayacak. "Install Driver"a bastıktan sonra WinUSB yükleniyor ama bu işlem iki defa tekrarlanmalı. Bunu yapınca osiloskop, aygıt yöneticisinde "Universal Serial Bus devices" altında listelendi. Zadig veya PV, MSVCR100.DLL bulunamadı hatası verirse VC++ 2010 kütüphanesinin yüklenmesi gerekiyor.

 
Hantek Sürücüsü

 
 
WinUSB sürücüsü

 

Bundan sonra OpenHantek Windows'ta sorunsuz çalışıyor. Aslında bu işlem çok karışık değil, tersi daha karışık. Daha önce zadig'le WinUSB sürücülerini kaldırdığımda HKY'yi tekrar çalıştıramadım. Sonrasında Hantek sürücülerini de silip tekrar yüklemem gerekti. Hantek sürücüsü yüklüyse OpenHantek'i çalıştırınca "UploadFirmware: Couldn't open device..." şeklinde bir hata mesajı alınıyor. Son olarak, iki programı aynı makinada çalıştırmanın tek yolu Windows'ta bir linux sanal makina kurmak. Tam tersi de mümkün ama disksiz bir linux (Fedora veya Mint) sanal makinada OpenHantek tek komutla kurulabiliyor.

Peki ya diğer alternatifler? Önceden diğer alternatifler konusunda bilgim yoktu. Bu yazıyı hazırlarken Hantek osiloskop için altı farklı yazılım alternatifiyle ilgili yazıya rastladım. Bu karşılaştırmada OpenHantek öne çıkmış. Bunlardan yalnız Open6022'yi duymuştum ama son indirdiğimde dağınık C++ kodlarından oluşan ve içinde nasıl derleneceğinin bile anlatılmadığı bir zip dosyadan oluşuyordu. Anlaşılan zaman içerisinde geliştirilmiş. Örn. Open6022'de "Measure" bilgileri yukarıda yazdığım gibi iki kanal için yanyana listeleniyor. Bu nedenle bunlar da denemeye değer olabilir.


29 Eylül 2020 Salı

MIT App Inventor2 ile Mobil Uygulama Geliştirelim (Bluetooth Termometre)


Merhaba. Bir önceki seri termometre yazısına bir dostumdan gelen yorum, bana bu yazı için fikir verdi. Malum, seri portlu bilgisayarlar artık bulunmuyor. Sıcaklık verisini cep telefonumdan okumak istesem, telefonumda bunu okuyacak uygulama var mı? Belki evet, PlayStore'da Bluetooth'dan (BT) ham (raw) veri okuyan uygulamalar bulunabiliyor. Bu uygulama ne kadar güvenli? Neden tek işi BT'a veri göndermek/almak olan bir uygulama kamerama, mikrofonuma erişmek istiyor?

İlk akıllı telefonumda kullandığım BluetoothSPP adlı uygulama, yeni telefonumdaki Android sürümüne uyumlu olmadığından BT kontrollü arabam için bir uygulama yazmıştım. Yaptığı iş, bir byte uzunluğunda mesajlarla (daha doğrusu karakterlerle) arabayı ileri geri götürmek, döndürmek veya durdurmak. Bu uygulama için, MIT App Inventor'u keşfetmemle sıfırdan Android programlama öğrenmeme gerek kalmadı. App Inventor'la, bir satır bile kod yazmadan uygulamaları sürükle bırak tekniğiyle geliştirmek mümkün. Belki mükemmel değil ama hızlıca birşeyler geliştirmek için ideal. Güvenli mi? Güvenliğini kişisel olarak test etmedim ama MIT gibi bir kurumun malware içeren bir platform geliştirmesi kulağa pek olası gelmiyor. Üstelik günde ~60K aktif kullanıcısı (toplamda >8M kullanıcı) olan bir platformun güvenlikle ilgili sıkıntıları olsaydı, ortaya çıkardı.

App Inventor'a geçmeden önce, kullandığım donanımı ve Arduino'da çalışan yazılımı açıklayacağım. Bu arada, bu yazıda yalnızca projenin yapılabilirliğini göstermek istedim. Uygulama "yeterince" iyi çalışıyor, tasarım açısından da mükemmel değil çünkü benim uzmanlığım değil. Devreyi belki Arduino yerine mikrodenetleyiciyle yapsaydım daha basit olabilirdi ama bu da (eğer yapabilirsem) başka bir yazının konusu olsun.


Parça Listesi
Arduino Uno
Arduino Bluetooth Shield
2x 4.7K direnç
DS1621 Entegre
Breadboard
Jumper Kablo (yaklaşık 1 düzine)


Devrenin Kurulması
Devre olarak, Arduino ile I2C yazısındaki devreye Keyes Bluetooth modülünü ekleyip bunu kullandım. Orjinal devre şeması aşağıda. 

DS1621 Termometre

Bluetooth modülünün 4 pini var (yanda). RxD ve TxD bacakları sırayla Arduino'nun D3 ve D4 pinlerine takılı. Bunun özel bir nedeni yok, kodda gerekli değişiklik yapılarak herhangi bir data bacağına takılabilir. Kalan iki pin zaten Vcc ve GND. 
 
Bu arada Fritzing aşağı yukarı bir yıl önce paralı oldu. Kaynak kodları github'da açık ve derlemek isteyenler için hala ücretsiz ama Windows'ta sorunsuz derlenir mi emin değilim. Anasayfada eski sürümlerine bağlantı olmasa da dosyalar henüz fritzing.org sunucusundan silinmemiş (biraz araştırarak bulunabilir). Linux Mint ve Fedora repolarında eski sürümleri (v0.8.5, v.0.9.2) bulunabiliyor. Bu arada uygulama 8€, çok pahalı değil. Şu anda kurulu olduğu için kişisel olarak ödemeyi tercih etmiyorum ama ileride neden olmasın. Kurulum dosyası üçüncü parti sitelerde de bulunabiliyor ama bence güvenli değil.


Devrenin bütünü basitçe aşağıdaki görselde:

Arduino'da çalışan kodu I2C yazısından alıp, BT için aşağı yukarı 16 satır kod ekledim. BT modülünü farklı pine bağlamak için onuncu ve onbirinci satırdaki sabit tanımlamalarını değiştirmek gerekiyor. 

 /* DS1621 sicaklik sensorunun (I2C bus) Arduino ile kullanilmasi
 *
 * sicaklik sensorunun I2C ID'si A0 = A1 = A2 = GND ise 0x48 olacak
 */

#define DS1621 B1001000
#include <Wire.h>
#include<SoftwareSerial.h>  // BT modulu icin

const int BTRxD = 3;        // BT RxD Pin
const int BTTxD = 4;        // BT TxD Pin
// SoftwareSerial nesnesi tanimla
SoftwareSerial Serial1(BTTxD, BTRxD);

void setup()   {
  // I2C initialization
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(DS1621);
  Wire.write(0xAC);     // Access Config komutu
  Wire.write(0x02);     // Config register'a 02 yaziliyor.
                        // Output polarity bit = 1 => Active High
  delay(10);

  Wire.beginTransmission(DS1621);
  Wire.write(0xEE);     // Start Convert: sicaklik okumaya basla
  Wire.endTransmission();       // Stop bit
 
  BluetoothInit();
}

void BluetoothInit()    {   // Adi uzerinde Bluetooth initialization
  Serial1.begin(9600);
  Serial1.flush();
  // burada baska kodlar da vardi ama bu proje icin gerekli degil
}


void loop()  {
  float derece;
  byte SH, SL, X;
  char buffer[8]; // BT buffer
  // SH: Sicaklik yuksek anlamli byte
  // SL: Sicaklik dusuk anlamli byte

  Wire.beginTransmission(DS1621);
  Wire.write(0xAA);     // Read Temperature komutu
  Wire.endTransmission();

  Wire.requestFrom(DS1621, 2);
  // Read Temperature komutunu gonderdikten
  // sonra chip'ten sicaklik icin 2 byte iste
  X = Wire.available();
  if(X >= 2)  {                 // Chip'ten 2 byte geldiyse
    // Yuksek anlamli byte sicakligin tamsayi degeri SH
    SH = Wire.read();
    // dusuk anlamli byte SL sicakligin ondaligi 0x80 => 0.5
    SL = Wire.read();
  }

  delay(500); // sicakligi saniyede iki kere oku ve BT'den gonder
 
  derece = (float)SH + float(SL) / 256.0;
  dtostrf(derece, 2, 2, buffer);  // dereceyi string'e cevir.
  Serial.println(buffer);
 
  // buffer'daki veriyi BT'den gonder
  Serial1.println(buffer);
  Serial1.flush();

}

Program basitçe sıcaklık sensöründen veriyi alıyor, float olarak dereceye çeviriyor ve bu değeri metne çevirip hem seri porta hem de Serial1 olarak tanımladığım BT'a gönderiyor. Bu kodu derleyip cihaza gönderiyorum. Henüz hiçbir BT istemci bağlı olmadığından BT modülünün ışığı yanıp sönecek.


MIT App Inventor2 (AI2) ile Uygulamanın Tasarlanması
Bu bölümde telefon uygulamasının nasıl geliştirileceğini anlatacağım. AI2'yle ilgili ilk adımlar https://appinventor.mit.edu/explore/ai2/setup adresinde de açıklanıyor. İhtiyacım olan:
1- http://ai2.appinventor.mit.edu/ adresine girip google hesabıyla oturum açmak. Bu sayfanın HTTPS değil HTTP olması açıkçası çok hoşuma gitmiyor ama yapacak başka birşey yok. Bir de google hesabı yerine, bu sitede kendi hesabımızı açabilseydik güzel olurdu. 
2- Play Store'dan MIT AI2 Companion'u indirip kurmak. Bildiğim kadarıyla AI2'nin iPhone desteği yok. AI2 Companion olmadan da web arayüzünden uygulama geliştirilebiliyor ama geliştireceğim uygulamayı telefonumda denemek için AI2 Companion gerekli. 

Yukarıdaki bağlantıda birinci seçenekte anlatılan kurulumu kullanacağım. Uygulamayı telefonuma kurdum. Bilgisayarın ve telefonun aynı WiFi ağında olması gerek diyor. Benim bilgisayarım kabloyla bağlı ve herşey sorunsuz çalışıyor. Aynı ağda olması yeterli diye düzeltiyorum. 

Tarayıcıda "Start new project"e basıp, bir proje oluşturuyorum. Ben TermoBT adını verdim. Proje adı uygulamanın da adı olacak. Proje oluşturulduktan sonra açılıyor, açılmazsa "My Projects" ekranından açılabilir. Proje açıldıktan sonra yanda görüldüğü gibi Connect -> AI Companion'a tıklayınca ekrana bir kare kod ve şifre gelecek. Şimdi telefonumdan uygulamayı açıp kare kodu tarattığımda App Inventor telefonuma bağlanıyor. Bu andan itibaren web arayüzünde yaptığım herşey telefonda da görünecek. 

Ekranın sol tarafında, GUI oluşturmak için gereken bileşenler var. Bu bileşenleri sürükleyip ekrandaki sanal telefona bırakınca, ekranın solundaki menüye sırayla yerleşiyorlar. Sonradan eklenecek bileşenleri hizalamak için önce Layout sekmesinden "Arrangement"ları yerleştirmem gerek. Bunlar görünmez tablolar gibi düşünülebilir. Yerleşim için iki tane "HorizontalArrangement" ve bir tane "Vertical Arrangement"i altalta ekliyorum. Aynı bileşeni birden fazla kullandığımda onları ayırdetmek için adlarını değiştiririm. Üstteki "HorizontalArrangement"i "HorizontalArrangementTemp" olarak, ortadakini "HorizontalArrangementControls" olarak ve alttakini "VerticalArrangementMain" olarak ayarladım. Bileşenleri yeniden adlandırmak için "Components" menüsünün altında "Rename" düğmesi var. Son olarak VerticalArrangementMain'in içine bir VerticalArrangement daha yerleştirip onun adına VerticalArrangementSub diyorum. Bu Arrangement'ların kullanımı için aşağıdaki videodan yararlandım. Bu arada youtube, AppInventor için bulunabilecek en iyi kaynak:



Şimdi tekrar bileşenlere gidip "User Interface" sekmesinden iki tane "Label"i en üstteki HorizontalArrangementTemp'in içine yanyana yerleştiriyorum. Ardından ortadaki genel görünümden veya sağdaki bileşen listesinden Label1'e tıklayıp en sağdaki "Properties" bölümünde Text'i "Temperature: " (veya Derece) olarak değiştiriyorum. Aynı şekilde Label2'nin Text'ini boş bırakıyorum. Bir de Label2'nin yüksekliğini (Height) %50 (50 percent) olarak ayarlıyorum.
 

Son olarak HorizontalArrangementTemp'in özelliklerinden Width'i tıklayıp "Fill Parent"i seçtikten sonra OK diyorum. Böylece bu kutucukla işim bitti.

Ardından ikinci kutucuğa yanyana bir "CheckBox", bir de "Button" sürüklüyorum. Elbette tasarımı herkes istediği gibi yapmakta serbest ama kodları(!) yazarken adlandırmalara dikkat etmek gerekiyor. Butonun adını "ButtonReadTemp" olarak değiştirip üzerindeki metni "Read Temperature" olarak değiştiriyorum. CheckBox'un adını değiştirmiyorum ama metnini "Read automatically" yapıyorum. Bir de HorizontalArrangementControl'ün de özelliklerinden Width'i tıklayıp "Fill Parent"i seçmemle bu kutucukla da işim bitiyor (aşağıda):


Şimdi VerticalArrangementMain'in özelliklerinden, "AlignHorizontal"a "Center:3" ve "AlignVertical"a "Bottom:3" değerini atıyorum ve hem "Height" hem de "Width"i "Fill Parent" olarak işaretliyorum. İçteki VerticalArrangementSub, alt ortaya düşüyor. Onun da "AlignHorizontal" değerini "Center:3" olarak atıyorum ve içine bir butonla bir ListPicker yerleştiriyorum. Bunların hangisinin altta olduğunun çok önemi yok. Düğmenin metnini "Disconnect from device", ListPicker'ın metnini "Select Bluetooth device" olarak değiştirince, bu kutucukla da işim bitiyor. 

Soldaki bileşenlerden, "Sensors" sekmesinden "Clock"u ve "Connectivity" sekmesinden "Bluetooth Client"ı ekrandaki telefona sürüklüyorum. Bunlar görünmez kontroller. Bileşen listesinden Clock'a tıklayıp TimerEnabled özelliğini kapatıyorum ve TimerInterval özelliğini 500 yapıyorum. Bu, her 500ms'de Clock tetiklenecek anlamına geliyor. Herşey tamamlandığında ekran görüntüsü aşağıdaki gibi olmalı: 


Uygulama, bir BT cihaza bağlı değilse sıcaklıkla ilgili bileşenleri ve "Disconnect" düğmesini göstermeye gerek yok. Bir BT cihaza bağlanıldığında da "Select device" listesine gerek yok. Bu nedenle HorizontalArrangementTemp'in özelliklerine bir kere daha gidip en alttaki Visible kutucuğundan işareti kaldırıyorum. Aynısını HorizontalArrangementControls'a ve Button1'e de (Disconnect) yapıyorum. Şimdi uygulama açıldığında sadece cihaz seçme butonunu gösteriyor. Diğerleri, bağlantı kurulursa ekrana geliyor. Buraya kadar bütün tasarım tamamlandı. 


MIT App Inventor2 ile Kodlama
AI2'nin bence en güzel özelliği anladığımız anlamda kod yazmak yerine renkli parçalarla, lego oynar gibi prosedürleri oluşturabilmek. Bu bölümde BT ile ilgili kontrolleri hazırlarken aşağıdaki videodan faydalandım: 


Prosedürleri oluşturmak için üstteki yeşil barın en sağındaki "Blocks" düğmesine basıyorum. İlk oluşturacağım, ListPicker1'e basıldığı zaman BT cihazların listesini göstermesi. Bunun için soldaki bloklardan ListPicker1'i seçip içindeki olay (Event) listesinden TouchDown'a ait bloğu beyaz alana sürüklüyorum. Ardından yine ListPicker1'in olay listesinden "set ListPicker1.Elements to"yu beyaz alana bırakıyorum. Son olarak BluetoothClient1'in listesinden "BluetoothClient1.AddressesAndNames"i koyu yeşil "set ListPicker1..."in soluna yerleştiriyorum. İki parça yanyana eklendikten sonra kahverengi "when ListPicker1.TouchDown"da "do"nun hemen yanına uygun yere yerleştiriyorum. 


AI2'nin arayüzünde ne eklenirse aynı anda telefondaki uygulamaya da yansıdığını yazmıştım. Aynısı kodlar için de geçerli; uygulamayı geliştirirken aynı anda kodlar da telefona gönderiliyor. Önceden Arduino'yu bilgisayarıma bağlayıp kodu aktarmıştım. Telefonumu açıp "Select Bluetooth device" dediğim zaman BT cihazları listeleyecek (tabii ki telefonda BT açık olmalı) fakat listeyi alsak da henüz bağlanma kodu hazır değil. 

Cihaza bağlanmak için ListPicker1.AfterPicking olayını kullanacağım. Bunun içine Built-In -> Control'den "if ... then" bloğunu yerleştiriyorum. Sonra BluetoothClient1 fonksiyonlarından mor .Connectaddress'i if'in yanına ekleyip son olarak ListPicker1.Selection değerini (açık yeşil) de bunun yanına ekliyorum. Connectaddress fonksiyonu çağırılacak ve fonksiyon başarılı olursa then'in yanındaki blok işletilecek. Then'le birlikte herhangi bir handler vs. çağırmayacağım ama görsel ayarlamalar yapmam gerekiyor. 

1- Artık başka aygıta bağlanmayacağıma göre ListPicker1'i görünmez yapmalıyım:
set ListPicker1.Visible to false
2- Disconnect butonunu görünür yapmalıyım:
set Button1.Visible to true
3- Arrangement'ları görünü yapmalıyım:
set HorizontalArrangementTemp.Visible to true
set HorizontalArrangementControls.Visible to true
4- Başta sıcaklığın otomatik okunmaması için:
set CheckBox1.Checked to false

True / False değerleri Built-In -> Logical içinde bulunuyor. Koyu yeşil kutucuklar alt alta eklenebiliyor.


Şimdi telefondaki uygulamada, BT cihaz listesinden HC06'yı (Arduino modülü) seçtikten sonra modülün ışığı yanık kalacak çünkü telefon artık BT modülüne bağlı. Ama Disconnect'e bastığımda henüz bir şey olmuyor çünkü bunu yazmadım. Bağlantı kesme prosedürü, yukarıda yapılanların tam tersi olmalı. BluetoothClient1.Disconnect çağırılacak ve yukarıda görünür yaptıklarımı görünmez, görünmez yaptıklarımı da görünür yapacağım. Yazıyı çok uzatmamak için yalnız ilgili kod bloğunu paylaşıyorum: 


Artık uygulama bağlantıyı da kesiyor. Şimdi sıra, en önemli prosedür olan BT'dan veri okumada. Bunun için ButtonReadTemp'in Click olayını kullanacağım. Veriyi okumadan önce BT'un bağlı olduğunu ve ara bellekte veri olup olmadığını kontrol etmeliyim. Bağlantının kontrolü önemli çünkü bağlandıktan sonra bile cihaz bağlantısı fiziksel olarak kesilmiş olabilir. Bu nedenle ButtonReadTemp.Click olay bloğunun içine bir if ... then bloğu ekliyorum. Koşul olarak Built-In -> Logic'ten bir AND bloğu koyuyorum. Birinci koşul BluetoothClient1.IsConnected. İkinci koşul da ara bellekte okunacak birşeyler olması. Bunun için matematiksel bir karşılaştırma gerekiyor, o da Built-In -> Math içindeki eşittir bloğu. Bunu sürükledikten sonra eşittiri büyüktür yapıyorum. Eşitsizliğin soluna BluetoothClient1.BytesAvailableToReceive ve sağına yine Math içerisinden 0 (sıfır) koyuyorum.

Tüm bu koşullar sağlanıyorsa BT'dan okuduğum veriyi Label2'ye yazacağım. ButtonReadTemp'in son hali şöyle olacak: 


Uygulamaya geri dönüp "Read Temperature" düğmesine bastığımda, uygulama uzun bir süre veri okumadan bağlı kalmışsa, (ara bellekteki) bir sürü sıcaklık değeri ekranı dolduracak. Label2'nin yüksekliğini o nedenle telefon ekranının yarısıyla sınırlandırmıştım. En başta dediğim gibi bu projede hızlıca ortaya birşeyler koymaya çalıştım haliyle sonuç mükemmel olmadı. 

Ara bellek dolduğu için en iyisi veriyi otomatik okumak. Bunun için de CheckBox1.Changed olayına prosedür ekleyeceğim. Eğer CheckBox1.Checked ise Clock1'i aktifleştirip ButtonReadTemp'i pasifleştirmem gerekiyor veya tam tersi. Önceki prosedürlere göre bu oldukça basit. Bunun için bir if ... then ... else bloğu gerekli. Bu hazır olarak Control'de var ama if ... then bloğunun yanındaki mavi dişli simgesine basıp else'i sağdaki if'in içine sürükleyerek de oluşturulabilir.


Son olarak zamanlayıcı aktifleştirildiyse, ButtonReadTemp.Click prosedürünün yaptığını artık zamanlayıcının gerçekleştirmesi gerekiyor: 


Yukarıdaki prosedürü kolay yoldan oluşturmak için Read temp butonu için oluşturduğum "if ... then" bloğuna sağ tıkladım, "Duplicate"i seçip aynısını Clock1.Timer olayının içine yerleştirdim. Hepsi bu kadar. Eğer uygulama tamamen bittiyse bunu telefonuma apk olarak indirebilir ve AI2'ye gerek olmadan da kullanabilirim. Bunun için web arayüzünde, Build menüsünden "provide QR code for .apk"yı tıklamak gerekiyor. Uygulama derlendikten sonra, apk'yı indirmek için bir bağlantı ve bu bağlantıyı içeren bir QR kod ekrana geliyor. Bu adımda eğer AI2 Companion hala bağlıysa, QR kodu taramak için bağlantıyı kesmek gerek (Connect -> Reset Connection menüsüyle veya telefondaki uygulamayı kapatıp açarak). AI2 Companion'u yeniden açıp QR kodu tarattığımda -eğer telefondaki güvenlik ayarları apk kurmaya izin veriyorsa- uygulama gereken izinleri verdikten sonra kuruluyor ve bağımsız olarak kullanılabiliyor.

Oluşturulan uygulama "My Projects" menüsü altında "Export selected project (.aia) to my computer" ile bilgisayara indirilebiliyor. Benim bu yazıda hazırladığım projenin dosyası buradan indirilebilir.