29 Eylül 2020 Salı

MIT App Inventor2 ile Mobil Uygulama Geliştirelim (Bluetooth Termometre)


Merhaba. Bir önceki seri termometre yazısına bir dostumdan gelen yorum, bana bu yazı için fikir verdi. Malum, seri portlu bilgisayarlar artık bulunmuyor. Sıcaklık verisini cep telefonumdan okumak istesem, telefonumda bunu okuyacak uygulama var mı? Belki evet, PlayStore'da Bluetooth'dan (BT) ham (raw) veri okuyan uygulamalar bulunabiliyor. Bu uygulama ne kadar güvenli? Neden tek işi BT'a veri göndermek/almak olan bir uygulama kamerama, mikrofonuma erişmek istiyor?

İlk akıllı telefonumda kullandığım BluetoothSPP adlı uygulama, yeni telefonumdaki Android sürümüne uyumlu olmadığından BT kontrollü arabam için bir uygulama yazmıştım. Yaptığı iş, bir byte uzunluğunda mesajlarla (daha doğrusu karakterlerle) arabayı ileri geri götürmek, döndürmek veya durdurmak. Bu uygulama için, MIT App Inventor'u keşfetmemle sıfırdan Android programlama öğrenmeme gerek kalmadı. App Inventor'la, bir satır bile kod yazmadan uygulamaları sürükle bırak tekniğiyle geliştirmek mümkün. Belki mükemmel değil ama hızlıca birşeyler geliştirmek için ideal. Güvenli mi? Güvenliğini kişisel olarak test etmedim ama MIT gibi bir kurumun malware içeren bir platform geliştirmesi kulağa pek olası gelmiyor. Üstelik günde ~60K aktif kullanıcısı (toplamda >8M kullanıcı) olan bir platformun güvenlikle ilgili sıkıntıları olsaydı, ortaya çıkardı.

App Inventor'a geçmeden önce, kullandığım donanımı ve Arduino'da çalışan yazılımı açıklayacağım. Bu arada, bu yazıda yalnızca projenin yapılabilirliğini göstermek istedim. Uygulama "yeterince" iyi çalışıyor, tasarım açısından da mükemmel değil çünkü benim uzmanlığım değil. Devreyi belki Arduino yerine mikrodenetleyiciyle yapsaydım daha basit olabilirdi ama bu da (eğer yapabilirsem) başka bir yazının konusu olsun.


Parça Listesi
Arduino Uno
Arduino Bluetooth Shield
2x 4.7K direnç
DS1621 Entegre
Breadboard
Jumper Kablo (yaklaşık 1 düzine)


Devrenin Kurulması
Devre olarak, Arduino ile I2C yazısındaki devreye Keyes Bluetooth modülünü ekleyip bunu kullandım. Orjinal devre şeması aşağıda. 

DS1621 Termometre

Bluetooth modülünün 4 pini var (yanda). RxD ve TxD bacakları sırayla Arduino'nun D3 ve D4 pinlerine takılı. Bunun özel bir nedeni yok, kodda gerekli değişiklik yapılarak herhangi bir data bacağına takılabilir. Kalan iki pin zaten Vcc ve GND. 
 
Bu arada Fritzing aşağı yukarı bir yıl önce paralı oldu. Kaynak kodları github'da açık ve derlemek isteyenler için hala ücretsiz ama Windows'ta sorunsuz derlenir mi emin değilim. Anasayfada eski sürümlerine bağlantı olmasa da dosyalar henüz fritzing.org sunucusundan silinmemiş (biraz araştırarak bulunabilir). Linux Mint ve Fedora repolarında eski sürümleri (v0.8.5, v.0.9.2) bulunabiliyor. Bu arada uygulama 8€, çok pahalı değil. Şu anda kurulu olduğu için kişisel olarak ödemeyi tercih etmiyorum ama ileride neden olmasın. Kurulum dosyası üçüncü parti sitelerde de bulunabiliyor ama bence güvenli değil.


Devrenin bütünü basitçe aşağıdaki görselde:

Arduino'da çalışan kodu I2C yazısından alıp, BT için aşağı yukarı 16 satır kod ekledim. BT modülünü farklı pine bağlamak için onuncu ve onbirinci satırdaki sabit tanımlamalarını değiştirmek gerekiyor. 

 /* DS1621 sicaklik sensorunun (I2C bus) Arduino ile kullanilmasi
 *
 * sicaklik sensorunun I2C ID'si A0 = A1 = A2 = GND ise 0x48 olacak
 */

#define DS1621 B1001000
#include <Wire.h>
#include<SoftwareSerial.h>  // BT modulu icin

const int BTRxD = 3;        // BT RxD Pin
const int BTTxD = 4;        // BT TxD Pin
// SoftwareSerial nesnesi tanimla
SoftwareSerial Serial1(BTTxD, BTRxD);

void setup()   {
  // I2C initialization
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(DS1621);
  Wire.write(0xAC);     // Access Config komutu
  Wire.write(0x02);     // Config register'a 02 yaziliyor.
                        // Output polarity bit = 1 => Active High
  delay(10);

  Wire.beginTransmission(DS1621);
  Wire.write(0xEE);     // Start Convert: sicaklik okumaya basla
  Wire.endTransmission();       // Stop bit
 
  BluetoothInit();
}

void BluetoothInit()    {   // Adi uzerinde Bluetooth initialization
  Serial1.begin(9600);
  Serial1.flush();
  // burada baska kodlar da vardi ama bu proje icin gerekli degil
}


void loop()  {
  float derece;
  byte SH, SL, X;
  char buffer[8]; // BT buffer
  // SH: Sicaklik yuksek anlamli byte
  // SL: Sicaklik dusuk anlamli byte

  Wire.beginTransmission(DS1621);
  Wire.write(0xAA);     // Read Temperature komutu
  Wire.endTransmission();

  Wire.requestFrom(DS1621, 2);
  // Read Temperature komutunu gonderdikten
  // sonra chip'ten sicaklik icin 2 byte iste
  X = Wire.available();
  if(X >= 2)  {                 // Chip'ten 2 byte geldiyse
    // Yuksek anlamli byte sicakligin tamsayi degeri SH
    SH = Wire.read();
    // dusuk anlamli byte SL sicakligin ondaligi 0x80 => 0.5
    SL = Wire.read();
  }

  delay(500); // sicakligi saniyede iki kere oku ve BT'den gonder
 
  derece = (float)SH + float(SL) / 256.0;
  dtostrf(derece, 2, 2, buffer);  // dereceyi string'e cevir.
  Serial.println(buffer);
 
  // buffer'daki veriyi BT'den gonder
  Serial1.println(buffer);
  Serial1.flush();

}

Program basitçe sıcaklık sensöründen veriyi alıyor, float olarak dereceye çeviriyor ve bu değeri metne çevirip hem seri porta hem de Serial1 olarak tanımladığım BT'a gönderiyor. Bu kodu derleyip cihaza gönderiyorum. Henüz hiçbir BT istemci bağlı olmadığından BT modülünün ışığı yanıp sönecek.


MIT App Inventor2 (AI2) ile Uygulamanın Tasarlanması
Bu bölümde telefon uygulamasının nasıl geliştirileceğini anlatacağım. AI2'yle ilgili ilk adımlar https://appinventor.mit.edu/explore/ai2/setup adresinde de açıklanıyor. İhtiyacım olan:
1- http://ai2.appinventor.mit.edu/ adresine girip google hesabıyla oturum açmak. Bu sayfanın HTTPS değil HTTP olması açıkçası çok hoşuma gitmiyor ama yapacak başka birşey yok. Bir de google hesabı yerine, bu sitede kendi hesabımızı açabilseydik güzel olurdu. 
2- Play Store'dan MIT AI2 Companion'u indirip kurmak. Bildiğim kadarıyla AI2'nin iPhone desteği yok. AI2 Companion olmadan da web arayüzünden uygulama geliştirilebiliyor ama geliştireceğim uygulamayı telefonumda denemek için AI2 Companion gerekli. 

Yukarıdaki bağlantıda birinci seçenekte anlatılan kurulumu kullanacağım. Uygulamayı telefonuma kurdum. Bilgisayarın ve telefonun aynı WiFi ağında olması gerek diyor. Benim bilgisayarım kabloyla bağlı ve herşey sorunsuz çalışıyor. Aynı ağda olması yeterli diye düzeltiyorum. 

Tarayıcıda "Start new project"e basıp, bir proje oluşturuyorum. Ben TermoBT adını verdim. Proje adı uygulamanın da adı olacak. Proje oluşturulduktan sonra açılıyor, açılmazsa "My Projects" ekranından açılabilir. Proje açıldıktan sonra yanda görüldüğü gibi Connect -> AI Companion'a tıklayınca ekrana bir kare kod ve şifre gelecek. Şimdi telefonumdan uygulamayı açıp kare kodu tarattığımda App Inventor telefonuma bağlanıyor. Bu andan itibaren web arayüzünde yaptığım herşey telefonda da görünecek. 

Ekranın sol tarafında, GUI oluşturmak için gereken bileşenler var. Bu bileşenleri sürükleyip ekrandaki sanal telefona bırakınca, ekranın solundaki menüye sırayla yerleşiyorlar. Sonradan eklenecek bileşenleri hizalamak için önce Layout sekmesinden "Arrangement"ları yerleştirmem gerek. Bunlar görünmez tablolar gibi düşünülebilir. Yerleşim için iki tane "HorizontalArrangement" ve bir tane "Vertical Arrangement"i altalta ekliyorum. Aynı bileşeni birden fazla kullandığımda onları ayırdetmek için adlarını değiştiririm. Üstteki "HorizontalArrangement"i "HorizontalArrangementTemp" olarak, ortadakini "HorizontalArrangementControls" olarak ve alttakini "VerticalArrangementMain" olarak ayarladım. Bileşenleri yeniden adlandırmak için "Components" menüsünün altında "Rename" düğmesi var. Son olarak VerticalArrangementMain'in içine bir VerticalArrangement daha yerleştirip onun adına VerticalArrangementSub diyorum. Bu Arrangement'ların kullanımı için aşağıdaki videodan yararlandım. Bu arada youtube, AppInventor için bulunabilecek en iyi kaynak:



Şimdi tekrar bileşenlere gidip "User Interface" sekmesinden iki tane "Label"i en üstteki HorizontalArrangementTemp'in içine yanyana yerleştiriyorum. Ardından ortadaki genel görünümden veya sağdaki bileşen listesinden Label1'e tıklayıp en sağdaki "Properties" bölümünde Text'i "Temperature: " (veya Derece) olarak değiştiriyorum. Aynı şekilde Label2'nin Text'ini boş bırakıyorum. Bir de Label2'nin yüksekliğini (Height) %50 (50 percent) olarak ayarlıyorum.
 

Son olarak HorizontalArrangementTemp'in özelliklerinden Width'i tıklayıp "Fill Parent"i seçtikten sonra OK diyorum. Böylece bu kutucukla işim bitti.

Ardından ikinci kutucuğa yanyana bir "CheckBox", bir de "Button" sürüklüyorum. Elbette tasarımı herkes istediği gibi yapmakta serbest ama kodları(!) yazarken adlandırmalara dikkat etmek gerekiyor. Butonun adını "ButtonReadTemp" olarak değiştirip üzerindeki metni "Read Temperature" olarak değiştiriyorum. CheckBox'un adını değiştirmiyorum ama metnini "Read automatically" yapıyorum. Bir de HorizontalArrangementControl'ün de özelliklerinden Width'i tıklayıp "Fill Parent"i seçmemle bu kutucukla da işim bitiyor (aşağıda):


Şimdi VerticalArrangementMain'in özelliklerinden, "AlignHorizontal"a "Center:3" ve "AlignVertical"a "Bottom:3" değerini atıyorum ve hem "Height" hem de "Width"i "Fill Parent" olarak işaretliyorum. İçteki VerticalArrangementSub, alt ortaya düşüyor. Onun da "AlignHorizontal" değerini "Center:3" olarak atıyorum ve içine bir butonla bir ListPicker yerleştiriyorum. Bunların hangisinin altta olduğunun çok önemi yok. Düğmenin metnini "Disconnect from device", ListPicker'ın metnini "Select Bluetooth device" olarak değiştirince, bu kutucukla da işim bitiyor. 

Soldaki bileşenlerden, "Sensors" sekmesinden "Clock"u ve "Connectivity" sekmesinden "Bluetooth Client"ı ekrandaki telefona sürüklüyorum. Bunlar görünmez kontroller. Bileşen listesinden Clock'a tıklayıp TimerEnabled özelliğini kapatıyorum ve TimerInterval özelliğini 500 yapıyorum. Bu, her 500ms'de Clock tetiklenecek anlamına geliyor. Herşey tamamlandığında ekran görüntüsü aşağıdaki gibi olmalı: 


Uygulama, bir BT cihaza bağlı değilse sıcaklıkla ilgili bileşenleri ve "Disconnect" düğmesini göstermeye gerek yok. Bir BT cihaza bağlanıldığında da "Select device" listesine gerek yok. Bu nedenle HorizontalArrangementTemp'in özelliklerine bir kere daha gidip en alttaki Visible kutucuğundan işareti kaldırıyorum. Aynısını HorizontalArrangementControls'a ve Button1'e de (Disconnect) yapıyorum. Şimdi uygulama açıldığında sadece cihaz seçme butonunu gösteriyor. Diğerleri, bağlantı kurulursa ekrana geliyor. Buraya kadar bütün tasarım tamamlandı. 


MIT App Inventor2 ile Kodlama
AI2'nin bence en güzel özelliği anladığımız anlamda kod yazmak yerine renkli parçalarla, lego oynar gibi prosedürleri oluşturabilmek. Bu bölümde BT ile ilgili kontrolleri hazırlarken aşağıdaki videodan faydalandım: 


Prosedürleri oluşturmak için üstteki yeşil barın en sağındaki "Blocks" düğmesine basıyorum. İlk oluşturacağım, ListPicker1'e basıldığı zaman BT cihazların listesini göstermesi. Bunun için soldaki bloklardan ListPicker1'i seçip içindeki olay (Event) listesinden TouchDown'a ait bloğu beyaz alana sürüklüyorum. Ardından yine ListPicker1'in olay listesinden "set ListPicker1.Elements to"yu beyaz alana bırakıyorum. Son olarak BluetoothClient1'in listesinden "BluetoothClient1.AddressesAndNames"i koyu yeşil "set ListPicker1..."in soluna yerleştiriyorum. İki parça yanyana eklendikten sonra kahverengi "when ListPicker1.TouchDown"da "do"nun hemen yanına uygun yere yerleştiriyorum. 


AI2'nin arayüzünde ne eklenirse aynı anda telefondaki uygulamaya da yansıdığını yazmıştım. Aynısı kodlar için de geçerli; uygulamayı geliştirirken aynı anda kodlar da telefona gönderiliyor. Önceden Arduino'yu bilgisayarıma bağlayıp kodu aktarmıştım. Telefonumu açıp "Select Bluetooth device" dediğim zaman BT cihazları listeleyecek (tabii ki telefonda BT açık olmalı) fakat listeyi alsak da henüz bağlanma kodu hazır değil. 

Cihaza bağlanmak için ListPicker1.AfterPicking olayını kullanacağım. Bunun içine Built-In -> Control'den "if ... then" bloğunu yerleştiriyorum. Sonra BluetoothClient1 fonksiyonlarından mor .Connectaddress'i if'in yanına ekleyip son olarak ListPicker1.Selection değerini (açık yeşil) de bunun yanına ekliyorum. Connectaddress fonksiyonu çağırılacak ve fonksiyon başarılı olursa then'in yanındaki blok işletilecek. Then'le birlikte herhangi bir handler vs. çağırmayacağım ama görsel ayarlamalar yapmam gerekiyor. 

1- Artık başka aygıta bağlanmayacağıma göre ListPicker1'i görünmez yapmalıyım:
set ListPicker1.Visible to false
2- Disconnect butonunu görünür yapmalıyım:
set Button1.Visible to true
3- Arrangement'ları görünü yapmalıyım:
set HorizontalArrangementTemp.Visible to true
set HorizontalArrangementControls.Visible to true
4- Başta sıcaklığın otomatik okunmaması için:
set CheckBox1.Checked to false

True / False değerleri Built-In -> Logical içinde bulunuyor. Koyu yeşil kutucuklar alt alta eklenebiliyor.


Şimdi telefondaki uygulamada, BT cihaz listesinden HC06'yı (Arduino modülü) seçtikten sonra modülün ışığı yanık kalacak çünkü telefon artık BT modülüne bağlı. Ama Disconnect'e bastığımda henüz bir şey olmuyor çünkü bunu yazmadım. Bağlantı kesme prosedürü, yukarıda yapılanların tam tersi olmalı. BluetoothClient1.Disconnect çağırılacak ve yukarıda görünür yaptıklarımı görünmez, görünmez yaptıklarımı da görünür yapacağım. Yazıyı çok uzatmamak için yalnız ilgili kod bloğunu paylaşıyorum: 


Artık uygulama bağlantıyı da kesiyor. Şimdi sıra, en önemli prosedür olan BT'dan veri okumada. Bunun için ButtonReadTemp'in Click olayını kullanacağım. Veriyi okumadan önce BT'un bağlı olduğunu ve ara bellekte veri olup olmadığını kontrol etmeliyim. Bağlantının kontrolü önemli çünkü bağlandıktan sonra bile cihaz bağlantısı fiziksel olarak kesilmiş olabilir. Bu nedenle ButtonReadTemp.Click olay bloğunun içine bir if ... then bloğu ekliyorum. Koşul olarak Built-In -> Logic'ten bir AND bloğu koyuyorum. Birinci koşul BluetoothClient1.IsConnected. İkinci koşul da ara bellekte okunacak birşeyler olması. Bunun için matematiksel bir karşılaştırma gerekiyor, o da Built-In -> Math içindeki eşittir bloğu. Bunu sürükledikten sonra eşittiri büyüktür yapıyorum. Eşitsizliğin soluna BluetoothClient1.BytesAvailableToReceive ve sağına yine Math içerisinden 0 (sıfır) koyuyorum.

Tüm bu koşullar sağlanıyorsa BT'dan okuduğum veriyi Label2'ye yazacağım. ButtonReadTemp'in son hali şöyle olacak: 


Uygulamaya geri dönüp "Read Temperature" düğmesine bastığımda, uygulama uzun bir süre veri okumadan bağlı kalmışsa, (ara bellekteki) bir sürü sıcaklık değeri ekranı dolduracak. Label2'nin yüksekliğini o nedenle telefon ekranının yarısıyla sınırlandırmıştım. En başta dediğim gibi bu projede hızlıca ortaya birşeyler koymaya çalıştım haliyle sonuç mükemmel olmadı. 

Ara bellek dolduğu için en iyisi veriyi otomatik okumak. Bunun için de CheckBox1.Changed olayına prosedür ekleyeceğim. Eğer CheckBox1.Checked ise Clock1'i aktifleştirip ButtonReadTemp'i pasifleştirmem gerekiyor veya tam tersi. Önceki prosedürlere göre bu oldukça basit. Bunun için bir if ... then ... else bloğu gerekli. Bu hazır olarak Control'de var ama if ... then bloğunun yanındaki mavi dişli simgesine basıp else'i sağdaki if'in içine sürükleyerek de oluşturulabilir.


Son olarak zamanlayıcı aktifleştirildiyse, ButtonReadTemp.Click prosedürünün yaptığını artık zamanlayıcının gerçekleştirmesi gerekiyor: 


Yukarıdaki prosedürü kolay yoldan oluşturmak için Read temp butonu için oluşturduğum "if ... then" bloğuna sağ tıkladım, "Duplicate"i seçip aynısını Clock1.Timer olayının içine yerleştirdim. Hepsi bu kadar. Eğer uygulama tamamen bittiyse bunu telefonuma apk olarak indirebilir ve AI2'ye gerek olmadan da kullanabilirim. Bunun için web arayüzünde, Build menüsünden "provide QR code for .apk"yı tıklamak gerekiyor. Uygulama derlendikten sonra, apk'yı indirmek için bir bağlantı ve bu bağlantıyı içeren bir QR kod ekrana geliyor. Bu adımda eğer AI2 Companion hala bağlıysa, QR kodu taramak için bağlantıyı kesmek gerek (Connect -> Reset Connection menüsüyle veya telefondaki uygulamayı kapatıp açarak). AI2 Companion'u yeniden açıp QR kodu tarattığımda -eğer telefondaki güvenlik ayarları apk kurmaya izin veriyorsa- uygulama gereken izinleri verdikten sonra kuruluyor ve bağımsız olarak kullanılabiliyor.

Oluşturulan uygulama "My Projects" menüsü altında "Export selected project (.aia) to my computer" ile bilgisayara indirilebiliyor. Benim bu yazıda hazırladığım projenin dosyası buradan indirilebilir.