QML

Raspberry Pi | Buildroot | QT Projesi


Buildroot Nedir ?

Buildroot, gömülü bir sistem için eksiksiz bir şekilde Linux sistemi oluşturma sürecini basitleştiren ve bu süreci otomatikleştiren açık kaynak bir araçtır. Hedef sistem için, çapraz derleme araçları, kök dosya sistemi, kernel imajı ve bootloader üretebilir. Tüm bunları hedef sistem için birbirinden bağımsız şekilde de yapmanıza olanak sağlar. Örneğin çapraz derleme yapılabilmesi için ilgili araçlara sahip olduğunuz bir hedef sisteme sadece kök dosya sistemi buildroot üzerinden ürettirilebilir. Farklı bir çok hedef sistem ve versiyonları için destek vermektedir.

Buildroot aracını windows işletim sistemi üzerinde çalıştırabilir miyim ?

Hayır, buildroot linux sistemler üzerinde koşabilecek şekilde tasarlanmıştır. Windows makineniz üzerine kuracağınız bir sanal makine üzerinden çalışmalarınızı yapabilirsiniz. Bu yazının hazırlanma sürecinde kullanılan linux dağıtımı ve versiyonu Ubuntu 18.04.5 LTS‘dir.

Buildroot aracını kullanmak için sistemde bulunması gerekli olan araçlar var mıdır ?

Evet, buildroot ilgili bir çok paketi kendisi oluşturacak olmasına rağmen bazı sistem gereksinimleri vardır. Host sistem içerisinde olması zorunlu paketler şunladır ;

  • which
  • sed
  • make (version 3.81 or any later)
  • binutils
  • build-essential (only for Debian based systems)
  • gcc (version 4.8 or any later)
  • g++ (version 4.8 or any later)
  • bash
  • patch
  • gzip
  • bzip2
  • perl (version 5.8.7 or any later)
  • tar
  • cpio
  • unzip
  • rsync
  • file (must be in /usr/bin/file)
  • bc

Yukarda bahsi geçen araçlara ek olarak opsiyonel olarak yükleyebileceğiniz bazı araçlarda vardır.

İlgili araçların kurulumları için apt programını kullanabiliriz. (apt vs apt-get için ilgili yazıyı okuyabilirsiniz)

sudo apt install sed make binutils gcc g++ bash patch \gzip bzip2 perl tar cpio python unzip rsync wget libncurses-dev

Eğer 64 bit platform üzerinde çalışıyor iseniz, şu komutu da çalıştırmanız gerekmektedir.

apt-get install gcc-multilib

Buildroot aracına nereden ulaşabilirim ?

https://buildroot.org/download.html sitesine giderek Long Term Support versiyonunu linux tabanlı sisteminize indirmeniz gerekmektedir. İndirme işleminden sonra tüm dosyaları çalışmak istediğiniz bir dizine çıkartabilirsiniz.

volkan@volkan:~/Downloads$ tar -xvf buildroot-2020.02.10.tar.gz -C ~/buildroot_working_dir

Yukarda bulunan komut ile Downloads klasörü altına indirmiş olduğum buildroot-2020.02.10.tar.gz dosyasını home dizini altında buildroot_working_dir klasörü içerisine çıkartıyorum. Tar programının almış olduğu komut satırı argümanları XVF sırasıyla, Extraxt, Verbose, File anlamlarına gelmektedir. Eğer C argümanı olmasaydı bulunduğu dizin içerisine çıkartacaktı. İlgili komut ile başka bir dizine dosyaları çıkarmasını sağladık. Bu argüman sonrasına aldığı dizin eğer custom bir dizin ise yukarda örnekte olduğu gibi, daha önce oluşturulmuş olması gerekiyor. Aksi takdirde belirtilen dizin için Cannot read : Is a directory hatasını alabilrsiniz.

Bu süreçten sonra ilgili dizine geçiş yaparak artık orada çalışmanız gerektiğini unutmayın !

Hedef sistem için konfigürasyon oluşturmak

Bu aşamada mevcut konfigürasyonları bir template gibi görebilir ve onların üzerinden devam edebileceğiniz gibi, sıfırdan kendi konfigürasyon dosyasınıza oluşturabilirsiniz. İlgili tüm konfigürasyonlar configs/ dizini altında bulunmaktadır. Kendi donanım sürecimde Raspberry Pi 3 modelini kullandığım ve QT gibi paketlerin seçilmiş olmasını tercih ettiğim için;

make raspberrypi3_qt5we_defconfig

komutunu giriyorum. Bu komutun başarıyla sonuçlandığını gösteren çıktı;

#
# configuration written to /home/volkan/buildroot_working_dir/buildroot-2020.02.10/.config
#

Şimdi oluşturacağımız dağıtıma ekleyeceğimiz paketlerin seçimini yapabileceğimiz bir pencereye geçiş yapabiliriz. Bu yapılan seçimler belirlediğimiz konfigürasyon üzerine yazılacaktır. Geçiş yapacağımız pencere curses tabanlı bir arayüz olup, kullanımı oldukça rahattır. Bunun için;

make menuconfig

Sizi karşılayan pencerenin görselini yukarda görebilirsiniz. Kendi(host) sistemimiz üzerinden hedef(target) sisteme yazılım üretebilmek çalışmasına kabaca çapraz-derleme(cross-compile) denmektedir. Bu çalışmayı yapabilmemiz için gerekli toolchainleri kendi makinemizde göstermemiz gerekiyor. Bu yüzden ilk aşama Toolchain sürecini yönetmek, 3.seçenek olan Toolchain’e geçiş yapalım.

Sizi karşılayacak pencere;

Çapraz Derleme Toolchain’i konusunda buildroot iki tane çözüm sunmakta ve bunlar sırasıyla. Buildroot Toolchain ve External Toolchain‘dir. Default olarak Buildroot Toolchain seçili durumdadır. İkisi arasından seçiminizi Toolchain type (Buildroot toolchain) menüsü üzerinden yapabilirsiniz. Bugün External Toolchain seçimini yaparak devam ediyoruz. Bunu seçmemizin sebebi, iyi bilinen ve test edilmiş bir toolchain seçimi yapmak. Eğer Buildroot Toolchain seçili kalsaydı, yapılacak konfigürasyonlara özgü buildroot gerekli toolchain’i kendisi derleyip oluşturacaktı. External Toolchain seçimi yapıldıktan sonra görebileceğiniz pencere;

Dilerseniz Toolchain kısmında Linaro Toolchain‘i seçebilirsiniz ben Arm ARM olarak devam ediyorum. Görsel de seçili değil ancak ek olarak, debug yapmanızın gerektirdiği durumlarda ihtiyaç duyacağımız Copy gdb server to the Target seçeneğini de ekleyebiliriz.

Ana menüye dönerek sistem bazlı ayarlarımızı yapmak için, System Configuration penceresine dallanabiliriz.

Sizi karşılayacak pencere;

Bu pencere içerisinde System hostname, System banner, Root password gibi ayarları yapabilirsiniz. Buna ek olarak /dev seçeneği altında buildroot 4 farkı opsiyon sunmaktadır. Cihazlar eklenip kaldırıldığında kullanıcı alanının bilgilendirmesi istenildiğinde Dynamic using devtmpfs + mdev seçeneğinin seçilmesini iyi bir çözüm olduğu belirtilmiş. Default durumda o şekilde gelmekte ve bende ayarlarını değiştirmedim. Buna ek olarak pencere alt tarafa gittikçe görebileceğiniz Install timezone info seçeneğini de aktif edebilirsiniz.

Şimdi Kernel penceresinde herhangi bir değişiklik yapmadığım için o kısmı geçiyorum. Dilerseniz inceleyebilirsiniz.

Artık sisteme ekleyeceğimiz uygulamaları seçebiliriz. Bunu ekleyebileceğimiz sekme Target Packages sekmesidir. Bu sekme içerisinde bir çok paket bulunmakta. Şayet paketin adını biliyorsanız “/” tuşu ile arama penceresine geçiş yapabilir ve paket adını yazarak hangi menü dallanmasından sonra ilgili paketi aktif edeceğinizi görebilirsiniz. Örneğin nano editörünü kurmak istiyoruz;

Arama yardımıyla, bu text editörün, Target Packages -> Text editor and viewers altında bulunduğunu görebildik. Bu şekilde ilgil dizine giderek ekleme yapabilirsiniz.

QT modülünün eklenmesinde bir kaç ince detay olduğu için, onları birlikte yapalım. Öncelikle Target Packages -> Graphic libraries and applications sekmesine giderek Qt5 seçeneğini bulalım. Eğer benimle aynı konfigürasyon dosyasını belirlediyseniz, Qt5 sisteminizde ekli olarak gözükecektir. Qt5 içerisine girerek uygulamarınızda kullanmayı düşündüğünüz kütüphaneleri seçmeniz gerekiyor. Bazıları seçili gelirken bazı paketler seçili gelmeyecektir. Örneğin, seri port işlemleri içeren qt ortamında bir yazılım geliştiriyorsanız, qt5serialport paketini seçmeniz gerekmektedir.

Not : Eğer benimle aynı konfigürasyonu seçmediyseniz ve arayüz tabanlı bir uygulama geliştirecekseniz gui module‘u seçmeniz gerekmektedir. Ayrıca Raspberry Pi videocore eglfs destekli çalıştığı için, Default Graphical Platform olarak eglfs‘i seçmeniz gerekmektedir.

Yukarda bahsettiğim Raspberry Pi’nin videocore sürücüsünü kullanabilmek için, (farklı bir konfigürasyon seçtiyseniz ya da kendi herhangi bir template konfigürasyon seçmediyseniz) Target Packages -> Hardware Handling -> rpi-userland seçeneğini aktif etmeniz gerekmektedir.

Burada tüm paketleri tek tek açıklamam mümkün değil, ihtiyaca göre şekilleniyor gördüğünüz gibi. İçerisinde binlerce paket bulunmakta, eğer dokunmatik ekran kullanıyorsanız Tslib seçeneğini aktif edebilirsiniz. Ya da kablosuz ağlara bağlanmak istiyorsanız, Target Packages -> Networking applications -> wpa_supplicant paketini de eklemeniz gerekmektedir.

Önemli olduğunu düşündüğüm ve bu yazının devamında yapacağımız proje için zorunlu olan bir ayara değineceğim. Bugün QT üzerinden gerçekleştireceğimiz bir projede, nöbetçi eczaneleri ekran üzerinde listelemeye çalışacağız. Kullanacağımız API, HTTPS üzerinden çalıştığı için QT5’e SSL paketini eklememiz gerekmektedir. Şayet bu şekilde bir derleme yapmazsanız, HTTPS kullandığınız istekleriniz başarısız olacaktır. Bunu eklemek için, Target Packages -> Graphic libraries and applications içerisine giderek QT5 penceresine girelim. Burada Custom configuration options kısmına -openssl yazmanız gerekmektedir.

Sistemimiz için gerekli konfigürasyonları yapmış durumdayız. Exit diyerek tüm pencerelerden çıktığımızda konfigürasyonlarımızı kaydetmek istediğimizi belirterek ayrılıyoruz.

Daha önce seçmiş olduğumuz konfigürasyon üzerine yeni konfigürasyonlarımız yazılacaktır. Bu konfigürasyon dosyanızı saklamanızda fayda var. Ekibe yeni katılan ya da birlikte çalışacağınız arkadaşlarınıza bu konfigürasyon dosyasını iletmeniz gerekebilir.

Şimdi ise derleme sürecini başlatabiliriz, bu süreç boyunca sağlıklı bir internet bağlantı hızına sahip olmanız gerekmektedir. Seçtiğiniz paketlere göre ve belirlediğiniz paralel derleme argümanınıza göre bu süreç elbette değişkenlik gösterebilir. Sanal makine kullanıyorsanız ayırdığınız disk boyutunun yeterli olması gerektiğini unutmayın. Eğer yeterli değilse, bir süre sonra aşağıda bulunan hataya benzer hatalar alabilirsiniz.

cc1: out of memory allocating 66574076 bytes after a total of 148316160 bytes
make

komutu ile derleme işlemini başlatabiliriz. Eğer paralel derleme yapmak istiyorsanız, örneğin;

make -j4

4 çekirdek üzerine bu süreci bölebilirsiniz. Burada önemli olan sisteminizin bu çekirdek sayısını destekliyor olup olmadığıdır. Şayet windows makineniz üzerinde sanal makine kurmuşsanız ve bu makineyi tek çekirdek olarak belirlediyseniz, yazdığınız 4’ün bir anlamı olmayacaktır. Aşağıda bulunan komutu kullarak, linux makineniz için Core(s) per socket değerini görebilirsiniz.

lscpu

Derleme süreci sonrasında, çalıştığınız dizin içerisinde /output/images altında sdcard.img ismiyle oluşacaktır. Bu dosyayı sd karta yazdırmak için ben balenaEtcher programını kullandım, dilerseniz bu adımı komut satırı üzerinden de gerçekleştirebilirsiniz. Tamamen size kalmış bir durum. Yazdırma işleminden sonra, kartınızı Raspberry Pi’ye takarak testinize gerçekleştirebilirsiniz.

Donanımınıza ssh üzerinden root olarak erişim sağlamanız gerekecektir. Bunun için, sshd_config dosyası içerisinde bulunan PermitRootLogin‘i YES olarak değiştirmeniz gerekmektedir.

Raspberry pi üzerinde bulunan debug uart /dev/ttyAMA0 portunu kendi uygulamarınızda kullanmak istiyorsanız yapmanız gereken bazı ayarlar var. Eğer böyle bir şeye ihtiyacınız yok ise bu adımı geçebilirsiniz.

Bunun için, öncelikle sistem dosyalarını mount etmemiz gerekmekte. Bunun için mount komutunu kullanacağız, –t komut satırı argümanı ile öncelikle dosya formatını giriyoruz. Daha sonrasında nereden nereye mount edeceğimiz bilgisini giriyoruz. mmcblk0 raspberry pi’nin kendi sistemini bulundurduğu yer ve bunu sd kart ile eşleştirmemiz gerekiyor.

mount -t vfat /dev/mmcblk0p1 /mnt

Ek not : board/raspberrypi3.cfp içerisinde hangi dosyaların görüntülenebilmesi için mount edilmesi gerektiği yazmakta.

image boot.vfat {
vfat {
files = {
“bcm2710-rpi-3-b.dtb”,
“bcm2710-rpi-3-b-plus.dtb”,
“bcm2710-rpi-cm3.dtb”,
“rpi-firmware/bootcode.bin”,
“rpi-firmware/cmdline.txt”,
“rpi-firmware/config.txt”,
“rpi-firmware/fixup.dat”,
“rpi-firmware/start.elf”,
“rpi-firmware/overlays”,
“zImage”
}
}
size = 32M
}
….

Şimdi ise, nano /mnt/config.txt dosyasına giderek, son satıra ekleme yapıyoruz.

enable_uart=1

Daha sonra inittab içerisinde getty’i kapatmanız gerekiyor. Bunun için nano /etc/inittab dosyasına giderek,

console::respawn:/sbin/getty/ -L console 0 vt100

ilgili satırı yorum satırı haline getirelim.

Tüm bu adımlardan sonra sisteminizi tekrar başlatmanız gerekir.

Host Tarafında Bulunan QT’de Target Platform İçin Kit Oluşturmak

Tüm bu süreçlerden sonra bizim için önemli olan, çapraz derleme yapmamızı sağlayacak ayarları artık host tarafında yapabiliriz. QT Creator programını açarak Tools -> Option -> Build & Run -> Qt Versions sekmesine gidiyoruz. Daha önce derlediğimiz buildroot klasörümüzün olduğu yere giderek /output/host/bin/qmake dosyasını ekliyoruz.

Daha sonra Compilers sekmesine giderek, ADD → GCC → C yaparak, output/host/bin/arm-none-linux-gnueabihf-gcc ekliyoruz, tabi Cpp için de gerekli toolchain dosyasını vermek gerek. Yine aynı şekilde ADD → GCC → C++ dedikten sonra output/host/bin/arm-none-linux-gnueabihf-g++ seçiyoruz.

Yukarda bulunan adımları yaptıktan sonra Kits sekmesinden yeni bir kit ekliyoruz, Kit ekleme bölümünde bulunan yapıda

  • Device Type olarak → Generic Linux Device
  • Sysroot tarafında, output/host/arm-buildroot-linux-gnueabihf/sysroot klasörünü gösteriyoruz.
  • Compiler sekmesi altında bulunan kısımda daha önce eklediğimiz C ve C++’ veriyoruz.
  • Qt version : host olacak şekilde seçim yapıyoruz.

Artık Devices sekmesi altında yazacağımız kodu raspberry pi üzerinde çalıştırmak için gerekli hostname, username, passowrd kısımlarını giriyoruz. Device olarak eklediğiniz kiti seçmeniz gerekir. Bu işlemlerden sonra isterseniz Test sekmesine basarak test yapabilirsiniz. Önemli bir not, sanal makine üzerinde çalışıyor ve ethernet kartını bridge olarak seçmemişseniz, raspberry pi ile aynı ağda olmayacağınız için bağlantı sağlanmayacaktır. O yüzden sanal makinenizin ayarları üzerinden köprülemeyi unutmayın.

Daha sonra açıklayacağım ama sd kart üzerinde yapmanız gereken bir işlem bulunmaktadır. Host tarafında geliştirdiğiniz projenizi, target platforma gönderdikten sonra uygulamayı çalıştırdığınızda free disk ile ilgili bir hata alacaksınız. Bu durumda sd kartın resize yapılması gerekiyor. Çünkü buildroot üzerinden oluşturduğunuz sistem tam fit olacak şekilde bir imaj oluşturmakta. Konfigürasyon dosyanızı incelerseniz şöyle bir ibare görülebilir.

BR2_TARGET_ROOTFS_EXT2_SIZE="400M"

Gparted programı ile resize işlemini yapabilirsiniz. Grafik arayüze sahip olduğu için kullanımı oldukça kolay. İlgili storage seçimini yaptıktan sonra (/dev/sdb2 , ext4 ile formatlı yer burası. Diğer taraf rootfs, oraya herhangi bir şey yapmayın.) kendiniz istediğiniz kadar yer ayırabilirsiniz.

QT’de Proje Oluşturma Süreçleri ve Nöbetçi Eczane Uygulaması

QT üzerinde her zaman yaptığınız gibi projenizi oluşturabilirsiniz, projeyi oluştururken dikkat etmeniz gereken kısım kit olarak daha önceden oluşturduğumuz kiti seçmelisiniz. Oluşturduğunuz proje için Project sekmesinden Shadow build seçeneğini kapatmanız gerekmektedir.

Daha sonra .pro uzantılı dosyanıza aşağıda bulunan ayarı eklemeniz gerekir. Bu ayar sayesinde, projenizi deploy ettiğiniz zaman target platformda root altına gidecektir.

target.path = /root
INSTALLS += target

Nöbetçi eczane listesini çekebilmek için https://collectapi.com/tr/api/health/pharmacy-api sitesinde bulunan API’yi kullanabiliriz. Siteye üye olduktan sonra, kullanmak istediğiniz API’ye gittiğinizde Free modele abone olabilirsiniz. Size 100 tane istek yapmanıza olanak sağlıyor. Profiliniz kısmından hesabınızla ilişkilendirilmiş token’ı görebilirsiniz.

API’nin kullanımı site üzerinden görebiliriz. QML içerisinde kod yazımı gerçekleştireceğimiz için javascript penceresi bizi ilgilendirmekte.

Main.qml

import QtQuick 2.9
import QtQuick.Controls 2.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.11

Window {

    id: root
    visible: true
    maximumWidth: 720
    minimumWidth: 720
    maximumHeight: 1920
    minimumHeight: 1920

    Rectangle {

        width: parent.width
        height: parent.height
        border.width: 0
        color: "lightblue"

        ListModel {

            id: busModel
        }

        Component {

            id: busDelegate

            Item {

                id: delegateColumn
                width: root.width
                height: 190

                RowLayout {

                    id: row
                    anchors.fill: parent
                    spacing: 50

                    Text {

                        id: line
                        font.bold: true
                        text: name
                        font.family: "Helvetica"
                        font.pointSize: 18
                        Layout.fillWidth: true
                        Layout.minimumWidth: 100
                        Layout.preferredWidth: 45
                        Layout.maximumWidth: 120
                        wrapMode: Text.WordWrap
                    }

                    Text {

                        text: address
                        font.bold: true
                        font.family: "Helvetica"
                        font.pointSize: 20
                        Layout.fillWidth: true
                        Layout.minimumWidth: 300
                        Layout.preferredWidth: 300
                        Layout.maximumWidth: 300
                        Layout.alignment: Qt.AlignCenter
                        wrapMode: Text.WordWrap
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                    }

                    Text {

                        id: timertext
                        ColorAnimation on color {
                            id: anim
                            to: "red"
                            duration: 300
                        }

                        font.bold: true
                        text: phone
                        font.family: "Helvetica"
                        font.pointSize: 20
                        Layout.alignment: Qt.AlignCenter
                    }
                }

                Line {
                }
            }
        }

        ListView {

            anchors {
                fill: parent
            }
            model: busModel
            delegate: busDelegate
        }
    }

    function request(url, callback) {

        var xhr = new XMLHttpRequest()
        xhr.withCredentials = true

        xhr.onreadystatechange = (function (myxhr) {
            return function () {
                if (myxhr.readyState === 4) {
                    callback(myxhr)
                }
            }
        })(xhr)

        var data = null
        xhr.open("GET", url)
        xhr.setRequestHeader("Content-Type", "application/json")
        xhr.setRequestHeader(
                    "authorization",
                    "apikey yourtoken")
        xhr.send(data)
    }

    Timer {

        id: timer
        interval: 60000
        running: true
        repeat: true
        triggeredOnStart: true
        onTriggered: request(
                         "https://api.collectapi.com/health/dutyPharmacy?ilce=%C3%87ankaya&il=Ankara",
                         function (o) {
                             console.log(o.responseText)
                             if (o.status === 200) {

                                 busModel.clear()

                                 var obj = JSON.parse(o.responseText).result
                                 for (var i = 0; i < obj.length
                                      && i < 12; ++i) {
                                     busModel.append({
                                                         name: obj[i].name,
                                                         address: obj[i].address,
                                                         phone: obj[i].phone
                                                     })
                                 }
                             } else {

                                 console.log(" Connection Failed -> " + o.status)
                             }
                         })
    }
}

Line.qml

import QtQuick 2.0

Rectangle{
    width: parent.width
    height: 1
    border.width: 1
    border.color: "gray"
    color: "gray"
    anchors.horizontalCenter: parent.horizontalCenter
}

Hazırladığımız projeyi Build ettikten sonra hedef sisteme Deploy edebiliriz. Benim oluşturduğum proje adı untitled olarak isimlendirilmişti. Görselde görebileceğiniz gibi, root altına gelmiş durumda. ./untitled diyerek projeyi çalıştırabiliriz.

Raspberry Pi’nin hdmi çıkışı üzerinden kullandığım ekranı bu linkte bulabilirsiniz. Bu ekran ya da başka bir ekran olsun raspberry pi üzerine taktığınızda bir takım ayarlar yapmanız gerekiyor. Ekran çözünürlüğü, şayet ihtiyaç var ise ekran yönünün çevrilmesi gibi. Bu ayarları config.txt içerisinden ayarlayabiliriz. Daha önce bahsettiğim gibi sistem dosyalarını başka bir dizine mount etmemiz gerekiyor öncelikle.

Bu işlem sonrasında , config.txt içerisine girerek kendi ekranımız için şu 2 satırı giriyoruz.

display_rotate=3
hdmi_cvt=1920 720 60 3 0 0 0

Komutlardan anlayabileceğiniz, ekranı döndürme işlemi gerçekleştiriyor ve ekran uzunluk, genişlik, yenileme hızı gibi parametrelerini girmekteyiz. Uygulamayı çalıştırdığımda aldığım ekran görüntüsünü aşağıda görebilirsiniz.

Bu yazıyı hazırlama, bu bilgileri deneyimleme, öğrenme sürecinde başta Metin Koç olmak üzere aşağıda bulunan kaynakların sahiplerine de teşekkür ederim.

17.03.2021 Ekleme :

Ayarladığınız ekran çözünürlüğü enerji kesip verdiğinizde eski haline dönüyor fakat reboot ettiğinizde doğru çalışıyor ise, açılış için ufak bir süre vermeniz gerekebilir. Bunun için config.txt dosyası içerisine aşağıda bulunan satırı ekleyebilirsiniz.

boot_delay_ms=1000

Kaynaklar :