Bir ESP32 Modbus RS485 MQTT gateway, enerji analizörlerinden, frekans invertörlerinden (VFD), sıcaklık transmitterlerinden ve diğer Modbus RTU cihazlarından veri çekip bir PLC veya endüstriyel PC'ye gerek kalmadan Wi-Fi'a aktarmanızı sağlar. Bu eğitimde bir RS485 alıcı-vericiyi ESP32'ye bağlayacak, ModbusMaster kütüphanesiyle bir veya daha fazla slave cihazdan register okuyacak, ham değerleri ölçekleyecek ve bunları JSON olarak bir MQTT broker'ına yayınlayacaksınız.
Gateway Ne Yapar?
Modbus RTU, sorgulamaya dayalı bir master-slave protokolüdür: bir master belirli bir slave'den belirli bir register bloğunu istemedikçe hatta hiçbir şey olmaz. MQTT ise tam tersidir; olay güdümlü bir publish-subscribe protokolüdür. ESP32 bu ikisinin arasında durur:
- RS485 hattında Modbus master olarak görev yapar ve yapılandırılmış her cihazı sabit bir aralıkla sorgular.
- Bir register haritası kullanarak ham 16 bitlik register'ları mühendislik değerlerine (volt, derece, kilovatsaat) dönüştürür.
- MQTT client olarak görev yapar ve her cihaz için
connectmq/demo/mb-4d1e/meter1gibi bir topic'e bir JSON mesajı yayınlar.
Pek çok cihaz (her biri kendi slave adresiyle) tek bir RS485 çiftini paylaşabilir; böylece tek bir ESP32 enstrümanlarla dolu küçük bir panoya hizmet edebilir.
Donanım
| Parça | Notlar |
|---|---|
| ESP32 DevKit | RS485 için UART2 kullanılır |
| 3,3 V RS485 alıcı-verici (MAX3485, SP3485) | Klasik MAX485 5 V'luk bir parçadır; 3,3 V'luk bir çip tercih edin |
| Modbus RTU cihazı | Enerji analizörü, sıcaklık/nem transmitteri, VFD... |
| 120 Ω direnç | Uzun hatların uzak ucunda sonlandırma |
| Bükümlü çift kablo | Masa testinin ötesindeki her şey için ekranlı bükümlü çift |
Bazı alıcı-verici kartları yönü otomatik olarak değiştirir ve yalnızca RX ile TX'i dışarı verir. Bunlar da çalışır: aşağıdaki kodda DE/RE pinini kullanılmadan bırakmanız yeterlidir.
Bağlantılar
| Alıcı-verici pini | ESP32 pini | Amaç |
|---|---|---|
| VCC | 3V3 | Besleme |
| GND | GND | Toprak; varsa cihazın sinyal toprağına da bağlayın |
| RO | GPIO 16 (RX2) | Hattan ESP32'ye veri |
| DI | GPIO 17 (TX2) | ESP32'den hatta veri |
| DE ve RE (birleştirilmiş) | GPIO 4 | Göndermek için yüksek, almak için düşük |
| A / B | Cihaz A / B | Hat çifti |
En yaygın bağlantı hatası A ve B hatlarının ters bağlanmasıdır. Üreticiler hangisinin "artı" olduğu konusunda hemfikir değildir; bu yüzden yalnızca zaman aşımı alıyorsanız başka bir şeyi incelemeden önce bunları yer değiştirin. Sonlandırma dirençleri her cihaza değil, hattın iki fiziksel ucuna takılır.
Register Haritanızı Tanıyın
Her Modbus cihazı register'larını bir tabloda belgeler: adres, fonksiyon kodu, veri tipi ve ölçek. Bu tabloyu dikkatle okuyun, çünkü gateway hatalarının çoğuna yol açan sorulara o yanıt verir:
- Fonksiyon kodu: holding register'lar 03 fonksiyonuyla, input register'lar 04 fonksiyonuyla okunur. Yanlış fonksiyonla sormak bir istisna (exception) döndürür ya da hiçbir şey döndürmez.
- Adres ofseti: "30001" veya "40001" diyen dokümantasyon genellikle 04 veya 03 fonksiyonuyla 0 numaralı register adresini kasteder. Bazı kılavuzlar sıfır tabanlı, bazıları bir tabanlı adresler listeler.
- Ölçek: birçok cihaz 23,5 derece için
235gönderir (ölçek 0,1). - Veri tipi: 16 bit işaretsiz, 16 bit işaretli ya da iki register'a yayılmış 32 bitlik değerler; bazen word sırası ters çevrilmiş olarak.
- Seri port ayarları: baud hızı, parite ve stop bitleri birebir eşleşmelidir. 9600 8N1 yaygındır, ancak standartta Modbus varsayılanı 9600 8E1'dir.
ESP32 Modbus RS485 MQTT Sketch'i
Library Manager'dan Doc Walker'ın ModbusMaster ve Nick O'Leary'nin PubSubClient kütüphanelerini kurun. Cihazlarınız için değiştirmeniz gereken tek kısım en üstteki register haritasıdır. Örnek iki cihazı sorgular: 1 adresindeki bir sıcaklık/nem transmitteri ve 2 adresindeki bir enerji analizörü.
#include <WiFi.h>
#include <PubSubClient.h>
#include <ModbusMaster.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* CLIENT_ID = "mb-4d1e-gateway";
const char* TOPIC_PREFIX = "connectmq/demo/mb-4d1e";
const int RS485_RX = 16;
const int RS485_TX = 17;
const int RS485_DE = 4;
const uint32_t BAUD = 9600;
const uint32_t POLL_INTERVAL_MS = 10000;
struct Point {
const char* device; // topic seviyesi olur
uint8_t slave; // Modbus adresi
uint8_t fc; // 3 = holding, 4 = input
uint16_t reg; // register adresi (sıfır tabanlı)
uint8_t words; // 1 = 16 bit, 2 = 32 bit (önce yüksek word)
bool isSigned;
float scale;
const char* name; // JSON anahtarı
};
// Bu tabloyu cihazlarınızın dokümantasyonuna göre düzenleyin
Point points[] = {
{"climate1", 1, 4, 0x0001, 1, true, 0.1, "temperature_c"},
{"climate1", 1, 4, 0x0002, 1, false, 0.1, "humidity_pct"},
{"meter1", 2, 3, 0x0000, 1, false, 0.1, "voltage_v"},
{"meter1", 2, 3, 0x0001, 1, false, 0.01, "current_a"},
{"meter1", 2, 3, 0x0010, 2, false, 0.01, "energy_kwh"},
};
const size_t NUM_POINTS = sizeof(points) / sizeof(points[0]);
ModbusMaster node;
WiFiClient net;
PubSubClient mqtt(net);
uint32_t lastPoll = 0;
void preTx() { digitalWrite(RS485_DE, HIGH); }
void postTx() { digitalWrite(RS485_DE, LOW); }
bool readPoint(const Point& p, float& out) {
node.begin(p.slave, Serial2);
uint8_t rc = (p.fc == 4) ? node.readInputRegisters(p.reg, p.words)
: node.readHoldingRegisters(p.reg, p.words);
if (rc != node.ku8MBSuccess) {
Serial.printf("slave %u reg 0x%04X error 0x%02X\n", p.slave, p.reg, rc);
return false;
}
if (p.words == 2) {
uint32_t raw = ((uint32_t)node.getResponseBuffer(0) << 16) | node.getResponseBuffer(1);
out = p.isSigned ? (int32_t)raw * p.scale : raw * p.scale;
} else {
uint16_t raw = node.getResponseBuffer(0);
out = p.isSigned ? (int16_t)raw * p.scale : raw * p.scale;
}
return true;
}
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
}
void connectMqtt() {
char statusTopic[96];
snprintf(statusTopic, sizeof(statusTopic), "%s/status", TOPIC_PREFIX);
while (!mqtt.connected()) {
if (mqtt.connect(CLIENT_ID, statusTopic, 1, true, "offline")) {
mqtt.publish(statusTopic, "online", true);
} else {
delay(5000);
}
}
}
void pollAndPublish() {
// Aynı cihaza ait ardışık noktaları tek bir JSON mesajında grupla
size_t i = 0;
while (i < NUM_POINTS) {
const char* dev = points[i].device;
String json = "{";
int ok = 0, failed = 0;
while (i < NUM_POINTS && strcmp(points[i].device, dev) == 0) {
float v;
if (readPoint(points[i], v)) {
if (ok > 0) json += ",";
json += "\"" + String(points[i].name) + "\":" + String(v, 2);
ok++;
} else {
failed++;
}
delay(20); // çerçeveler arası boşluk, yavaş cihazlara yardımcı olur
i++;
}
if (ok > 0) json += ",";
json += "\"errors\":" + String(failed) + "}";
char topic[96];
snprintf(topic, sizeof(topic), "%s/%s", TOPIC_PREFIX, dev);
mqtt.publish(topic, json.c_str(), true);
Serial.printf("%s %s\n", topic, json.c_str());
}
}
void setup() {
Serial.begin(115200);
pinMode(RS485_DE, OUTPUT);
digitalWrite(RS485_DE, LOW);
Serial2.begin(BAUD, SERIAL_8N1, RS485_RX, RS485_TX);
node.preTransmission(preTx);
node.postTransmission(postTx);
connectWifi();
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
connectMqtt();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWifi();
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
if (millis() - lastPoll >= POLL_INTERVAL_MS) {
lastPoll = millis();
pollAndPublish();
}
}
Bu koddaki birkaç ayrıntı göründüğünden daha önemlidir:
preTransmissionvepostTransmissionDE pinini sürer; böylece alıcı-verici yalnızca ESP32 gönderim yaparken hatta konuşur. Bunlar olmadan alıcı-verici hattı hiç bırakmaz ve her okuma zaman aşımına uğrar.- Yayınlanan her mesaj bir
errorssayacı taşır. Böylece bir panel eski değerlerde sessizce donup kalmak yerine "cihaz mevcut ama bazı register'lar başarısız" bilgisini gösterebilir. - Mesajlar retained'dır; yeni bağlanan bir panel son değerleri anında görür.
setBufferSize(512), daha fazla nokta eklediğinizde daha uzun JSON için yer açar.
Çıktıyı kontrol edin:
mosquitto_sub -h broker.connectmq.com -p 1883 -i mb-4d1e-viewer -t "connectmq/demo/mb-4d1e/#" -v
ModbusMaster Hata Kodlarını Okumak
| Kod | Anlamı | Tipik neden |
|---|---|---|
0xE2 |
Yanıt zaman aşımına uğradı | Yanlış A/B polaritesi, yanlış baud veya parite, yanlış slave adresi, DE pininin değişmemesi |
0xE0 |
Yanıtta geçersiz slave ID | Aynı adrese sahip iki cihaz ya da gürültü |
0xE3 |
Geçersiz CRC | Eksik sonlandırma, uzun ekransız kablo, toprak potansiyel farkı |
0x02 |
Geçersiz veri adresi | Register mevcut değil; sıfır tabanlı ve bir tabanlı adreslemeyi kontrol edin |
0x01 |
Geçersiz fonksiyon | Yalnızca 04'e yanıt veren bir register için 03 fonksiyonunu kullanmak ya da tam tersi |
ESP32 Modbus RS485 MQTT İçin Sorun Giderme Kontrol Listesi
- Seri port ayarlarını cihaz menüsünden veya kılavuzundan doğrulayın: baud, parite, stop bitleri, adres.
- ESP32'yi devreye sokmadan önce bir cihazı USB-RS485 adaptörü ve bir PC Modbus aracıyla test edin. PC okuyamıyorsa ESP32 de okuyamaz.
- Her istek zaman aşımına uğruyorsa A ve B'yi yer değiştirin.
- Birkaç metreden uzun hatlarda her iki uca 120 Ω sonlandırma ekleyin ve sinyal toprağını bağlayın.
- Sorgulama aralığını gerçekçi tutun. 9600 baud'da bir düzine register okumak saniyenin hissedilir bir kısmını alır; hattın yanıt verebileceğinden daha hızlı sorgulamayın.
- Mümkün olduğunda ardışık register'ları tek bir istekte okuyun. On register için tek bir istek, tek register için on istekten çok daha hızlıdır.
Daha Fazla Değil, Daha Az Yayınlamak
Her on saniyede bir sorgulayıp her şeyi yayınlamak en basit tasarımdır, ancak sayaçlar yavaş değişir ve bulut trafiği birikir. Sketch'e kolayca eklenebilecek iki iyileştirme vardır: bir noktayı yalnızca bir ölü banttan (deadband) fazla değiştiğinde yayınlayın ve birkaç dakikada bir heartbeat olarak tam bir anlık görüntü gönderin. Bu desen değişimde raporlama (report by exception) yazısında anlatılıyor. Aynı soruna PC tabanlı gateway'ler ve daha büyük RTU ağları dahil gateway seviyesinden bakmak için Modbus RTU'dan MQTT'ye yazısına göz atın.
MQTT sizin için yeniyse MQTT broker'ın ne yaptığını anlatan yazıyla başlayın; JSON'u string birleştirme yerine bir kütüphaneyle oluşturmak için ArduinoJson MQTT rehberi iyi bir tamamlayıcıdır. ESP32 IoT eğitimi burada kullanılan Wi-Fi ve yeniden bağlanma temellerini ele alıyor.
Üretim İçin Sağlamlaştırma
Bir fabrika duvarındaki gateway yıllarca çalışır; bu yüzden ona altyapı gibi davranın:
- TLS ve kimlik bilgileri.
WiFiClientSecureile 8883 portunu ve bu gateway için verilmiş kimlik bilgilerini kullanın. Tesis verilerini asla herkese açık bir test topic'ine yayınlamayın. - Watchdog. Takılan bir Modbus işlemi veya Wi-Fi yığını kartı yeniden başlatsın diye task watchdog'u etkinleştirin.
- İzolasyon. Elektriksel gürültünün yüksek olduğu panolarda izole bir RS485 alıcı-verici, toprak döngülerinin ESP32'ye zarar vermesini önler.
- Varsayılan olarak salt okunur. MQTT üzerinden register yazmak motorları çalıştırabilir veya set değerlerini değiştirebilir. Yazma eklerseniz bunu izin verilen register'lardan oluşan bir listeyle sınırlayın ve değer aralıklarını doğrulayın.
- Broker seçimi. Tam yönetilen bir MQTT hizmeti, broker yamalarını ve sertifika yenilemeyi yapılacaklar listenizden çıkarır; ConnectMQ'nun topic gezgini de her cihazın raporladığını doğrulamanın hızlı bir yoludur.
ConnectMQ ile Başlayın
Prototipinizi broker.connectmq.com'un genel alanında geliştirin, ardından gateway'inize özel bir broker verin. ConnectMQ, TLS uç noktaları, broker başına kimlik bilgileri, canlı bir topic gezgini ve endüstriyel topic'ler için ISA-95 seviyelerine göre düzenlenmiş bir cihaz görünümü sunar. app.connectmq.com adresinde kredi kartı olmadan ücretsiz bir Serverless broker oluşturun ve sketch'i ona yönlendirin.
Sık Sorulan Sorular
Tek bir ESP32 birden fazla Modbus cihazını okuyabilir mi?
Evet. Aynı RS485 çifti üzerindeki her cihaza benzersiz bir slave adresi verin ve register'larını nokta tablosuna ekleyin. ESP32 bunları sırayla sorgular.
MAX485 mi, MAX3485 mi gerekir?
ESP32 3,3 V lojik kullanır; bu nedenle MAX3485 veya SP3485 gibi 3,3 V'luk bir alıcı-verici temiz bir seçimdir. 5 V'luk bir MAX485 kartı çalışabilir, ancak RO hattında seviye uyumuna dikkat etmek gerekir.
Neden sadece zaman aşımı hataları alıyorum?
Neredeyse her zaman bağlantı veya ayarlardan kaynaklanır: ters bağlanmış A/B, yanlış baud veya parite, yanlış slave adresi ya da DE/RE pininin değişmemesi. Cihazı önce bir PC aracıyla test edin.
Gateway MQTT'den Modbus register'larına yazabilir mi?
ModbusMaster yazmayı desteklediği için bu mümkündür, ancak yazmayı belirli register'larla sınırlayın ve değerleri doğrulayın. Endüstriyel ekipmana denetimsiz uzaktan yazma bir güvenlik riskidir.



