NodeMCU Lolin ile Modbus TCP
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ü
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
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.
Yorumlar
Yorum Gönder