MQTT Nedir ve Arduino ile MQTT Client Yapımı
|Merhabalar,
Bu konuda nesnelerin interneti ile sıklıkla karşılaştığımız bir haberleşme protokolüne değineceğiz. Message Queuning Telemetry Transport kelimelerinin baş harflerinden oluşan MQTT protokolü özellikle Arduino gibi mikrodenetleyici platformları ile internet üzerinden haberleşme yapılacağı zaman sıklıkla tercih edilir. Bunun en büyük sebebi açılımı Hyper Text Transfer Protocol olan HTTP’ ye göre çok çok küçük paket boyutuna sahip olmasıdır. Paket boyunun küçük olması ile MQTT aktarılacak veri yükünü hafifletmekte ve veri iletişimini hızlandırmaktadır. Öyle ki 3G ağlar için MQTT ile gönderilen bir veri HTTP ile gönderilene göre 93 kat daha hızlı gittiği ölçülmüştür. Bu tam olarak 20 Mhz’ de çalışan 256 byte haberleşme tamponu olan bir mikrodenetlecide aranan özelliktir. Ayrıca MQTT HTTP’ ye göre çok daha basit yapılıdır. İçerdiği eventler kullanımları açısından çok daha basit ve sayı olarak daha azdır. Bunun yanı sıra MQTT sadece noktadan noktaya haberleşeme gerektirmez. Örneğin siz HTTP iletişim kullananılan bir serverda soket üzerinden iletişim yapmak istiyorsanız karşıda kesinlikle bir dinleyen olması gerekir. Aksi taktirde veriyi gönderemez ve bağlantı hataları alırsınız ki özellikle istem dışı kesilmelerde çökmelere yol açar. Ancak MQTT’ de böyle bir durum söz konusu değildir. Siz veriyi bir yayıcı(publisher) üzerinden göndermeye çalışırsınız. Karşıda bunu bir abone(subscriber) varsa alır yoksa veri gönderilmez ama size bir hata olarak dönüşü olmaz. Bu da bağlantı kopması sonrası tekrar bağlanma gibi vakit harcatan durumların önüne geçilmesine sebep olur. Ancak ağı yöneten ve her MQTT network’ unda en az 1 tane olması gereken BROKER ile iletişim kesilirse, ki internet bağlantınızın kesilmesi anlamına gelir, tekrar ağa bağlanmanız gerekecektir. MQTT sadece elektonik cihazlarda kullanılan bir haberleşme protokolü değildir. Günümüzde Facebook ve Instagram gibi bazı sosyal medya platformları mesajların gönderilmesi için bu protokolü kullanmaktadır.
Şimdide biraz MQTT yapısına değinelim. Yukarıda da bahsettiğimiz gibi her MQTT ağında en az 1 “Broker” bulunmalıdır. Bu broker HTTP’ de server’ ın yaptığı gibi mesaj alıp gönderme işlemleri yapamaz. Kayıtlı “topic” isimlerine göre gelen mesajları yönlendirilmesini gerçekleştirir. Aşağıdaki resimde de görüldüğü gibi sıcaklık sensöründen gelen “temperature” topic’ iğini dinleyen herkese Broker üzerinden iletilmiştir.
Peki ya biz yapacağımız uygulamada bu Broker’ i nasıl oluşturacağız? Bunun birden fazla yolu var. En çok kullanılanı kendi bilgisayarınıza kurabileceğiniz açık kaynaklı Eclipse destekli Mosquitto’ dur. Bağlantı üzerinden kendi sitesine gidebilir ve kurulum adımlarını takip edebilirsiniz. Diğer bir yöntem javascprit vb bir dil ile yazılmış hazır framework’ ler kullanmak. Üçüncü ve test etmek için en kolay yöntem olan internet üzerinde hazır ücretsiz MQTT Broker kullanmak. Bu konuda bunlardan biri olan https://shiftr.io/ isimli Broker’ ı kullanacağız. Siteyi kullanabilmek için üye olmak gerekmektedir. Ücretsiz olarak hızlı bir şekilde üye olabilirsiniz. Bunun yerin bir çok farklı alternatif online Broker’ lar seçebilirsiniz. Lakin görselleştirmesi ve kullanımı kolay olduğu için bunu tercih ettim. Zaten bazı temel yapılar tüm MQTT Broker’ lar üzerinde aynı olacaktır.
Sıra geldi bizim uygulamamıza. Uygulamamızda elimizdeki bir Arduino’ yu ESP8266 kullanarak, bir ESP8266-12′ e yi tek başına ve bir bilgisayarı MQTT kullanarak haberleştirelim. Bu haberleşme senaryomuz şöyle olsun. Arduino’ ya bağlı olan butonlar ile ESP8266-12E’ ye bağlı 3 ledin durumunu değiştirirelim. Aynı zamanda ESP8266-12E üzerinden 2 saniye de bir veri gönderelim. Bütün verileri bilgisayarda çalışan NodeJS uygulaması ile görüntüleyelim.
İlk önce bağlantı şeması. Aşağıdaki bağlantı şemasını board üzerinde kuralım. Kullanılan regülatör LM317′ dir. Giriş voltajını ESP8266′ nın çalıştığı besleme gerilimi olan 3.3V’ a düşürmek için kullanılmıştır. Arduino üzerindeki 3.3V regülatörün çıkış gücü yeterli değildir kullanmanızı tavsiye etmem.
Şimdi sıra geldi yazılıma. Öncelikle Arduino yazılımına bakalım. Bu kısımda dikkat etmeniz gereken Arduino sadece veri gönderimi yapmanız gerektiği. Henüz subscribe yapabilen Arduino ile ESP8266 kütüphanesi bulamadım. Bu yüzden sadece publish fonksiyonunu kullanacağız. Bu uygulama için Arduino’ da ihtiyacımız olan kütüphaneler şunlar:
- WiFiESP : ESP8266 ile haberleşme için
- pubsubclient : MQTT protokolü ile ağdaki diğer cihazlara veri göndermek için
Bunlara ek olarak Arduino içerisinde dahili olarak bulunan SoftwareSerial kütüphanesini kullanacağız. Bu kütüphaneyi ESP8266 ile Arduino arasındaki haberleşmeyi 0 ve 1 pinlerini kullanarak yapmamak için kullanacağız. Bildiğiniz üzere Arduino üzerinde 0 ve 1 pinlerinden çalışan donanımsal Seri haberleşme bağlantısı mevcut. Ancak bu bağlantıyı herhangi bir donanım ile haberleşme için kullanırsak hem “Serial.print” vb fonksiyonlar ile yaptığımız veri görüntüleme özelliklerinden feragat etmiş oluruz hem de Arduino’ ya her yazılım yüklemede bu bağlantıyı sökmek zorunda kalırız. Bunun yerine yazılımsal olarak SoftwareSerial ile farklı pinlerden oluşturacağımız haberleşme bu sorunları çözmemizi sağlayacaktır. Sadece dikkat edilmesi gereken Arduino’ daki SoftwareSerial kütüphanesi en çok 38400 baud rate ile çalışmaktadır. Üstünü desteklememektedir. Bizde uygulamamızda bu hızı kullanacağız. ESP8266′ nın Baud rate’ ini değişmeyi bilmiyorsanız bu hızı sizin kullandığınız hız ile değiştirebilirsiniz.
Arduino’ ya yüklenecek yazılımda yapılması gereken ayarlamalar aşağıdaki gibi olmalıdır. Bu ayarlarda bulunan “user_token_name” ve “user_token” shiftr.io websitesinde namesapce settings altından alınabilen token’ lerdir. shiftr.io websitesine bu tokenler yardımı ile erişebiliriz. Bunları yanlış girerseniz bağlantı hatası alırsınız.
char connID[] = "broker.shiftr.io"; char ssid[] = "***WIFI SSID***"; // your network SSID (name) char pass[] = "***WIFI PASS***"; // your network password char user_token_name[] = "**TOKEN_NAME**"; //your shiftr user token name char user_token[] = "**TOKEN**"; //your shiftr user token int status = WL_IDLE_STATUS; // the Wifi radio's status // Initialize the Ethernet client object WiFiEspClient espClient; PubSubClient client(espClient); int ledPin = 13; SoftwareSerial soft(10, 11); // RX, TX
Setup kısmındaki ayarlamaları şu şekilde yapacağız. Burada butonları arduino içerisindeki dahili pull up dirençlerini kullanarak giriş olarak tanımladık. Böylece herhangi bir ek direnç bağlamamıza gerek kalmadı. Pull up bağlantı kullanıldığı için butona basılmadığı zaman lojik – 1, butona basılınca lojik – 0 değer okunacaktır. Ayrıca ESP8266 yukarıda da belirttiğim gibi 38400 baud rate ile kullanılamak üzere ayarlanmıştır. Siz bu değeri 38400 değerini geçmeyecek şekilde size uygun olarak değiştirebilirsiniz.
//butons used with pull up resistors pinMode(btn1, INPUT_PULLUP); pinMode(btn2, INPUT_PULLUP); pinMode(btn3, INPUT_PULLUP); // initialize serial for debugging Serial.begin(9600); // initialize serial for ESP module soft.begin(38400); // initialize ESP module WiFi.init(&soft);
Sıra geldi loop kısmına bu kısımda broker’ a bağlımıyız diye kontrol edip bağlı değilsek bağlantı işlemini gerçekleştirmeliyiz. Bağlantı sağlandıktan sonra da butona basılınca veri gönderimi yapmalıyız.
Broker’ a bağlı olup olmadığımızı kontrol işlemi aşağıdaki gibidir. Broker’ a bağlanırken subscribe edilecek topic’ lerin ayarları yapılmalıdır. Yukarıda da belirttiğim gibi Arduino ile ESP8266 kullanırken mqtt ile subscribe işlemi düzgün çalışmamaktadır. Bu yüzden şimdilik kullanmamanızı tavsiye ederim. (04.2018)
void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect, just a name to identify the client if (client.connect("arduino-client", user_token_name, user_token)) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("Wake", "Arduino-Wake"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void loop() { if (!client.connected()) { reconnect(); } }
Bağlantı kısmından sonra sıra geldi butonlara. Arduino üzerinde 4, 5, 6 pinlerine bağlı butonlara basıldığı zaman şu şekilde veri gönderelim. Burada butona her basıldığında 1 yada 0 olacak şekilde bir veri gönderdik. Veriyi gönderdikten sonra bool değişken olan ledS1 değişkenini değilledik. Böylece bir sonraki basmamızda diğer komutu göndermiş olduk.
if (!digitalRead(btn1)) //buton1' e basıldı mı { if (ledS1) strcpy(val, "1"); //ledin durumunu else strcpy(val, "0"); //tampona yazdık client.publish("/esp8266/led1", val); //tampondaki veriyi gönderdik ledS1 = !ledS1; //led durumunu değiştirdik }
Yukarıdaki yazılım tamamına bağlantıdan ulaşabilirsiniz.
Bu kısımdan sonra sıra geldi ESP8266-12E donanımına yüklenecek yazılıma. Burada kullanacağımız kütüphanelerin biri farklı olacaktır. Bu kütüphaneyi Arduino için ESP8266 araçlarını yükleyerek yükleyebiliriz. Bu araçları bağlantıdaki adımları izlemelisiniz. Buradaki yazılım üsttekinden çok farklı olmayacaktır. Kullanılan kütüphane farklı olsa da içerisindeki fonksiyonlar benzer yazıldıkları için yazılım ana yapısı benzerdir. Sadece bu yazılımda subscribe işlemi yapacağımız için (direk esp8266′ ya kod yüklerken) bir callback fonksiyonu kullanacağız. Bu callback şu şekilde olmalıdır.
void callback(char* topic, byte* payload, unsigned int length) { //show incoming message Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); //toggle led with incoming message if (strcmp(topic, "/esp8266/led1") == 0) //if topic name led1 { if ((char)payload[0] == '1') { //if message '1' digitalWrite(led1, LOW); } else { digitalWrite(led1, HIGH); } }else if (strcmp(topic, "/esp8266/led2") == 0) //if topic name led2 { if ((char)payload[0] == '1') { digitalWrite(led2, LOW); } else { digitalWrite(led2, HIGH); } }else if (strcmp(topic, "/esp8266/led3") == 0) //if topic name led3 { if ((char)payload[0] == '1') { digitalWrite(led3, LOW); } else { digitalWrite(led3, HIGH); } } }
Ayrıca buna ek olarak analog pine bağlı değeri 2 sn’ de bir publish edelim. Bunu da diğer işlemleri geciktirmeden şu şekilde gerçekleştirebiliriz.
if (millis() - lastMsg > 2000) { value = analogRead(A0); //read analog value sprintf (msg, "{\"analog1\": %d}", value); //write to buffer as a json Serial.print("Publish message: "); Serial.println(msg); client.publish("sens/val1", msg); //publish message lastMsg = millis(); }
Yukarıdaki yazılımın tamamına bağlantıdan ulaşabilirsiniz. Sıra geldi bilgisayarda tüm verileri görmeye. Bunun için Node.Js ile hazırlanmış küçük bir yazılım oluşturacağız. Bu yazılım MQTT broker’ a bağlancak ve tüm topic’ lere subscribe olacaktır. Gelen verileri konsolda görüntülenecektir. Konsolda görüntüledikten sonra isterseniz kolaylıkla web sayfası üzerinde görselleştirme yapabilirsiniz. Bu kısımdaki yazılımımız kısa ve basit olacak. Nodejs yüklü değilse yada bu kısma ihtiyacınız yoksa bu adımları geçebilirsiniz. Zaten başta bahsettiğimiz gibi MQTT’ nin en güzel yanlarından birisi de alıcı ve vericinin birbirine bağlı olmamasıdır.
Nodejs ile oluşturacağımız yazılımda aşağıdaki kütüphaneyi kullanacağız.
Yazılım içeriği ise basitce şu şekilde olacak. Önce Borker’ a bağlan subscribed topic’ leri belirle ve yayına başla. Öncelikle kütüphaneyi dahil edelim.
var mqtt = require('mqtt'); //include library var my_token_name = "**TOKEN_NAME**"; //your token name var my_token = "**TOKEN**"; //your token code var client = mqtt.connect('mqtt://' + my_token_name +':' + my_token + '@broker.shiftr.io');
Daha sonra subcribe edeceğimiz topic’ leri belirleyelim.
//subscribed topics client.subscribe('sens/val1');
Son olarak da callback ayarı yapalım. Böylece o topic’ e bir şey yazıldığı zaman konsolda görüntülensin.
//subscribed topic callback client.on('message', function(topic, message, packet) { console.log(topic + ' ' + message); });
Yukarıdaki yazılımın tamamına buradan ulaşabilirsiniz.
Umarım yardımcı olabilmişimdir.
İyi çalışmalar dilerim.