K_WORD2_TO_FLOAT & K_WORD4_TO_FLOAT: Function Block untuk Konversi Data Modbus 16/32/64‑bit ke FLOAT
Konversi register Modbus tidak sesederhana membaca angka. Perbedaan endianness, addressing, dan varian protokol kerap menimbulkan kebingungan di lapangan. Artikel ini memperkenalkan K_WORD2_TO_FLOAT dan K_WORD4_TO_FLOAT sebagai solusi praktis dan konsisten.
Photo by Possessed Photography / Unsplash
Dari register mentah ke nilai proses — menjembatani protokol klasik dengan kebutuhan data modern.
Ditulis oleh Ketut Kumajaya — 17 Oktober 2025
Latar Belakang
Modbus adalah protokol komunikasi klasik yang diperkenalkan sejak akhir 1970‑an dan hingga kini tetap menjadi standar de facto di berbagai sistem industri. Kesederhanaan dan keterbukaannya membuat Modbus terus digunakan secara luas, baik pada perangkat lama maupun perangkat modern, mulai dari PLC hingga flowmeter.
Namun, karena Modbus hanya mendefinisikan pertukaran data dalam bentuk register 16‑bit, interpretasi data bernilai 32‑bit atau 64‑bit memerlukan mekanisme konversi tambahan. Tantangan umum yang muncul meliputi perbedaan endianness, perbedaan addressing, serta variasi hardware layer dan varian protokol.
Endianness
Endianness adalah urutan penyimpanan byte atau word dalam data biner:
- Big‑endian: byte paling signifikan (MSB) disimpan lebih dulu.
- Little‑endian: byte paling signifikan disimpan paling akhir.
Contoh pada Modbus 32‑bit
Nilai 1234.56 (FLOAT 32‑bit) dapat dikirim sebagai dua register 16‑bit:
| Word Order | Register1 | Register2 | Keterangan |
|---|---|---|---|
| Big‑endian (MSB dulu) | 0x449A | 0x51EC | MSB → LSB |
| Little‑endian (LSB dulu) | 0x51EC | 0x449A | LSB → MSB |
Catatan untuk 64‑bit
Pada data 64‑bit, variasi urutan bisa terjadi baik di level register maupun byte, sehingga interpretasi lebih kompleks.
Praktik Lapangan
Jika hasil konversi tidak sesuai ekspektasi, langkah pertama yang umum dilakukan adalah menukar urutan register.
Pemahaman mengenai endianness ini penting karena menjadi salah satu sumber kesalahan paling sering saat integrasi perangkat Modbus lintas vendor.
Addressing
Selain endianness, Modbus juga memiliki perbedaan sistem penomoran alamat register:
- Zero‑based addressing: register pertama ditandai sebagai alamat 0.
- One‑based addressing: register pertama ditandai sebagai alamat 1.
Akibatnya, dapat terjadi perbedaan satu offset antara dokumentasi perangkat dan implementasi di SCADA/DCS. Misalnya, dokumentasi menyebutkan data di register 40001, tetapi pada sistem zero‑based harus diakses pada alamat 40000.
Perbedaan addressing ini sering menjadi sumber kebingungan saat integrasi, sehingga penting untuk selalu memverifikasi konvensi yang digunakan oleh perangkat maupun sistem SCADA/DCS.
Hardware Layer dan Varian Modbus
Modbus hadir dalam beberapa varian:
- Modbus RTU: berjalan di atas RS‑232/RS‑485 dengan format biner yang efisien, paling umum digunakan di lapangan.
- Modbus ASCII: juga berbasis serial, data dikirim dalam teks ASCII, lebih mudah dibaca manusia tetapi jarang dipakai di aplikasi modern.
- Modbus TCP/IP: berjalan di atas Ethernet, menggunakan MBAP header menggantikan CRC, umum pada perangkat generasi baru.
- Modbus RTU over TCP: frame RTU (lengkap dengan CRC) ditransmisikan melalui TCP tanpa MBAP header. Biasanya digunakan untuk kompatibilitas dengan perangkat lama melalui jaringan IP.
Perbedaan antara Modbus TCP/IP dan Modbus RTU over TCP sangat penting dipahami, karena driver atau library harus sesuai dengan format frame yang digunakan. Pemilihan varian yang tepat memastikan komunikasi berjalan konsisten di berbagai perangkat dan sistem.
Catatan Tambahan
- Pada Modbus RTU, integritas data dijaga dengan CRC di level frame.
- Pada Modbus TCP/IP, CRC tidak digunakan karena protokol TCP/IP sudah memiliki mekanisme pemeriksaan integritas bawaan (checksum, sequence number, retransmission). MBAP header menggantikan CRC dalam frame TCP/IP.
- Frame Modbus RTU juga dapat ditransmisikan melalui media nirkabel seperti LoRa dalam mode transparent. Dalam hal ini, LoRa hanya berfungsi sebagai saluran komunikasi, sementara integritas data tetap dijaga oleh CRC Modbus.
K_WORD2_TO_FLOAT
Function Block ini digunakan untuk menggabungkan dua register UINT menjadi nilai FLOAT. FB ini menjadi baseline parsing untuk data 16‑bit maupun 32‑bit, baik ketika perangkat mengirimkan data dalam format IEEE 754 maupun sebagai akumulator numerik.
IN0 UINT LSB
IN1 UINT MSB"] --> B{Cek Endianness
Hasil sesuai?} B -->|Ya| C["Gabung ke 32-bit
Temp32 = IN0 OR (IN1 << 16)"] B -->|Tidak - Endian salah| D["Tukar Urutan
Swap IN0 ↔ IN1
Ulangi Cek"] D --> B C --> E{"D = 0?"} E -->|Ya| F["RawFloat = 0.0
Proteksi Div-by-Zero"] E -->|Tidak| G{IEE = TRUE?} G -->|Ya| H["Mode IEEE 754
RawFloat = GETFLOAT(Temp32)/D"] G -->|Tidak| I["Mode Numerik
RawFloat = Temp32 as UDINT / D"] H --> K{"IHL = ILL?"} I --> K F --> K K -->|Ya| L["OUT = OLL
(Fallback jika range input nol)"] K -->|Tidak| M["Scaling Linier:
OUT = (OHL-OLL)/(IHL-ILL) * (RawFloat-ILL) + OLL"] L --> N["End: Output FLOAT
(Siap untuk Tampilan/Proses)"] M --> N classDef start fill:#e1f5fe,stroke:#333,stroke-width:2px classDef final fill:#c8e6c9,stroke:#333,stroke-width:2px classDef swap fill:#fff3e0,stroke:#333,stroke-width:2px classDef decision fill:#f3e5f5,stroke:#333,stroke-width:2px classDef process fill:#ffffff,stroke:#333,stroke-width:2px class A start class N final class D swap class B,E,G,K decision class C,F,H,I,L,M process
Fitur Utama
- Mendukung data 16‑bit: IN0 diisi, IN1 = 0,
IEE = FALSE. - Mendukung data 32‑bit: IN0 dan IN1 diisi sesuai register.
- Mode IEEE 754 (
IEE = TRUE) atau mode numerik (IEE = FALSE). - Skala pembagi (
D) dengan proteksi pembagian nol. - Fleksibel terhadap variasi endian antar perangkat.
Klik untuk menampilkan kode K_WORD2_TO_FLOAT
(*
------------------------------------------------------------------------------
FB Name : K_WORD2_TO_FLOAT
Purpose : Konversi 2 register UINT menjadi FLOAT lalu scaling linier
ke Engineering Unit (EU) dengan proteksi div/0.
Author : Ketut Kumajaya
Contributor : Copilot (Microsoft AI)
Version : 1.1
Date : 21/10/2025
Input :
IN0 (UINT) - LSB Modbus
IN1 (UINT) - MSB Modbus
D (LONG) - Skala pembagi (anti-div-zero tahap dasar)
IEE (BOOL) - TRUE = interpretasi IEEE 754 FLOAT
ILL (FLOAT)- Input Low Limit
IHL (FLOAT)- Input High Limit
OLL (FLOAT)- Output Low Limit
OHL (FLOAT)- Output High Limit
Output :
OUT (FLOAT) - Nilai hasil scaling ke EU
Notes :
- Proteksi div/0:
* Jika D=0 maka OUT=0.0
* Jika IHL=ILL maka OUT=OLL
- Rumus scaling:
OUT = (OHL - OLL) / (IHL - ILL) * (Raw - ILL) + OLL
- Urutan IN0/IN1 dapat ditukar jika perangkat menggunakan endian berbeda
- Cocok untuk konversi register mentah atau data FLOAT ke engineering unit
- FB ini tidak mendukung data 64-bit
- Untuk "tanpa scaling", gunakan ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0
-> OUT = Raw (identitas)
Contoh konfigurasi:
1. Sensor 4–20 mA -> 0–100 bar
ILL=4.0, IHL=20.0, OLL=0.0, OHL=100.0
2. Register 0–32767 -> 0–5000 Nm³/h
ILL=0.0, IHL=32767.0, OLL=0.0, OHL=5000.0
3. Tanpa scaling (nilai asli diteruskan)
ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0
------------------------------------------------------------------------------
*)
FUNCTION_BLOCK K_WORD2_TO_FLOAT
VAR_INPUT
IN0 : UINT;
IN1 : UINT;
D : LONG;
IEE : BOOL;
ILL : FLOAT;
IHL : FLOAT;
OLL : FLOAT;
OHL : FLOAT;
END_VAR
VAR_OUTPUT
OUT : FLOAT;
END_VAR
VAR
Temp32 : DWORD;
RawFloat : FLOAT;
END_VAR
(* Gabungkan 2 UINT menjadi pola bit 32-bit *)
Temp32 := ULONG_TO_DWORD(UINT_TO_ULONG(IN0));
Temp32 := Temp32 + SHL_DWORD(ULONG_TO_DWORD(UINT_TO_ULONG(IN1)), 16);
(* Hitung nilai dasar *)
IF D <> 0 THEN
IF IEE THEN
RawFloat := GETFLOAT(Temp32) / LONG_TO_FLOAT(D);
ELSE
RawFloat := LONG_TO_FLOAT(DWORD_TO_LONG(Temp32)) / LONG_TO_FLOAT(D);
END_IF;
ELSE
RawFloat := 0.0;
END_IF;
(* Scaling linier ke engineering unit *)
IF (IHL <> ILL) THEN
OUT := ((OHL - OLL) / (IHL - ILL)) * (RawFloat - ILL) + OLL;
ELSE
OUT := OLL; (* fallback jika range input nol *)
END_IF;
END_FUNCTION_BLOCK
K_WORD4_TO_FLOAT
Function Block ini digunakan untuk menggabungkan empat register UINT menjadi nilai 64‑bit, kemudian dikonversi ke FLOAT untuk keperluan tampilan, trending, atau estimasi. FB ini umumnya dipakai untuk perangkat yang mengirimkan akumulator energi, counter besar, atau nilai kumulatif lainnya.
IN0 UINT LSB Low
IN1 UINT MSB Low
IN2 UINT LSB High
IN3 UINT MSB High"] --> B{Cek Endianness
Hasil sesuai?} B -->|Ya| C["Gabung Low32 = IN1 << 16 OR IN0
High32 = IN3 << 16 OR IN2"] B -->|Tidak - Endian salah| D["Tukar Urutan
Swap IN0 ↔ IN1 (Low)
Swap IN2 ↔ IN3 (High)
Ulangi Cek"] D --> B C --> E{"D = 0?"} E -->|Ya| F["Raw64 = 0.0
Proteksi Div-by-Zero"] E -->|Tidak| G["Hitung 64-bit dasar
Raw64 = (High32 * 2^32 + Low32) / D"] F --> K{"IHL = ILL?"} G --> K K -->|Ya| L["OUT = OLL
(Fallback jika range input nol)"] K -->|Tidak| M["Scaling Linier:
OUT = (OHL-OLL)/(IHL-ILL) * (Raw64-ILL) + OLL"] L --> N["End: Output FLOAT
(Untuk Monitoring/Estimasi)"] M --> N classDef start fill:#e1f5fe,stroke:#333,stroke-width:2px classDef final fill:#c8e6c9,stroke:#333,stroke-width:2px classDef swap fill:#fff3e0,stroke:#333,stroke-width:2px classDef decision fill:#f3e5f5,stroke:#333,stroke-width:2px classDef process fill:#ffffff,stroke:#333,stroke-width:2px class A start class N final class D swap class B,E,K decision class C,F,G,L,M process
Fitur Utama
- Menggabungkan Low32 dan High32 secara manual.
- Rumus:
OUT = (High32 × 2^32 + Low32) ÷ D. - Proteksi pembagian nol.
- Catatan presisi: konversi ke FLOAT 32‑bit dilakukan agar nilai dapat diproses di DCS. Hasil ini memadai untuk kebutuhan operasional sehari‑hari, tetapi tidak memenuhi standar akurasi untuk audit atau billing. Untuk keperluan tersebut, gunakan data 64‑bit asli dari perangkat.
Klik untuk menampilkan kode K_WORD4_TO_FLOAT
(*
------------------------------------------------------------------------------
FB Name : K_WORD4_TO_FLOAT
Purpose : Menggabungkan 4 register UINT menjadi nilai 64-bit lalu scaling
linier ke Engineering Unit (EU).
Author : Ketut Kumajaya
Contributor : Copilot (Microsoft AI)
Version : 1.1
Date : 21/10/2025
Input :
IN0 (UINT) - LSB dari Low32
IN1 (UINT) - MSB dari Low32
IN2 (UINT) - LSB dari High32
IN3 (UINT) - MSB dari High32
D (LONG) - Skala pembagi (anti-div-zero tahap dasar)
ILL (FLOAT)- Input Low Limit
IHL (FLOAT)- Input High Limit
OLL (FLOAT)- Output Low Limit
OHL (FLOAT)- Output High Limit
Output :
OUT (FLOAT) - Nilai hasil scaling ke EU
Notes :
- OUT = (High32 * 2^32 + Low32) / D
- Konstanta 4294967296.0 = 2^32, digunakan untuk menggeser High32
- Proteksi div/0:
* Jika D=0 maka OUT=0.0
* Jika IHL=ILL maka OUT=OLL
- Scaling linier:
OUT = (OHL - OLL) / (IHL - ILL) * (Raw - ILL) + OLL
- Urutan IN0/IN1 dan IN2/IN3 dapat ditukar jika perangkat endian berbeda
- Presisi terbatas karena hasil disimpan sebagai FLOAT 32-bit
- Untuk "tanpa scaling", gunakan ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0
(hasil OUT = Raw, identitas)
- Gunakan hanya untuk tampilan/estimasi, bukan akurasi billing
Contoh konfigurasi:
1. Energi Wh 64-bit ke kWh
D=1000, ILL=0.0, IHL=1000000.0, OLL=0.0, OHL=1000.0
2. Counter 0–1e12 ke 0–100 %
D=1, ILL=0.0, IHL=1.0e12, OLL=0.0, OHL=100.0
3. Tanpa scaling
D=1, ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0
------------------------------------------------------------------------------
*)
FUNCTION_BLOCK K_WORD4_TO_FLOAT
VAR_INPUT
IN0 : UINT;
IN1 : UINT;
IN2 : UINT;
IN3 : UINT;
D : LONG;
ILL : FLOAT;
IHL : FLOAT;
OLL : FLOAT;
OHL : FLOAT;
END_VAR
VAR_OUTPUT
OUT : FLOAT;
END_VAR
VAR
Low32 : DWORD;
High32 : DWORD;
Raw64 : FLOAT;
END_VAR
(* Gabungkan masing-masing pasangan *)
Low32 := SHL_DWORD(ULONG_TO_DWORD(UINT_TO_ULONG(IN1)), 16)
+ ULONG_TO_DWORD(UINT_TO_ULONG(IN0));
High32 := SHL_DWORD(ULONG_TO_DWORD(UINT_TO_ULONG(IN3)), 16)
+ ULONG_TO_DWORD(UINT_TO_ULONG(IN2));
(* Hitung nilai dasar sebagai FLOAT *)
IF D <> 0 THEN
Raw64 := (LONG_TO_FLOAT(DWORD_TO_LONG(High32)) * 4294967296.0 (* 2^32 *)
+ LONG_TO_FLOAT(DWORD_TO_LONG(Low32)))
/ LONG_TO_FLOAT(D);
ELSE
Raw64 := 0.0;
END_IF;
(* Scaling linier ke engineering unit *)
IF (IHL <> ILL) THEN
OUT := ((OHL - OLL) / (IHL - ILL)) * (Raw64 - ILL) + OLL;
ELSE
OUT := OLL; (* fallback jika range input nol *)
END_IF;
END_FUNCTION_BLOCK
Panduan Singkat Penggunaan
Kapan menggunakan K_WORD2_TO_FLOAT
- 16‑bit: isi IN0, set IN1 = 0, gunakan
IEE = FALSE. - 32‑bit: isi IN0 dan IN1 sesuai register.
- IEEE 754 →
IEE = TRUE. - Numerik →
IEE = FALSE.
- IEEE 754 →
- Jika hasil tidak sesuai, coba tukar IN0 dan IN1.
- Setelah nilai dasar diperoleh, FB otomatis melakukan scaling linier dari range input (
ILL–IHL) ke range output (OLL–OHL). - Untuk tanpa scaling, gunakan konfigurasi identitas:
ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0.
FB ini ideal untuk parsing data register tunggal atau ganda yang umum ditemui pada sensor dan flowmeter.
Kapan menggunakan K_WORD4_TO_FLOAT
- 64‑bit: isi IN0–IN3 sesuai register.
- Cocok untuk akumulator besar (misalnya energi, volume kumulatif, atau counter jarak jauh).
- OUT hanya untuk monitoring, bukan billing.
- Jika hasil tidak sesuai, coba tukar pasangan register.
- Setelah nilai dasar diperoleh, FB otomatis melakukan scaling linier dari range input (
ILL–IHL) ke range output (OLL–OHL). - Untuk tanpa scaling, gunakan konfigurasi identitas:
ILL=0.0, IHL=1.0, OLL=0.0, OHL=1.0.
FB ini relevan untuk perangkat yang mengirimkan nilai kumulatif besar, dengan catatan hasil konversi hanya dipakai untuk tampilan dan analisis tren.
Contoh Konfigurasi Uji
| FB | Input Register | Mode | D | ILL/IHL | OLL/OHL | OUT (ekspektasi) |
|---|---|---|---|---|---|---|
| K_WORD2_TO_FLOAT | IN0=0x51EC, IN1=0x449A | IEE=TRUE | 1 | 0.0 / 1.0 | 0.0 / 1.0 | 1234.56 |
| K_WORD2_TO_FLOAT | IN0=1000, IN1=0 | IEE=FALSE | 10 | 0.0 / 1.0 | 0.0 / 1.0 | 100.0 |
| K_WORD4_TO_FLOAT | IN0=0, IN1=0, IN2=1, IN3=0 | Numerik | 1 | 0.0 / 1.0 | 0.0 / 1.0 | 4294967296.0 |
Catatan Umum
- D = 0 → OUT = 0.0.
- Addressing dapat berbeda (0‑based vs 1‑based).
- Endianness antar vendor tidak selalu sama.
- Pembagi (
D) didefinisikan sebagaiLONG(32‑bit signed). Rentang ini sudah memadai untuk kebutuhan scaling Modbus, sekaligus lebih sederhana dibandingULONGyang di Supcon memerlukan konversi tambahan.
Lanjutan
Setelah data akumulator berhasil direkonstruksi menjadi nilai FLOAT yang siap dipakai, langkah berikutnya sering kali adalah menghitung delta energi untuk kebutuhan laporan periodik, rekonsiliasi antar meter, maupun audit energi.
Untuk tujuan tersebut, tersedia Function Block K_ACCDELTA sebagai modul terpisah. FB ini dirancang untuk menghitung delta akumulator secara andal, dengan proteksi nilai negatif dan mekanisme snapshot reset untuk sinkronisasi baseline antar meter. Modul ini menjadi kelanjutan alami dari FB parsing (K_WORD2/4_TO_FLOAT), sehingga alur pengolahan data tetap modular dan konsisten.
→ K_ACCDELTA: Function Block untuk Perhitungan Delta Akumulator
Penutup
Konversi register Modbus membutuhkan ketelitian terhadap format data, endianness, addressing, dan varian protokol. Kedua Function Block di atas dirancang untuk menyajikan nilai proses secara konsisten dan dapat ditelusuri kembali, sekaligus mengakui batasan presisi ketika menggunakan FLOAT 32‑bit.
Dengan dokumentasi yang jelas dan logika yang eksplisit, konversi bukan sekadar kalkulasi teknis, melainkan sarana untuk memastikan operator, engineer, dan auditor memahami proses dengan cara yang sama. Transparansi inilah yang menjadikan integrasi data lebih andal dan berkelanjutan.