Raspberry Pi için Android Uygulaması
IoT ile ilgili önceki eğitimlerimizde, bir Raspberry Pi ‘nin nasıl kurulacağını ve nesneleri sunucuya kaydetmek ve Sorgular ve Canlı Sorgular gerçekleştirmek için Back4App API kullanarak Parse Sunucusuna nasıl bağlanacağını ele aldık.
Şimdi Raspberry tarafında yapılan her şeyin Uygulama tarafında nasıl yeniden üretileceğini ele alıyoruz. Bu eğitimde, daha önce yapılandırılan IoT cihazıyla etkileşime girecek bir Android Uygulaması tanımlayacağız. Parse Sunucusu tarafında, Uygulama Raspberry ile aynı görevleri yapar: nesneleri yazar ve Sorgular ve Canlı Sorgular gerçekleştirir. Bir IoT uygulaması geliştirmeyi planlamıyor olsanız bile bu işlevlerin yararlı olabileceğini lütfen unutmayın!
Parse, IoT uygulamaları oluşturmak için harika bir çerçeve olduğunu kanıtlamıştır. 2020’de GraphQL API protokolünün eklenmesiyle, veri almak için daha da iyi bir yol sağlar.
Kodlarımızı, istediğiniz Uygulamaları geliştirmeniz için bir ilk adım olarak sunuyoruz.
Contents
- 0.1 Ön Koşullar
- 0.2 Bölüm 1: Uygulamanızı oluşturmanın ve Back4App ile bağlantı kurmanın temelleri
- 0.3 Bölüm 2: Nesneleri Parse Üzerine Kaydetme ve Alma ve Uygulamada Görüntüleme
- 0.4 Bölüm 3: Canlı Sorgu Kullanarak Gerçek Zamanlı Olayları Dinleme ve Uygulamada Görüntüleme
- 1 Bir Android uygulaması bir IoT cihazıyla nasıl etkileşime girer?
Ön Koşullar
Tek ön koşul Android QuickStart eğitimimizi tamamlamaktır. Bunun için aşağıda belirtilen linke tıklamanız gerekmektedir.
https://www.back4app.com/docs/pages/android/how-to-build-an-android-app-on-back4app
İşte bu IoT Serisi eğitiminin 3. bölümünde gerekli olacak LiveQuery ile ilgili bir eğitim. Yine de önceden tamamlamak zorunda değilsiniz.
https://docs.back4app.com/docs/android/live-query/
Eğitim boyunca herhangi bir sorunuz olursa, aşağıdaki bağlantıda yer alan Android için resmi Parse kılavuzunu takip ederek çözebilirsiniz.
http://docs.parseplatform.org/android/guide/
Bölüm 1: Uygulamanızı oluşturmanın ve Back4App ile bağlantı kurmanın temelleri
Bu projede sınıfımızı “MainActivity.java” olarak adlandırıyoruz. İşte başlamanıza yardımcı olacak temel kod.
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
// Uygulamaya araç çubuğu ekleyin. Bu çubuğun daha fazla açıklaması menu_main_activity.xml dosyasında Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(araç çubuğu); }
@Override public boolean onCreateOptionsMenu(Menu menü) { // Menüyü şişirin; bu, varsa öğeleri eylem çubuğuna ekler. getMenuInflater().inflate(R.menu.menu_main_activity, menu); true döndür; }
@Override public boolean onOptionsItemSelected(MenuItem item) { // Eylem çubuğu öğesi tıklamalarını burada işleyin. Eylem çubuğu // Home/Up düğmesine tıklamaları otomatik olarak işleyin, bu kadar uzun // AndroidManifest.xml'de bir üst aktivite belirttiğiniz gibi. int id = item.getItemId();
//noinspection SimplifiableIfStatement if (id == R.id.action_settings) { true döndür; }
return super.onOptionsItemSelected(item); } }
Aşağıdaki içe aktarmaları eklemeniz gerekebilir:
import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Görünüm; import android.view.Menu; import android.view.MenuItem; import android.widget.EditText; import android.widget.Button; import android.widget.ToggleButton; import android.view.View.OnClickListener;
Düzen XML dosyanıza aşağıdaki kodu eklediğinizden emin olun:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/myCoordinatorLayout" tools:context="com.example.guilherme.myiotapplication.MainActivity">
<includelayout="@layout/content_main_activity" />
<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Araç Çubuğu android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
Uygulamanızı Back4App’e bağlamak için sınıfınızın onCreate() fonksiyonuna aşağıdaki kodu ekleyin.
// Parse Sunucusu Başlatılıyor Parse.initialize(new Parse.Configuration.Builder(this) .applicationId("YOUR_APP_ID") // Çekirdek Ayarlar'dan, "Özellikler" üzerinde .clientKey("YOUR_CLIENT_KEY") // Çekirdek Ayarlar'dan, "Özellikler" üzerinde .server("https://parseapi.back4app.com/") .build() );
Uygulamanıza İnternet Erişimi vermek için Hızlı Başlangıç’taki adım adım talimatları izlemeyi unutmayın.
Bölüm 2: Nesneleri Parse Üzerine Kaydetme ve Alma ve Uygulamada Görüntüleme
Aşağıdaki içe aktarmaları ekleyerek başlayın
import com.parse.FindCallback; import com.parse.Parse; import com.parse.ParseException; import com.parse.ParseObject; import com.parse.ParseQuery; import com.parse.SaveCallback; import org.json.JSONException; import org.json.JSONObject; import java.util.List;
Bu bölümde “CommandGPIO1” sınıfının nesnelerini bir düğmeye tıklayarak kaydediyoruz. Her Parse nesnesinin üç varsayılan özniteliği vardır: objectId, createdAt ve updatedAt. Bizim sınıfımız iki ek niteliğe daha sahip olacak: content ve destination. Content için değerler ya “on” ya da “off” olacak ve bir LED’e gönderilecek bir durumu temsil edecektir. Hedef için, tüm nesneler “command” içerecektir, ancak farklı LED’ler için farklı dizeler tanımlayabiliriz.
Örneğin:
İki düğme oluşturuyoruz ve her biri içeriğe farklı bir değer yazıyor. Bir düğme oluşturmak için aşağıdaki kodu düzen XML dosyanıza ekleyin:
<Buton android:id="@+id/buttonSendOn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:text="Açık Gönder"/>
Sınıf dosyanızda, düğmeyi oluşturmak yazmak kadar kolaydır:
Button buttonOn = (Button) findViewById(R.id.buttonSendOn);
Android programlamaya yeni başlayan biriyseniz, “findViewById” argümanının “buttonSendOn” içerdiğini ve bunun XML’de tanımladığımız Android:Id olduğunu lütfen unutmayın.
Bu butonun tıklandığında Parse Server’a bir nesne kaydetmesini istiyoruz. Bunu yapmak için aşağıdaki kodu ekleyin:
buttonOn.setOnClickListener( new OnClickListener(){ @Override public void onClick(final View view) {
// Yeni nesne oluşturma ve uygun öznitelikleri atama ParseObject command = new ParseObject("CommandGPIO1");
command.put("content","on"); command.put("hedef", "komut"); command.saveInBackground(new SaveCallback(){ @Override public void done(ParseException e){ Snackbar.make(view, "Sent ON to Output", Snackbar.LENGTH_LONG ) .setAction("Eylem", null).show(); } }); });
onClick geri çağırma işlevi içinde gerçekleştirilmesini istediğimiz herhangi bir işlevi ekleyebileceğimizi unutmayın. Burada, bir “CommandGPIO1” sınıf nesnesi oluşturuyoruz, içeriği “on” ve hedefi “command” olarak ayarlıyoruz.
Bu sınıfı Parse panosunda önceden tanımlamak zorunda OLMADIĞIMIZI bilmenizde fayda var! Daha sonra yeni bir nitelik eklemeyi veya sınıfınızın adını değiştirmeyi seçerseniz, kodunuz üzerinde çalışmanız yeterlidir ve değişiklikler otomatik olarak kontrol panelinde güncellenecektir.
Bu noktada, uygulamanızı test etmeli ve nesnenin oluşturulup oluşturulmadığını kontrol etmelisiniz!
SaveCallback, yukarıdaki şekilde gösterildiği gibi ekranınızın alt kısmında hafif bir geri bildirim olan bir snackbar gösterir.
Nesneleri “off” ile yazan bir düğme oluşturmak için bu kodu kopyalayın. Düzen XML’sinde aşağıdaki satırları değiştirin
android:id="@+id/buttonSendOff" android:layout_gravity="top|center" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:text="KAPAT gönder"/>
Yeni bir düğme oluşturun ve içeriğin yazıldığı satırı değiştirin:
Button buttonOff = (Button) findViewById(R.id.buttonSendOff);
command.put("content","off");
Artık Parse Server’da işler düzgün çalıştığına göre, uygulama kullanıcısına kalıcı bir geri bildirim sağlamak istiyoruz. Bunun için aşağıda gösterildiği gibi EditText öğesini kullanacağız.
Bu öğeyi düzen XML’inde tanımlayın:
<EditText android:id="@+id/status1Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:maxLines ="2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Sınıfınızın onCreate() kapsamına aşağıdaki kodu ekleyin:
// Uygulamadan komut alan çıkış piminin durumunu göstermek için metin öğesi tanımlama // Komutlar, bu kodda daha sonra açıklanacak olan düğmeler tarafından gönderilir. final EditText textOutPin = (EditText) findViewById(R.id.status1Text); textOutPin.setFocusable(false); textOutPin.setClickable(true); textOutPin.setText("Çıkış pininin durumu yükleniyor...");
İlk satır nesneyi oluşturur. İkinci satır onu kullanıcılar için düzenlenemez hale getirir. Üçüncü satır onu tıklanabilir hale getirir, böylece kopyalanabilir ve yapıştırılabilir. Dördüncü satır başlangıç metnini ayarlar.
Parse üzerinde kaydedilen son “CommandGPIO1” nesnesinin içerik alanını bu EditText nesnesine yazmak istiyoruz. Bunu kod içinde bazı mantıklar kullanarak yapabilsek de, daha gerçekçi ve sağlam sonuçlar sağladığı için aslında bir ParseQuery gerçekleştirerek Parse’den nesneleri alacağız.
Aşağıdaki kod, bir Parse Sorgusunu bildirir, parametreleri ayarlar ve sonuçlarını yazdırır.
ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1"); queryOut.whereEqualTo("hedef", "komut"); queryOut.addDescendingOrder("createdAt"); queryOut.setLimit(1);
queryOut.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textOutPin.setText("Output is " + objects.get(0).getString("content")); } else{ textOutPin.setText("Hata: " + e.getMessage()); } } });
İlk satır sorguyu oluşturur. İkincisi, yalnızca hedef alanı “command” içeren nesneleri seçmek için bir kısıtlama ekler. Üçüncü satır, nesneleri en yenilerinden başlayarak sıralar. Dördüncü satır, en yeni nesneyi alacağımızı garanti ederek sonuçları bir ile sınırlar.
Beşinci satır sorguyu harekete geçirir ve bittiğinde geri arama fonksiyonunu çağırır. Burada, alınan nesnenin içeriğini önceden tanımlanmış EditText nesnesine yazıyoruz.
Bu kod parçasını EditText nesnesini bildirdikten hemen sonra ekleyeceğiz, böylece uygulama açıldığında bir sorgu gerçekleştirilir ve ayrıca yeni nesneler oluştururken metni otomatik olarak güncellemek için Parse üzerinde yeni bir nesne kaydederken SaveCallback ‘te.
Bu noktada, uygulamanız aşağıdaki ekran görüntülerinde gösterildiği gibi çalışmalıdır.
Son olarak, kullanıcıların yukarıdaki sorguyu istedikleri zaman gerçekleştirmelerini sağlamak için bir yenileme düğmesi ekliyoruz. Bu işlem farklı bir düğme stiliyle, yani bir Yüzen Eylem Düğmesi ile yapılacaktır.
Bu kodu düzen XML dosyasına ekleyin:
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|end" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" app:srcCompat="@android:drawable/ic_popup_sync" />
Şimdi, sınıftaki onCreate() kapsamına aşağıdaki kodu ekleyin:
// İstendiğinde çıktı durumunu elde etmek için yenileme düğmesi. // İlkiyle aynı sorgu burada gerçekleştirilir FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(final View view){ ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1"); queryOut.whereEqualTo("hedef", "komut"); queryOut.addDescendingOrder("createdAt"); queryOut.setLimit(1); queryOut.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textOutPin.setText("Output is " + objects.get(0).getString("content")); } else{ textOutPin.setText("Hata: " + e.getMessage()); } Snackbar.make(view, "Çıktının güncellenmiş durumu", Snackbar.LENGTH_LONG ).setAction("Action", null).show(); } }); } });
Bu noktada artık nesneleri Parse Server’a kaydedebilir, onlardan bilgi alabilir ve uygulamanızda görüntüleyebiliriz! Uygulamanız aşağıdaki şekilde gösterildiği gibi çalışıyor olmalıdır:
Bölüm 3: Canlı Sorgu Kullanarak Gerçek Zamanlı Olayları Dinleme ve Uygulamada Görüntüleme
Bu bölümde, Parse Dashboard’daki “InputGPIO” sınıfını gerçek zamanlı olarak izliyor ve kullanıcı için nesnelerin “içeriğini” gösteriyoruz.
Düzen XML’inde yeni bir EditText tanımlayarak başlayın:
<EditText android:id="@+id/status2Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|start" android:layout_marginBottom="0dp" android:maxLines ="2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
onCreate() kapsamında yeni bir EditText nesnesi oluşturun ve hemen ardından bir başlatma sorgusu (henüz canlı değil) gerçekleştirin.
final EditText textInPin = (EditText) findViewById(R.id.status2Text); textInPin.setFocusable(false); textInPin.setClickable(true); textInPin.setText("Giriş pininin durumu yükleniyor...");
// Pin'in depolanan son durumunu elde etmek için ilk (canlı olmayan) sorgu ParseQuery<ParseObject> queryIn = ParseQuery.getQuery("InputGPIO"); queryIn.whereEqualTo("type", "interrupt"); queryIn.addDescendingOrder("createdAt"); queryIn.setLimit(1);
queryIn.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textInPin.setText("Input is " + objects.get(0).getString("content")); } else{ textInPin.setText("Hata: " + e.getMessage()); } } });
“InputGPIO” özniteliklerinin içerik ve tür olduğunu, ikincisinin “CommandGPIO” sınıfında hedef rolü oynadığını unutmayın.
Bu noktada, uygulamanız aşağıda gösterilen şekle benziyor olmalıdır.
Şimdi Canlı Sorguyu uygulayacağız. Daha önce bahsedilen eğitim burada gereklidir.
https://docs.back4app.com/docs/android/live-query/
Canlı Sorgunun hangi sınıflarda etkinleştirileceğini seçmek ve bir alt alan adı tanımlamak için Canlı Sorgu eğitimimizdeki 1. adımı izlediğinizden emin olun. Özelliği etkinleştirmek için “InputGPIO” sınıfını (zaten mevcut değilse) oluşturmanız gerekebilir. Android’de Live Query istemcisini kurmak için öğreticideki 2. adımı izleyin.
Aşağıdaki içe aktarmaları eklemeyi unutmayın:
import tgio.parselivequery.BaseQuery; import tgio.parselivequery.LiveQueryClient; import tgio.parselivequery.LiveQueryEvent; import tgio.parselivequery.Subscription; import tgio.parselivequery.interfaces.OnListener;
LiveQuery’yi başlatmak ve parametrelerini tanımlamak için aşağıdaki kodu ekleyin.
// Canlı Sorguyu Başlatma LiveQueryClient.init("wss:YOUR_SUBDOMAIN_NAME.back4app.io", "YOUR_APP_ID", true ); LiveQueryClient.connect();
// LiveQuery'nin niteliklerini tanımlama Subscription subIn = yeni BaseQuery.Builder("InputGPIO") .where("type","interrupt") .addField("content") .build() .subscribe();
Aboneliği tanımlarken, yalnızca type özelliği “interrupt” olan öğeleri alır ve alan içeriğini alırız.
Şimdi, abonelik tarafından tanımlanan bir nesne Parse Server’da oluşturulduğunda yanıt vermek için aşağıdaki kodu ekleyin.
// LiveQuery CREATE olaylarını dinlemeye başlamak, içeriğini almak ve yazmak subIn.on(LiveQueryEvent.CREATE, new OnListener() { @Override public void on(JSONObject object) { dene { final String subInContent = (String) ((JSONObject) object.get("object")).get("content"); runOnUiThread(new Runnable() { @Override public void run() { textInPin.setText("Input is " + subInContent); Snackbar.make(findViewById(R.id.myCoordinatorLayout), "Input pin was changed to " + subInContent.toUpperCase(), Snackbar.LENGTH_LONG ).setAction("Action", null).show(); } }); } catch (JSONException e){ e.printStackTrace(); } } });
Nesnenin içerik alanı yeni EditText elemanımızda görüntülenecektir.
Bu noktaya kadar yaptıklarımız IoT cihazı tarafından gönderilen herhangi bir girdiyi görüntülemek için yeterlidir. Ancak, uygulamanın nasıl çalıştığını doğrulamak için başka bir düğme türü, yani bir “InputGPIO” sınıfı nesnesi oluşturacak ve bunu Parse Server’a kaydedecek bir Toggle Button uygulayacağız.
Düzen XML dosyasına aşağıdaki kodu ekleyin:
<ToggleButton android:id="@+id/toggleTest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|end" android:layout_marginBottom="0dp" android:layout_marginTop="100dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:textOn = "giriş açık" android:textOff = "input off"/>
MainActivity sınıfındaki onCreate() fonksiyonu kapsamına aşağıda belirtilen kodu ekleyin.
// Toggle düğmesi sadece donanımın oluşturacağı nesneleri taklit etmek için burada ToggleButton toggleTest = (ToggleButton) findViewById(R.id.toggleTest); toggleTest.setOnClickListener( new OnClickListener(){ @Override public void onClick(final View view) { ParseObject command = new ParseObject("InputGPIO"); if(gpioStatusTest.equals("off")) gpioStatusTest = "on"; else gpioStatusTest = "off";
command.put("type","interrupt"); command.put("content", "From Toggle: " + gpioStatusTest); command.saveInBackground(new SaveCallback(){ @Override public void done(ParseException e){ Snackbar.make(view, "Changed input pin", Snackbar.LENGTH_LONG ).setAction("Action", null).show(); } }); } });
Ayrıca, MainActivity kapsamında String’i de bildirin.
String gpioStatusTest = "off";
Uygulama ile işimiz bitti! Bu eğitimin sonunda, uygulamanız aşağıda gösterilen şekle oldukça benzer görünmelidir.
IoT uygulamamızı geliştirmeye devam etmek istiyorsanız, lütfen Raspberry Pi’yi kurmanın temellerini ve JavaScript kullanarak Parse Server’dan nesneleri alma ve kaydetme gibi belirli uygulamaları öğreten adım adım bir kılavuz olan IoT Serimizi okuyun.
Kodlarımız aşağıdaki linkte mevcuttur:
https://github.com/back4app/iot-raspberry-node
Referanslar
Raspberry Pi’yi Parse Sunucusuna Bağlama
Bir Android uygulaması bir IoT cihazıyla nasıl etkileşime girer?
Bölüm 1: Uygulamanızı oluşturmanın ve sunucu tarafına bağlanmanın temelleri.
Bölüm 2: Nesneleri Parse Kaydetme ve Alma ve Uygulamada Görüntüleme.
Bölüm 3: Canlı Sorgu Kullanarak Gerçek Zamanlı Olayları Dinleme ve Uygulamada Görüntüleme