Önceki teknik yazımız olan "Smart Display RS485 Introduction" başlıklı makalede RS485 haberleşmesi ve Modbus protokolünün temel çalışma prensiplerini ele almıştık. Bu yazıda ise çok sayfalı bir uygulama örneği üzerinden sık kullanılan bazı geliştirme yöntemlerini daha ayrıntılı olarak inceleyeceğiz. Ele alınacak Smart Display geliştirme konuları şunlardır:
- Button widget kullanarak sayfalar arasında geçiş yapma.
- Sayfa geçişinden sonra widget değerlerini güncelleme.
- Farklı sürelerde buzzer sesi oluşturma.
- Graph widget kullanımı.
Gerekli Malzemeler
- RS485 arayüzüne sahip bir Smart Display. Bu örnekte 3,5" Smart Display kullanılmaktadır.
- Arduino (Uno veya Mega2560).
- Arduino RS485 genişletme kartı (DFRobot bağlantısı).
- Ayarlı direnç (VR).
Bağlantılar
- Ayarlı direnç: Vcc ucunu 5V'a, GND ucunu GND'ye ve çıkış ucunu Arduino A0 girişine bağlayın.
- RS485 genişletme kartının A terminalini Smart Display üzerindeki RS485+ terminaline bağlayın.
- RS485 genişletme kartının B terminalini Smart Display üzerindeki RS485- terminaline bağlayın.
Örnek Uygulamanın İçeriği
Bu proje üç sayfadan oluşmaktadır ve sayfalar arasında Button widget kullanılarak geçiş yapılabilir. Her sayfanın içeriği ve işlevi aşağıda açıklanmıştır:
Sayfa 1:
Sağ tarafta bir sonraki sayfaya geçmek için kullanılan bir buton, alt bölümde ise ekran parlaklığını ayarlamak için bir Horizontal Slider bulunmaktadır. Sayfanın önizlemesi aşağıdaki gibidir:

Sayfa 2:
Sol taraftaki buton önceki sayfaya, sağ taraftaki buton ise sonraki sayfaya geçiş için kullanılır. Alt bölümde bulunan üç buton ile kısa, orta ve uzun süreli buzzer sesleri oluşturulabilir.

Sayfa 3:
Sol taraftaki buton ile önceki sayfaya dönülebilir. A0 girişine bağlı ayarlı direnç analog giriş sinyalini simüle etmek için kullanılır ve elde edilen değerler Graph widget üzerinde görüntülenir.

Bu örnekte bir RS485 genişletme kartı kullanıldığından, programlamaya başlamadan önce gerekli kütüphaneyi Arduino IDE'deki Library Manager üzerinden indirip kurun:

Sayfa İçeriğinin Oluşturulması
İlk olarak GUI Builder kullanarak sayfa içeriğini oluşturun. Her sayfadaki metinler doğrudan Text widget içerisine girilebildiğinden, bu metinlerin program içerisinde ayrıca tanımlanmasına gerek yoktur.

Widget'lara ait ayrıntılı yapılandırma bilgileri aşağıdaki tabloda gösterilmektedir:

Elinizdeki donanıma göre uygun Smart Display modelini seçebilir ve farklı widget'lar kullanarak arayüz tasarımını ihtiyacınıza göre düzenleyebilirsiniz. Burada dikkat edilmesi gereken nokta, her sayfadaki widget ID ve Type değerlerinin programda tanımlanan değerlerle aynı olmasıdır. Uygulamayı gereksiz yere karmaşıklaştırmamak için sayfa içeriğini tabloda belirtilen widget ID ve Type değerlerine göre oluşturmanız önerilir.
Şimdi programlama bölümüne geçebiliriz.
Örnek Programın Açıklaması
(1) Include Dosyaları
ModbusMaster kütüphanesini indirdikten sonra, #include direktifini kullanarak projeye eklemeyi unutmayın:

Ayrıca, yönetimi kolaylaştırmak amacıyla Smart Display ile ilgili bazı sabitler SmartDisplayModbus.h dosyasında tanımlanmıştır. Bu dosyanın da projeye dahil edilmesi gerekir.
(2) Widget ID'den Register Adresine Dönüşüm
Önceki makalede, widget ID değerinin ilgili register adresine nasıl dönüştürüleceği aşağıdaki şekilde açıklanmıştı:

Ancak bu tablo, bir sayfada en fazla 10 widget kullanılabildiği önceki yapı için hazırlanmıştır. Güncel yapıda her sayfada en fazla 64 widget kullanılabilir. Tablodaki adresleme yöntemi ilk 10 widget için geçerliliğini korurken, 10. widget'tan sonraki widget'lar için yeni bir register bloğunun kullanılması gerekir. Bu nedenle aşağıdaki makrolar tanımlanmıştır:

Kullanımı kolaylaştırmak için widget ID üzerinden okuma ve yazma işlemlerini gerçekleştiren bir fonksiyon tanımlanmıştır. Gerekli register adresi dönüşümü de bu fonksiyon içerisinde yapılır:

(3) Widget ID ve GUI Tasarımı
GUI Builder ile sayfa içeriği oluşturulurken widget ID değerlerinin programdaki tanımlarla aynı olması gerektiğini belirtmiştik. Bu nedenle program içerisinde widget ID sabitleri aşağıdaki şekilde tanımlanmıştır:

Bu sabitlerin kullanılması, widget türlerinin daha kolay takip edilmesini ve programdaki tanımların gerçek GUI tasarımıyla karşılaştırılmasını sağlar. GUI tasarımında daha sonra herhangi bir değişiklik yapılırsa, hataları önlemek için ilgili widget ID değerlerinin programdaki tanımlarla eşleştiğinden emin olun.
(4) Buzzer
Firmware, belirli bir gecikme süresinin ardından buzzer'a 0/1 sinyali göndererek ses üretir. Diğer sayaçlar ise sesin ne zaman başlayacağını ve ne kadar süre aktif kalacağını kontrol eder. Buzzer'ı çalıştırmak için dört register'a değer yazılması gerekir:

Ses süresini ayarlamak için SMARTDISPLAY_BUZZER_HIGH parametresini alan bir fonksiyon kullanıyoruz. Böylece farklı sürelerde buzzer sesi oluşturulabilir:

SMARTDISPLAY_BUZZER_ACTIVE değeri yalnızca bir önceki yazılan değerden farklı olduğunda buzzer'ı tetikler. Bu nedenle ilgili değer fonksiyon içerisinde static olarak tutulur ve fonksiyon her çağrıldığında değeri tersine çevrilir.
Son olarak, sayfa işleme fonksiyonunda butonların durumuna göre farklı sürelerde buzzer sesi oluşturulabilir:

(5) Sayfa Geçişi
Sayfa değiştirmeden önce CONFIG_MODE moduna geçilmesi gerekir. Yeni sayfa ayarlandıktan sonra tekrar DISPLAY_MODE moduna geçilir. Sayfa değişimi sırasında Smart Display, yeni sayfadaki tüm widget'ları yeniden yükler. Yükleme tamamlanmadan okuma/yazma işlemi yapılmasını önlemek için genellikle kısa bir bekleme süresi eklenir. Gerekli bekleme süresi sayfanın karmaşıklığına bağlıdır. Sayfa geçişinden sonra beklenmeyen bir durumla karşılaşılırsa, bekleme süresini artırarak sorunun giderilip giderilmediği kontrol edilebilir.

Sayfa değiştirildiğinde yeni sayfadaki widget'lar varsayılan değerleriyle yüklenir. Bu nedenle sayfa geçişini yönetmek, widget değerlerini kaydetmek ve yeni sayfa yüklendiğinde gerekli değerleri geri yüklemek için bir işlem akışı oluşturuyoruz.
İlk olarak, sayfa tablosunu oluşturabilmek ve her sayfaya ayrı bir işleme fonksiyonu atayabilmek için sayfa işleme fonksiyonuna ait bir fonksiyon işaretçisi tanımlıyoruz:

Ardından sayfa tablosunu oluşturuyoruz:

MAX_WIDGETS_PER_PAGE sabiti, her sayfa için değerleri kaydedilecek widget sayısını tanımlar. İleride sayfaya yeni widget'lar eklenirse bu değerin de buna göre güncellenmesi gerekir.
Son olarak, sayfa geçişi ve widget işlemleri loop() fonksiyonu içerisinde gerçekleştirilir:

Geçerli sayfa _curPage ile bir sonraki sayfa _nextPage birbirinden farklıysa, sayfa değişikliği göstergesi newPage true olarak ayarlanır. Böylece sayfa işleme fonksiyonu yeni sayfa için başlangıç işlemlerinin yapılması gerektiğini anlayabilir. Page0 üzerinden örnekleyelim:

init parametresi true ise sayfaya yeni geçildiği anlamına gelir. Bu durumda WIDGET_P0_BUTTON_NEXT değeri 0 olarak ayarlanır. Yeni bir sayfa yüklendiğinde butonun varsayılan değeri 0'dır. Belirli bir işlemin —bu örnekte PAGE_1 sayfasına geçişin— yalnızca butona basıldığında, yani değer 0'dan 1'e değiştiğinde gerçekleştirilmesini istiyoruz. Önceki kullanımda değer 1 olarak kalmış olabileceğinden, başlangıç sırasında tekrar 0 olarak ayarlanması gerekir.
Slider widget da sayfa ilk yüklendiğinde 0 başlangıç değeriyle gelir. Ancak ekran parlaklığı daha önce değiştirilmiş olabilir. Bu nedenle Slider değeri, mevcut parlaklık değerini tutan _brightness ile yeniden ayarlanır.
Diğer durumlarda, yani init false olduğunda, widget'lar değerlerinde değişiklik olup olmadığına göre işlenir. İşlem sonunda tüm widget değerleri bir sonraki işlem döngüsünde karşılaştırılmak üzere kaydedilir.
Bir sonraki sayfayı belirleyen _nextPage değerini ayarlamak için aşağıdaki fonksiyon kullanılır:

Sayfa işleme fonksiyonu, ilk çalıştırmadan sonraki her döngüde widget değerlerini kontrol eder. Bir buton değerinin 0'dan 1'e değiştiği algılandığında, sayfa geçişi talep etmek için ilgili fonksiyon çağrılır:

(6) Graph Widget
Graph widget, kendisine yazılan değerleri grafik üzerinde görsel olarak göstermek için kullanılır. Bu örnekte, analog giriş sinyalini simüle etmek amacıyla A0 girişine bir ayarlı direnç bağlanır ve verideki değişim Graph widget üzerinde görüntülenir. Arduino'nun analog giriş aralığı 0-1023, Graph widget'ın kabul ettiği değer aralığı ise 0-100 olduğundan, değer aralığını dönüştürmek için map() fonksiyonu kullanılır.
Graph widget yalnızca değer değiştiğinde ekranı güncelleyecek şekilde tasarlanmıştır. Belirli aralıklarla güncellenen dinamik bir grafik elde etmek için basit bir yöntem kullanılabilir: değer periyodik olarak kontrol edilir ve yeni değer bir önceki değerle aynıysa 1 artırılır veya azaltılır. Böylece giriş değeri değişmese bile grafiğin düzenli olarak güncellenmesi sağlanabilir.

Sonuç
Bu örnek uygulamada, Smart Display ile RS485/Modbus üzerinden çok sayfalı bir arayüzün nasıl yönetilebileceğini ve farklı widget işlevlerinin program içerisinde nasıl ele alınabileceğini inceledik. Burada kullanılan yapı, benzer projeler için başlangıç noktası olarak kullanılabilir ve geliştirme sürecinin daha hızlı ilerlemesine yardımcı olabilir. Örnek programın tamamına aşağıdaki bağlantıdan ulaşabilirsiniz: GitHub'da Modbus-MultiPageOperation.
RS485/Modbus konusunda temel bilgilere ihtiyaç duyuyorsanız, iletişim yapısı ve temel çalışma prensipleri için önceki teknik yazımızı inceleyebilirsiniz: Smart Display RS485'e Giriş.