NodeMCU Lolin ile Modbus TCP

 


Youtube ta izle..

Fabrikalarda kullanılan plc lerde, scada sistemlerinde, robotlarda veya otomasyon işlerinde bilgi alış verişi için çok sık kullanılan, modbus tcp protokolünü öğrenmek için NodeMCU V3 heralde piyasadaki en ucuz karttır. Koca fabrikayı NodeMCU lar ile yönetebilirsiniz demek istemiyorum tabi, öğrenmek, anlamak için sadece. Ben robolink ten aldım, 5 doların altında fiyat olarak. Dolar yazdım, malum TL sürekli değer kaybediyor, bu yazıyı okuduğunuzda TL fiyatı gerçekçi olmayabilir. Nodemcu, ESP8266-12F modülünü içeriyor yani wifi ile kullanabiliyoruz. 12F ile 12E aynı sadece anten şemaları farklı. İnce uçlu USB kablo girişinden beslenebiliyor, aynı kablodan seri iletişim de kurulabiliyor, program atma veya debug yapmak için...  Önceki bir yazıda ESP8266-01 ve blynk ile kapı açıldığında telefona bildirim yollayan bir çalışmamı anlatmıştım. Fakat ESP nin daha ucuz olan o modeli, breadboard a uyumsuz ve üzerinde usb portu yoktu. 

Nodemcu, USB için CH340 çipini kullanıyor, sizde yoksa driver ı indirmeniz gerekiyor. Bacakları, giriş veya çıkış olabiliyor, hepsi değil tabi, D0-D8 arası DIO veya GPIO olabilirken bir adet de A0 analog giriş bacağı var. USB den gelen 5V, AMS1117 ile 3.3V a düşürülüyor. Bacaklara bir şey bağlarken, 3.3V u düşünerek bağlantı yapın aksi halde duman çıkabilir. DIO bacaklarından her birinden maksimum 12mA çekebilirsiniz. 3.3V çıkışına (AMS1117 çıkışı) 1A e kadar yüklenebilirsiniz. USB den beslemek istemezseniz, Vin girişinden (AMS1117 girişi)  5-12V verip beslenebilir.

Bu sayfadan, kartın modbus slave olarak programlanabildiğini gördüm. Programlama için Arduino IDE kullanılabiliyor.. Sizde ide nin versiyon 1.6.4 üstü varsa indirmenize gerek yok, yoksa buradan 1.6.5 versiyonunu indirebilirsiniz. Trialcommand sayfasındaki kodu biraz değiştirdim, cihazın sabit ip almasını sağladım, biraz sadeleştirdim.




Sol taraftaki GPIO lar dahili 4Mb flash ile iletişim için kullanıldığından haricen kullanılamıyor. Sağ taraftaki D0-D8 arası bacaklar tarafımızdan kullanılabiliyor demiştim, arduino ide de seri monitör e birşey yazdırılmayacaksa D8 in altındaki RX ve TX de D9 ve D10 olarak kullanılabiliyor. Arduino ide de kartta yazan etiketleri #define LED D4 gibi kullanabiliyoruz, GPIO numaralarını bilmemize gerek kalmıyor. Bu arada kartın üstünde bulunan mavi led D4 bacağına bağlıdır. Yani test için led bağlamanıza gerek yok. Burada da best pins to use başlığında bacakların durumları hakkında bilgi veriliyor. Nodemcu V3 ün eski versiyonları da var, onlarda VU bacağı yada pini yoktur. VU da usb den gelen 5V u bize sunuyor, ama dediğim gibi bunu alıp diğer bacaklara dokundurursanız cihaz bozulur.


#define LED D4

void setup() {

  pinMode(LED, OUTPUT); 

}

void loop() {

  digitalWrite(LED, HIGH);                      

  delay(1000);          

  digitalWrite(LED, LOW); 

  delay(1000); 

}


Bu kodu cihaza yüklerseniz, led, 1 sn ara ile yanıp sönecektir. Yalnız karta program atmak için arduino ide ye birkaç ayar çekmemiz gerekiyor... Robotistan ın maker sayfasında anlatılıyor. Bunları yaptıktan sonra kart yöneticisinde aşağıdaki görüntüyü görmek gerekiyor, esp8266 versiyon 3.0.2 installed. Önceden kurduğunuz eski versiyon varsa, sağ alt köşede update butonu çıkacaktır, güncelleme yapmak için..




Windows ta aygıt yöneticisinden nodemcu nun bağlı olduğu portu görüp, arduino ide içinde aynı portu seçin. Baud 115200 de kalsın. Kart olarak NodeMCU 1.0 seçilmelidir. Program yüklerken, cihazın üstündeki butonlara basmaya gerek kalmıyor. Öncelikle, yukarıdaki basit kodu yükleyip kartı test edin. 

Cep telefonlarını veya windows ta çalışan bir programı, modbus client olarak kullanabilirsiniz. Uygulama marketlerinde modbus yazıp aratmak yeterli oluyor. Bunun için nodemcu ve telefon aynı wifi ağına bağlı olması gerekiyor diye söylememe gerek yoktur heralde. Bu şekilde, örneğin evde kafasına göre kitlenip görüntüyü donduran Next Kanky model uydu alıcısını, başka bir cihazı veya lambaları, telefon ile kapatıp açabilirsiniz, hiç bir yere üye olmaya ve telefona devasa uygulamaları indirmeye gerek kalmadan hem de. Sonoff akıllı priz benzeri cihazlar tek priz olarak satılıyor, tek bir nodemcu ya 11 adet priz bağlanabilir... Bacaklar 3.3V ile uyumlu olduğundan gidip normal arduino röle kartını cihaza bağlamayın, yanar. Raspberry pi nin GPIO pinleri de 3.3V olduğundan raspberry pi uyumlu röle kartı diye arama yapmak gerekiyor. Aynı amaçla solid state röle kartı veya ssr nin kendisini kullanabilirsiniz. n11.com de ssr-10da diye aratınca bulabilirsiniz. Ssr girişinde + klemensine, nodemcu dan gelen 3.3V pinlerinden birini, - klemensine, örneğin nodemcu D4 pinini bağlayabilirsiniz. D4, 0 yani false olduğunda, ssr aktif olacak 220V u geçirecektir. Yok ben lehim işini, ekonomik işleri çok severim derseniz, ve aynı zamanda size pcb kazıyacak bir cnc router sahibi değilseniz.. Delikli bir kart alıp bu malzemelerle :

5v röle (2)
2N2222 Transistor (2)
1N4007 Diyot(2)
220 ohm direnç (2)
5V dc veya telefon şarj adaptörü

ve bu şemayla :


çift kanallı bir lamba aç/kapa devresi yapabilirsiniz. 

Bu iş, nodemcu, web server olacak şekilde kod yazılarak da yapılabilir ama konumuz modbus tcp.


Modbus tcp nedir ?

Ethernet kabloları veya wifi üzerinden çalışan bir haberleşme protokolüdür. Rs485 protokolü ile çalışan, Modbus RTU protokolünün , daha yeni, daha sadeleşmiş ve tcp/ip protokolü üzerinden çalışabilen versiyonudur. Otomasyon cihazlarının denetimi ve kontrolü amaçlı tasarlanan basit kullanımı olan bir protokoldür. 1979 daki ismi Modicon şu anki ismi Schneider Electric olan plc üreticisi firma tarafından geliştirilmiş, modbus, 43 yıldır kullanılan endüstri standardı olmuş bir protokoldür. Modbus RTU da sadece bir adet master olabilirken, modbus tcp de birden fazla master olabilir. Modbus RTU paketinde sonda CRC sağlama bilgisi varken, modbus tcp de CRC kullanılmamaktadır. Yani mesela gürültüden dolayı iletişimde oluşabilecek karşıya yanlış bilgi gitmesi durumunda, rtu bunu yakalar, tcp yakalayamaz.
Daha önce S7.net isimli .net kütüphanesi ile yaptığım S7-1200 simülasyonu ile ilgili birkaç konu yazmıştım. Amaç simülasyon dışında S7-1200 plc ve bir .net uygulamasının haberleşmesiydi. Daha sonra bizzat tecrübe ettim ve duydum ki S7.net, fabrika ortamında yani üretimde kullanılamıyor. Bir şekilde pc plc arası iletişim bazen bloke oluyor. S7.net kütüphanesinin, simülasyon veya plc öğrenme amaçlı kullanımı tamam, ama profesyonel üretim ortamında kullanımını kesinlikle tavsiye etmiyorum. İşte modbus tcp veya rtu tam da bu sıkıntıları çözmek için geliştirilmiş, bütün dünyada kabul görmüştür. Modbus protokolünü destekleyen  birçok firma tarafından envai çeşit cihaz üretilmiştir. Open protokol olduğundan, donanım üretici firmalar, herhangi bir lisans ücreti ödemeden bu protokolü, cihazlarına adapte edebilmekteler. Bu da kullanım popülerliğini artıran bir etkendir. Örneğin endüstriyel bir robot üretici firma, robotunun profinet i (siemens plc desteği) destekleyecek şekilde çalışabilmesi için ek bir kartı ücreti karşılığı satarken, modbus tcp yi ücretsiz olarak desteklemektedir.


Master slave nedir ?

Sırası gelmişken, nodemcu, slave yani server  (sunucu) oluyor, öylesine pasif şekilde bekliyor ta ki telefon olan master (istemci veya client) ona bir şey söyleyene kadar. Telefon ona birşey söylerse, nodemcu ona cevap veriyor. Yoksa durduk yere ortalığı velveleye vermiyor. Yani telefon ona konuşma hakkı verirse konuşur, tam bir köle (slave) yani. Web siteleri veya SQL server da aynı mantıkla çalışır, siz google chrome veya firefox tan adres satırına bir web sitesini girince web server size sayfayı açar. Burada google chrome client, web server da slave yani köle oluyor. İngilizce kelimeleri kullanıyorum ki hem piyasada bu şekilde kullanılır hem de  master, slave türkçeye usta, köle olarak çevirilince anlamsız oluyor. 

Slave cihazlar birden fazla olabilir. Master, ağa paket yollarken paket içindeki ilk byte slave id denilen slave kimliğidir. Paketi alan bütün slave ler, buradan mesajın kendisine gelip gelmediğini anlar, master, kendisine sormuyorsa cevap vermez. Slave ler kendi aralarında konuşmazlar. Böylece ağda gereksiz bir trafik oluşmaz. 


Örnek senaryolar

Genel kural, işi veya iletişimi başlatan master, pasif konumda sessizce bekleyen slave olur. İstisnai durumlar olabilir.

Örneğin plc, bu yerli uzak i/o ile modbus üzerinden iletişim kurabilir. Sessiz bekleyecek olan uzak i/o dur, onun giriş çıkışlarını kullanacak olan plc master olur.

Plc nin bağlı olduğu HMI, master olur, plc slave olur. İşi başlatan HMI ya dokunan el olduğundan HMI master olur. Ama yok arkadaş plc master olsun diyebilirsiniz, bu durumda plc, HMI yı sürekli sorgulamalıdır, biri sana dokundu mu diyerekten.

Fabrikalarda kullanılan robotlar bile bu protokolü tanımaktadır. Dışarıdan robotu kontrol etmek istiyorsunuz. Robot slave olur, plc veya .net yazılımı master olur. Tersi de mümkün, diyelim plc, enkoder den bilgi alıyor ve robota sadece bu bilgi lazım. Robot master, plc slave olur.

Bu da işimizle ilgili bir senaryo olsun… .Net kullanılarak etiket aplikatörü ve bağlı olduğu konveyör hat plc si yönetilmek isteniyor. Konveyörde, etiket aplikatörünün önüne hangi ürün geldiği bilgisi plc de var. Plc ye bu bilgi, ürünü konveyöre koyan robottan veya bir barkod okuyucudan geliyor. Hangi ürüne hangi etiketin yazılacağı bilgisi de fabrikanın server ında bulunmaktadır. Etiketi yapıştır sinyali, etiket yapıştırıldı sinyali gibi 24V sinyaller de plc ve etiket aplikatörü arasında geçen diyaloglardır. Bu durumda işin etiketleme kısmını yönetecek olan .net yazılımıdır, yani master olmalıdır. Etiketlenecek ürünü, aplikatörün önüne gelince durdurup, ürünün serbest bırakılması veya işin devam etmesi için talimat bekleyecek olan plc, slave (etiket bilgisi vs. gelmesi için) konumundadır. Aplikatör, modbus protokolünü tanımayan, sadece gelen bilgiyi etikete yazan ve ürüne uygulayan bir cihazdır, dolayısı ile i/o portu üzerinden plc ile sinyalleşirken, ethernet portundan .net üzerinden gelen etikete yazılacak bilgileri almaktadır.

Konumuza dönersek, nodemcu slave konumunda beklemekte, harekete geçme emrini veren cep telefonundaki uygulama master olmaktadır. İki cihaz arasındaki haberleşme, slave de bulunan holding registers denilen word hafızalarla yapılmaktadır. Bir word, iki byte tır. 0 nolu holding register adresi 40001 dir. Bu adres , protokole göre 49999 a kadar artırılabilir. Master, bu hafızadan okuma da yapabilir, buraya yazabilir de. İki taraf önceden nereye yazıp, hangi adresten okuma yapacağı konusunda anlaşmalıdırlar.  İki tarafı da burada olduğu gibi bir kişi ayarlıyorsa sorun yok yani. Master, Nodemcu nun içindeki holding register 3 e 0 yazarsa, D4 e bağlı dahili mavi led yanacaktır, 1 yollarsa sönecektir.  Yani master dan gelen bilgiye göre yanıp sönecek, ayrıca master, holding register lar için okuma yaparsa slave ona 4 register da bulunan içerikleri yollayacaktır. Nodemcu yu, modbus tcp slave yapmak için gereken kod aşağıdadır.. Arduino ide de, sketch -> include library -> ESP8266WiFi ye tıklayınca aşağıdaki bir sürü #include ifadesi geldi..


#include <ArduinoWiFiServer.h>

#include <BearSSLHelpers.h>

#include <CertStoreBearSSL.h>

#include <ESP8266WiFi.h>

#include <ESP8266WiFiAP.h>

#include <ESP8266WiFiGeneric.h>

#include <ESP8266WiFiGratuitous.h>

#include <ESP8266WiFiMulti.h>

#include <ESP8266WiFiScan.h>

#include <ESP8266WiFiSTA.h>

#include <ESP8266WiFiType.h>

#include <WiFiClient.h>

#include <WiFiClientSecure.h>

#include <WiFiClientSecureBearSSL.h>

#include <WiFiServer.h>

#include <WiFiServerSecure.h>

#include <WiFiServerSecureBearSSL.h>

#include <WiFiUdp.h>


#define LED D4 // mavi led D4 e bağlı

 

const char* ssid = "******"; // kendi wifi isminizi girin

const char* password = "***********"; // wifi parolanızı girin

int ModbusTCP_port = 502;


// Static IP istemiyorsanız alttaki 5 satırın başına ve void setup() taki 2 satırın başına // koyun

IPAddress local_IP(192, 168, 1, 253); 

IPAddress gateway(192, 168, 1, 1);

IPAddress subnet(255, 255, 255, 0);

IPAddress primaryDNS(8, 8, 8, 8);   

IPAddress secondaryDNS(8, 8, 4, 4); 

 

//////// Required for Modbus TCP / IP  

#define maxInputRegister 20  

#define maxHoldingRegister 20

#define MB_FC_NONE 0

#define MB_FC_READ_REGISTERS 3  // modbus fonksiyon kodları

#define MB_FC_WRITE_REGISTER 6 

#define MB_FC_WRITE_MULTIPLE_REGISTERS 16

// MODBUS Error Codes

#define MB_EC_NONE 0

#define MB_EC_ILLEGAL_FUNCTION 1

#define MB_EC_ILLEGAL_DATA_ADDRESS 2

#define MB_EC_ILLEGAL_DATA_VALUE 3

#define MB_EC_SLAVE_DEVICE_FAILURE 4

// MODBUS MBAP offsets

#define MB_TCP_TID 0

#define MB_TCP_PID 2

#define MB_TCP_LEN 4

#define MB_TCP_UID 6

#define MB_TCP_FUNC 7

#define MB_TCP_REGISTER_START 8

#define MB_TCP_REGISTER_NUMBER 10

 

byte ByteArray[260];

unsigned int MBHoldingRegister[maxHoldingRegister];

 

//////////////////////////////////////////////////////////////////////////

 

WiFiServer MBServer(ModbusTCP_port);


void setup() {

  pinMode(LED, OUTPUT); 

  Serial.begin(9600);

  delay(100) ;


  // Static IP istemiyorsanız alttaki 2 satırın başına // koyun

  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {

    Serial.println("Failed to configure.."); }

  

  WiFi.begin(ssid, password);

  delay(100) ;

  Serial.println(".");

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    Serial.print(".");

  }

  MBServer.begin();

  Serial.println("Connected ");

  Serial.print("ESP8266 Slave Modbus TCP/IP ");

  Serial.print(WiFi.localIP());

  Serial.print(":");

  Serial.println(String(ModbusTCP_port));

  Serial.println("Modbus TCP/IP Online");

}


void loop() {

 // Check if a client has connected 

 WiFiClient client = MBServer.available();

 //client bağlı ise 1 değilse 0 basar seri monitöre

 //Serial.println(String(client.connected()));

 //delay(1000); 

 if (!client) { 

 return;   // client veya master yoksa hiç aşağıdaki kodlara bakma demek

 }

 boolean flagClientConnected = 0;

 byte byteFN = MB_FC_NONE;

 int Start;

 int WordDataLength;

 int ByteDataLength;

 int MessageLength;

 

 while (client.connected()) {

     if(client.available())

     {

     flagClientConnected = 1;

     int i = 0;

         while(client.available())

         {

         ByteArray[i] = client.read();

         i++;

         }

     client.flush();

     MBHoldingRegister[0] = 12345;  //40001

     MBHoldingRegister[1] = random(0,100); //40002

     MBHoldingRegister[2] = random(0,100); //40003

     // Register 3 ün değerine göre LED yanar veya söner 

     // yakmak için 0 söndürmek için 1 yollanır

     // D4 bacağı, LED yanarken 0V sönünce 3.3V oluyor

     digitalWrite(LED, MBHoldingRegister[3] ); //40004 teki bilgiye göre led yanar veya söner

     // Seri monitöre register değerlerini yazar

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

         Serial.print("[");

         Serial.print(i);

         Serial.print("]");

         Serial.print(MBHoldingRegister[i]);

         Serial.print(" ");

     }

     Serial.println("");


 //// rutine Modbus TCP

 byteFN = ByteArray[MB_TCP_FUNC];

 Start = word(ByteArray[MB_TCP_REGISTER_START],ByteArray[MB_TCP_REGISTER_START+1]);

 WordDataLength = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]);

 }

 

     // Handle request

     switch(byteFN) {

     case MB_FC_NONE:

     break;

 

     case MB_FC_READ_REGISTERS: // 03 Read Holding Registers

     ByteDataLength = WordDataLength * 2;

     ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.

     ByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data).

     for(int i = 0; i < WordDataLength; i++)

     {

     ByteArray[ 9 + i * 2] = highByte(MBHoldingRegister[Start + i]);

     ByteArray[10 + i * 2] = lowByte(MBHoldingRegister[Start + i]);

     }

     MessageLength = ByteDataLength + 9;

     client.write((const uint8_t *)ByteArray,MessageLength);

     byteFN = MB_FC_NONE;

     break;


     case MB_FC_WRITE_REGISTER: // 06 Write Holding Register

     MBHoldingRegister[Start] = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]);

     ByteArray[5] = 6; //Number of bytes after this one.

     MessageLength = 12;

     client.write((const uint8_t *)ByteArray,MessageLength);

     byteFN = MB_FC_NONE;

     break;

 

     case MB_FC_WRITE_MULTIPLE_REGISTERS: //16 Write Holding Registers

     ByteDataLength = WordDataLength * 2;

     ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.

     for(int i = 0; i < WordDataLength; i++)

     {

     MBHoldingRegister[Start + i] = word(ByteArray[ 13 + i * 2],ByteArray[14 + i * 2]);

     }

     MessageLength = 12;

     client.write((const uint8_t *)ByteArray,MessageLength); 

     byteFN = MB_FC_NONE;

     break;

     } // switch sonu

 } // while sonu


Master olarak cep telefonu da olabilir, ben size windows ta çalışan ücretsiz bir uygulamayı göstereceğim, master telefon niyetine kullanacağız. Chipkin Cas modbus scanner programını bu linkten indirebilirsiniz.



Soldaki boşluğa sağ klik yapıp add connection a tıklıyoruz. Nodemcu nun ip adresini yazıp Add TCP Connection a tıklıyoruz. Program modbus rtu yu da desteklemektedir. Solda oluşan bağlantıya sağ klik yapıp Add device a tıklıyoruz. Burada slave id yani kimliği veriliyor ama modbus tcp de bunun önemi yok, çünkü iletişim ip adresi üzerinden gerçekleşiyor. Modbus rtu da ise çok önemli, slave lere farklı adresler vermelisiniz. Add Device a tıklayıp cihazı ekledikten sonra yine solda Device a sağ tık yapıp Add task a tıklıyoruz.


Burada 03 Read holding registers fonksiyonunu seçip Lenght kısmına 4 yazıyoruz yani sadece 4 word okuyacağız 40001-40004 arasını. Add Request e tıklıyoruz. Bir de ledi yakıp söndürmek için yine solda Device a sağ tık yapıp Add write task seçiyoruz. Offset değerine 4 yazıyoruz yani 40004 registerine yazacağız. Value değer kutusuna 0 yazıyoruz.


Add write task işlemini bir de value 1 için yapalım. Ve hazırız..


Solda Read register task (okuma görevi) ını seçip Poll butonuna basarsak Nodemcu dan gelen bilgileri görebiliriz. 40004 için 1 gelmiş yani led sönük durumda. Poll a her bastığımızda 40002, 40003 register lerden 0-100 arası rastgele sayılar gelecektir. Arduino da öyle program yazılmıştı çünkü. Alt tarafta gönderilen ve nodemcu dan gelen response bilgileri hex olarak yazmaktadır. Poll ve response da ilk byte 00 slave id dir, ikinci byte 03 fonksiyon kodudur. 03 yani read holding registers.. Slave olan NodeMCU dan gelen cevap yani Response taki 3. byte, 08 gönderilen bilginin byte olarak uzunluğu. 4 ve 5. byte 30 39 hex, ondalık sayıya çevirirsek 12345. Bu çevrimler için windows 10 da bulunan hesap makinasından sol üstteki menü butonundan programlayıcı olanı seçin.

Ledi yakmak için Write single register at 40004 : 0 ı seçip Poll butonuna tıklayın, yalnız arduino kodu nedeniyle butona iki kez basınca led yanıyor, yani iki kez yazma paketi yollamak gerekiyor. Arduino seven arkadaşlardan biri, kodu, tek paketle çalışacak hale getirebilirse kodu buradan yayınlarız.. Nodemcu nun ağa bağlı olup olmadığını anlamanın bir yolu da ping atmak, cmd yi açın ve ping 192.168.1.253 yazın.. Eğer cevap geliyor ama pc de çalışan cas modbus scanner programı çalışmıyorsa, programı kapatıp açın.. Velhasıl, en bilinen bir şey onarma yöntemidir kapat aç çalışsın… Yeri gelmişken teknik bir tiyo daha vereyim, bir şey çalışıyorsa sakın kurcalama.. Bu nedenle kodu fazla kurcalamadım. 





Yorumlar

Bu blogdaki popüler yayınlar

VBA - Mscomm (seri port) ile veri loglama

RJ45 2 - Novexx barkod yazıcıya, S7-1200 plc ile etiket yazdırma

PC_SIMU V3 ile S7-1200 simülasyonu