STM32 ile ESP8266 kullanarak MQTT Client (MBED)

 esp8266-iot-mqtt-cloud

Merhabalar,

Bu konuda STM32 kullanarak daha önce Arduino ile yaptığımız MQTT haberleşmesinin benzeri bir uygulama gerçekleştireceğiz. Arduino ile yaptığımız konuya bağlantıdan ulaşabilirsiniz. MQTT’ nin ne olduğuna ve avantajlarına önceki konuda değinmiştim. Bu yüzden tekrardan üzerinde fazla durmayacağım.  Ancak özellikle IOT sistemlerde çok önemli olduğunu belirtmek isterim. Veri trafiğini sağlayan Broker oluşturulması da gayet kolay. Eclipse’ in sağladığı bağlantıdaki open source broker’ ı rahatlıkla kurup çalıştırabilirsiniz. Yada internet üzerinden verilen ücretsiz MQTT Broker hizmetlerini kullanabilirsiniz. Bizde bu konuda ücretsiz bir internet servisi olan shiftr.iou kullanacağız.

MBED-Cli

Projeyi gerçekleştirirken mbed-cli kullanacağız. Bunun sebebi kullanacağımız kütüphanelerin karmaşık yapıda oldukları için platform.io yeterli olmamakta. Bu yüzden mbed-cli yüklemiş olmanız gerekmekte. Bunun için bağlantıdaki konuyu takip edebilirsiniz. Zaten yüklü ise aşağıdaki komut ile kullanacağımız yazılım örneğini ve bağlantılı kütüphaneleri indireceğiz.

mbed import http://os.mbed.com/teams/ublox/code/HelloMQTT/

İndirdiğimiz örnek ile birlikte gelen “easy-connect” isimli kütüphane ublox tarafından paylaşılmış olup bir çok wifi ve hücresel bağlantı aracı desteklemekte. ESP8266′ da bunlardan bir tanesi. Bağlantıdaki github sayfasına ulaşabilirsiniz. Burada farklı donanımlar ile kullanımı hakkında açıklamalar mevcut. En güzel yanı ise farklı donanımlar ile kullanırken yazılımınızda çok az değişiklik ihtiyacı duymanız. Zaten bu özelliği ile de bu kütüphane basit bir sürücü niteliğindedir.

İndirdiğimiz bu yazılımı kullanabilmemiz için bazı değişikler yapmalıyız. Bu değişikliklerden ilki “mbed_app.json” isimli dosyada yapılacak. Bu bir JSON dosyası ve alıştığımız normal C/C++ syntax’ ının dışında bir yapıya sahip. Bu yüzden düzenleme yaparken JSON formatına uygun düzenleme yapmalıyız. İlk düzenlememiz “network inteface” isimli değerde. Bu değer kullanacağımız donanımı seçmemizi sağlıyor. Teoride “help” değerindeki her donanım bu kütüphane ile sorunsuzcu kullanılabilir.  Ancak ben sadece ESP8266′ yı denedim. Esp8266′ yı seçmek için aşağıdaki değişikliği yapmalıyız.

     "network-interface":{
            "help": "options are ETHERNET, WIFI_ESP8266, WIFI_ODIN, WIFI_RTW, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD",
            "value": "WIFI_ESP8266"
        }

Daha sonra Wi-Fi ağının adını ve şifresini girmeliyiz. Böylece ESP8266 sorunsuzca Wi-Fi ye bağlanabilecektir. Bunun için “esp8266-ssid” ve “esp8266-password”  değerlerini değişmeliyiz. Bu değerleri aşağıdaki gibi değiştirebiliriz.

        "esp8266-ssid": {
            "value": "\"YOUR_SSID\""
        },
        "esp8266-password": {
            "value": "\"YOUR_PASSWORD\""
        }

Şimdi sıra geldi ESP8266′ yı bağlayacağımız pinlerin ayarlanmasına. Bu ayarları da yine aynı dosya içerisindeki “esp8266-tx” ve “esp8266-rx” değerleri ile yapacağız. ESP8266′ yı bağlamak istediğimiz pinleri bu değelerin karşısına aşağıdaki gibi yazacağız.

        "esp8266-tx": {
            "help": "Pin used as TX (connects to ESP8266 RX)",
            "value": "PE_8"
        },
        "esp8266-rx": {
            "help": "Pin used as RX (connects to ESP8266 TX)",
            "value": "PE_7"
        }

Bu kısımda yapacağımız değişiklikleri bitirdik. Bu JSON dosyası mbed ile yapacağımız derleme ve kullanacağımız ide için export etme işleminde ayarlamarın otomatik olarak yapılmasını sağlayacaktır. Bu değişikliği export işleminden sonra oluşturulacak olan “mbed_config.h” dosyası içerisinde de yapabilirdik. Ancak her export işleminde o dosyanın üzerine ayarlar yeniden yazılacaktır. Yani yapılacak değişiklikleri tekrar yapmanız gerekecektir. Bu yüzden JSON dosyasında değişiklik yapmak kalıcı ve daha kolaydır.

Yapmamız gereken son bir ekleme kaldı. Bu da picojson isimli kütüphaneyi projeye eklemek. Bu kütüphane JSON formatında verileri C ile kullanımını kolaylaştırır. Bu tamamen benim tercih ettiğim bir yöntem. Bunu hiç kullanmadan kendi iletişim yönteminizi byte yada string’ ler ile oluşturabilirsiniz. Yukarıda yaptığımız ayarlar yada MQTT ile bir ilgisi yoktur. Bunun için komut satırından şu komutu giriyoruz.

mbed add http://os.mbed.com/users/mimil/code/picojson/

Daha sonra projemizi bir ide ile kullanmak üzere export işlemine hazırlamalıyız. Bunun için temel iki adım bulunmakta. İlki kullanacağımız derleme aracını (toolchain) ve kartımızın modelini belirlemek. Bunun için şu komutları girmeliyiz.

mbed toolchain GCC_ARM
mbed target NUCLEO_F767ZI

Üstteki satır muhtemelen hepimizde aynıdır. Lakin alttaki satırı elinizdeki NUCLEO kartı modeli ile değiştirmelisiniz. Hangi kartların desteklendiğini görmek için aşağıdaki komutu kullanabilirsiniz. Bu komut çıktıları komut satırında değil dosya konumundaki “supported.txt” isimli bir dosyaya oluşturacaktır. Daha sonra metin belgesi ile bu dosyayı açarak hangi kartları hangi ide kullanabileceğinizi görebilirsiniz. Benim çıktı dosyama bağlantıdan ulaşabilirsiniz.

mbed export --supported >> supported.txt

Son olarak da yazılımızı bir ide için export edeceğiz. Ben bu işlem için keil kullandığımdan keil için export edeceğim. Siz kullandığınız IDE için export işlemini gerçekleştirebilirsiniz.

mbed export -i uvision5

mbed_app.json dosyasının bendeki tamamına bağlantıdan ulaşabilirsiniz.

Keil IDE

Bu aşamadan sonra sıra geldi “main” dosyamızda yapacağımız yazılım değişikliklerine. Hikayemizde STM32 ye bağladığımız ledleri MQTT iletişim ile Processing’ de oluşturduğumuz bir arayüz ile kontrol edelim. Bu esnada STM32 analog bir değeri arayüze göndersin ve bu arayüzde bunu grafik olarak çizdirelim. İlk başta biraz karmaşık gibi gelse de MQTT ile bu iletişimi sağlamak oldukça basit. Giriş ve çıkışlarımızı tanımlayarak başlayalaım.

//normal leds to use on - off funciton
DigitalOut led1(LED1);
DigitalOut led2(LED2);

//RGB led pins
PwmOut redPin(PB_10);
PwmOut greenPin(PE_12);
PwmOut bluePin(PE_14);

//analog input pin
AnalogIn tempPin(A0);

Burada önemli olan RGB led bağlantıları. Bu led’ in pinleri PWM pinlerine bağlanmalı. Çünkü RGB led ile tüm renkleri elde etmek istiyoruz. Her rengi elde etmek içinde RGB led içerisindeki kırmızı, mavi ve yeşil ledi farklı oranlarda yakmamız gerekecek. Bu işlemi PWM yada DAC ile sağlayabiliriz. Ben bu örnekte PMW kullanmayı tercih ettim. RGB ledin çalışma prensibine kısa bir değinecek olursak. Bu ledler 7 segment’ lerde olduğu gibi ortak anot ve ortak katot olarak ikiye ayrılır. Böylece her led için ayrı pin kullanmak gerekmez. Resimde ikisi için olan bağlantı şemasını görebilirsiniz.

download

Elimde ortak anot RGB led olduğu için bu örnekte onu kullanacağım. Ortak anot rgb led kullanılırken dikkat edilmesi gereken ufak bir ayrıntı vardır. Normalde ledler bir bacağı lojik-1 diğeri ise lojik-0 olduğu zaman yanarlar. İki bacağı da lojik – 0 olduğu zaman sönerler. Ancak burada ortak bacağı doğrudan beslemeye bağladığımızdan onu anahtarlayabilme şansımız yok. Bu yüzden ledi söndürmek için diğer bacağı da lojik – 1 yapmamız gerekmekte. Böylece gerilim farkı olmayacağından akım akmayacak ve led sönecektir.  Bunu PWM olarak uygularken de aynı şekilde yapmalıyız. Yani bizim uygulamamızda RGB led in herhangi bir ledinin yanması için PWM ile %0 çevrim oranlı işaret uygulamalıyız, söndürmek için %100 çevrim oranlı işaret uygulamalıyız. Daha önceden 7 segment kullanmış olanlar için bu duurm çok yabancı değildir. Sadece pwm ile kullanmak biraz farklı gelebilir. Uygulama sırasında bütün soru işaretlerinin giderileceğini umuyorum.

Şimdi main içerisindeki ayarlamalara sıra geldi. Burda ilk olarak bağlantı arayüzlerini oluşturmalı ve bağlanacağımız adres bilgilerini girmeliyiz.

    //create network interface with debug on parameter
    NetworkInterface* network = easy_connect(true);
    if (!network) {
        return -1;
    }

    MQTTNetwork mqttNetwork(network);

    MQTT::Client<MQTTNetwork, Countdown> client(mqttNetwork);

Bu kısım kalıp kullanım olduğu için üzerinde çok durmayacağım. Ancak alt kısımda yapacağımız değişiklikler önemli. Eğer sizde shiftr.io kullanacaksanız “hostanme” kısmında değişiklik yapmanıza gerek yok. Ancak farklı bir Broker sevisi kullanacaksanız yada kişisel server’ inizde işlem yapıyorsanız o kısmı kendi IP bilginiz ile değiştirmelisiniz.  Ayrıca kullancağınız servis size bir bağlantı ID’ si ve parolası verecektir. Bu bilgileri de “username” ve “password” değişkenlerindeki değerler ile değiştirmelisiniz.

    const char* hostname = "broker.shiftr.io";								//mqtt broker name
    int port = 1883;																					//mqtt port number
		
    pc.printf("Connecting to %s:%d\r\n", hostname, port);
    
    int rc = mqttNetwork.connect(hostname, port);                                //connect mqtt broker
    if (rc != 0)																							//if cannot connected return number will not be zero
        pc.printf("rc from TCP connect is %d\r\n", rc);				//print returned error code

    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 3;
    data.clientID.cstring = "mbed";									//our mqtt client ID
    data.username.cstring = "YOUR-ID";							//our shiftr.io connection ID
    data.password.cstring = "YOUR-PSWD";			     //our shiftr.io password

Artık Broker’ a bağlandık. Verileri almak üzere topic’ lere bağlanmaya hazırız.  Bu topic lerin ismi tamamen keyfi olarak belirlenir. Ancak büyük sistemlerde bunlar bir ağaç mimarisi oluşturmak için “/device1/sensor1/value”, “/device2/sensor1/value”. Böylece shiftr.io’ da görebileceğiniz üzere her cihaz ayrı bir ağaca sahip olur ve verilerin nereye ait oldukları anlaşılması kolay olur. Bizim projemizde iki client olduğu için bu şekilde bir ağaçlandırma yapmayacağım.

    //subscribe rgb led messages
    if ((rc = client.subscribe("rgbLed", MQTT::QOS2, rgbLedMessage)) != 0)
        pc.printf("rc from MQTT subscribe is %d\r\n", rc);

    //subscribe led1 messages
    if ((rc = client.subscribe("led1", MQTT::QOS2, led1Message)) != 0)
        pc.printf("rc from MQTT subscribe is %d\r\n", rc);
		
    //subscribe led2 messages
    if ((rc = client.subscribe("led2", MQTT::QOS2, led2Message)) != 0)
        pc.printf("rc from MQTT subscribe is %d\r\n", rc);

Yukarıdaki “subscribe” fonksiyonlarında QOS isimli bir parametre daha mevcut. Açılımı Quality Of Service olan QOS, adından da anlaşıldığı üzere kaliteyi sembolize eder. Neyin Kalitesi? Tabiki mesaj iletiminin. En düşük seviye olan QOS0′ da veri sadece 1 kez gönderilir. İletilip iletilmediğine bakılmaz. QOS2′ de ise mesaj kesin iletilecektir. Yani iletilene kadar gönderilmeye devam edecektir. Bu düşük enerji gerektiren uygulamalar için fazla enerji kullanımı demektir. Yani önemli olan veriler gönderilirken kalite seviyesi yüksek düşük olan veriler gönderirken kalite seviyesi düşük olarak ayarlama yapmalıyız.

Sıra geldi ana döngü içerisinde analog ölçüm değerini belirli aralıklar ile göndermeye. Bu işlem için asenkron gecikmeli bir sistem uygulayabilmek için 100 adımda bir gönderim şeklinde bir algoritma oluşturdum. Böylece 10 ms’ lik zaman aralıklarında client gelen mesajları dinleyebilecek. 1 sn süre geçince de veriyi gönderdik.

                while(true)
		{
			if(cnt == 100)												//if 100 steps passed send message
			{
				double adcVal = tempPin.read() * 40;											//read adc value and scale it between 0 - 40
				adcJson["temp"] = picojson::value(adcVal);                //save value with "temp" key to JSON
				string msgStr = picojson::value(adcJson).serialize();			//convert JSON value to string
				
				sprintf(buf, "%s", msgStr.c_str());                       //convert string value to char array
				message.payload = (void*)buf;															//add converted value to MQTT message
				message.payloadlen = strlen(buf)+1;												//set MQTT message length
				if((rc = client.publish("adcVal", message)) != 0)					//send message to "adcVal" topic
				{
					pc.printf("rc from MQTT subscribe is %d\r\n", rc);			
				}
				
				cnt = 0;
			}

			cnt++;
			Thread::wait(10);															//wait 10 ms between steps
		}

Bu aşamadan sonra sıra geldi subscriber’ ların callback fonksiyonları ayarlamaya. Bu fonksiyonlar mesaj geldiği zaman çağrılır ve içerisindeki işlem gerçekleştirilir. RGB led için ayarlayacağımız callback fonksiyonuna bir bakalım. Gelen mesajda string e çevrilmiş bir JSON verisi bulunmakta. Burdan JSON verisini ayrıştırıp elde etmek mümkün. Bunun için “picojson” kütüphanesini kullanacağız. JSON’ a çevrilen verinin içerisinde “red”, “green” ve “blue” etiketinde 3 tane adet değer mevcut. Bunları Processing IDE’ sinde biz ayarlayıp gönderdik ki konunun sonunda bundan da bahsedeceğim. Ayrıştırdığımız değerleri tek tek ledlere aktaracağız. Bunun için daha önce RGB’ ledin çalışma prensibini anlatırken anlattığım kontrol mekanizması sebebi ile ayrıştırdığımız değerden 1.0 çıkartarak lede PWM göndereceğiz. Böylece RGB ledde doğru rengi elde etmiş olacağız.

//MQTT callback for rgb led messages
void rgbLedMessage(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    picojson::value v;
    char *msg = (char*)message.payload;				//get message as a char array
	
    string err = picojson::parse(v, msg, msg + message.payloadlen);			//parse JSON from char array
		
    //get red, green, blue value from JSON with key name
    pc.printf("redVal =%f\r\n" ,  v.get("red").get<double>());
    pc.printf("greenVal =%f\r\n" ,  v.get("green").get<double>());
    pc.printf("blueVal =%f\r\n" ,  v.get("blue").get<double>());
	
    //subtract all values from max value to use with common anode rgb led
    redPin = 1.0 - (v.get("red").get<double>() / 255.0);
    greenPin = 1.0 - (v.get("green").get<double>() / 255.0);
    bluePin = 1.0 - (v.get("blue").get<double>() / 255.0);
}

Keil ile yazılmış yazılımın tamamına bağlantıdan ulaşabilirsiniz.

Processing IDE

Keil tarafında yapmamız gereken değişiklikler değindik. Sıra geldi arayüzümüzü oluşturacağımız Processing IDE’ sine. Processing’ e basitçe bir değinecek olursak. Bir görsel programlama dili olan Processing Java tabanlı olup hızlı ve göze hitap eden uygulamaları kısa sürede oluşturmanızı sağlar. IDE’ si Arduino Ide’  sine çok benzerdir. Örnekler kısmında bir çok örnek barındırır ve topluluğu geniş olduğu için kütüphanesi de geniştir. Biz bu uygulamayı daha da kısa sürede oluşturabilmek için bu kütüphanelerden birini kullanacağız. Processing IDE’ sini bağlantıdan indirebilirsiniz.  İndireceğimiz kütüphaneler arayüz oluşturmak için “controlP5” ve MQTT bağlantısını kurmak “mqtt” isimli kütüphanelerdir. Bu kütüphaneleri indirmek için araç çubuğunda aşağıdaki yolu izlemelisiniz.

Sketch --> Import Library... --> Add Library...

Daha sonra üstteki filter kısmına kütüphane ismini yazıp çıkan kütüphaneyi seçtikten sonra alttaki “install” butonuna basıyoruz. Böylece kütüphanemiz yüklenmiş oluyor. Oluşturacağımı arayüzde iki switch buton, bir renk seçici, bir normal buton ve bir grafik olacak. Grafik STM32′ den gelen veriyi gösterecek. Switch’ ler normal led’ leri yakacak. Renk seçici ile belirlediğimiz rengi butona basarak STM32′ ye göndereceğiz.

Processing’ de Arduino’ da olduğu gibi iki ana fonksiyonu vardır. Bunlar “setup” ve “loop”. “setup” fonksiyonunda ayarlamaları yapmamız gerek. Bu ayarlamalar bu proje için MQTT servise bağlanmak ve arayüz araçlarını oluşturmak şeklindedir.  MQTT ayarları şu şekildedir.

  String username = "YOUR_ID";
  String password = "YOUR_PASSWORD";

  client = new MQTTClient(this);
  client.connect("mqtt://" + username + ":" + password + "@broker.shiftr.io", "processing");
  client.subscribe("/adcVal");

Şimdi de arayüz araçlarımızı ekleyelim. Processing’ de kullandığımız bu kütüphanenin güzel yanlarından biri de oluşturduğunuz araç ismi ile aynı isimdeki fonksiyona callback olarka bağlanması. Yani siz bir butonun ismini “button1” olarak tanımlarsanız, o butona her basılmasında “button1” ismi ile oluşturduğunuz fonksiyon çağrılacaktır. Ekstra callback ayarı yapmanıza gerek yoktur.

  //Add button to Gui
  cp5.addButton("SEND")     //Button name
      .setValue(0)            //Button first value
      .setPosition(275, 150)    //Button Position in gui (X, Y)
      .setSize(75, 200);        //Button Size (X, Y)

  //Add color picker to gui
  cp5.addColorWheel("c")          //color picker name
      .setRGB(color(128,0,255))   //color picker first RGB value
      .setSize(200, 200)          //color picker size
      .setPosition(50, 150);      //color picker position

  //add chart to gui
  myChart = cp5.addChart("dataflow")      //chart name
               .setPosition(50, 400)      //chart position
               .setSize(300, 150)         //chart size
               .setRange(0, 40)           //chart min and max value
               .setView(Chart.LINE)       //chart type, you can use Chart.LINE, Chart.PIE, Chart.AREA, Chart.BAR_CENTERED
               .setStrokeWeight(1.5)      //chart line thickness
               .setColorCaptionLabel(color(40))     //chart line color
               ;

  myChart.addDataSet("tempVal");                  //chart dataset name
  myChart.setData("tempVal", new float[20]);      //set data length

  //add switch to gui
  cp5.addToggle("SW1")            //switch name
     .setPosition(50,40)          //switch position in gui
     .setSize(100,40)             //switch size
     .setValue(false)             //switch first value
     .setMode(ControlP5.SWITCH)   
     ;

Yazılımızın son kısmına geldin. Processing’ de oluşturacağımız callback fonksiyonları. Yukarıda da belirttiğim gibi bu callback’ ler için herhangi bir ayarlama gerekmiyor. Sadece fonksiyonların isimlerinin araçların isimleri ile aynı olması yeterli. Oluşturduğumuz araçlardan Butonun ve switch’ lerin callback’ ine ihtiyaç duymaktayız.

//color valur send button callback
public void SEND(int theValue) {
  //println("a button event from colorA: "+ theValue);
  JSONObject json;                                            
  json = new JSONObject();                                    //create JSON object
  json.setInt("red", cp5.get(ColorWheel.class,"c").r());      //set red value
  json.setInt("green", cp5.get(ColorWheel.class,"c").g());    //set green value
  json.setInt("blue", cp5.get(ColorWheel.class,"c").b());     //set blue value
  //println(json.toString());
  client.publish("/rgbLed", json.toString());                 //send json data as a string
}

//switch1 change callback
void SW1(boolean theFlag) {
  JSONObject json;
  json = new JSONObject();                         //create JSON object
  if(theFlag==true) {                              //if switch ON set led1 value = 1
    json.setInt("led1", 1);
  } else {                                         //if switch ON set led1 value = 0
    json.setInt("led1", 0);
  }
  client.publish("/led1", json.toString());        //send json data as a string
  //println("a toggle1 event.");
}

Verileri yine JSON formatı olarak oluşturup string’ e çevirip gönderdik. Bunun sebebi anladığınız üzere özellikle çoklu verileri birbirinden kolay ayrıştırmak. Ayrıca günümüz bir çok sistem veri iletimin JSON vb standart formatlarda sağlamakta. Processing yazılımının tamamına bağlantıdan ulaşabilirsiniz.

Yazılım kısımlarımız bu kadardı. Sıra geldi devre çizimize ve Processing arayüz çıktımızın görseline.

schematic

Resimdeki kart Nucleo serisinin en çok kullanılan modeli olan NUCLEO-F401RE’ dir. Fritzing’ de benim kullandığım kart olmadığı için bu model üzerinden çizimi yapıp pinleri doğru olarak yerleştirdim. Ancak yazılım üzerinde kendi kullandığım kart için olan bağlantılar mevcut. Onları düzenlemeyi unutmayın. Oluşturduğumuz arayüzün görüntüsü de bu şekildedir.

gui

 

Add a Comment

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir