PYTHON İLE UART STREAMER KULLANICI ARAYÜZÜ

Zaman zaman PC üzerinde de yazılım geliştirmem gerekiyor.PC diskinde kayıtlı olan bir Gerber dosyasını satır satır okuyarak seri port üzerinden CNC ye aktarmam gerektiğinde Python gündeme geldi.

Python öğrenmeye girişmemin nedeni de bu oldu. Öğrencilik yıllarımdan itibaren Fortran, Basic, Pascal, Delphi, Visual Basic programlama dillerinin birinin devri kapanırken diğerine geçmek zorunda kaldım. 4-5 yıl öncesinde de Python’a el atmak zorunda kaldım. Python ile birlikte Object Oriented Programlama tekniklerini de öğrenmek zorunda kaldım. Ancak, bu sonuncusunu işimi görecek en alt düzeyde tuttuğumu itiraf etmem gerekir. Bu konuda bir uzman olmamakla birlikte, bu yayında bildiğim, yaptığım kadarını paylaşacağım.

Burada anlatacağım arayüz programının kapsamında Python ile PC ekranında diyalog pencereleri, seçme kutucukları, komut butonları oluşturmak, Python ile PC nin seri portlarına erişmek, PC diskinde kayıtlı text dosyalarına erişip okumak gibi işlemleri ele alacağız. Vermiş olduğum kodların, bir Python uzmanı tarafından daha şık ve verimli şekilde yazılabileceğini de peşinen belirterek başlayayım.

KULLANDIĞIM PLATFORM – PYCHARM

Her şeyden önce bilgisayarımızda Python’un yüklü olması gerekir. Bu yayın sırasında Python 3.9 sürümü en güncel olanı idi. Python’u Jet Brains’in PYCharm platformu ile kullanıyorum. Bir de seri haberleşme için pySerial.py isimli paketin yüklenmesine ihtiyacımız olacak. Bunların PC ye nasıl yükleneceğini bu yayında anlatmayacağım. İşe başlamak için ilk adımların eksik kaldığının farkındayım, ama bu işlemlere basit Google sorgulamaları ile kolaylıkla erişilebiliyor. Gene de ayrıca yapacağım birkaç yayın ile bunları da anlatmayı planlıyorum.

Bilgisayarım Mac, verdiğim ekran görüntüleri bu nedenle Windows ekranlarından farklı olabilir ama önemli farklar değil.

YENİ BİR PROJE OLUŞTURUYORUZ

pyCharm’ı açtığımızda karşımıza mevcut projelerden birisini açmak ya da yeni bir proje oluşturmak üzere seçenekler çıkıyor.

Bunlardan “New Project” seçeceğini tıklıyoruz. Açılan ekranda projemizin adını “UserInterfaceTutor” olarak veriyorum. Diğer seçenekleri olduğu gibi bırakarak “Create” butonunu tıklıyoruz.

PyCharm yeni projeyi oluşturduğunda “main.py” isimli bir python dosyası şablonu ile çalışma sayfasına geçiyor. Programımızı ister main.py içine istersek yeni oluşturacağımız bir başka python dosyası içine yazabiliriz. Ben main.py ile devam etmeyi tercih ettim.

PROGRAM GELİŞTİRME ADIMLARI

Amacımız aşağıdaki gibi bir grafik arayüz oluşturmak.

Görüldüğü gibi ekranda oluşturulan iç içe çerçeveler, drop down menü satırları ve komut butonlarından oluşan bir grafik arayüzümüz var. Bunun için Python grubunun geliştirdiği “tkinter” kütüphanesinden yararlanacağız.

Üstten aşağıya doğru nesnelerimiz :

  • PC nin mevcut seri portlarının listelendiği bir liste kutusu
  • UART hızının seçildiği bir “drop down” seçim kutusu
  • Seçilen Seri Port’un açılması için bir “Connect” butonu
  • Diskten okunacak dosya adının yazılacağı text penceresi
  • Dosya adı yazmak yerine disk dosya sistemi aracılığı ile seçim yapmak üzere bir “File” butonu
  • Seçilen dosyayı satır satır seri port’a aktarmak üzere bir “Stream” butonu
  • Dosyayı en baştan değil de istenen bir satırdan itibaren okuyup aktarmak üzere bir “LineNr” text kutusu.
  • Seri port’a gönderilen satırların listeleneceği bir text penceresi, bu pencere düşey scroll bar özellikli olacak
  • Text penceresini temizlemek üzere bir “Clear Window” butonu
  • Manuel olarak yazılıp seri porta gönderilecek text için bir text kutusu
  • Yazılan text’i göndermek üzere bir “Xmit” butonu
  • En altta da kullanıcıya bilgilendirme mesajlarının yazılacağı bir “Status” satırı

Bütün bu nesnelerin ekrandaki görsellerine ilaveten, bunlarla ilişkilendirilmiş, üzerlerine yazılıp tıklandıklarında istenen işlemi yapacak fonksiyonları da yazmamız gerekecek.

Şimdi iç içe çerçevelerde yer alan bu “nesne” leri sıra ile oluşturarak ilerleyelim.

ANA ÇERÇEVENİN OLUŞTURULMASI

İlk olarak import komutu ile kullanacağımız “tkinter” kütüphanesini yüklüyoruz.

Sonra da en üst düzeydeki genel grafik arayüz çerçevemizi oluşturalım. Bu çerçevenin adını “root” koyuyorum. “root” isimli nesnemizi tkinter paketindeki bir class ile yarattıktan sonra bunun başlığını ve minimum boyutlarını tanımlıyor, ardından da programın ana çevrimini başlatıyoruz.

Minimum boyutları tanımlamazsak programı çalıştırdığımızda oluşan pencere -içi henüz boş olduğndan- ekranımızın sol üst köşesindeki bir benekten ibaret kalıyor.

Şimdi bu kadarcık programımızı çalıştırmak üzere Run… menüsü altında main.py yi seçelim.

Program çalışınca grafik arayüzümüzün ilk çerçevesi ekranımızda aşağıdaki gibi beliriyor.

ALT ÇERÇEVE VE NESNELERİN OLUŞTURULMASI

“Root” isimli en üst düzey çerçevenin içine bir “main” çerçevesi daha oluşturuyorum. Bunun da içine ilk olarak seri port liste, seçim, connect ve dosya seçimi nesnelerini koyacağım bir “portAndFileFrame” paneli oluşturuyorum.

Bunun için “tkinter” kütüphanesinden “ttk” adlı paketi de ayrıca ithal etmek gerekiyor. (Bunun neden ilk komutun içinde gelmediğini bilmiyorum, araştırmadım).

Görüldüğü gibi her bir panelin başlığının, bir üst çerçevenin içinde yerleşeceği satır ve kolonların belirlendiği parametreler var.

Portların listeleneceği text penceresini oluşturmadan önce biraz Python fonksiyon kodu yazmamız gerekecek. “listSerialPorts” adlı bu fonksiyon ile PC’de mevcut olan Seri portları belirleyerek bir “result[]” adını verdiğimiz bir text dizini içine yerleştireceğiz. Daha sonra da bunun içeriğini ekranda oluşturacağımız seri port penceresinde listeleyeceğiz.

Bu iş için Python geliştiricilerin hazırlayıp yayınladıkları pySerial paketini kullanacağız. Bu paketi daha önceden bilgisayarımıza indirmiş olmamız gerekiyor. Şimdi de indirmiş olduğumuz bu paketi proje alanımıza tanıtmamız gerekiyor.

Bunun böyle iki aşamada -önce bilgisayara indirip sonra tekrar projeye yüklemek- yapılmasının nedeni, farklı projelerde farklı paketler kullanılmasına olanak vermek. Bu projede pySerial kullanıyoruz ama bir başka projede bir başka paketi kullanmak isteyebiliriz.

pySerial PAKETİNİN PROJE ALANIMIZA (Virtual Environment) YÜKLENMESİ

Adım 1: PyCharm/Properties menüsünü tıklıyoruz.

Adım 2: Açılan pencerede Python Interpreter seçeneğine geldiğimizde o an için yüklü olan paketlerin bir listesini göreceğiz. Bu listede henüz projede kullanmak istediğimiz pySerial yok.

Adım 3: Alt taraftaki “+” işaretini tıkladığımızda bilgisayarımızda mevcut (çoğu Python ile birlikte yüklenmiş, kimisi de sonradan bizim indirip yüklediğimiz) paketlerin bir listesi çıkacaktır. Bunların arasında pySerial’ı bulup “install Package” butonunu tıklayacağız.

Adım 4:Package ‘pyserial’ installed succesfully” mesajı ile bu işlemin başarılı olduğunu anlıyoruz.

Görüldüğü gibi “pyserial” paketi projemizde yerini aldı. OK tuşlayarak bu pencereyi kapatıp editör sayfamıza dönebiliriz.

SERİ PORT LİSTELEME FONKSİYONU

Önce programın baş tarafına bu işlem ve diğer seri port işlemleri için gereken kütüphaneleri ithal edecek bildirimleri koymalıyız.

Ardından seri port listeleme fonksiyonumuz geliyor. Bu sistemimizin Windows, Linux ya da Mac OS olmasına göre farklı filtreler ile seri portlarını sorgulayıp “result[]” adını verdiğimiz text dizinine yerleştiriyor.

Fonksiyon ayrıca, zaten açık olan seri portları kapatıyor. Aksi halde zaten açık olan bir portu seçip yeniden açmaya kalktığımızda hata mesajı alıyoruz, programımız ilerlemiyor.

SERİ PORT LİSTE VE HIZ KUTUCUKLARININ OLUŞTURULMASI

Ana programımıza aşağıdaki satırları ekleyerek port listesi penceresini oluşturalım.

Şimdi programımızı çalıştırırsak aşağıdaki gibi bir liste belircektir. Benim bilgisayarımda sadece bir Seri Port olduğundan tek satırdan ibaret. Bilgisayarınızda seri port bulunmazsa program hata mesajı vererek duracaktır. Bu nedenle bilgisayarınızda benimki gibi sadece USB portlar var ise, bunlardan birine bir USB/FTDI seri çevirici takınız ki kodunuz bu işlemi başarı ile sonuçlandırsın.

SERİ PORT HIZ SEÇİMİ

Ana programımıza aşağıdaki satırları ekleyerek seri haberleşme hızını seçebileceğimiz bir “drop down list” menü oluşturacağız.

Programı bu haliyle çalıştırdığımızda aşağıdaki sonucu alıyoruz. Varsayılan hız 9600 baud.

Şimdi sırada “Connect” butonu var. Listeden seçtiğimiz portu, drop down listeden seçtiğimiz hız ile çalışacak şekilde açacak olan buton.

CONNECT BUTONUNUN OLUŞTURULMASI

Butonu oluşturmadan önce istenen portu açacak olan fonksiyonu yazmamız gerekiyor. Buton bu fonksiyonu çağırarak iş görecek.

Bu fonksiyon bilgilendirme mesajını _SD fonksiyonu ve biraz aşağıda vereceğim kod satırları ile oluşturacağımız “Status”satırında veriyor. Bu satıra mesaj yazma işini _sd fonksiyonu ile yapıyoruz.

_sd fonksiyonunu da aşağıdaki gibi tanımlayalım.

Şimdi Connect butonunu oluşturacak iki satırı ana programımıza ekleyebiliriz.

Bu aşamada penceremizin dibine kullanıcıya bilgilendirme mesajları vereceğimiz bir “status” satırını da ekleyelim.

Şimdi programımızı çalıştırıp sonucu görelim. “Connect” butonunu da tıklayınca artık portumuzu açan bir programımız oldu.

SERİ PORT ÜZERİNDEN VERİ GÖNDERMEK

Şimdi sırada seri port üzerinden veri göndermeye geldi.

Önce gönderdiğimiz satırları listeleyeceğimiz bir text penceresi oluşturalım. Binlerce satırlık dosyalar göndermek söz konusu olacağından bu pencerenin aşağı yukarı kaydırılarak okumaya olanak sağlayacak bir “scroll bar” (düşey kaydırma) kenarı olmasını istiyoruz.

Bunun için ana programımıza aşağıdaki satırları ekliyoruz:

Programımızı şimdi çalıştırırsak aşağıdaki ekranı elde ederiz:

Bu pencerede şimdilik başlangıç mesajı olan “BAŞLADIK” dışında birşey yok. Az sonra ekleyeceğimiz fonksiyon ve nesneler ile seri portun bireyler gönderdikçe, her gönderdiğimiz satır bu pencereye eklenecek.

SERİ GÖNDERME FONKSİYONLARI

Bu yayının en önemli bölümlerinden birisine geldik. Bu iki fonksiyon -transmitFromTextBox ve Stream- kendilerine verilen her bir satırın başına bir tilde ‘~’, sonuna -eğer zaten yoksa- bir return ‘\n’ koyuyor. Satırın Mod256/8 bit sistemi ile CheckSum byte’ını hesaplayıp iki adet ascii karakter halinde sonuna ekleyip seri porttan gönderiyor.

Bu benim CNC ile iletişim için tanımladığım bir protokol, benim sistemime has yani. Eğer başlangıç ve bitiş karakterlerine, check sum karakterine ihtiyacınız yok ise fonksiyonlarda ihtiyaç kalmayan kodları silip yolunuza devam edebilirsiniz.

def stream(text, lineNr):
    # gelen satırın sonunda zaten '\n' yok ise ekleyelim,
    # alış tarafına satır sonunu '\n' ile bildiriyoruz
    if text.find('\n') == -1:
        text = text + '\n'
    # print("ham text + newLine {}-".format(text))

    # check sum hesabı ve iki karakterlik checksum'ın text sonuna eklenmesi
    # Hata ayıklama ve checksum byte değeri bilinen dizilerle testler için :
    # text = 'GPRMC,202325.00,A,4008.94847,N,11135.48840,W,0.011,, 241017,,,D' 
    # yukarıdaki satırın checksum byte'ı 0x17
    # text = '00300005000'  
    # bu satırın checksum byte'i aşağıdaki hesaplama ile: 0xE8
    toplam = 0
    for c in text:
        toplam += ord(c)
    checksum = toplam % 256  # mod256 (0xFF e bölününce kalan)
    checksum = -checksum  # checksum byte : 2 nin komplementi

    “”” Yukarıda elde edilen toplamın en küçük 8 bitini alıyoruz.
    0x00 dan 0xFF e kadar olabilen bu bilgiyi UART protokolu ile
    tek ascii karakter olarak gönderemeyiz. Bu nedenle 4 + 4 bit
    olarak ikiye bölerek iki ASCII karakter olarak göndereceğiz.
    Örneğin : 0x17 yi 0x31 ve 0x37 olarak gönderiyoruz
    “””
    hexsum = '%2X' % (checksum & 0xFF)  
    # Yukarıda hex degerini iki karakterlik diziye çeviriyoruz ,0x şeklindeki eklenti olmadan
                      # https://docs.python.org/3/library/stdtypes.html#old-string-formatting

    """ başa komut başlangıç işareti olarak tilde '~' 0x7E,
        sona da checksum karakterlerini ekliyoruz
        (checksum değerini RSR232 den gönderilebilmek için
        HEX den iki adet ASCII koda çevirerek text_to_send e 
        yerleştiriyoruz)
        text = '~' + text
        text = text + checksum_chars
        """

    # checksum doğruluğunun kontrolu
    # print("toplam {0!s}".format(toplam))
    # result = (checksum + toplam) & 0xFF  # bu test 0x00 sonucunu verir

    text_to_send = '~' + text + hexsum

    # bunu yapmanın bie alternatifi - aşağıdaki gibi format metodunu kullanmak
    # text_to_send = "{0}{1}{2}".format('~', text, hexsum)
    # print("{0} sonuc:{1}".format(text_to_send,str(hex(result))))

    seri.write(text_to_send.encode('ascii'))

    streamedLinesViewBox.insert(0, str(lineNr) + '-' + text)
    streamedLinesViewBox.update() # Bunu koymazsak loop bitene kadar listBox'u güncellemiyor.

Görüldüğü gibi fonksiyon içinde geliştirme sırasında kullandığım bol miktarda -şimdi # ile kapatılmış durumda- hata ayıklama ve izleme kodu var. Program akışı içinde neler olup bittiğini incelemek isterseniz bu satırlardan istediklerinizi açarak çalışabilirsiniz.

CHECK SUM KONTROL ALGORİTMASI

Yukarıda verdiğim kod ile gönderdiğimiz her satır bir ‘\n’ karakteri ile sonladırılıyor. Her satırın devamına da iki karakter ile checksum bilgisini ekleyerek gönderiyoruz. Aslında check sum değeri 8 bitten oluyor ama 0x00 dan 0xFF e kadar bilgileri UART protokolu ile gönderemediğimiz için bunu 4+4 ikiye bölerek, her birisi “0” ila “F” arasında iki adet ascii karakter olarak gönderiyoruz. Alış tarafında bu iki karakter tekrar birleştirilerek checksum değeri yeniden elde ediliyor.

Kullandığım Python algoritmayı bir başka yayında anlatmıştım. O yayına erişmek için burayı tıklayabilirsiniz.

Şimdi bu fonkisyonları kullanan görsel nesneleri oluşturalım.

TEXT KUTUSUNDAN SERİ GÖNDERME İÇİN GRAFİK ÖĞELER

Aşağıdaki satırları ana programa ekleyerek göndermek istediğimiz satırı içine yazacağımız bir text kutusu ile “Gönder” komutunu çalıştıran bir buton oluşturuyoruz.

Şimdi programımızı tekrar çalıştırarak sonucu görelim. Program arayüzü ekrana gelince seri haberleşme hızını seçip “CONNECT” basarak portu açalım. En alttaki status satırında portun açıldığı mesajı belirince “Key Board Entry Text to Transmit” kutucuğuna bir şeyler yazıp “XMIT” butonuna basalım. Klavyeden ENTER bastığınızda gitmez, sadece satırın sonuna ‘\n’ eklemiş olursunuz, satırı göndermek için ekrandaki XMIT tuşuna basılması gerekiyor.

Ben varsayılan olarak yazılmış olan G1 X0 Y0 Z0 satırını gönderdim. Görüldüğü gibi gönderilen satır Streamed Lines penceresinde de belirdi.

Burada görünmüyor ama arka planda bu satırın başına bir tilde, sonuna bir return karakteri eklendi ve bunu izleyen iki ascii karakterden oluşan check sum değerini de gönderdi. Bunu incelemek için en iyisi bir terminal uygulaması ile gönderilmiş olan veriyi bir başka seri porttan geri okumak. İşin bu kısmını sizlere bırakıyorum, zira ben bu çalışmaları, kodu geliştirirken fazlası ile yaptım.

Böylece Python kullanarak bir seri porttan veri gönderen işe yarar bir program geliştirmiş olduk. Sıra geldi veri alıp işlemeye.

SERİ PORTTAN VERİ ALIŞI

Bunun için farklı yöntemler var. Girişten veri gelmesini beklerken diğer süreçleri bloke eden, ya da ana süreci engellemeden çalışan yöntemler var. Ben burada ana süreci ve kullanıcı ara yüzünü bloke etmeyen bir algoritma sunacağım.

CNC uygulamamda bu algoritmayı kullanmıyorum. Orada uygulamaya yönelik olarak geliştirdiğim biraz farklı bir fonksiyon var.

Şimdi, ara yüzümüze, alışdan gelen verileri görüntüleyeceğimiz yeni bir text penceresi ekleyelim:

Ana programımızın sonuna alış portunu sürekli olarak dinleyen bir çevrim ekleyelim.

Şimdi programımızı yeniden çalıştıralım :

Böylece Seri gönderme/alma uygulamamızı tamamlamış olduk. Kodun tamamına ulaşmak için burayı tıklayabilirsiniz : PYTHON SERI PORT UYGULAMASI

SONUÇ

Bu program masa üstü CNC ile PC yi konuşturma gereksiniminden doğmuştu. Blog sitemin ziyaretçilerinden gelen talepler üzerine kodlarımı paylaşmaya karar verdim.

CNC uygulamasında bulunup da burada eksik kalan bir şey var. O da PC diskindeki bir text dosyasını okuyup gönderen algoritma. Onu da bir fırsat bulduğumda yayını güncelleyerek tamamlayacağım. Tabi bunun ne zaman, hangi öncelikle olacağı bu yayına olan ilgiye bağlı olacak. İlginizi çekiyor ise yorumlarınızı beklerim.

Bu yayının sonu : Selçuk Özbayraktar Ocak 2021

One Reply to “PYTHON İLE UART STREAMER KULLANICI ARAYÜZÜ”

  1. Selçuk bey merhaba
    sayfanıza gelen ziyaretçilerin isteklerini dikkate alıp bu uygulamanızı paylaştığınız için çok teşekkür ederim. Yetersiz olduğum bir konuydu. Yaptığım projelerde bu paylaşımınıza benzer uygulamalar yapmayı bende çok istiyorum. Ben ve benim gibi arkadaşlar için çok faydalı olacağını düşünüyorum. PYTHON diline iki yıl öncesinde kısa bir giriş yapmış yazım kurallarından sıkılıp bırakmıştım. Sanırım öğrenme vakti geldi.
    Paylaşımlarınız için teteşekkür ediyorum. Yeni paylaşımlarınızı sabırsızlıkla bekliyorum.
    İyi çalışmalar.

Comments are closed.