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
- 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. - Return'ü unutmak. Return ifadesi olmayan bir function hiçbir şey göndermez; bu da bozuk bir subscription gibi görünür.
- String'i nesne gibi kullanmak.
mqtt innode'u bir string çıkarıyorsamsg.payload.tempdeğeriundefinedolur. Çıkışı ayrıştırılmış JSON olarak ayarlayın veya bir JSON node'u ekleyin; bkz. Node-RED JSON node rehberi. - 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.
- 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.
- 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.



