Node.js Modbus开发実践完全ガイド:modbus-serial库 TCP/RTUクライアント側は データの解析与MQTTトップクラウドゲートウェイ

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

Node.js以其异步非阻塞、事件驱动的特性,非常适合开发工业データの収集和Modbus通信ゲートウェイ。modbus-serial是Node.js生态中最流行、最活跃的Modbus库,纯JavaScript実装,サポートModbus RTU(serial port)、TCP和RTU-over-TCP,同时提供クライアント側は和サーバー側。API。本記事从インストール設定、TCP/RTUクライアント側は、データ型解析、サーバ側シミュレーション、バッチ収集。、MQTT上云到工业级ゲートウェイ実践,包括的讲解Node.js Modbus开发。

一、modbus-serial库概述

1.1 什么是modbus-serial

modbus-serial是一个纯JavaScript実装的Modbusプロトコル库,由Yaacov Zamir维护,在npm上每周下载量超过10万次,是Node.js生态中最成熟的Modbus solution。它同时サポートModbus RTU(経由シリアルポート)、Modbus TCP和RTU-over-TCP3つの伝送方式,提供Promise和回调两种API风格,并内置了Modbusサーバー側。(slave)模拟功能。

1.2 核心特性

  • 多协议サポート:Modbus RTU(serial port)、Modbus TCP、RTU-over-TCP
  • クライアント側は+サーバー側。:既可以做マスター采集データ,也可以做ステーションからのシミュレーションデバイス
  • Promise API:すべての操作戻るPromise,サポートasync/await
  • データ型解析:内置32ビット整数、浮動小数点数、ストリング·ストリング等多レジスタのデータの解析
  • シリアルポートサポート:基于node-serialport,サポートWindows/Linux/macOS
  • 连接管理:自動再接続。、タイムアウト控制、トランザクションID管理
  • 纯JavaScript:无需编译原生モジュール(TCPモード),跨プラットフォーム
  • 活跃维护:継続的な更新,issueレスポンス快,社区活跃

1.3 サポート的機能コード

function code方法説明
01readCoils(addr, count)リードコイルを読む
02readDiscreteInputs(addr, count)read discrete inputs
03readHoldingRegisters(addr, count)read holding registers
04readInputRegisters(addr, count)read input registers
05writeCoil(addr, value)write single coil
06writeRegister(addr, value)write single register
15writeCoils(addr, values)write multiple coils
16writeRegisters(addr, values)write multiple registers
43readDeviceIdentification()读デバイス标识(MEI)

二、インストール与早速スタート。

# インストールmodbus-serial
npm install modbus-serial

# もし需要RTUシリアルポートサポート,还需インストールserialport
npm install serialport

# 验证インストール
node -e "const ModbusRTU = require('modbus-serial'); console.log('modbus-serial已インストール');"
// quick_start.js - 5数分で素早く
const ModbusRTU = require("modbus-serial");

// 作成クライアント側は实例
const client = new ModbusRTU();

// 設定駅からの住所(ユニットID)
client.setID(1);

// 設定タイムアウト(毫秒)
client.setTimeout(5000);

// connectModbus TCPdevice
async function main() {
    try {
        // 连接到Modbus TCPslave
        await client.connectTCP("192.168.1.100", { port: 502 });
        console.log("接続しましたModbus TCPdevice");

        // read10レジスタを保持する。(住所から。0开始)
        const result = await client.readHoldingRegisters(0, 10);
        console.log("保持Register:", result.data);

        // Write to a single register(address100,值1234)
        await client.writeRegister(100, 1234);
        console.log("書き込み済み。レジスタ100=1234");

        // read81つのコイル
        const coils = await client.readCoils(0, 8);
        console.log("Coil status:", coils.data);

        // 接続を閉じる
        client.close();
        console.log("连接已关闭");
    } catch (err) {
        console.error("操作に失敗しました:", err.message);
    }
}

main();

三、Modbus TCPクライアント側は详解

// tcp_client.js - Modbus TCPクライアント側は完全な例
const ModbusRTU = require("modbus-serial");

class ModbusTcpClient {
    constructor(host, port = 502, unitId = 1, timeout = 5000) {
        this.client = new ModbusRTU();
        this.host = host;
        this.port = port;
        this.unitId = unitId;
        this.timeout = timeout;
        this.connected = false;
    }

    // connect
    async connect() {
        this.client.setID(this.unitId);
        this.client.setTimeout(this.timeout);

        await this.client.connectTCP(this.host, {
            port: this.port,
            // 可选:自動再接続。
            // autoReconnect: true,
            // reconnectInterval: 1000,
        });
        this.connected = true;
        console.log(`Connected ${this.host}:${this.port}, slaveID=${this.unitId}`);
    }

    // read holding registers
    async readHolding(address, count) {
        const result = await this.client.readHoldingRegisters(address, count);
        return result.data;  // 戻るnumber配列
    }

    // read input registers
    async readInput(address, count) {
        const result = await this.client.readInputRegisters(address, count);
        return result.data;
    }

    // リードコイルを読む
    async readCoils(address, count) {
        const result = await this.client.readCoils(address, count);
        return result.data;  // 戻るboolean配列
    }

    // read discrete inputs
    async readDiscrete(address, count) {
        const result = await this.client.readDiscreteInputs(address, count);
        return result.data;
    }

    // write single register
    async writeRegister(address, value) {
        await this.client.writeRegister(address, value);
    }

    // write multiple registers
    async writeRegisters(address, values) {
        await this.client.writeRegisters(address, values);
    }

    // write single coil
    async writeCoil(address, value) {
        await this.client.writeCoil(address, value);  // value: true/false
    }

    // Close
    close() {
        this.client.close();
        this.connected = false;
    }
}

// 使用例の例
async function main() {
    const mb = new ModbusTcpClient("192.168.1.100", 502, 1);
    await mb.connect();

    // 読み取りセンサーはデータ
    const temp = await mb.readHolding(0, 4);
    console.log("temperature:", temp[0] / 10, "°C");
    console.log("湿度:", temp[1] / 10, "%");
    console.log("电压:", temp[2] / 10, "V");
    console.log("电流:", temp[3] / 100, "A");

    // 控制デバイス
    await mb.writeCoil(0, true);   // 启动电机
    await mb.writeRegister(10, 50); // 設定频率50Hz

    mb.close();
}

main().catch(console.error);

四、Modbus RTUシリアルポートクライアント側は

// rtu_client.js - Modbus RTUシリアルポートクライアント側は
const ModbusRTU = require("modbus-serial");

async function main() {
    const client = new ModbusRTU();
    client.setID(1);        // slave address
    client.setTimeout(5000); // タイムアウト5秒

    // Connect serial port(Linux: /dev/ttyUSB0, Windows: COM3, macOS: /dev/tty.usbserial-xxx)
    await client.connectRTU("/dev/ttyUSB0", {
        baudRate: 9600,      // Baud rate
        dataBits: 8,         // data bit
        parity: "none",      // check digit: none/even/odd
        stopBits: 1,         // stop bit
        // 可选:RS485自动收发控制
        // rtsControl: "hardware",
    });
    console.log("RTUシリアルポート接続された");

    // Read and hold register
    const result = await client.readHoldingRegisters(0, 10);
    console.log("register data:", result.data);

    // 読み取り多个スレーブ(バス上多个デバイス)
    for (let id = 1; id <= 5; id++) {
        client.setID(id);
        try {
            const data = await client.readHoldingRegisters(0, 4);
            console.log(`slave${id}:`, data.data);
        } catch (err) {
            console.log(`slave${id}応答なし。:`, err.message);
        }
    }

    client.close();
}

main().catch(console.error);

4.1 シリアルデバイス查找

// list_ports.js - 查找可用シリアルポート
const SerialPort = require("serialport");

async function listSerialPorts() {
    const ports = await SerialPort.list();
    console.log("可用シリアルポート:");
    ports.forEach(port => {
        console.log(`  路径: ${port.path}`);
        console.log(`  制造商: ${port.manufacturer || "Unknown"}`);
        console.log(`  序列号: ${port.serialNumber || "无"}`);
        console.log(`  pnpId: ${port.pnpId || "无"}`);
        console.log("---");
    });
}

listSerialPorts();

// 常见シリアルポート路径:
// Linux:   /dev/ttyUSB0 (USB转RS485), /dev/ttyS0 (板载シリアルポート)
// Windows: COM3, COM4
// macOS:   /dev/tty.usbserial-A123456, /dev/cu.usbserial-A123456

五、データ型解析

Modbusレジスタ是16位的,但实际デバイス经常用多1つのレジスタ组合存储32ビット整数、浮動小数点数、ストリング·ストリング等データ。modbus-serial提供了buffer工具类来解析这些データ。

// data_types.js - 多レジスタのデータ型解析
const ModbusRTU = require("modbus-serial");

// 方法一:使用modbus-serial内置的bufferTools
async function readDataTypes(client) {
    // read81つのレジスタ(足够存放各种データ型)
    const result = await client.readHoldingRegisters(0, 8);
    const regs = result.data;  // [r0, r1, r2, r3, r4, r5, r6, r7]

    // ====== 16ビット整数(单1つのレジスタ) ======
    const uint16 = regs[0];           // 符号なし。16位
    const int16 = regs[0] > 32767 ? regs[0] - 65536 : regs[0];  // 記号付き。16位

    // ====== 32ビット整数(两1つのレジスタ) ======
    // ビッグエンディアンモード(前の単語。):value = (r0 << 16) | r1
    const uint32_be = (regs[0] << 16) | regs[1];
    // リトルエンディアンモード(前に低い言葉。):value = (r1 << 16) | r0
    const uint32_le = (regs[1] << 16) | regs[0];

    // ====== 32ビット浮動小数点数(两1つのレジスタ,IEEE 754) ======
    // 使用Buffer转换
    const buf = Buffer.alloc(4);
    buf.writeUInt16BE(regs[2], 0);  // 高字
    buf.writeUInt16BE(regs[3], 2);  // 低字
    const float32 = buf.readFloatBE(0);  // ビッグエンディアン浮動小数点数

    // ====== 64ビット浮動小数点数(四1つのレジスタ) ======
    const buf64 = Buffer.alloc(8);
    for (let i = 0; i < 4; i++) {
        buf64.writeUInt16BE(regs[4 + i], i * 2);
    }
    const double64 = buf64.readDoubleBE(0);

    // ====== ストリング·ストリング(多1つのレジスタ,すべてのレジスタ。存2个ASCIIcharacters) ======
    let str = "";
    for (let i = 0; i < 4; i++) {
        str += String.fromCharCode((regs[i] >> 8) & 0xFF);
        str += String.fromCharCode(regs[i] & 0xFF);
    }
    str = str.replace(/\0/g, "").trim();  // 去掉空文字

    console.log("16ビット符号なし:", uint16);
    console.log("16位記号付き。:", int16);
    console.log("32ビット符号なし(ビッグエンディアン):", uint32_be);
    console.log("32ビット浮動小数点数:", float32);
    console.log("64ビット浮動小数点数:", double64);
    console.log("ストリング·ストリング:", str);
}

// 方法二:使用modbus-serial的readHoldingRegisters戻る的buffer
// result.buffer 是一个Buffer对象,可以直接使用する。標準方法読み取り
async function readWithBuffer(client) {
    const result = await client.readHoldingRegisters(0, 8);
    const buf = result.buffer;  // Buffer对象

    const uint16 = buf.readUInt16BE(0);
    const uint32 = buf.readUInt32BE(2);
    const float = buf.readFloatBE(6);

    console.log("使用Bufferread:", uint16, uint32, float);
}

六、Modbusサーバー側。(slave)模拟

modbus-serial不仅可以做クライアント側は,还可以作成Modbus TCPサーバー側。(slave),用于模拟デバイス或搭建データゲートウェイ。

// modbus_server.js - Modbus TCPサーバー側。(slave)模拟
const ModbusRTU = require("modbus-serial");

// 作成サーバー側。
const server = new ModbusRTU.Server();

// ====== 定义データ向量(回调函数) ======
// 当マスター·ステーション·リーディング时,这些回调函数被调用,戻る对应データ
const vector = {
    // read holding registers(function code03)
    getHoldingRegister: function(addr, unitID, callback) {
        // addr: register address
        // unitID: slave address
        // callback(err, value): 戻るレジスタの値
        const holdingRegs = [250, 650, 2200, 135, 0, 0, 0, 0];
        if (addr >= 0 && addr < holdingRegs.length) {
            callback(null, holdingRegs[addr]);
        } else {
            callback({
                exceptionCode: 2,  // 違法な住所。
            });
        }
    },

    // read input registers(function code04)
    getInputRegister: function(addr, unitID, callback) {
        const inputRegs = [100, 200, 300, 400];
        if (addr >= 0 && addr < inputRegs.length) {
            callback(null, inputRegs[addr]);
        } else {
            callback({ exceptionCode: 2 });
        }
    },

    // リードコイルを読む(function code01)
    getCoil: function(addr, unitID, callback) {
        const coils = [true, false, true, true, false, false, false, false];
        if (addr >= 0 && addr < coils.length) {
            callback(null, coils[addr]);
        } else {
            callback({ exceptionCode: 2 });
        }
    },

    // read discrete inputs(function code02)
    getDiscreteInput: function(addr, unitID, callback) {
        callback(null, addr % 2 === 0);
    },

    // write single register(function code06)
    setRegister: function(addr, value, unitID, callback) {
        console.log(`写Register: address=${addr}, 值=${value}, slave=${unitID}`);
        // ここで执行实际动作,如控制デバイス
        callback(null);  // 写入成功
    },

    // write multiple registers(function code16)
    setRegisterArray: function(addr, values, unitID, callback) {
        console.log(`一括書き込みき込みRegister: address=${addr}, 值=${values}`);
        callback(null);
    },

    // write single coil(function code05)
    setCoil: function(addr, value, unitID, callback) {
        console.log(`写Coil: address=${addr}, 值=${value}`);
        callback(null);
    },
};

// 启动TCPサーバー側。,监听502ポート
server.listenTCP(502, vector, {
    host: "0.0.0.0",
    // 可选:允许的スレーブID
    // unitID: 1,
});

console.log("Modbus TCPサーバー側。已启动,监听ポート502");
console.log("レジスタを保持する。0-3: temperature25.0°C, 湿度65.0%, 电压220.0V, 电流1.35A");
console.log("按 Ctrl+C Stop");

// 模拟センサーはデータ实时更新
setInterval(() => {
    // 可以ここで更新内部データ
    // 例如从真实センサーは読み取りデータ
}, 1000);

七、批量データの収集与ポーリング

// poller.js - 工业级批量データの収集器
const ModbusRTU = require("modbus-serial");

class ModbusPoller {
    constructor(config) {
        this.client = new ModbusRTU();
        this.config = config;
        this.data = {};        // 采集到的データ
        this.running = false;
        this.timer = null;
    }

    // connect
    async connect() {
        this.client.setID(this.config.unitId || 1);
        this.client.setTimeout(this.config.timeout || 5000);

        if (this.config.type === "tcp") {
            await this.client.connectTCP(this.config.host, {
                port: this.config.port || 502,
            });
        } else if (this.config.type === "rtu") {
            await this.client.connectRTU(this.config.port, {
                baudRate: this.config.baudRate || 9600,
                parity: this.config.parity || "none",
            });
        }
        console.log("接続しましたModbusdevice");
    }

    // 采集单1つのレジスタ组
    async readGroup(group) {
        try {
            const result = await this.client.readHoldingRegisters(
                group.address, group.count
            );
            // 按設定解析データ
            group.points.forEach((point, idx) => {
                let value = result.data[idx];
                // 应用缩放系数
                if (point.scale) value = value * point.scale;
                // 应用偏移
                if (point.offset) value = value + point.offset;
                // 保留小数位
                if (point.decimals !== undefined) {
                    value = Number(value.toFixed(point.decimals));
                }
                this.data[point.name] = value;
            });
            return true;
        } catch (err) {
            console.error(`采集 ${group.name} 失敗:`, err.message);
            return false;
        }
    }

    // 启动周期性采集
    start(intervalMs = 1000) {
        this.running = true;
        const poll = async () => {
            if (!this.running) return;
            for (const group of this.config.groups) {
                await this.readGroup(group);
            }
            // 出力当前データ
            console.log(JSON.stringify(this.data, null, 2));
            this.timer = setTimeout(poll, intervalMs);
        };
        poll();
    }

    // Stop
    stop() {
        this.running = false;
        if (this.timer) clearTimeout(this.timer);
        this.client.close();
    }
}

// 設定サンプル:采集一个スマートメーター
const config = {
    type: "tcp",
    host: "192.168.1.100",
    port: 502,
    unitId: 1,
    groups: [
        {
            name: "电参数",
            address: 0,
            count: 6,
            points: [
                { name: "电压", scale: 0.1, decimals: 1, unit: "V" },
                { name: "电流", scale: 0.01, decimals: 2, unit: "A" },
                { name: "有効なパワー", scale: 0.001, decimals: 3, unit: "kW" },
                { name: "無効なパワー", scale: 0.001, decimals: 3, unit: "kvar" },
                { name: "力率は", scale: 0.001, decimals: 3 },
                { name: "频率", scale: 0.01, decimals: 2, unit: "Hz" },
            ],
        },
        {
            name: "电能",
            address: 100,
            count: 2,
            points: [
                { name: "正向有功电能", scale: 0.01, decimals: 2, unit: "kWh" },
                { name: "反向有功电能", scale: 0.01, decimals: 2, unit: "kWh" },
            ],
        },
    ],
};

// run
async function main() {
    const poller = new ModbusPoller(config);
    await poller.connect();
    poller.start(2000);  // 每2秒采集一次
}

main().catch(console.error);

八、Modbus转MQTT上云実践

// modbus_mqtt_gateway.js - Modbus转MQTTIoTゲートウェイ
const ModbusRTU = require("modbus-serial");
const mqtt = require("mqtt");

// ====== 設定 ======
const MODBUS_CONFIG = {
    host: "192.168.1.100",
    port: 502,
    unitId: 1,
};

const MQTT_CONFIG = {
    url: "mqtt://broker.emqx.io",
    port: 1883,
    topic: "modbus/gateway/device001",
    clientId: "modbus_gateway_" + Math.random().toString(16).substr(2, 8),
};

const POLL_INTERVAL = 5000;  // 5秒采集一次

// ====== データ点設定 ======
const DATA_POINTS = [
    { name: "temperature", address: 0, scale: 0.1, unit: "°C" },
    { name: "humidity", address: 1, scale: 0.1, unit: "%" },
    { name: "voltage", address: 2, scale: 0.1, unit: "V" },
    { name: "current", address: 3, scale: 0.01, unit: "A" },
    { name: "power", address: 4, scale: 0.001, unit: "kW" },
    { name: "energy", address: 100, scale: 0.01, unit: "kWh" },
];

// ====== 主程序 ======
async function main() {
    // 1. connectModbus
    const mbClient = new ModbusRTU();
    mbClient.setID(MODBUS_CONFIG.unitId);
    mbClient.setTimeout(3000);
    await mbClient.connectTCP(MODBUS_CONFIG.host, { port: MODBUS_CONFIG.port });
    console.log("ModbusConnected");

    // 2. connectMQTT
    const mqttClient = mqtt.connect(MQTT_CONFIG.url, {
        clientId: MQTT_CONFIG.clientId,
        port: MQTT_CONFIG.port,
    });

    mqttClient.on("connect", () => {
        console.log("MQTTConnected");
        // 订阅ダウンタウン制御主题
        mqttClient.subscribe(MQTT_CONFIG.topic + "/command");
    });

    // 3. 処理ダウンタウン制御指令(MQTT → Modbus写)
    mqttClient.on("message", async (topic, message) => {
        try {
            const cmd = JSON.parse(message.toString());
            console.log("收到控制指令:", cmd);
            if (cmd.type === "write_register") {
                await mbClient.writeRegister(cmd.address, cmd.value);
                console.log(`書き込み済み。レジスタ${cmd.address}=${cmd.value}`);
            } else if (cmd.type === "write_coil") {
                await mbClient.writeCoil(cmd.address, cmd.value);
                console.log(`書き込み済み。コイル${cmd.address}=${cmd.value}`);
            }
        } catch (err) {
            console.error("控制指令执行失敗:", err.message);
        }
    });

    // 4. 周期性采集并上报(Modbus → MQTT)
    async function pollAndPublish() {
        try {
            const payload = {
                deviceId: "device001",
                timestamp: new Date().toISOString(),
                data: {},
            };

            for (const point of DATA_POINTS) {
                const result = await mbClient.readHoldingRegisters(point.address, 1);
                let value = result.data[0];
                if (point.scale) value = value * point.scale;
                payload.data[point.name] = {
                    value: Number(value.toFixed(3)),
                    unit: point.unit,
                };
            }

            // 发布到MQTT
            mqttClient.publish(
                MQTT_CONFIG.topic + "/data",
                JSON.stringify(payload),
                { qos: 1 }
            );
            console.log("已上报:", JSON.stringify(payload.data));
        } catch (err) {
            console.error("采集上报失敗:", err.message);
        }
    }

    // 启动采集
    setInterval(pollAndPublish, POLL_INTERVAL);
    pollAndPublish();  // 立即执行一次
}

main().catch(console.error);

九、エラー処理与再接続机制

十、よくある質問与トラブルシューティング

問題原因解決方法
接続タイムアウトIP/ポートエラー或网络通信不可ping测试IP,telnet测试502ポート,ファイアウォールの確認
例外コードを返す01機能コードサポートなし。デバイスサポートの確認该機能コード,查阅デバイス手册
例外コードを返す02レジスター·アドレス不存在チェックアドレス範囲,注意デバイスアドレス是0-basedまだ1-based
例外コードを返す03読み取り数超出範囲减少読み取り数,最大のシングル。1251つのレジスタ
データ值正しくないendianness/缩放系数エラー確認ビッグエンディアン/リトルエンディアンモード,核对scale和offset
シリアルポート打不开ポート被占用或权限不足Linux需sudo或加入dialout组,チェック是否被其他程序占用
RTU応答なし。Baud rate/チェックビット不マッチング核对デバイス通信パラメータ,チェックA/B线是否接反
频繁断连网络不稳定或デバイス连接数限制增加再接続机制,使用长连接,デバイスの最大接続数の
并发リクエスト冲突同时送信多个リクエスト使用队列串行送信,待機中レスポンス后再发下一个
浮動小数点数解析エラーバイトオーダー不マッチング尝试readFloatBE/readFloatLE,機器の確認ドキュメント

十一、パフォーマンス最適化ベストプラクティス

  • バッチ読み取り:尽量一次読み取り连续アドレス的多1つのレジスタ,减少リクエスト次数
  • 长连接复用:不要频繁作成/接続を閉じる,保持TCP长连接
  • 串行リクエスト:Modbus是ハーフデュプレックス协议,必须待機中レスポンス后再发下一个リクエスト
  • 合理ポーリング間隔:根据データ变化频率設定采集周期,不要过于频繁
  • アドレス优化:将需要频繁読み取り的レジスタ安排在连续アドレス,便于バッチ読み取り
  • タイムアウトの設定:TCP建议3-5秒,RTU建议根据ボーレート計算(9600ボーレート约2秒)
  • エラー重试:单次失敗不要立即报错,重试1-2次后再上报異常
  • 连接池:多デバイス场景使用连接池,避免重复握手开销
  • データ缓存:对变化慢的データ(如电能累计值)降低読み取り频率,缓存结果
  • 内存管理:长时间运行的采集程序注意内存泄漏,定期再起動或モニタリング

十二、其他Node.js Modbus库对比

库名Protocolクライアント側は/サーバー側。特点おすすめ场景
modbus-serialRTU/TCP/RTU-over-TCP都サポート最流行,API简洁,ドキュメント完善通用场景,首选
modbus-rtuRTU/TCPクライアント側はサポートデータ型自动转换需要复杂データの解析
modbus-streamRTU/TCP都サポート基于Stream,灵活需要底层控制
jsmodbusTCP都サポート纯TCP,TypeScriptサポート仅TCPScenarios
node-modbusTCPクライアント側は轻量简单TCP采集

十三、学习リソース

  • npm包アドレス:https://www.npmjs.com/package/modbus-serial
  • GitHubsource code:https://github.com/yaacov/node-modbus-serial
  • APIDocument:https://yaacov.github.io/node-modbus-serial/
  • Modbusプロトコル规范:https://modbus.org/specs.php
  • node-serialportDocument:https://serialport.io/
  • MQTT.jsDocument:https://github.com/mqttjs/MQTT.js

Node.js凭借异步非阻塞的天然优势和modbus-serial库的成熟API,成为开发工业データの収集ゲートウェイ、Modbus转MQTT桥接器和デバイス模拟服务的理想選択。本記事カバー了从基础连接、データの解析到バッチ収集。、MQTT上云、エラー再接続完全に。开发流程,代码均可直接运行。关键是注意Modbusハーフデュプレックス特性(リクエスト必须串行)、レジスタアドレスオフセット(0-based vs 1-based)和多Bytesのデータ的バイトオーダーマッチング这三个よくある穴点。

📦

VIP专属:Node.js Modbus完全なコードパッケージの開発

modbus-serial库TCP/RTUクライアント側は、サーバ側シミュレーション、データの解析、MQTTトップクラウドゲートウェイ、強力な再接続メカニズム。

Activate VIP即可下载完全代码,同时解锁 30+ 工程実践パッケージの内容:スクリプトのデバッグ、クイックチェック·テーブル、项目模板、トラブルシューティング案例……

前往VIP资料库下载 → 月费仅9.9元 / 年费199元
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support