PySide2 Arayüz ile UART Kullanarak STM32 Haberleşme

Merhaba,

Bu konuda Qt arayüzlerinin Python ile kullanılmasını sağlayan PySide2 ile basit bir Seri Port haberleşme uygulaması kullanacağız. İnternette arama yaptığınızda PySide2 yerien PyQt5 üzerine daha fazla konu anlatımı bulabilirsiniz. Ancak bu iki platform neredeyse aynı. İlk başta PyQt bağımsız bir topluluk (yada bir kişi) tarafından oluşturulmuştur. İlerleyen zamanlarda ise Qt kendi Python kütüphanesini oluşturmak için PySide isimli bir proje başlattı. Daha sonra PyQt’ yi geliştiren kişi Qt ile birleşerek PySide2 isimli kütüphaneyi geliştirmeye başladı. Bağlantıdan Qt’ nin sayfasında PySide üzerine bazı kaynaklara ulaşabilirsiniz. Ancak PySide2 üzerine kaynak internet üzerinde arattığınızda bulmakta zorlanabilirsiniz. Bu sebeple PyQt5 için olan kaynakları aratabilirsiniz. Kütüphanaler neredeyse aynı olup kullanımı oldukça benzerdir. Bu sebeple öğrenim aşamsında PyQt5 kütüphanalerini kullanabilirsiniz. Bende çoğu örneği PyQt5 üzerinden vereceğim.

PySide’ nin sağladığı avantajlardan bahsedelim. Normalde QT kullanarak bir Widget uygulaması oluşturmak için C++ kullanmanız gerekir. PySide2′ de ise Python kullanılmakta. Bu başlı başına bir dil avantajı oluşturmakta. Ayrıca QT’ de oluşturduğunuz arayüzleri doğrudan PySide2 ile kullanabilirsiniz. Bu da PySide2′ yi daha da rahat kullanılabilir olmasını sağlamaktadır. Daha modern tasarımlar için QML desteği de bulunmaktadır. Ancak QML kullanımı biraz daha profesyonellik gerektirmektedir. Bu sebeple başlangıç için widget uygulaması tasarımı önerilmektedir. 

Şimdi yapacağımız uygulamanın detaylarına değinelim. Seri port ile haberleşen bir uygulama oluşturacağız. Bu uygulama ile STM32′ ye bağlı LED’ leri kontrol edeceğiz ve potansiyometre’ nin değerini bu arayüz üzerinde görüntüleyeceğiz. LED olarak bir RGB led kullanıp 3 kanalın parlaklığını ayrı ayrı değiştirebileceğiz.  Tasarım için yazacağımız yazılım biraz uzun gelebilir. Aslında birbirinin tekrarı eden kod satırlarından oluşturuğu için uzun görünüyor. Oluşturulan araçların yerleşimi ve basıldığı zaman ne yapacağını belirten fonksiyonları yazılımda ayrı ayrı yazmak gerekiyor. Tasarımı oluşturmak için oluşturacağımız yazılım yanı sıra birde Seri Port haberleşmesini oluşturmak için yazılım yazmalıyız. Bu yazılımı ayrı bir sınıf olarak tanımlayıp tasarım için olan yazılımdan ayrı bir dosyada yazacağız. 

Oluşturacağımız tasarımın düzgün görünmesi ve kolay yerleşmesi için “GridLayout” kullanacağız. Farklı layout’ lar kullanılarak da tasarım gerçekleştirmek mümkün belirttiğim gibi kendim bunu seçtim. Yazılım sonunda oluşacak tasarım aşağıdaki resimdeki gibidir. Görüldüğü üzere bir satırda en fazla 3 sütun bulunmakta. Bu önemli bir detay çünkü “GridLayout” kullanırken yerleştirilmek istenilen aracın satır ve sütun sayısına göre yerleşim yapmaktayız.

pyside2_gui

Öncelikle bir adet Gridekleyelim. Grid’ i basitçe aşağıdaki şekilde herhangi bir parametre vermeden tanımlayabiliriz.

Grid’ i oluşturduktan sonra butonlara bakalım. Butonlar tıklandığında bir işlem gerçekleştirmesini istediğimizden tıklandığında çalışan callback fonksiyonuna bağladık. Basit olarak bir butonu aşağıdaki şekilde oluşturabiliriz. Butonu oluştururken parametre olarak üzerine yazılacak yazıyı girebiliriz.

İlk satırda butonu oluşturduk. İkinci satırda ise butona basıldığında çalıştırılacak callback fonksiyonu bu butona bağladık. Bu buton ile seri portu açmayı yada kapatma işlemini gerçekleştireceğiz. Eğer port kapalı ise port açılacak, açık ise kapanacak. Bu butonu GridLayout’ a aşağıdaki şekilde bağlayabiliriz. Grid’ de yerleştirme yaparken eklenen araçların üst üste gelmemesine ve aynı aracın birden fazla kez kullanılmamasına dikkat edilmelidir. Aksi takdirde istediğiniz tasarım oluşmayacağı gibi çok saçma görüntüler meydana gelecektir.

Grid’ e araç eklerken en az 3 parametre girmeniz gerekmektedir. Bu parametrelerin ilki eklenecek araç, ikincisi ve üçüncüsü ise aracın yerleştirilecek olduğu satır ve sütun.  Üstteki resimde de görüldüğü üzere tasarımımızı 3 sütuna bölmüştük.  Butonun yeri 2. satır ve 3. sutünda olduğu için parametreleri bu şekilde ayarlamalıyız. İlk satırda port ism ve baud rate yazan label’ lar bulunmakta. Bu araçların hepsi bir sutun yer kapaldığı için grid’ e eklerden 3 adet parametre kullandık. Ancak ekleyeceğimiz aracın genişliğini de belirtmek istediğimiz durumlarda grid’ e ekleme işleminde 5 adet parametre kullanmalıyız. Slider 3. satırda 2 sütun yer kaplamakta. Şimdi onun nasıl eklendiğine bakalım. Öncelikle Slider’ ı oluşturalım.

Öncelikle slider nesnesini oluşturduk ve yatay slider olarak belirledik. İsterseniz bunu uygun parametre ile dikey olarak da değiştirebilirsiniz. 3. satırda ise slider’ ın aralığını belirledik. Led parlaklığını 0 ile 100 arası ayarlamak istediğim için ben o şekilde ayarladım. 4.satırda slider’ ın değeri değiştiğinde çalışacak fonksiyonu belirledik. 5. satırda slider’ ın o anki değerini göstermesi için bir label ekledik. Bu label’ da yazan yazıyı slider’ ın değeri değişince çalışan fonksiyonda değiştireceğiz. 

Üstteki satırda görüldüğü üzere slider eklenirken 5 parametre kullanıldı. Bunlardan 2. ve 3. parametre üstteki örneklerde olduğu gibi eklenecek aracın yerini belirlemek için son iki parametre satır ve sütun cinsinden araca ayrılan genişliği belirtmek için.  Slider resimde de görüldüğü üzere 1 satır ve 2 sutün yer kaplamış bu sebeple 4. ve 5. parametreleri (1, 2) olarak girilmiştir. Onun hemen yanında durmasını istediğimiz slider’ ın değerini gösterecek label ise 1 sütun yer kaplayacağından 3 parametre kullanılarak grid’ e eklenmiştir. Slider’ ı ekledikten sonra sıra geldi progressbar eklemeye. Progress bar eklerken yapacağımız işlemler öncekilerden farklı olmayacak. Progressbar nesnesini oluşturup grid’ de yerleştirelim.

Burada ek olarak progress bar’ ın değerini sıfırladım. Bunu yapmak zorunlu değil. Ancak yapmazsanız arayüz ilk açıldığında progress bar’ ın değeri görünmeyecektir. Bu sebeple yapmanızı tavsiye ederim. Son olarak 8. satırda bulunan butonlar. Bunlar LED’ leri yakıp söndürmek için eklediğimiz butonlar. Görüldüğü gibi butonlar arasında bir boşluk bulunmakta. Bu boşluğu eklemek için ek bir ayar yapmamıza gerek yok. Grid üzerinde 1. ve 3. sütunlara ekleyerek butonlar arasına bir boşluk ekleyebilirsiniz.

Yukarıda da belirttiğimiz gibi ilk butonu 8. satır 1. sütuna ekledik.  İkinci butonu ise 8. satır 3. sütuna ekledik. Böylece arada bir sütun boşluk kaldı. Son butonları da ayarladığımıza göre sıra geldi araçların callback fonksiyonlarına. Başta da belirttiğim gibi yapacağımız işlemler temel seviye Python bilgisi gerektirmektedir. Bu sebeple Python’ daki sınıflardan kaynaklı yaptığımız işlemleri açıklamayacağım. Öncelikle bağlantı butonundan başlayalım. Bağlantı butonu seri portu hem açmayı hemde kapatmayı sağlıyor. Butonun callback’ inde seri port sınıfına erişip port açma, kapama ile baud rate ve com port ismi girme işlemlerini gerçekleştirmekte. Bunların hangisini hangi satırda yapıldığını tek tek açıklamayacağım. Çünkü yazılımdaki fonksiyonların ne iş yaptıkları zaten açık bir şekilde belli oluyor.

Yazılımda da görüldüğü üzere bütün fonksiyonlar açık. Sadece ufak bir detayı belirteceğim. Seri port’ u yöneten sınıf bir thread ile çalışıyor. Çünkü arayüz çalışırken thread olmadan aynı anda seri porttan gelen veriyi okumaya çalışırsak arayüz donar. Çünkü seri porttan ne zaman veri geleceğini bilmiyoruz ve sürekli olarak portu kontrol etmemiz gerekiyor. Bu sebeple bir thread kullanmalıyız. Thread kullandığımız için seri port açıldığı zaman thread’ i başlatmalı, seri port kapanacağı zaman ise önceden thread’ i sonlandırmamız gerekiyor.  Buton callback’ ini ayarladıktan sonra slider callback’ in bakalım. Slider için callback’ i değeri değiştiği zaman çalışacak şekilde ayarladık. Böylece her değeri değiştiği zaman callback fonksiyonu çalışacak ve slider’ ın değerini seri porttan göndereceğiz. Slider değerini seri porttan gönderirken string olarak değilde byte olarak gönderelim. Verinin başını eklemek için “0xA5” ve “0x5A” ekleyeceğiz. Bunu tamamen keyfi olarak ekledim. STM32 tarafında bu iki byte arka arkaya geliyorsa veri gelmeye başlamıştır deyip veriyi ayırmaya başlayacağız. Bunun yanı sıra slider’ lara ve butonlara da birer etiket eklemeliyiz. Ben etiketleri aşağıdaki şekilde belirledim. Bu etiketleri de ben keyfi olarak seçtim.

  • Slider – 1 => 0x80
  • Slider – 2 => 0x81
  • Slider – 3 => 0x82
  • Buton – 1 => 0x84 (LED yak)
  • Buton – 2 => 0x84 (LED Söndür)

 Buton – 1 ve Buton – 2′ nin etiketlerini aynı belirledim. Çünkü bu butonlar ile aynı LED’ i kontrol edeceğim. Bu sayede butona basıldığında gönderilen değeri doğrudan LED’ e yazacağız ve LED’ in yanmasını yada sönmesini sağlayacağız.  Slider callback’ ini aşağıdaki şekilde ayarlayabiliriz. 

Yazılımda da görüldüğü üzere öncelikle paketi oluşturup göndereceğimiz veriyi sonuna ekledikten sonra, veriyi byte dizisine çeviriyoruz. Byte dizisine çevirdiğimiz veri seri porttan gönderilmeye hazır haldedir. Daha sonra seri port sınıfının fonksiyonlarını kullanarak hazırladığımız veriyi STM32′ e gönderiyoruz. Slider2 ve Slider3 için de bu callback’ler benzer şekilde ayarlayacağız. Ancak progressbar’ larda görülen veri STM32′ den geleceği için biraz farklı olacak. STM32′ den iki adet analog veriyi Progressbar’ lar üzerinde görmek istediğimizi söylemiştik. İki veri aynı anda geleceği için bunları da PySide tarafında doğru şekilde ayırmalıyız. Bunu basit olarak veriyi aşağıdaki formatta göndererek yapabiliriz.

Burada ilk karakter olan ‘#’ verinin paketinin başını belirtmek için. ‘ | ‘ ler veriler arası ayraç karakterler. Sonuncu olan ‘\n’ ise alt satıra inme karakteri olup paketin sonunu ifade etmektedir. Farkettiğiniz üzere  Arayüzden STM32′ ye veri gönderirken byte olarak göndermiştik. Burda ise string kullandık. Bunun bu şekilde seçmemin sebebi verileri ayırma işlemlerini platformlarda kolay yapabilmektir. PySide arayüzünde işlem yaparken Python kullanıyoruz ve Python string’ leri bir ayraç yardımı ile ayıran bir fonksiyon bulunmakta. Ancak bu fonksiyonun bir benzeri STM32′ de kullanıdğımız C++’ da yok. İstenirse yazılabilir ama ek uğraş gerektiren bir işlem. Bu sebeple STM32′ ye veri gönderirken byte olarak gönderip gelen veriyi sırası ile kontrol ederek ayırma işlemi gerçekleştirdik. PySide veri ayırmak için ise ayraç karakterler kullandık. 

Daha önce de belirttiğimiz gibi Python’ da seri porttan gelen verileri bir thread okumakta. Thread’ e bir veri geldiğinde bunu PySide arayüzde gösterebilmek için Qt’ nin sağlamış olduğu signal yapısını kullanacağız. “signal” fonksiyonu sayesinde seri port sınıfı ile arayüz sınıfı arasında bağlantı sağlanabilmektedir. Seri porttan veri geldiğinde sinyal tetiklenecek ve gui üzerinde işlem yapabileceğiz. 

“signal” sınıfını oluştururken parametre olarak hangi tip veri gönderileceğini belirlememiz gerekiyor. Bu sınıf aynı zamanda widget’ lar ile de kullanımı mümkün. PyQT5 kaynaklarından “signal” sınıfı ile ilgili detaylı bilgi bulabilirsiniz. Gönderen taraf böyle bunun birde arayüz tarafında alıcı kısmı olması lazım. Arayüz tarafında bir callback oluşturacağız. Bu callback serial sınıfı tarafından veri geldiği “signal” yardımı ile çağrılacak. Callback çağrıldığında gelen veriyi ayırıp ilgili widget’ lere gönderecek. 

Yazılımda da görüldüğü üzere seri port sınıfından gönderilen veri callback parametresi olarak arayüze ulaşdı. Bu veriyi alıp ayırıp doğru sıra ile ilgili widget’ lere gönderebiliriz. Ayırma işlem için Python’ ın sağladığı kolaylık olan “spilit” fonksiyonunu kullanacağız. Daha öncede belirttiğim üzere bu fonksiyon string ifadeyi, parametre olarak verilen karaktere göre ayırmayı sağlamaktadır. Ayrıma ” | ” karakterine ayırma işlemini gerçekleştirdikten sonra verinin ilk karakteri ‘ # ‘ mi diye kontrol ediyoruz. Eğer doğru ise veri doğru sıra ile alınmış demektir. Daha sonra ikinci ve üçüncü değerleri de tam sayıya çevirip progress bar’ lara gönderiyoruz. Bazı havada kalan şeyler olabilir yazılımın tamamını incelediğinizde her şey daha iyi yerine oturacaktır diye düşünüyorum. Bu kısımdan sonra STM32′ de çalıştırdığımız yazılımı verelim. 

Bu kısımda UART’ dan gelen verileri ayırıp LED’ lerin kontrolünü gerçekleştirdik. İlk başta uart’ dan veri geliyormu diye kontrol ettik. Daha sonrasında gelen verileri ard arda okuyup gönderdiğimiz sıra ile geliyor mu diye kontrol ettik. Eğer gelen verinin sırası 0xA5 ve 0x5A ile ilerliyorsa veri paketinin başını yakaladık demektir ve sonrasında gelen verileri okuyup ayırma işlemini gerçekleştirebiliriz. Arayüzden gelen komutları ID’ ye göre ayırmıştık.  Bu sebeple STM32 tarafında da bir switch kullanarak ayırma yaptık ve yapılacak işlemi belirledik. Daha sonrasında veri gönderdiğimiz kısma gelelim.

STM32′ den PySide arayüze veri gönderdiğimiz bu kısımda bir timer kullanarak veriyi 100ms’ de bir göndermeyi sağladık. Burada timer yerine delay kullanırsanız gelen verileri de kaçırma ihtmaliniz artar. Bu sebeple timer yada ticker benzeri yapılar kullanmanızı tavsiye ederim. Bu konuda anlatacaklarım bu kadardı. Zaten yeterince uzun bir konu oldu. Proje dosyalarının tamamına bağlantıdan ulaşabilirsiniz.

İyi çalışmalar dilerim…

 

 

 

Add a Comment

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir