Linux Kernel ile Raspberry Pi 3'den TCP Protolü Ile Sensör Verileri Alınması

     
     Bu yazımızda Raspberry Pi 3 ile bağlanan DHT11 Sıcaklık Nem sensörü ve Water Sensor 'den alınan verileri TCP protoölü ile kernel modda loga yazılmasından bahsedeçeğiz.

     TCP/IP Nedir ?
     (Transmission Control Protocol/Internet Protocol), bilgisayarlar ile veri iletme/alma birimleri arasında organizasyonu sağlayan, böylece bir yerden diğerine veri iletişimini olanaklı kılan pek çok veri iletişim protokolüne verilen genel addır. (Yani, TCP/IP protokolleri bilgisayarlar arası veri iletişiminin kurallarını koyar). Bu protokollere örnek olarak, dosya alma/gönderme protokolü FTP (File Transfer Protocol), Elektronik posta iletişim protokolü SMTP (Simple Mail Transfer Protocol), TELNET protokolü(Internet üzerindeki başka bir bilgisayarda etkileşimli çalışma için geliştirilen *login* protokolü) verilebilir. Adını sıkça duyduğumuz WWW ortamında birbirine link objelerin iletilmesini sağlayan protokol ise Hyper Text Transfer Protocol (HTTP) olarak adlandırılmaktadır. TCP/IP protokolü aynı zamanda, diğer iletişim ağlarında da kullanılabilir. Özellikle pek çok farklı tipte bilgisayarı veya iş istasyonlarını birbirine bağlayan yerel ağlarda (LAN)kullanımı yaygındır.
     
     Raspberry Pi 3 Nedir ?
     Raspberry Pi kredi kartı boyutunda bir bilgisayardır. Televizyonunuza bağlayıp görüntü alabilir, bir klavye bağlayabilirsiniz. Yetenekli küçük bir bilgisayar diye adlandırdığımız Raspberry Pi ile normal masaüstü bilgisayarlarda yaptığınız işleri örneğin, sözcük işlemciler ve hesap programları (Word, Excel) ile çalışabilir çeşitli oyunlar oynayabilirsiniz. Ayrıca yüksek çözünürlüklü HD videolar oynatabilirsiniz. Ayrıca tüm dünyada çocukların alıp kullanabileceği, basit programlama yapabilecekleri hatta deneylerinde kullanabileceği uygun fiyatlı bir bilgisayar gibi düşünebilirsiniz.

     Gerekli donanımlar ve yazılımlar :
     Raspberry Pi 3 işletim sistemi kurulması
     İndirdiğimiz imaj dosyasını zip içerisinden çıkarıyoruz. Ardından win32diskImager( https://www.gezginler.net/indir/win32-disk-imager.html ) programını açıyoruz. İmaj dosyamızı belirtilen yerden seçiyoruz. Sd kartınızın bilgisayara takılı olduğundan emin olduktan sonra Devic ekısmında görebilirsiniz. Ardından Write butonuna tıklayıp yazma işlemini başlatıyoruz. Yazma işlemi yaklaşık 2-3 dk sürmektedir. Yazma işleminin bitmesini yeni açılan pencerede "Write Succesful." yazısını görene kadar bekleyiniz.
     SD Kartı Formatlamak için Disk Formatter programını kullanıyoruz.

     İşletim Sistemi kurulan Raspberry Pi ile gerekli bağlantıları yapıyoruz.

     Şimdi sıra bir Raspberry Pi’ye hayat vermeye geldi. Önce SD Kartı bilgisayardan çıkarıp Pi 3’e takıyoruz. Raspberry Pi’yi direk olarak HDMI bir ekrana bağlıyoruz. Elimizde HDMI girişi olan bir ekran yoksa VGA girişli bir ekran da kullanabiliriz. Bu durumda HDMI to VGA dönüştürücü kullanmamız gerekecek. Son olarak adaptör ile cihazımıza enerji veriyoruz. İlk olarak sizi şu şekilde bir ekran karşılayacak;


     VNC SERVER ile Bağlantı kurmak (isteğe bağlı)

Raspberry Pi üzerindeki soketleri kullanmadan bilgisayarımızdaki klavye, mouse gibi aygıtları kullanmak için VNC Viewer Kullanabiliriz. RealVNC'in VNC Viewer programını indirip bilgisayarınıza kurun ( https://www.realvnc.com/en/connect/download/viewer/ ) ve RPI konsoluna şu komutları sırayla yazın ; 

  • sudo apt-get update 
  • sudo apt-get upgrade
  • sudo apt-get install tightvncserver 

VNC Serverımız Raspberry Pi üzerinde kuruldu. Artık tek bir komutla VNC Server'ı başlatabiliriz. 
  • vncserver :1 
Bilgisayarımıza indirdiğimiz VNC Viewer programını başlatalım ve sol üst köşedeki File sekmesinden yani bir bağlantı oluşturalım. Açılan pencerede VNC Server yazan yere Angry Ip Scanner'dan öğrendiğimiz özel ip adresini (192.168.137.xxx) girelim ayrıntı unutulmamalıdır, ip adresinin sonuna portumuzu (1) eklemeliyiz. Yani VNC Server kısmına girilecek adres şuna benzemelidir ; 
  • 192.168.137.12:1 
Bağlantıyı başlatınca Raspberry Pi’nin masaüstü ekranınıza ulaşacaksınız. Şimdi projeye devam edersek, konsol ekranına ulaştığınızda şu komutları girerek RPI'nin güncel olmasını sağlayalım ;
  • sudo apt-get update 
  • sudo apt-get upgrade 
Ardında programlamamızı C dilinde yapacağımız için C derleyicisi (gcc) gerekli bunun için; 
  • sudo apt-get install gcc 
Ve sensörü bağlamak için wiringPi yazılımı gerekli. 
  • sudo apt-get install 
  • git-core cd git clone git ://git.drogon.net/wiringPi 
Ardından sensörü Raspberry Pi'ye bağlayıp programı yazmaya başlayabiliriz.

     Sensorlerin Kurulması

Sensörler üzerinde bulunan;
  • + pini : 3v/5v Güç
  • - pini : Topraklama
  • D/S pini : Data Pinidir
DHT11 Bağlantı Pini (7. pin soketine bağlandı)
Water Sensor Pini (7. pin soketine bağlandı)

     Raspberry Pi 3 Gerekli C kodlarının yazılması

Eğer ki terminal üzerinden ;
  • vim / vi shell komutları ile
yazmak istemiyorsanız shell üzerinden gedit text editörünü tavsiye ederiz.

  • sudo apt-get install gedit

 Water Sensor İçin Raspberry Pi kodları (source.c)(TCP Server):
//Rasp. Pi kütüphanesi
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//TCP Kütüphaneleri
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h> 
//Bağlanılan Pin numarası
#define PIN_NO             7
int main( void )
{
    //TCP bağlantı kodları
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        puts("Could not create socket");
         exit(0);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    //TCP port numarası
    server.sin_port = htons( 8888 );
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
        puts("bind failed. Error");
         exit(0);
    }    
    listen(socket_desc , 3);
    c = sizeof(struct sockaddr_in);    
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
        puts("accept failed");
        exit(0);
    }
         if ( wiringPiSetup() == -1 )
         {
                 exit( 1 );
         }
         //Sensor kurulumu için 
         pinMode( PIN_NO, OUTPUT );
         digitalWrite( PIN_NO, LOW );
         digitalWrite( PIN_NO, HIGH );
         pinMode( PIN_NO, INPUT );
         delay( 900 );
         //Sensorden gelen verileri kontrol etmek için
         while ( 1 )
         {
                 if(digitalRead( PIN_NO ) == HIGH )
                 {
                          printf("Su Alarm : Aktif\n");
                          char *msg="Su Alarm : Aktif";                          
                          write(client_sock , msg , strlen(msg));    
                 }
                 else
                 {
                          printf("Su Alarm : Pasif\n");
                          char *msg="Su Alarm : Pasif";
                          write(client_sock , msg , strlen(msg)); 
                 }
                 delay( 2000 );
         }        
         return(0);
}
 DHT11 Sıcaklık Nem İçin Raspberry Pi kodları (source.c)(TCP Server):
//Rasp. Pi kütüphanesi
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//TCP Kütüphaneleri
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h> 
 
//Bağlanılan Pin numarası
#define PIN_NO             7
int main( void )
{
    //TCP bağlantı kodları
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        puts("Could not create socket");
         exit(0);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    //TCP port numarası
    server.sin_port = htons( 8888 );
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
 
        puts("bind failed. Error");
         exit(0);
    }    
    listen(socket_desc , 3);
    c = sizeof(struct sockaddr_in);    
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
        puts("accept failed");
        exit(0);
    }
    //Sensor'den verilerin okunması
    while(1){
    uint8_t laststate = HIGH;
 uint8_t counter  = 0;
 uint8_t j  = 0, i;
 float f; /* fahrenheit */

 dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;
 pinMode( DHTPIN, OUTPUT );
 digitalWrite( DHTPIN, LOW );
 delay( 18 );
 digitalWrite( DHTPIN, HIGH );
 delayMicroseconds( 40 );
 pinMode( DHTPIN, INPUT );

 for ( i = 0; i < MAXTIMINGS; i++ )
 {
  counter = 0;
  while ( digitalRead( DHTPIN ) == laststate )
  {
   counter++;
   delayMicroseconds( 1 );
   if ( counter == 255 )
   {
    break;
   }
  }
  laststate = digitalRead( DHTPIN );

  if ( counter == 255 )
   break;

  
  if ( (i >= 4) && (i % 2 == 0) )
  {
   dht11_dat[j / 8] <<= 1;
   if ( counter > 16 )
    dht11_dat[j / 8] |= 1;
   j++;
  }
 }


 if ( (j >= 40) && (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
 {
  char msg[100];
  sprintf(msg,"Sıcaklık = %d.%d *C | Nem = %d.%d  ",dht11_dat[2], dht11_dat[3], dht11_dat[0], dht11_dat[1]);
  write(client_sock , msg , strlen(msg));
 }
 else
 {
  char *msg="Veriler hatalı..";
  write(client_sock , msg , strlen(msg)); 
 }
 delay(1000);
 }
}
Derlemek için gerekli kütüphanlerin indiirlmesi
  • sudo apt-get update
  • sudo apt-get upgrade
  • sudo apt-get install git-core
  • git clone  git ://git.drogon.net/wiringPi
  • sudo apt-get install gcc
C Kodunun derlenmesi ve Çalıştırıması

  • gcc -o serverTCP source.c -lwiringPi -lwiringPiDev
  • sudo ./serverTCP 
     
      Ethernet Kablosu ile bağlanılan bilgisayar için(TCP Client)

Öncellikle bu yazımızda bahsetiğimiz kernel kurulumunu yapıyoruz ve daha sonra bu yazımızda bahsedinilen kernel modül programlama adımlarını yapıp chachter device yapısını aşağıdaki kodu yazıyoruz


#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/init.h>

//TCP Socket Kütüphaneleri

#include <linux/net.h>

#include <net/sock.h>

#include <linux/tcp.h>

#include <linux/in.h>

#include <linux/socket.h>

#include <linux/slab.h>



MODULE_LICENSE("GPL");

MODULE_AUTHOR("NativeCore");


//port numarası
#define PORT 8888  

#define DEVICE_NAME "NativeCore"

#define CLASS_NAME "CoreClass"

#define MAX_SIZE 1024

static int    majorNumber; 

static struct class*  coreClass  = NULL;

static struct device* coreDevice = NULL;

struct socket *conn_socket = NULL;

static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset);

static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset);

u32 CreateIP(u8 *ip);

int ClientReceive(struct socket *sock, char *str,unsigned long flags);

int ClientConnect(void);

static char DeviceBuffer[MAX_SIZE];

static int BufferSize;

static struct file_operations fo =

{

 .owner = THIS_MODULE,

 .read = DeviceRead,

 .write = DeviceWrite

};

static int onStart(void)

{

        printk("NativeCore | Communucation to Raspberry Pi Water & Tempature Sensor  \n");

 majorNumber = register_chrdev(0, DEVICE_NAME, &fo);

 if (majorNumber<0)

 {

  printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber);

  return majorNumber;

 }

 coreClass = class_create(THIS_MODULE, CLASS_NAME);

 if (IS_ERR(coreClass))

 {

  unregister_chrdev(majorNumber, DEVICE_NAME);

  printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber);

  return PTR_ERR(coreClass);

 }

 coreDevice = device_create(coreClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);

 if (IS_ERR(coreDevice)){

  class_destroy(coreClass);

  unregister_chrdev(majorNumber, DEVICE_NAME);

  printk(KERN_ALERT "Device oluşturulmadı\n");

  return PTR_ERR(coreClass);

 }

 printk(KERN_ALERT"Device oluşturuldu %d : %s.\n",majorNumber,DEVICE_NAME);

 return 0;

}

static void onExit(void){

 device_destroy(coreClass, MKDEV(majorNumber, 0));

 class_unregister(coreClass);

 class_destroy(coreClass);

 unregister_chrdev(majorNumber, DEVICE_NAME);

 printk(KERN_ALERT "Device silindi %d : %s.\n",majorNumber,DEVICE_NAME);

        DECLARE_WAIT_QUEUE_HEAD(exit_wait);

        if(conn_socket != NULL)

        {

                sock_release(conn_socket);

        }

        printk("Connection Canceled. \n");



}

static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset)

{

 ssize_t sst = 1;

 return simple_read_from_buffer(buffer,len,offset,DeviceBuffer,sst);

}

static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset){

 printk(KERN_ALERT "DeviceWrite : çalıştırıldı.\n");

 BufferSize = len;

 if (BufferSize > MAX_SIZE) 

 {

  BufferSize = MAX_SIZE;

 }

 if (copy_from_user(DeviceBuffer, buffer, BufferSize)) 

 {

  printk(KERN_ALERT "DeviceWrite : Device dosyası kopyalanmadı\n");

  return -EFAULT;

 }

 ClientConnect();

 return BufferSize;

}

u32 CreateIP(u8 *ip)

{

 u32 addr = 0;

 int i;

        for(i=0; i<4; i++)

        {

  addr += ip[i];

                if(i==3)

  {

  break;

  }

                addr <<= 8;

        }

        return addr;

}

int ClientReceive(struct socket *sock, char *str,\

                        unsigned long flags)

{

        struct msghdr msg;

        struct kvec vec;

        int len;

        int max_size = 50;

        msg.msg_name    = 0;

        msg.msg_namelen = 0;

        msg.msg_control = NULL;

        msg.msg_controllen = 0;

        msg.msg_flags   = flags;

        vec.iov_len = max_size;

        vec.iov_base = str;

read_again:

        len = kernel_recvmsg(sock, &msg, &vec, max_size, max_size, flags);



        if(len == -EAGAIN || len == -ERESTARTSYS)

        {

                printk("Error %d : A Problem occured while receive data from TCP/IP\n", len);

                goto read_again;

        }

 printk("%s\n",str);

        return len;

}



int ClientConnect(void)

{

        struct sockaddr_in saddr;
        //Ethernet kablosu ile bağlantı kurulan IP adresi
        unsigned char destip[5] = {192,168,0,21,'\0'};

        int len = 49;

        char response[len+1];

        int ret = -1;

        DECLARE_WAIT_QUEUE_HEAD(recv_wait);       

        ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &conn_socket);

        if(ret < 0)

        {

                printk("Error %d : Server or port does not connection\n", ret);

                goto err;

        }



        memset(&saddr, 0, sizeof(saddr));

        saddr.sin_family = AF_INET;

        saddr.sin_port = htons(PORT);

        saddr.sin_addr.s_addr = htonl(CreateIP(destip));



        ret = conn_socket->ops->connect(conn_socket, (struct sockaddr *)&saddr\

                        , sizeof(saddr), O_RDWR);

        

 if(ret && (ret != -EINPROGRESS))

        {

                printk("Error: %d : setup_connection \n", ret);

                goto err;

        }

read_again:

        wait_event_timeout(recv_wait,!skb_queue_empty(&conn_socket->sk->sk_receive_queue),2*HZ);

                  

 if(!skb_queue_empty(&conn_socket->sk->sk_receive_queue))

        {

          memset(&response, 0, len+1);

         ClientReceive(conn_socket, response, MSG_DONTWAIT);

  goto read_again;

        }

err:

        return -1;

}

module_init(onStart);

module_exit(onExit);
C Kodunun derlenmesi ve Çalıştırıması

  • gcc -o clientTCP source.c 
  • sudo ./clientTCP 
      Sensorlerin ve Modülün Çalıştırılması

Kodlar yazıldıktan sonra Server kurulumu olan Raspberry Pi ile kodlarımızı çalıştırmaya başlıyoruz ve Serverimizi başlatıyoruz.

  • sudo ./serverTCP
Water Sensor İçin Raspberry PI (Server)

Server başladıktan sonra ağa bağlanan bilgisayar ile TCP üzerinden kernel modda verilerimizi alıyoruz.

  • insmod source.ko
  • echo "GET">/dev/NativeCore


Water Sensor İçin Ağa Bağlanan Bilgisayar (Client)

DHT11 İçin Ağa Bağlanan Bilgisayar (Client)

Proje Mimarisi ve Genel Adımları :
  • Raspberry Pi’ye İşletim Sistemi Yükleme
  • Sensörleri Gerekli GPIO Pinine Bağlamak
  • Eğer Harici Ekipmanlar Kullanmak İstemiyosak PUTTY Ve VNCServer’i İle Raspberry Pi’yi Bilgisayara Bağlamak
  • Raspberry Pi Üzerine Öncelikle Seçtiğimiz Sensörden Verileri Okuyacak C Kodunu Yazmak 
  • Ek Olarak TCP/IP Protokoli İle Bu Okunan Değerleri Ağa Bağlı Olan Diğer Bilgisayarlara Mesaj Göndermek
  • Raspberry Pi Üzerinden Kodumuzu Derlemek Ve Çalıştırmak
  • Ağdaki Diğer Bilgisayardan Kaynak Kod İle Kurduğumuz Kernel Üzerine Bir Modül Oluşturmak 
  • Bu Modüle Gerekli Character Device Kodları Yazma 
  • Ek Olarak Kernel Mod İçin Socket Data Send Receive İçin C Kodlarını Fonksiyon Olarak Yazma 
  • Ağ Üzerinde Bağlı Olan Raspberry Pi’nin Ağ İp’sini Bulma 
  • TCP Protokolü İçin IP Ve Port Değerlerini Yazma 
  • Make Komutu İle Modülümüzü Derlemek Ve Character Device’yi Oluşturmak 
  • ins Mod xxx  Modülümü Devreye Sokma
  • Raspberry Pi Üzerinden sudo ./xxx ile Raspberry Pi Üzerinden Kodumuzu Çalıştırmak 
  • echo ’get’>/dev/nativecore ile Character Device Right Methodunu Etkinleştirmek
  • Character Device Dosyasına Yazılan Raspberry Pi Üzerinden TCP Protokolü İle Alının Verileri Kernel Satırına Yazdırmak 
  • dmesg İle Verileri Görmek RMMOD xxx İle Modülden Çıkış Yapmak 



TCP/IP protokolü Raspberry Pi sensöründen alınan verileri Kernel Modda Kernel loga yazmayı anlattık Bu yazımızın videolu sunumu ;






Yorumlar

Bu blogdaki popüler yayınlar

Web Servis ve Kimlik Doğrulama (Authentication) Yöntemleri

Yazılım Kalite Metrikleri