4

ESP32 ve QT Arasında Protocol Buffers Kullanımı

Bu yazıda Protocol Buffers’ın(Protobuf) ESP32 ve Qt framework ile kullanımına bir örnek vermek istiyorum. Bu örneği hobi olarak uğraştığım bir topraksız tarım projesi üzerinden göstermek istiyorum. Bu örnekte ESP32 ile Qt/QML uygulamasını protobuf ile  UDP üzerinden haberleştireceğiz. Burada ESP32, topraksız tarım projesinin gömülü sistem tarafını, Qt/QML ise verileri görüntüleme, kontrol etme vb. işlemleri yapan uygulama tarafını temsil etmektedir. Öncelikle Protocol Buffers nedir buna bir değinelim.

Protobuf Nedir?

Protocol Buffers, Google tarafından geliştirilen bir veri serileştirme metodudur. Protobuf, XML veya JSON gibi metin tabanlı formatların aksine binary format kullanır ve bu da daha düşük boyutlu ve daha hızlı iletişim sağlar. Protobuf’ın temel özelliklerinden biri de bir dil gibi sentaksa ve derleyiciye sahip olmasıdır. Bu yüzden farklı platformlar arasında veri paylaşımını kolaylaştırır ve dil bağımsızlığı sağlar. Protobuf, geniş bir dil desteği sunar ve C++, Java, Python, GO vb. dilleri desteklemektedir.  Protobuf daha çok gRPC ile birlikte kullanılsa da kendimize ait bir haberleşme metodu veya paket yapısı ihtiyacı hissettiğimiz herhangi bir uygulamada kullanılabilir. Hatta protobuf’ı kullanmanın diğer alanlara göre daha zor olduğu gömülü sistemlerde bile.
Bir protobuf mesajı en basit hali ile aşağıdaki gibi tanımlanabilir. Bu mesajlar .proto uzantılı bir dosya içerisine yazılır. Örnegin; “person.proto”

Protobuf’ın kendine ait bir sentaksı ve derleyicisi olduğundan bahsetmiştik. Yukarıdaki gibi bir proto mesaj oluşturup aşağıdaki gibi hedef dile göre derleme yapabiliriz.
Derleme sonucunda hedef dile göre kütüphane dosyaları oluşturulur. Yukarıdaki örnek için çıktı dosyaları “person_pb.cc” ve “person_pb.h” olacaktır. Bu kütüphaneleri projemize dahil ederek protobuf’ı kullanmaya başlayabiliriz.
Bu örnekte proto mesaj yapısını biraz daha gerçeğe yakın, alt mesajlar içerek şekilde oluşturmak istiyorum. Burada kullanacağım hydroponic_data.proto dosyasının içeriği aşağıdaki gibidir.
Burada bahsetmek isteğim bir diğer konu alt mesaj(submessage) konusudur. Yukarıda verilen .proto uzantılı dosya incelendiğinde burada bir tane üst seviye( top level) mesaj ve bunların altında birden fazla alt mesajlar(submessage) olduğu görülmektedir. Burada “oneof” keywordünün kullanım amacı ise alt mesajlardan tek seferde sadece birinin kullanılacağı anlamına gelmektedir. Yani bir Hydroponic mesajı oluşturulduğunda içinde sadece bir tane alt mesaj olabilir.

Nanopb Nedir?

ESP32 tarafında protocol buffer için Nanopb kütüphanesini kullanacağız. Nanopb, C programlama dilinde kullanılabilen hafif ve verimli bir protobuf kütüphanesidir. Espressif Systems tarafından geliştirilen Nanopb, özellikle mikrodenetleyiciler ve kaynak kısıtlı cihazlar için optimize edilmiştir.
Nanopb kendi derleyicisine sahiptir. Derleme sonrası C kütüphaneleri oluşturur. Nanopb de alt mesaj kullanabilmek için proto dosyasına aşağıdaki satırları ekliyoruz.
Nanopb derleyicisi tarafından derlenecek proto dosyasının son hali aşağıdaki gibidir.
Yukarıdaki proto dosyasını nanopb’ye göre derlemek için nanopb’yi indirdiğiniz dizinde bulunan protoc yi kullanmamız gerekmektedir.
Nanopb’yi ESP32 ile kullanabilmek için yine indirdiğimiz dizinde bulunan aşağıdaki kütüphaneleri ve protoc derleyicisinin ürettiği kütüphaneleri projeye eklememiz gerekmektedir.
Ayrıca nanopb tarafında string ve submessage encode/decode etmek için callback fonksiyonlarının oluşturulması gerekmektedir. Bu fonksiyonları protobuf_callbacks.h kütüphanesinde oluşturuyorum.
Bu aşamadan sonra protobuf’ı ESP32 ile kullanabiliriz.
Kısaca bir mesaj yukarıdaki gibi oluşturulabilir. Mesaj oluşturulduktan sonra aşağıdaki gibi encode fonksiyonu ile serialize edebiliriz.
Bu aşamadan sonra serialize edilmiş buffer’ı istediğimiz bir haberleşme protokolü üzerinden gönderebiliriz.

Karşı taraftan gelen protobuf mesajı yine serialize olmuş bir byte dizi seklinde gelecektir. Bu mesajı decode ve callback fonksiyonları ile deserialize edip ilgili verileri çekebiliriz.

Qt ile Kullanımı

Protocol buffers’ı Qt tarafında kullanabilmek için C++ a göre derlememiz gerekmektedir. Protobuf nasıl derleneceği ile ilgili bilgiler için protobuf’ın github sayfasında bulunan dökümantasyonları okuyabilirsiniz. Ayrıca Qt6.6.1 versiyonu ile birlikte protobuf desteği gelmiş. Protobuf derleme sonrasında Qt tabanlı kütüphaneler oluşturulabiliyormuş. En kısa zamanda bunu da denemeyi düşünüyorum. Şimdilik eski usül devam etmek istiyorum.
Derleme sonucu üretilen static kütüphane (.lib vb.) ve header dosyalarını qt projesine dahil etmemiz gerekmektedir. Daha sonra linker’a static kütüphaneyi(libprotobufd.lib) bildirmemiz gerekmekte. Bunu yapmak için .pro uzantılı qt dosyasına aşağıdaki satırları eklememiz gerekmektedir.
Ayrıca protobuf için gerekli olan Run Time Library Debug parametresini aynı dosyaya eklememiz gerekmektedir.
Daha sonra hydroponic.proto uzantılı dosyamızı c++ a göre derleyip gerekli kütüphaneleri üretebiliriz. Burada nanopb için eklediğim satırları siliyorum çünkü protocol buffer derleyicisi olarak githubtan indirip derlediğimiz protobuf’ın derleyicisini kullanacağız. Bu derleyici nanopb yi tanımadığı için .proto dosyası içinde bulunan nanopb ile ilgili kısımlarda hata verecektir.

 

Derleme sonucunda üretilen kütüphaneyi yine aynı şekilde qt projemize ekledikten sonra protobuf’ı kullanmaya başlayabiliriz.
Protobuf işlemleri için ProtobufManager isminde bir sınıf oluşturuyorum. Bu sınıf arkaplanda UDP den gelen protobuf mesajları çözüp gerekli verileri elde edecek ve gerektiğinde protobuf mesajını encode edip ESP32 ye gönderecek. Ayrıca bu sınıfı QML tarafında da main.cpp içine aşağıdaki satırı ekleyerek kullanabileceğiz.
C++ tarafında bir protobuf mesajı oluşturmak için aşağıdaki adımları izleyebiliriz.
Öncelikle bir mesaj sınıfı oluşturulur.
Daha sonra hangi alt mesaj gönderilecek ise ilgili mesajın sınıfı oluşturulur.
Daha sonra gönderilmek istenen alt mesajın veya var ise top level mesajın alanları doldurulur.
Üst seviye mesajın hangi alt seviye mesajı içereceği ayarlarlanır.
Bu aşamadan sonra mesaj serialize edilebilir.
Encode edilmiş veriler bir diziye kaydedildikten sonra istenilen bir haberleşme protokolü ile gönderilebilir. Bu örnekte ESP32 ile haberleşeceğimiz için UDP üzerinden cmd mesajı içeren hydroponic mesajı ESP32’ye gönderilmiştir.
ESP32 den gelen mesajı decode etmek için ise aşağı adımlar izlenebilir.
Encode edilmiş veriler UDP üzerinden doğru bir şekilde alındıktan sonra aşağıdaki gibi parse fonksiyonları kullanılabilir.
Üst seviye mesaj parse edildikten sonra içinde hangi alt mesaj olduğuna karar verilmesi gerekmektedir. Bunun için protobuf tarafından üretilen sınıfın “has” metodları  da kullanılabilir.
Daha sonra ilgili alt mesaj için sınıf oluşturup yukarıdaki gibi altmesajı çekebiliriz.
Bu aşamadan sonra alt mesajın verilerini sınıfın ilgili metodları yardımıyla elde edebiliriz. Hepsi bu kadar. En basit haliyle C++ tarafında kullanımı bu kadar. Daha sonrasında elde edilen verileri kullanabilirsiniz.

QML Tarafında Verilerin Gösterilmesi

Protobuf üzerinden alınan verileri QML ile tasarlanmış bir arayüz üzerinde gösterelim. Yukarıda qmlRegisterType tanımlamasını göstermiştik. Burada tanımlanan isim ile ProtobufManager sınıfını yine aynı isim ile QML tarafında sanki bir componentmiş gibi kullabiliriz.
C++ tarafında signal/slot mekanizması ile mesajın geldiğini QML tarafında algılayıp ilgili verileri çekebiliriz.
Protobuf mesajı göndermek için ise istenilen bir durumda aşağıdaki gibi bir mesaj gönderilebilir.
ESP tarafından gelen verileri göstermek için geçici olarak aşağıdaki gibi bir arayüz tasarladım. Arayüz biraz basit gelebilir biliyorum ama hala geliştirme aşamasında:)
Not: Arayüzde kullanılan tüm iconlar ve görseller freepik ve flaticon sitelerinden alınmıştır.

Sonuç

Protocol Buffers, JSON ve XML in aksine haberleşmede daha düşük bir boyut kullanmaktadır. Daha düşük boyutlu olması sebebi ile haberleşme hızında avantajları olmaktadır. Sadece server-server veya server-client arasındaki haberleşmeden ziyade özel bir paket ihtiyacı duyduğumuz iki gömülü sistem cihazı arasında istenilen haberleşme protokolü(UART, SPI, I2C vb.) üzerinde kullanılabilir. Böylece iki cihazı haberleştirmek için tasarlanması gereken paket yapısı, paketi oluşturma , parse etme vb. işlemlere harcanan zamandan kazanç sağlayabiliriz. Tabii protobuf’ın gömülü sistemlere entegrasyonuna harcanan zamanı saymaz isek 🙂
Yazıda kodların tamamına değinmek mümkün değildi. Bu yüzden olabilidiğince protobuf ile ilgili kısımları göstermeye çalıştım. ESP32 ve Qt kodlarının tamamını incelemek için github hesabımı ziyaret edebilirsiniz -> Github

Kaynaklar:

Mehmet Topuz

4 Comments

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.