Node-RED

Node-RED Function Node: MQTT Mesajları İçin JavaScript

MQTT çalışmaları için Node-RED function node'una pratik bir rehber: payload'ları yeniden şekillendirme, gürültüyü filtreleme, birden fazla çıkışa yönlendirme, durum tutma ve hataları yönetme; canlı bir broker'a karşı içe aktarıp çalıştırabileceğiniz bir flow ile.

MQTT sensör mesajlarını dönüştüren JavaScript kodu içeren Node-RED function node editörü
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

Node-RED function node, editörden geçen her mesaj üzerinde kendi JavaScript kodunuzu çalıştırmanızı sağlar; bu da onu MQTT verilerini yeniden şekillendirmek için elinizdeki en esnek araç yapar. Bu rehberde birimleri dönüştürmek, hatalı ölçümleri ayıklamak, mesajları birden fazla çıkışa yönlendirmek, süregelen durumu tutmak ve hataları temiz biçimde yönetmek için onu kullanacaksınız; hepsini saniyeler içinde erişebileceğiniz canlı bir broker'a karşı yapacaksınız.

Node-RED Function Node Gerçekte Ne Yapar?

Node-RED'deki her mesaj msg adlı düz bir JavaScript nesnesidir. Function node bu nesneyi alır, yazdığınız kodu çalıştırır ve döndürdüğünüz her şeyi iletir. Aynı nesneyi döndürürseniz mesaj yoluna devam eder. null döndürürseniz mesaj durur. Bir dizi döndürürseniz her eleman farklı bir çıkış portuna gider. Sözleşmenin tamamı budur ve geri kalan hemen her şey bunun bir varyasyonudur.

Function içinde küçük bir yardımcı kümesine erişiminiz vardır:

Nesne Ne işe yarar
msg Genellikle payload ve topic içeren gelen mesaj
node Loglama (node.warn, node.error), durum (node.status), asenkron gönderim (node.send, node.done)
context, flow, global Mesajlar arasında node, sekme veya örnek kapsamında değer saklama
env env.get() ile ortam değişkenlerini ve subflow özelliklerini okuma
RED.util RED.util.cloneMessage() gibi yardımcı araçlar

Editörde ayrıca üç sekme bulunur: On Start node deploy edildiğinde bir kez çalışır, On Message her mesaj için çalışır ve On Stop flow durdurulduğunda veya yeniden deploy edildiğinde çalışır. Sayaçları başlatmak için doğru yer On Start, oluşturduğunuz zamanlayıcıları temizlemek için doğru yer ise On Stop'tur.

Daha önce bir MQTT flow'u bağlamadıysanız broker config node'unu ve mqtt in ile mqtt out node'larını ayrıntılı olarak ele alan Node-RED MQTT eğitimimizle başlayın.

Herkese Açık Broker'da Bir Test Akışı Kurun

Üzerinde çalışmak için bir mesaj akışına ihtiyacınız var. broker.connectmq.com adresindeki ücretsiz herkese açık MQTT broker anonim bağlantıları kabul eder; böylece hiçbir kayıt olmadan terminalden test verisi publish edebilirsiniz. Topic'lerinizin başkalarınınkiyle çakışmaması için benzersiz bir önek seçin:

# Her iki saniyede bir ölçüm publish et (durdurmak için Ctrl+C)
while true; do
  T=$(awk -v r=$RANDOM 'BEGIN{printf "%.1f", 18 + (r % 150) / 10}')
  mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-pub-7f3a \
    -t "connectmq/demo/7f3a/line1/sensor1/telemetry" \
    -m "{\"temp\": $T, \"unit\": \"C\", \"ts\": $(date +%s)}"
  sleep 2
done

Node-RED'de bir mqtt in node'u ekleyin, sunucusunu broker.connectmq.com port 1883'e yönlendirin, broker node'una boş olmayan benzersiz bir client ID verin, connectmq/demo/7f3a/# topic'ine subscribe olun ve Output ayarını "a parsed JSON object" yapın. Artık her mesaj msg.payload zaten ayrıştırılmış olarak gelir.

Herkese açık alanın herkes tarafından okunabildiğini unutmayın. Onu yalnızca deneyler için kullanın; gerçek tesis veya müşteri verileri için asla kullanmayın.

MQTT İçin Beş Node-RED Function Node Örneği

1. Bir payload'ı dönüştürün ve zenginleştirin

Cihazlar nadiren panonuzun veya veritabanınızın tam olarak istediğini publish eder. Bu function Celsius'u Fahrenheit'a çevirir, bir saha etiketi ekler ve cihaz adını topic'ten çıkarır:

// On Message
const parts = msg.topic.split("/");      // connectmq/demo/7f3a/line1/sensor1/telemetry
const line = parts[3];
const device = parts[4];

msg.payload = {
    device: device,
    line: line,
    tempC: msg.payload.temp,
    tempF: Math.round((msg.payload.temp * 9 / 5 + 32) * 10) / 10,
    receivedAt: new Date().toISOString()
};
return msg;

Topic'i bölmek, her cihazdan kimliğini payload içinde tekrarlamasını istemekten daha ucuz ve daha güvenilirdir.

2. Hatalı ölçümleri filtreleyin

Sensörler arıza yapar. null döndüren bir function node mesajı sessizce düşürür; böylece veri depolamaya ulaşmadan önce makullük sınırları uygulayabilirsiniz:

const t = msg.payload.temp;
if (typeof t !== "number" || Number.isNaN(t)) {
    node.warn("Non-numeric temp on " + msg.topic);
    return null;
}
if (t < -40 || t > 125) {
    return null;   // sensör aralığı dışında, at
}
return msg;

node.warn debug kenar çubuğuna ve Node-RED log'una yazar; bu da ek debug node'ları eklemeden filtrelemenin ne sıklıkla gerçekleştiğini görmenize yardımcı olur.

3. Birden fazla çıkışa yönlendirin

Node ayarlarında Outputs değerini 2 yapın, ardından bir dizi döndürün. 0. konum ilk porta, 1. konum ikinci porta gider. Tetiklenmemesi gereken port için null kullanın:

const limit = 30;
if (msg.payload.temp > limit) {
    const alert = {
        topic: msg.topic.replace("/telemetry", "/alert"),
        payload: { level: "high", temp: msg.payload.temp }
    };
    return [msg, alert];   // normal veri + alarm
}
return [msg, null];        // yalnızca normal veri

İkinci çıkış, topic alanı boş bir mqtt out node'unu besleyebilir; böylece msg.topic ne diyorsa oraya publish eder. Basit eşik yönlendirmesi için Switch node çoğu zaman daha anlaşılırdır; koşul hesaplamalar veya birden fazla alan içerdiğinde function'a başvurun.

4. Mesajlar arasında durum tutun

Node context, flow çalıştığı sürece mesajlar arasında korunur. Bu örnek yalnızca değer yarım dereceden fazla değiştiğinde publish eder; yavaş değişen sinyallerde trafiği büyük ölçüde azaltan basit bir ölü bant (deadband):

const key = "last_" + msg.topic;
const last = context.get(key);
const current = msg.payload.temp;

if (last !== undefined && Math.abs(current - last) < 0.5) {
    return null;
}
context.set(key, current);
return msg;

Değere başka bir sekmede veya yeniden başlatmadan sonra ihtiyacınız varsa flow, global veya kalıcı bir depo kullanın. Node-RED context rehberimiz kapsamları ve dosya tabanlı depolamanın nasıl etkinleştirileceğini açıklar.

5. Asenkron gönderin

Kodunuz bir zamanlayıcıyı veya bir promise'i beklediğinde function'dan dönmek çok erkendir. Complete ve Catch node'larının doğru çalışması için node.send() kullanın ve iş bitince node.done() çağırın:

// Ölçümleri biriktir ve her 10 mesajda bir özet gönder
let buf = context.get("buf") || [];
buf.push(msg.payload.temp);

if (buf.length >= 10) {
    const avg = buf.reduce((a, b) => a + b, 0) / buf.length;
    context.set("buf", []);
    setTimeout(() => {
        node.send({ topic: "connectmq/demo/7f3a/line1/sensor1/avg", payload: { avg: Number(avg.toFixed(2)) } });
        node.done();
    }, 0);
} else {
    context.set("buf", buf);
    node.done();
}
return null;

İçe Aktarabileceğiniz Eksiksiz Bir Flow

Bu JSON'u kopyalayın, editörde Ctrl+I'ya basın, yapıştırın ve deploy edin. Ardından yukarıdaki publish döngüsünü çalıştırın. 7f3a önekini her iki yerde de kendi rastgele dizenizle değiştirin.

[
  {"id":"b1","type":"mqtt-broker","name":"ConnectMQ public","broker":"broker.connectmq.com","port":"1883","clientid":"nr-demo-7f3a","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true},
  {"id":"in1","type":"mqtt in","z":"t1","name":"telemetry","topic":"connectmq/demo/7f3a/+/+/telemetry","qos":"0","datatype":"json","broker":"b1","x":130,"y":100,"wires":[["fn1"]]},
  {"id":"fn1","type":"function","z":"t1","name":"enrich + alert","func":"const p = msg.topic.split('/');\nmsg.payload.device = p[4];\nmsg.payload.tempF = Math.round((msg.payload.temp * 9 / 5 + 32) * 10) / 10;\nif (msg.payload.temp > 30) {\n  return [msg, {topic: msg.topic.replace('/telemetry','/alert'), payload: {level:'high', temp: msg.payload.temp}}];\n}\nreturn [msg, null];","outputs":2,"x":330,"y":100,"wires":[["dbg1"],["out1"]]},
  {"id":"dbg1","type":"debug","z":"t1","name":"data","active":true,"complete":"payload","x":530,"y":80,"wires":[]},
  {"id":"out1","type":"mqtt out","z":"t1","name":"alerts","topic":"","qos":"1","retain":"false","broker":"b1","x":530,"y":130,"wires":[]},
  {"id":"t1","type":"tab","label":"Function demo"}
]

Hata Yönetimi ve Hata Ayıklama

Function node içinde fırlatılan bir istisna bir hata loglar, ancak varsayılan olarak mesaj basitçe kaybolur. İki alışkanlık hataları görünür kılar.

Birincisi, mesajı ikinci argüman olarak vererek node.error(err, msg) kullanın. Bu biçim aynı sekmedeki tüm Catch node'larını tetikler; böylece hataları özel bir debug node'una, bir log dosyasına veya bir alarm topic'ine yönlendirebilirsiniz.

try {
    msg.payload = JSON.parse(msg.payload.raw);
    return msg;
} catch (err) {
    node.error("Bad raw payload: " + err.message, msg);
    return null;
}

İkincisi, node'un ne yaptığını doğrudan çalışma alanında göstermek için node.status() kullanın; örneğin görülen son değeri veya düşürülen mesaj sayısını:

node.status({ fill: "green", shape: "dot", text: "last " + msg.payload.temp + " C" });

Sık Yapılan Hatalar Kontrol Listesi

  1. Başka yere de ilettiğiniz bir mesajı değiştirmek. Bir çıkış iki kabloya gidiyorsa Node-RED mesajı sizin için klonlar, ancak context'e koyduğunuz nesneler referanstır. Saklamadan önce RED.util.cloneMessage(msg) kullanın.
  2. Return'ü unutmak. Return ifadesi olmayan bir function hiçbir şey göndermez; bu da bozuk bir subscription gibi görünür.
  3. String'i nesne gibi kullanmak. mqtt in node'u bir string çıkarıyorsa msg.payload.temp değeri undefined olur. Çıkışı ayrıştırılmış JSON olarak ayarlayın veya bir JSON node'u ekleyin; bkz. Node-RED JSON node rehberi.
  4. Zamanlayıcıları çalışır hâlde bırakmak. Interval'leri On Stop'ta temizleyin; aksi hâlde her yeniden deploy bir tane daha ekler.
  5. Mesaj başına ağır iş yapmak. Function node, Node-RED'in geri kalanıyla aynı event loop üzerinde çalışır. Yüksek frekanslı topic'lerde uzun döngüler tüm flow'ları yavaşlatır. Önce Node-RED delay node ile hız sınırlayın.
  6. Gizli bilgileri koda koymak. Token'ları function gövdesine yapıştırmak yerine env.get("API_KEY") kullanın; çünkü flow dosyaları sıklıkla paylaşılır veya git'e commit edilir.

Function Node Ne Zaman Kullanılmamalı?

Function node güçlüdür, ancak yirmi function node'dan oluşan flow'ları ekip arkadaşlarınızın okuması zordur. Özellikleri ayarlamak ve taşımak için Change node'u, basit koşullarla yönlendirme için Switch node'u ve ayrıştırma için JSON node'u tercih edin. Function node'larını gerçekten kod gerektiren mantık için saklayın ve her birine açık bir ad verin. Aynı function birden fazla sekmede görünüyorsa hataları tek bir yerde düzeltebilmek için onu bir subflow olarak paketleyin.

settings.js içinde functionExternalModules etkinleştirildiğinde Setup sekmesi aracılığıyla bir function node'una npm modülleri de yükleyebilirsiniz. Bu, tarih biçimlendirme kütüphaneleri gibi şeyler için kullanışlıdır, ancak eklediğiniz her modül güncellenmesi ve denetlenmesi gereken bir bağımlılık daha demektir. Resmi function node belgeleri sandbox'ta kullanılabilen her şeyi listeler.

Node-RED Flow'larınızı ConnectMQ'da Çalıştırın

Function mantığınız herkese açık broker'da çalıştığında, tek bir config node'unu düzenleyerek onu özel bir broker'a taşıyın. ConnectMQ; 8883 portunda TLS, MQTT 3.1.1 ve 5.0 desteği ve tarayıcı içi istemci ile canlı topic gezgini içeren bir portal sunan yönetilen bir MQTT broker'dır; böylece function'ınızın publish ettiği alarmları izleyebilirsiniz. app.connectmq.com adresinde kredi kartı olmadan ücretsiz bir Serverless broker oluşturun, oluşturulan kullanıcı adı, parola ve client ID'yi Node-RED broker node'una kopyalayın, Use TLS kutusunu işaretleyin ve yeniden deploy edin. Barındırma seçeneklerini karşılaştırıyorsanız bulut MQTT broker genel bakışımız nelere dikkat etmeniz gerektiğini açıklar.

Sık Sorulan Sorular

Node-RED function node async/await kullanabilir mi?

Evet. Function gövdesi asenkron bir sarmalayıcı içinde çalışır; böylece promise'leri await ile bekleyebilirsiniz. Await sonrasında dönüş değerine güvenmek yerine sonuçları node.send() ile gönderin ve node.done() çağırın.

Tek bir function node'dan birden fazla mesajı nasıl gönderirim?

Farklı çıkışlar için bir mesaj dizisi döndürün veya ilk çıkıştan art arda birkaç mesaj göndermek için return [[m1, m2, m3]] gibi dizi içinde dizi döndürün.

Function node'um neden hiçbir çıktı vermiyor?

Genellikle function null veya undefined döndürüyordur ya da bir istisna fırlatılmıştır. Debug kenar çubuğunda hataları kontrol edin ve mesajların geldiğini doğrulamak için en üste node.warn(msg) ekleyin.

Function node yüksek frekanslı MQTT verileri için yavaş mı?

Basit dönüşümler hızlıdır, ancak her function tek bir Node.js event loop'u üzerinde çalışır. Çok yüksek mesaj hızlarında kodu hafif tutun ve flow'un başlarında filtreleme veya hız sınırlama yapın.

İlgili yazılar

ISA-95 Topic'leriyle Node-RED OPC UA İstemcisinden MQTT'ye

Bir PLC veya simülasyon sunucusundaki OPC UA değişkenlerine node-red-contrib-opcua ile abone olun, node ID'lerini ISA-95 tarzı MQTT topic'lerine eşleyin ve değerleri kalite bilgisi ve zaman damgalarıyla yayınlayın.

8 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.