back to top

Arduino ve C# ile Veri Okuma ve Kaydetme – 2. Ders

Herkese merhabalar! Bu yazımızda projelerimizden seri haberleşme ile nasıl veri alabileceğimizi ve bunu bir text dosyası içine nasıl kaydedebileceğimizi göreceğiz. Sonraki derslerde de bu metodu kullanarak farklı uygulamalar geliştirmeye devam edeceğiz. Önceki derste Arduino ve C# ile seri haberleşmeye giriş yapmıştık ve C# ile tasarladığımız arayüzden led kontrol etmiştik. Eğer doğrudan bu yazıya geldiyseniz önceki yazıya göz atmanızı tavsiye ederiz.

Akış Diyagramı

Hatırlayacağınız üzere önceki dersimizde bilgisayarımız üzerinden led kontrol etmiştik, bu sefer bir veri gönderme işlemi değil de okuma işlemi gerçekleştireceğiz. Bu iş için bir LDR kullanacağız. LDR’den gelen değerleri arayüz üzerinden görebileceğiz.

LDR ile Işık Algılama Uygulaması

Oldukça basit bir uygulama yapacağız. LDR’den aldığımız verileri seri porta gönderen basit bir kod kullanacağız. LDR ile alakalı daha detaylı bilgiye buraya tıklayarak ulaşabilirsiniz.

Devremizi şemadaki gibi kurarak işe başlıyoruz. Bu uygulama için gereken malzemeler:

Bağlantıyı aşağıdaki şemaya uygun şekilde yapıyoruz.

LDR Bağlantı Şeması

Kodumuzu yüklüyoruz.

 int ldr = 0;
 void setup() {
   Serial.begin(9600);
 }
 void loop() {
   int deger = analogRead(ldr);  //ldr'den gelen değeri oku
   Serial.println(deger);        //değeri seri port ekranına yaz
   delay(1000);
 }

C# ile Arduino’dan Veri Alma

Bu kısımda kurduğumuz devreden aldığımız verileri arayüz üzerinden anlık zaman bilgisi ile beraber görüntülemeyi göreceğiz. Ardından bu verileri bir text dosyası içine kaydedeceğiz. Visual Studio ile alakalı genel özelliklere ilk yazımızda değinmiştik. Genel bilgilerle alakalı takıldığınız noktalarda önceki yazıdan yardım alabilirsiniz.

C# Arayüz Tasarımı

Kullanacağımız arayüzü tasarlayarak işe başlıyoruz. İlk yazımızdaki arayüzde kullandığımız araçlardan farklı olarak bu uygulamada TextBox, PictureBox ve ProgressBar araçlarını da kullanacağız. Görseldeki düzen için 3 Button, 1 ComboBox, 2 Label, 1 TextBox, 1 ProgressBar, 1 PictureBox ve 1 SerialPort’u araç kutusundan sürükleyerek forma taşıyorum.

Arayüz Tasarımı

Butonları veri almayı başlatma-durdurma ve TextBox içinde biriken veriyi sıfırlamak için kullanacağız. PictureBox’a bir “Kaydet” ikonu ekleyeceğiz ve kaydetme işlemini bu ikona tıklayarak gerçekleştireceğiz. Bu işlem için bir buton da kullanılabilir. ProgressBar üzerinden de güncel LDR verisi ile ilgili bilgi alacağız.

TextBox Multiline Özelliğini Aktif Hale Getirm

TextBox’ı forma sürüklediğinizde tek satır olarak gelir. Biz ardı sıra veriler alacağımız için MultiLine özelliğini aktif etmemiz gerekiyor. Aynı zamanda TextBox üzerindeki ScrollBars özelliğini de aktif ediyoruz.

ScrollBars Aktif Hale Getirme

Öncesinde bilgisayarımıza yüklediğimiz ikonumuzu -kaydet butonu için- PictureBox‘a ekliyoruz ve boyut modunu StretchImage olarak seçiyoruz.

PictureBox Resim Ekleme

ProgressBar için ise maksimum ve minimum değerleri belirlememiz gerekiyor. Maksimum okuyabileceğimiz değer 1023 olduğu için buna uygun şekilde bir düzenleme yapabiliriz.

ProgressBar Özellikler

Tasarım kısmını tamamladıktan sonra kodumuzu yazmaya başlayabiliriz.

C# Proje Kodu

Kod tarafında neler yapacağımızı kısaca görelim.

Kod içinde try-catch yapısını birkaç kez kullanacağız. Bunu basitçe şu şekilde açıklayabiliriz:“try{}” butona tıklandığında gerçekleşmesini istediğimiz kod satırlarını; “catch{}” ise bir hata türü ile karşılaşıldığında gerçekleşmesini istediğimiz komutları barındırıyor.

İşe gelen verileri eşitleyeceğimiz string tipindeki değişkeni tanımlayarak başlıyorum. Bu değişkeni Form1 : Form class’ı içinde tanımlıyorum.

private string data;

Ldr üstünden aldığımız verileri DataReceived eventini(olayını) kullanarak alacağız. Bu event ile seri portu dinleyerek porta veri geldiğinde gelen veriyi alarak data değişkenine eşitleyeceğiz. Form1_Load kısmına bu eventi tanımlayan kodumu ekliyorum. (Tasarım kısmından eklediğimiz SerialPort üstünden de bu eventi ekleyebilirsiniz)

serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPort1_DataReceived);

DataReceived fonksiyonu içinde serialPort üstünden gelen veriyi okuması için “ReadLine()” fonksiyonunu kullanacağım. Gelen veriyi önceden tanımladığım “data” değişkenine eşitliyorum. Sonrasında Invoke() içinde bir event oluştuyorum. Bu fonksiyonun içinde progressBar’daki değeri ve barın hemen üstündeki label1 metnini güncel veriye eşitliyoruz. Aynı şekilde textBox içine de data değişkeni ile beraber güncel tarih ve saati de eklemeyi ihmal etmiyoruz.

Data_Received

Invoke() kullanmadan tüm kodu DataReceived içine yazmış olsaydık “Çapraz iş parçacığı işlemi geçerli değil: …” gibi bir hata ile karşılaşacaktık. Farklı threadlerde işlem yapmaya çalıştığımız için karşımıza çıkacak bu hatayı önlemek adına fonksiyonları Invoke() içinde yazdık.

Baslat_click olayı için yazdığımız kodlara bir göz atalım. try{} içerisine seri port ismini ve iletişim hızını belirleyerek iletişimi başlatan komutlarımı ekliyorum. Bu kısma önceki yazıdan aşinayız. Butona basıldığında tekrar basarak bir hata ile karşılaşmamak adına başlat butonunu pasif; durdur butonunu aktif hale getiriyorum. Hata durumunda hata mesajını kullanıcıya aktaran fonksiyonumu da catch{} içine ekliyorum.

Baslat_Click olayı

Veri alma işlemini durduracak olan buton(Durdur) ve textBox içindeki yazıyı sıfırlayacak olan buton(Sıfırla) için de gerekli kodları ekliyorum.

Durdur_Click olayı

“Kaydet” ikonuna tıkladığımızda textBox1 içindeki tüm veriyi bir text dosyası içine kaydetmesini istiyorum. Bunun için dosyayı kaydedeceğim lokasyonu ve kaydedeceğim dosyanın ismini belirlemem gerekiyor. Bu isimleri string tipindeki 2 değişkene ekliyorum. Ardından WriteAllText() fonksiyonu ile textBox1 içindeki değerleri belirlediğimiz lokasyona kaydediyorum.

Kaydet butonu

Yalnızca Form1_Load ve Form1_FormClosed için yazdığımız kodlardan bahsetmedik. Bu kısımlara önceki yazıda değinmiştik. Tüm kodu aşağıda bulabilirsiniz. Yorum satırlarını da takip ederek kodu detaylı bir şekilde inceleyebilirsiniz.

using System;
using System.Windows.Forms;
using System.IO.Ports;

namespace arduino_ldr_veri_okuma
{
    public partial class Form1 : Form
    {
        private string data;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            textBox1.ReadOnly = true;                    //textBox1'i yalnızca okunabilir şekilde ayarla
            string[] ports = SerialPort.GetPortNames();  //Seri portları diziye ekleme
            foreach (string port in ports)
                comboBox1.Items.Add(port);               //Seri portları comboBox1'e ekleme

            serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPort1_DataReceived); //DataReceived eventini oluşturma
        }

        private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            data = serialPort1.ReadLine();                      //Veriyi al
            this.Invoke(new EventHandler(displayData_event));                           
        }

        private void displayData_event(object sender, EventArgs e)
        {
            progressBar1.Value = Convert.ToInt16(data);                          //Gelen değeri ProgressBar'ın değerine eşitle
            textBox1.Text += DateTime.Now.ToString() + "        " + data + "\n"; //Gelen veriyi textBox içine güncel zaman ile ekle
            label1.Text = "Işık Değeri = " + data;                               //Gelen veriyi label'1 e eşitle
        }


        private void baslat_Click(object sender, EventArgs e)
        {
            try
            {
                serialPort1.PortName = comboBox1.Text;  //ComboBox1'de seçili nesneyi port ismine ata
                serialPort1.BaudRate = 9600;            //BaudRate 9600 olarak ayarla
                serialPort1.Open();                     //Seri portu aç
                durdur.Enabled = true;                  //Durdurma butonunu aktif hale getir
                baslat.Enabled = false;                 //Başlatma butonunu pasif hale getir
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Hata");    //Hata mesajı göster
            }
        }

        private void durdur_Click(object sender, EventArgs e)
        {
            serialPort1.Close();        //Seri Portu kapa
            durdur.Enabled = false;     //Durdurma butonunu pasif hale getir
            baslat.Enabled = true;      //Başlatma butonunu aktif hale getir
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (serialPort1.IsOpen) serialPort1.Close();    //Seri port açıksa kapat
        }


        private void sifirla_Click(object sender, EventArgs e)
        {
            textBox1.ResetText();           //textBox1'i sıfırla
        }

        private void kaydet_Click(object sender, EventArgs e)
        {
            try
            { 
                string filelocation = @"C:\Users\alper\Desktop\dosya\";                                   //Dosyanın kaydedileceği konumu belirliyoruz
                string filename = "data.txt";                                                               //Kaydedilecek dosyanın ismi
                System.IO.File.WriteAllText(filelocation + filename, "Zaman\t\t\tDeğer\n" + textBox1.Text); //Dosya konumuna textBox1 üstündeki verilerden oluşan text dosyamızı kaydediyoruz
                MessageBox.Show("Dosya başarıyla kaydedildi", "Mesaj");                                     //Dosya kaydedildiğinde kullanıcıya mesaj gönder
            }
            catch (Exception ex2)
            {
                MessageBox.Show(ex2.Message, "Hata");       //Hata mesajı
            }
        }
    }
}

Böylece serinin 2. yazısının da sonuna gelmiş olduk. Sonraki yazılarda da Arduino projeleri için arayüzler geliştirmeye devam edeceğiz. 3. derse kadar pratik yapmanızı ve öğrendiklerimizi kullanarak arayüzler geliştirmenizi tavsiye ederim. Yazı hakkındaki yorumlarınızı bekliyorum, bir sonraki projede görüşmek dileğiyle, hoşça kalın! 🙂

*** 22.06.2019’da güncellenmiştir.

Son Çıkan Yazılar

9 YORUMLAR

    • Timer için tanımladığınız interval ile Arduino’daki delay sürelerinin eşit olduğundan emin olun. Timer ile veri alımında hata ve donma gibi problemlerle karşılaşılma olasılığı daha fazla oluyor. Bu yüzden bu yazıda güncelleme yaparak veri alma işlemini DataReceived ile gerçekleştirilecek şekilde güncelledim. Timer ile problem yaşamaya devam ederseniz bu yöntemi kullanın.Veriyi bu şekilde almak çok daha sağlıklı olacaktır. Yine bir hata ile karşılaşırsanız yazabilirsiniz.

  1. merhabalar,
    ben arduino ilk çalıştığında textbox içinde değerleri düzgün şekilde görebiliyorum. Fakat formu kapatıp tekrar açtığımda “246\r80\r”, “267269\r” v.b. değerler geliyor doğal olarak int16 ya convert edemiyor.

    arduinoyu kapatıp açarsam düzgün bir şekilde çalışıyor.Sorun nerden kaynaklanıyor olabilir acaba?

  2. merhabalar,

    kodda sürekli “Pencere işleyicisi oluşturuluncaya kadar denetim üzerinde Invoke veya BeginInvoke çağrılamaz.”
    hatası alıyorum, yardımcı olabilir misiniz?

  3. merhaba her şeyi eksiksiz uygulasım visuo studio hata vermiyor seriporttan veride okunabiliyor ama verileri textboxta ve txt uzantılı dosyada gözlemleyemedim.

  4. Aynı sorun bende de var. Türkçesi şu hiçbir BerialPort1_DataReceived yeniden yüklemesi SerialDataReceivedEventHandler temsilcisiyle eşleşmiyor.

CEVAP VER

Lütfen yorumunuzu giriniz!
Lütfen isminizi buraya giriniz