IOT kartlarında multiprocessing ‘in yan etkileri

Kategori: Arduino, IOT, OrangePI, RaspberryPI
Tarih: 10th Haziran 2018

Merhaba! eğer hala bu bloğu takip eden bir okuyucu var ise ona; değilse, dağda bayırda ekolanmış ve kimsenin çekiç, örs yada üzengi kemiğine dokunamadan sıfıra yakınsamış, gitmiş bir nara gibi.

 

Bu yazıda; sosyal/toplumsal neredeyse hiçbir konuya dokunmadan; 24 hazirana çok yaklaşmış olmamız hasebiyle, şu aralar canını çıkartırcasına üzerine çullanılan siyaset konu başlığını hiç rahatsız etmeden, şuan uyur isem muhtemelen uyanamayıp üzüleceğim, sahur yemeğine kadar, yaklaşık bir buçuk saat teknik bir konu üzerine yazıp; defolup gideceğim. Her ne kadar, uzun süredir sosyal ve manevi konularda, geçmiş ve geleceğe dair içimde uzun uzun serzenme isteği olsa da. Ah ah nerede o eski sahur tebrikleri. “Vay anasını! Bu tebriği paylaşalı bile 3 sene olmuş” demekten kendini alamıyor insan. Ki o zamanlar da “Vay anasını 8 sene olmuş Kütahya ‘ya geleli, neredeyse askere gidiyorum” diye geçen zamanın hızına şahitlik ettiğimi hatırlıyorum. Ahir zamanda, zamanın bereketsizliğine her an şahidiz demek ki. Sarı ışıkla aydınlanan, anti-hijyenik ama samimi; izmarit dağlarının kokusunda sabahlara kadar muhabbet çevrilen; zaman zaman konusu, zaman zaman kendisi yüksek rakımlı; -dolu dizgin- öğrenci günleriydi. Çok sevgili Ali amcanın da dediği gibi; öğrenci pisikolojisinden çıkamamış olmamdan mütevellit…

 

Geçen hafta içerisinde, geliştirmiş olduğumuz bir cihazda buzzer ile birkaç uyarı tonu çalma ihtiyacı hasıl oldu. Önce bu ihtiyaç doğrultusunda internette yazılmış mesajlara ve örnek kodlara göz attık. Devreye bağlantı şekli -bir led gibi- gayet basit. Birçoğu mantık olarak otursa da pratikte tam istediğimizi alamadık. Tekrar tekrar test ettik. Hatta acaba bizim kartlarımızda işlemcilerimiz de mi bir sorun var diye düşünüp; bu duruma biraz kafa patlattıktan sonra fark ettik ki bu; pratik ve teori uyuşmazlığının sebebi gözden kaçırılmış olan multiprocessing meseles imiş. Hemen herkesin bu konuda şu cümle kulağındadır “Bir bilgisayar, aslında bir anda sadece bir işlem yapabilir. Ama bilgisayarlar o kadar hızlıdır ki, işlemleri arkası arkasına o kadar hızlı çalıştırır ki: biz, müzik çalma, internette gezme ve dosya indirme… hepsi aynı anda oluyor gibi görürüz.” Bu cümlede bahsedilen iş tam olarak budur. Çok işlem yapabilme kabiliyeti.

 

Arduino, NodeMCU gibi üzerinde bir işletim sistemi çalışmayan kartlar multiprocessing değildir (özel olarak bir framework yazılır ise yapılabilir). Ama RaspberryPi, OrangePi gibi daha hızlı ve üzerinde işletim sistemi koşturduğumuz kartlar aynı anda çok işlem çalıştırabilir(Bu aslında işletim sisteminin maharetidir). Şimdilik burada kalalım.

 

Önce internete önerilen şekilde geliştirdiğimiz kodu paylaşmak istiyorum.

 


import time

from pyA20.gpio import gpio

from pyA20.gpio import port

buzzer = port.PA1

gpio.init()

gpio.setcfg(buzzer, gpio.OUTPUT)

def ses_cal(f, s):

st = int((f/2) * s)

for x in range(0, st):

time.sleep(1.0 / f)

gpio.output(buzzer, 1)

time.sleep(1.0 / f)

gpio.output(buzzer, 0)

ses_cal(400, 0.5)

 

Bu kod parçacığını incelersek görürüz ki 400Hz frekansında 0.5 sn bir ton çalınmak isteniyor. Yani yarım saniye boyunca Do notası çalmak planlanmış. Multiprocessing olmayan bir sitemde düşünülür ise bir problem yok. İşlemciler çok hızlı. Buzzera tam olması gerektiği zamanlarda 1 yada 0 lar gönderilir. Peki ama multiprocessing için ne değişiyor? Onun için işin matematiğini inceleyelim.

 

400 Hz bir ton çalmak için buzzera bir saniyede 400 adet 1 ve 0 sıfır sinyali göndermek gerekir. Bu da her 1 ve 0 sinyalinin arasında 0.00125 sn olacak demek olur. En düşük hızdaki işlemciler için bile bu yakalanabilir bir aralıktır. Buzzera tam 0.00125 sn de gerekli sinyalleri gönderebilir. Eğer tek işi sadece bu ise.

 

İşte çadır tam bu noktada karışıyor. Multiprocessing bir sistemde işlemci buzzera bir sinyal gönderiyor sonra biraz da diğer işlemleri yapıyım diyor geri döndüğünde bir bakıyor eyvah 0.002 sn geçmiş. Bu da tam sizin istediğiniz tonu üretmenizi engelliyor. Daha detaya inmek gerekirse; time.sleep fonksiyonu iki sinyal arasında tam sizin istediğiniz kadar süre bırakmaz. O satır sadece “burada 0.00125 sn bekle” demektir. Sizin multiprocessing den dolayı biraz o satırdan önce biraz da sonra kaybınız olabilir. Olmayadabilir. Hatta yeteri kadar hızlı olan işlemcilerimiz time.sleep esnasında bile birkaç sefer sizin işleminize gelmiş ve biraz daha zaman var gidiyim başka işlemleri yapıp geliyim demiş olabilir. Olmayadabilir.

 

Çözüm de işte burada çıkıyor. Buzzer sizin multiprocessing den, sleep ‘den falan anlamaz. Bana hangi aralıklarla sinyal gönderirsen ben ona göre bi ton çalarım der. Siz işi time.sleep e bırakmamalı kontrollerinizi kendiniz yapmalısınız. Datetime kütüphanenizin microsecond özelliği var iki sinyal arasındaki süreyi kendiniz hesaplayıp bir sonsuz döngü icerisinde kalıp yeterli süre geçtiği anda döngüden çıkıp yeni sinyali göndermelisiniz. Bu söylediğim esaslara göre yazılmış olan kod bloğu da aşağıdadır.

 


import time

import datetime

from pyA20.gpio import gpio

from pyA20.gpio import port

buzzer = port.PA1

gpio.init()

gpio.setcfg(buzzer, gpio.OUTPUT)

def ses_cal(f, s):

st = int(f * s)

za = int((1.0 / f) * 1000000)

t = datetime.datetime.now().microsecond

s = False

for x in range(0, st):

temp = datetime.datetime.now().microsecond - t

if temp < 0:

temp += 1000000

while temp < za:

i = 0

temp = datetime.datetime.now().microsecond - t

if temp < 0:

temp += 1000000

gpio.output(buzzer, s)

s = not s

t = datetime.datetime.now().microsecond

ses_cal(400, 1)

 

Bu kod bile; kartı eğer yoğun bir işlem yükünde kullanıyorsanız tam istediğiniz sonucu veremeyebilir. Bunun için de önerim: buzzer ile kartımız arasına arduino micro gibi düşük maliyetli bir kart daha takılıp buzzer o kart ile sürülebilir.

Gömülü sistemler ve hafıza kısıtı

Kategori: Arduino, Genel Programlama
Tarih: 12th Şubat 2018

Merhaba uzun süredir bir sürü konuda yazmak istememe rağmen yoğunluktan bir fırsat bulup da yazamadım. Gerçi “youtuber” lık bu kadar popülerken bu yazıyı okuyan olacak mı? O da muamma. Ama ben sanırım bir süre daha buradan sosyal ve teknik konularda yazmaya devam edeceğim. Hatta sonraki senelerde ilgili bir stajer arkadaş gelir ise onlarca projeyi/çözümü yazıya döktürmek gibi bir planım var.

 

Neyse; her eve bilgisayar her eve internet falan derken olay döndü dolaştı her şeye internete kadar geldi. Özetle IOT demek bu demek. Kombilerin, kumandaların, termometrelerin bile interneti var. Bizim herşeyden anında haberdar olma isteğimizle birlikte bu gömülü sistemler olayı da yürüdü gitti. Önceleri akıllı cihazlar geliştirmek bu kadar kolay değildi. Hatta cihazı internete bağlamak hayal bile değildi; yoktu! Sizin internete bağlı bir şeyiniz var ise bunun adı bilgisayardı. Bir şeyi internete bağlama fikri kimsenin aklına gelmemişti. Belkide ihtiyaç da değildi. Sonra teknolojinin x küp eğrisi şeklinde gelişiyor olması kısa zamanda mikroişlemcileri hızlandırdı, iletişim standartlarını geliştirdi, Çin ‘in de sahaya inmesi ile donanım ucuzladı ve biz nihayet lehim makinesinden bile internete bağlanabiliyor hale geldik.  İşin trajikomik tarafı, bu son verilen örnek tamamen gerçektir.

 

Artık neredeyse sadece yazılım bilerek bile birşeyler tasarlayabiliyorsunuz. 16F877 de ki iskele vs. … mantığını kavramak zorunda olduğunuz yapılar, günlerin ayların sınıflarına karıştı gitti. Her şeyin kartı çıktı. Çarli ‘nin melekleri! Yani I2C, SPI ve Seri iletişim ne demek öğrenin kullanamayacağınız teknoloji neredeyse kalmadı. Üzerinde işletim sistemi bile çalıştırabileceğiniz kartlar çıktı.

 

Sonuç olarak sıkıntılı kısma gelirsek. Daha doğrusu benim bu yazıda deyinmek istediğim sıkıntıya: hala bazı kartlarda hafıza kısıtınız var. Özellikle arduino nano, mikro gibi çok çok küçük boyutlu kartlarda. (Yazar aslında burada bu kartın kullanmış olduğu işlemcinin hafızasından bahsetmektedir.)

 

Özet geçmek gerekir ise arduino, temel olarak 2 program hafızasına sahiptir. Birincisi ve büyük olan, program kodlarının yüklendiği kısım. Yazdığınız kodlar makine koduna dönüştürülür ve buraya yüklenir. Programın çalışması demek de bu kodların satır satır, işlemcinin ALU devresine gönderilmesi/sonucun yorumlanması demektir. İkinci hafıza ise Arduino IDE ‘nin de -derleme bilgi ekranında- bahsettiği -kabaca- global değişkenlerin tutulduğu hafızadır. Bu çok daha küçüktür. ATmega328P işlemcisi için -ki arduino nano bunu kullanır- bu hafızalar 32KB ve 2KB dır. Yine bu işlemci üzerinden konuşmaya devam edersek; aynı anda Ethernet kartı ve Sd kart kullanarak bir geliştirme yapmak istiyorsanız muhtemelen size bu 2KB SRAM yetmeyecektir.

 

İşte tam da bu noktada, yani “acaba mega ile mi geliştirmeye devam etsem, o da çok büyük be bilader” dediğiniz; umutsuzluğun kapısında. Yürümekten çok, geri dönmenin sıcak geldiği bu anda; bu yazının hedef kitlesi olmuş durumdasınız. Bence aşağıdaki kodu inceleyin. Dizileri ve değişkenleri SRAM den kullanmadan tanımlayabilirsiniz. Umarım işinize yarar.

 

const char me0[] PROGMEM = "me0";
const char me1[] PROGMEM = "me1";
const char* const mDizi[] PROGMEM = {me0, me1};

const char me[] PROGMEM = "me";

const PROGMEM uint16_t sDizi[] = {1, 2};

const PROGMEM uint16_t se = 3;

char bufferStr[30];
int bufferInt;

void bufferStrDoldur(int a)
{
    strcpy_P(bufferStr, (char*)pgm_read_word(a));
}

void bufferIntDoldur(int a)
{
    bufferInt = pgm_read_word_near(a);
}

void setup()
{
    Serial.begin(9600);
    Serial.println(F("Basliyor..."));

    bufferStrDoldur(&mDizi[1]);
    Serial.println(bufferStr);

    strcpy_P(bufferStr, me);
    Serial.println(bufferStr);

    bufferIntDoldur(&sDizi[1]);
    Serial.println(bufferInt);

    bufferIntDoldur(&se);
    Serial.println(bufferInt);
}

void loop() { }

 

Not: bu yazıda paylaşılan kodlar ve çözümleme taa m.ö. yapılmış ancak bir türlü oturulup da bir yazı haline getirilememiştir. Bir başka deyişle; bugün IOT konusunda burun farkıyla önde görülen NetCAD, daha Netigma 2.0 ‘ı baştan mı yazsak yoksa 1. versiyonun üzerine mi geliştirsek diye tartışırken; biz IOT argeleri yapıyor, bu kodu projelerimizde kullanıyorduk. Saygılarımla…

Sıcaklık/Nem Kontrol ve Uyarı Sistemi – 2

Kategori: Arduino, C#, Güvenlik, Referanslar
Tarih: 13th Mart 2015

Bu yazıda artık ortamın sıcaklık ve nem değerlerini ölçüp bilgisayarımıza aktaracak olan devreyi tasarlamaya geçebiliriz. Burada devreyi bağımsız iki parça halinde ele alabiliriz. Birinci parça sıcaklık sensöründen verinin okunması. İkinci parça okunan değerin 7 segmentli göstergede gösterilmesi.

Önce sıcaklık sensörü kısmını halledelim. Biz burada DHT11 sıcaklık ve nem sensörünü kullanacağız. İsteyen buradan sensörü detaylı inceleyebilir. Sensörü devremize aşağıdaki gibi bağlıyoruz.

dht11_arduio_baglanti_semasi

 

Şimdi ikinci kısma geçelim. Bu kısım biraz daha karışık. Önce 7 segmentli göstergeden başlayalım. Aşağıda elemanın yapısı eklenmiştir.

 

ortak-katot-display-baglanti-semasi

 

İncelenince görülecektir ki 7 segmentli gösterge için 1 gönderiyim 1 yazsın 3 gönderiyim 3 yazsın gibi bir yapı yok. Birbirinden bağımsız 7 tane led var. Mesela 1 yazmak için GND bağlantılarını yaptıktan sonra, 6. ve 4. bacaktan 5V vermelisiniz. Her sayı için bu ayrı ayrı hesaplanıp yazılım tarafında bir fonksiyon ile çözülebilir. Ama bu şekilde Arduino tarafında 7 pin meşgul edecektir. İki tane digit bağlarsak 14 pin. Bu çok saçma bir kullanım olur. Ne yapalım. İlk akla gelen bir lojik devre tasarlayıp binary gelen sayıyı 7 segmentli göstergede yazdırmak. Bu şekilde 4 pin kullanarak tüm rakamları yazdırırız. (Örneğin pinlerden 0011 verildiğinde göstergede 3 yazdırmak) Bunun için bir devre tasarlamamıza gerek yok zaten bu işi yapan bir entegre var. 4511 entegresi. Bu entegre 4 pin ile binary gelen değeri 7 segmentli göstergede yazdırmak için tasarlanmış. Buradan datasheeti inceleyebilirsiniz. Bacak bağlantıları ve açıklama tablosu aşağıda.

 

4511

pin isim

bağlantıları

1 B girişi B girişi
2 girişi C C girişi
3 lamba test girişi Lojik-1
4 ripple girişi Lojik-1
5 aktif etme girişi Lojik-0
6 girişi D D girişi
7 A girişi A girişi
8 VSS 0 V güç kaynağı 0 V
9 segment e 7-segment ortak katot ekran, e
10 segment d 7-segment ortak katot ekran, d
11 segment c 7-segment ortak katot ekran, c
12 segment b 7-segment ortak katot ekran, b
13 segment a 7-segment ortak katot ekran, a
14 segmenti g 7-segment ortak katot ekran, g
15 segment f 7-segment ortak katot ekran, f
16 VDD + V güç kaynağı +9 V (aralık 5-15 V)

 

Aşağıdaki örnek bir bağlantı görülebilir.

 

4511-proteus

 

Bu şekilde sadece 4 pini kullanarak tüm rakamları gösterebiliyoruz. Hala bir sorun var. Sıcaklık değeri göstereceğimiz için iki rakama ihtiyacımız var. İki göstergeyi iki ayrı entegre ile tasarlayıp toplam 8 pin kullanarak bunu yapabiliriz. Ama bu çok hızlı olan donanımımızı çok hantalca kullanmak olur. Bunun yerine bilgisayarların mükemmel hızlı olmasını kullanarak maliyeti düşürebiliriz. Tek entegrenin çıkışılarını iki göstergeye birden versek. Sonra bu göstergelerden ilkini bir kaç mili saniye, ardından diğerini bir kaç milisaniye yakıp söndürsek. Ve yanma anlarında da entegreye ilgili rakamları göndersek aynı entegreyi kullanarak iki ayrı rakamı gösterebilmiş oluruz. Göstergeleri yakıp söndürme işlemleri içinde GND bacağını kullanırız. Bir katot göstergede GND bacağından 5V verir isek gösterge söner, toprak verirsek gönderdiğimiz rakam yanar.

 

Herşey tamam artık devreyi tasarlayıp uygulamamızı yazabiliriz. Sıcaklık sensörünü bağlamıştık. Şimdi entegreyi ve digitleri bağlayalım.

 

 

Scanned-image-0

Scanned-image-1-0

 

Devreyi resimdeki gibi oluşturalım. Karmaşık görüntüyü engellemek için 4511 entegresinden diğitlere giden kablolar çizilmemiştir; harflendirilmiştir. Artık programımızı yazıp arduino’ya yükleyebiliriz. Program kodları aşağıdaki gibidir. Bu uygulama  bir yandan, sensörden okuduğu sıcaklık değerini digitlere yazdırır. Diğer yandan da bağlı bulunduğu bilgisayarın seri portunu dinler. Eğer seri porttan “a”, “b”, yada “c” verisi gelirse yine seri porttan sırsaıyla sıcaklık, nem ve çiğ oluşturma verisini yazar. Bu konu ile ilgili ayarlamaları serialEvent() fonksiyonundan görebilirsiniz.

Bu serinin bir sonraki yazısında bir wcf servis yazacağız. Bu servis gelen isteklere göre sıcaklık nem ve çiğ oluşma noktası verisini seri port aracılığıyla arduino devremizden okuyup istemciye iletecek. Bu şekilde oda sıcaklık vs bilgilerini hem digitler ile odada gösterip hemde masaüstü uygulama, android cihaz vs. gibi farklı platformlara servis edebiliyor olacağız.

 


#include <SimpleTimer.h>
#include <dht11.h> 

dht11 SicaklikSensoru;
SimpleTimer timer;

int pinOtele = 10;
int digitEntegreIzinPin = 9;
int digitBirIzinPin = 8;
int digitSifirIzinPin = 7;
int ZamanKesmesiSuresi = 20000;

float sicaklik = 0, nem = 0, cig = 0;

void digiteYaz(int rakam)
{
 if(rakam < 0 || rakam > 9) return;

 int deger, cikis;

 for(int i = 3; i >= 0; i--)
 {
 deger = pow(2, i) + (i > 1 ? 1 : 0); 

 digitalWrite(i + pinOtele, deger > rakam ? LOW : HIGH);

 rakam -= deger > rakam ? 0 : deger;
 }

 digitalWrite(digitEntegreIzinPin, HIGH);
}

void digitSec(int d)
{
 boolean kontrol;
 if(d == 0)
 kontrol = false;
 else if(d == 1)
 kontrol = true;

 digitalWrite(digitBirIzinPin, !kontrol);
 digitalWrite(digitSifirIzinPin, kontrol);
}

void digitlereYaz(int sayi)
{
 if(sayi < 0 || sayi > 99)
 return;

 int bekleme = 5;

 digitSec(0);
 digiteYaz((sayi > 9 ? sayi % 10 : sayi));
 delay(bekleme);
 digitalWrite(digitEntegreIzinPin, LOW);
 delay(bekleme);

 digitSec(1);
 digiteYaz((sayi > 9 ? sayi/ 10 : 0));
 delay(bekleme);
 digitalWrite(digitEntegreIzinPin, LOW);
 delay(bekleme);
}

void timerInterrupt()
{
 SicaklikSensoru.read(2);

 sicaklik = (float)SicaklikSensoru.temperature;
 nem = (float)SicaklikSensoru.humidity;
 cig = (float)SicaklikSensoru.dewPoint();
}

void serialEvent()
{
 char istek = (char)Serial.read();

 if(SicaklikSensoru.read(2) != 0)
 {
 Serial.print("Hata: 1|Sensor Okunamadı");
 return;
 }

 switch(istek)
 {
 case 'a':
 sicaklik = (float)SicaklikSensoru.temperature;
 Serial.println(sicaklik, 2);
 break; 

 case 'b':
 nem = (float)SicaklikSensoru.humidity;
 Serial.println(nem, 2);
 break; 

 case 'c':
 cig = (float)SicaklikSensoru.dewPoint();
 Serial.println(cig, 2);
 break; 

 default:
 Serial.println("Hata: 2|Gecersiz Istek");
 }
}

void setup()
{
 Serial.begin(9600);

 //Pinler Aktif
 for(int i = 0; i <= 3; i++)
 pinMode(i + pinOtele, OUTPUT);

 //Digitler Aç/Kapat
 pinMode(digitEntegreIzinPin, OUTPUT);
 pinMode(digitBirIzinPin, OUTPUT);
 pinMode(digitSifirIzinPin, OUTPUT);

 timerInterrupt();
 timer.setInterval(ZamanKesmesiSuresi, timerInterrupt);
}

void loop()
{
 digitlereYaz((int)sicaklik); 

 timer.run();
}

Yorumlar: 0 »

Sıcaklık/Nem Kontrol ve Uyarı Sistemi – 1

Kategori: Arduino, C#, Güvenlik, Referanslar
Tarih: 15th Aralık 2014

Yazmaya uzun bi süre ara verdikten sonra ancak bugün birşeyler yazmaya fırsat bulabildim. Bu yazıda bir sıcaklık/nem kontrol devresi ve sıcaklığın farklı platformlara sunulabilmesi hakkında planlamalar yapcağız. İhtiyacımız sıcaklığı farklı sebeplerden dolayı sık sık yükselen server odasını kontrol altında tutmak. Öngörülemeyen bi sıcaklık artışı olduğunda da ilgili personele bilgi verilmesi.

 

Sıcaklık kontrol devresi için ilk başlarda 16F877A mikrodenetleyici ve uyumlu bir sıcaklık sensörü kullanmayı planlamıştım. Ama biraz araştırma yaptıktan sonra Arduino diye birşey gördüm. Bu mikrodenetleyici dünyası için bir framework diye düşünülebilir. Açık kaynak, İtalyan menşeli bir proje. Açık kaynak olduğundan dolayı piyasada çok farklı kombinasyonlarda hatta çin üretimi ürünler bile mevcut. Peki tam nedir bu Arduino. Bu, üzerinde bir mikrodenetleyicisi olan, özel tasarım bir kart. Bu kart sayesinde bir çok özel yazılmış fonksiyonu bu microdenetleyici üzerinde çalıştırabiliyor, kart üzerindeki portları kullanarak da microdenetleyici ile çok rahat bir şekilde iletişim kurabiliyor hatta bir bilgisayar ile seri port iletişimine geçebiliyorsunuz. Buda sizi hem devre kurma çabasından kurtarırken hem de projelerini daha hızlı kodlayabilme imkanı sunuyor. Dezavantajı yok mu? Var tabiki. Mikrodenetleyici üzerinde halihazırda çalışan bir framework olduğundan dolayı bu donanımı 100% verimle kullanamıyorsunuz. Biraz daha yavaş kalıyor. Bu durum C/C++ ve C# dillerinin kıyaslanması gibi düşünülebilir. Daha yavaş olduğu için -bazı özel projeler hariç- C# dan vazgeçmezsiniz herhalde.

 

Arduino’ ya uyumlu bir sensör ile (DHT11) sıcaklığı ve nemi okuyup, 7 segmentli digitler kullanarak yazdırmayı planlıyorum. Ayrıca okunan değerleri seri port üzreinden server odasındaki bir bilgisayarada ileteceğim. İş buradan sonra sizin planınıza kalmış. Ben bütün platformlardan bu bilgiye erişmek istediğim için; bu sunucu üzerinden bilgileri servis edeceğim. Devam eden yazılarımda tarasımları ve kodları paylaşarak bunu bir seri yazı halinde yazmayı planlıyorum. Yapılacak işin kaba taslak bir çizimi aşağıdadır. Herkese iyi günler.

 

Scanned-image-0

Yorumlar: 1 »
Optimization WordPress Plugins & Solutions by W3 EDGE