@egjr/mc3e
v1.1.0
Published
Cliente MC Protocol 3E nativo para PLCs Mitsubishi Serie Q/L. Leitura e escrita de dispositivos (D, M, X, Y, W, R, etc.) com suporte a Word, Double Word e Float.
Maintainers
Readme
@egjr/mc3e
Biblioteca MC Protocol 3E (binário) nativa para comunicação com PLCs Mitsubishi Série Q/L em Node.js.
- Zero dependências — usa apenas TCP sockets nativos (
net). - Suporta todos os dispositivos: D, M, X, Y, W, R, Z, Timers, Counters e mais.
- Tipos de dados: Word (16 bits), Double Word (32 bits), Float IEEE 754 (32 bits).
- Pronta para uso como dependência em qualquer aplicação Node.js.
Índice
- Instalação
- Quick Start
- API da Biblioteca
- Dispositivos Suportados
- Tipos de Dados
- Exemplos de Integração
- Códigos de Erro MC
- Tratamento de Erros
- Arquitetura do Protocolo
- Observações de Segurança
- Licença
Instalação
npm install @egjr/mc3eQuick Start
const { Mc3eClient } = require("@egjr/mc3e");
async function main() {
const client = new Mc3eClient({
host: "192.168.68.40",
port: 2001,
});
// Ler registrador D0 (Word 16 bits)
const d0 = await client.readDevice("D", 0);
console.log("D0 =", d0); // ex: 1234
// Escrever valor no registrador D0
await client.writeDevice("D", 0, 5678);
// Ler entrada digital X0
const x0 = await client.readDevice("X", 0);
console.log("X0 =", x0); // true ou false
// Ler temperatura como Float (D10 + D11)
const temp = await client.readFloat("D", 10);
console.log("Temperatura =", temp);
}
main().catch(console.error);API da Biblioteca
Construtor
const { Mc3eClient } = require("@egjr/mc3e");
const client = new Mc3eClient(options);Parâmetros de options
| Parâmetro | Tipo | Padrão | Descrição |
|-----------|------|--------|-----------|
| host | string | — | (obrigatório) Endereço IP do PLC |
| port | number | — | (obrigatório) Porta TCP do módulo Ethernet |
| timeoutMs | number | 3000 | Timeout de comunicação em milissegundos |
| networkNo | number | 0x00 | Número da rede (Network No.) |
| pcNo | number | 0xFF | Número do PC (PC No.) |
| ioNo | number | 0x03FF | Número do módulo I/O (Request destination module I/O No.) |
| stationNo | number | 0x00 | Número da estação (Request destination station No.) |
| monitoringTimer | number | 0x0010 | Timer de monitoramento do PLC (em unidades de 250ms) |
Exemplo com todas as opções
const client = new Mc3eClient({
host: "192.168.68.40",
port: 2001,
timeoutMs: 5000,
networkNo: 0x00,
pcNo: 0xFF,
ioNo: 0x03FF,
stationNo: 0x00,
monitoringTimer: 0x0010,
});readDevice(deviceName, deviceNo, count)
Lê um ou mais pontos de um dispositivo do PLC.
Parâmetros
| Parâmetro | Tipo | Padrão | Descrição |
|-----------|------|--------|-----------|
| deviceName | string | — | Nome do dispositivo ("D", "M", "X", "Y", etc.) |
| deviceNo | number | — | Número do ponto (ex: 0, 100, 500) |
| count | number | 1 | Quantidade de pontos a ler |
Retorno
| Tipo de dispositivo | count = 1 | count > 1 |
|---------------------|-------------|-------------|
| bit (M, X, Y...) | boolean | boolean[] |
| word (D, W, R...) | number (0–65535) | number[] |
Exemplos
// Leitura única — Word
const d100 = await client.readDevice("D", 100);
// => 4567
// Leitura múltipla — 10 registradores consecutivos
const d0_d9 = await client.readDevice("D", 0, 10);
// => [100, 200, 300, 0, 0, 0, 0, 0, 0, 0]
// Leitura de bit
const m0 = await client.readDevice("M", 0);
// => true
// Leitura múltipla de bits
const m0_m7 = await client.readDevice("M", 0, 8);
// => [true, false, true, false, false, false, false, false]
// Leitura de entrada digital (Input)
const x0 = await client.readDevice("X", 0);
// => true
// Leitura de saída digital (Output)
const y10 = await client.readDevice("Y", 10);
// => falsewriteDevice(deviceName, deviceNo, value)
Escreve um ou mais pontos em um dispositivo do PLC.
Parâmetros
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| deviceName | string | Nome do dispositivo |
| deviceNo | number | Número do ponto inicial |
| value | number \| boolean \| Array | Valor a escrever. Para word: inteiro (-32768 a 65535). Para bit: true/false. Aceita array para escrita múltipla. |
Exemplos
// Escrever Word
await client.writeDevice("D", 0, 1234);
// Escrever múltiplos registradores (D0=100, D1=200, D2=300)
await client.writeDevice("D", 0, [100, 200, 300]);
// Escrever bit — ligar M0
await client.writeDevice("M", 0, true);
// Escrever bit — desligar Y10
await client.writeDevice("Y", 10, false);
// Escrever múltiplos bits (M0=ON, M1=OFF, M2=ON)
await client.writeDevice("M", 0, [true, false, true]);readDWord(deviceName, deviceNo)
Lê um valor Double Word (32 bits inteiro) ocupando 2 registradores consecutivos.
Parâmetros
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| deviceName | string | Nome do dispositivo word ("D", "W", "R", etc.) |
| deviceNo | number | Número do primeiro registrador |
Retorno
number — Valor inteiro de 32 bits (0 a 4294967295 unsigned, ou -2147483648 a 2147483647 signed).
Exemplo
// Lê D0 (low) + D1 (high) como inteiro 32 bits
const value = await client.readDWord("D", 0);
// => 70000Nota: O valor é montado como
(D[n+1] << 16) | D[n](little-endian, padrão Mitsubishi).
writeDWord(deviceName, deviceNo, value)
Escreve um valor Double Word (32 bits inteiro) em 2 registradores consecutivos.
Parâmetros
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| deviceName | string | Nome do dispositivo word |
| deviceNo | number | Número do primeiro registrador |
| value | number | Valor inteiro (-2147483648 a 4294967295) |
Exemplo
await client.writeDWord("D", 0, 70000);
// D0 = 4464 (0x1170), D1 = 1 (0x0001)
// Resultado: (1 << 16) | 4464 = 70000readFloat(deviceName, deviceNo)
Lê um valor Float IEEE 754 (32 bits) ocupando 2 registradores consecutivos.
Parâmetros
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| deviceName | string | Nome do dispositivo word |
| deviceNo | number | Número do primeiro registrador |
Retorno
number — Valor de ponto flutuante IEEE 754.
Exemplo
// Lê D10 + D11 como float
const temperatura = await client.readFloat("D", 10);
// => 3.140000104904175writeFloat(deviceName, deviceNo, value)
Escreve um valor Float IEEE 754 (32 bits) em 2 registradores consecutivos.
Parâmetros
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| deviceName | string | Nome do dispositivo word |
| deviceNo | number | Número do primeiro registrador |
| value | number | Valor numérico finito |
Exemplo
await client.writeFloat("D", 10, 3.14);Atalhos retrocompatíveis
Métodos de conveniência para o registrador D:
// Equivale a readDevice("D", deviceNo)
const value = await client.readD(0);
// Equivale a writeDevice("D", deviceNo, value)
await client.writeD(0, 1234);Utilitários
DEVICES
Constante exportada com todos os dispositivos suportados e seus códigos binários MC:
const { DEVICES } = require("@egjr/mc3e");
console.log(DEVICES.D);
// => { code: 0xa8, type: "word", label: "Data Register" }
console.log(Object.keys(DEVICES));
// => ["X", "Y", "M", "L", "F", "V", "B", "S", "SS", "SC", "TS", "TC", "CS", "CC", "SB", "SM", "D", "W", "R", "ZR", "TN", "SN", "CN", "SW", "SD", "Z"]getDevice(name)
Busca um dispositivo pelo nome (case-insensitive). Lança erro com a lista de dispositivos válidos se não encontrado.
const { getDevice } = require("@egjr/mc3e");
const dev = getDevice("D");
// => { code: 0xa8, type: "word", label: "Data Register", name: "D" }
getDevice("INVALIDO");
// => Error: Dispositivo 'INVALIDO' nao reconhecido. Validos: X, Y, M, L, ...Dispositivos Suportados
Dispositivos de Bit
| Dispositivo | Código MC | Descrição |
|-------------|-----------|-----------|
| X | 0x9C | Input (Entrada digital) |
| Y | 0x9D | Output (Saída digital) |
| M | 0x90 | Internal Relay (Relé interno) |
| L | 0x92 | Latch Relay (Relé com retenção) |
| F | 0x93 | Annunciator (Anunciador) |
| V | 0x94 | Edge Relay (Relé de borda) |
| B | 0xA0 | Link Relay (Relé de link) |
| S | 0x98 | Step Relay (Relé de passo) |
| SM | 0x91 | Special Relay (Relé especial) |
| SB | 0xA1 | Link Special Relay |
| TS | 0xC1 | Timer Contact (Contato de timer) |
| TC | 0xC0 | Timer Coil (Bobina de timer) |
| SS | 0xC7 | Timer Contact — Retentive (Com retenção) |
| SC | 0xC6 | Timer Coil — Retentive (Com retenção) |
| CS | 0xC4 | Counter Contact (Contato de contador) |
| CC | 0xC3 | Counter Coil (Bobina de contador) |
Dispositivos de Word
| Dispositivo | Código MC | Descrição |
|-------------|-----------|-----------|
| D | 0xA8 | Data Register (Registrador de dados) |
| W | 0xB4 | Link Register (Registrador de link) |
| R | 0xAF | File Register (Registrador de arquivo) |
| ZR | 0xB0 | File Register — Block (Bloco) |
| TN | 0xC2 | Timer Current Value (Valor atual do timer) |
| SN | 0xC8 | Timer Current Value — Retentive |
| CN | 0xC5 | Counter Current Value (Valor atual do contador) |
| SW | 0xB5 | Link Special Register |
| SD | 0xA9 | Special Register (Registrador especial) |
| Z | 0xCC | Index Register (Registrador de índice) |
Tipos de Dados
| Tipo | Bits | Registradores | Método de Leitura | Método de Escrita | Faixa |
|------|------|---------------|-------------------|-------------------|-------|
| Word | 16 | 1 | readDevice() | writeDevice() | 0–65535 (unsigned) |
| Double Word | 32 | 2 | readDWord() | writeDWord() | 0–4294967295 (unsigned) |
| Float | 32 | 2 | readFloat() | writeFloat() | IEEE 754 single precision |
| Bit | 1 | — | readDevice() | writeDevice() | true / false |
Para Double Word e Float, os dados ocupam 2 registradores consecutivos no formato little-endian: registrador
N= word baixa, registradorN+1= word alta.
Exemplos de Integração
Monitoramento contínuo de variáveis
const { Mc3eClient } = require("@egjr/mc3e");
const client = new Mc3eClient({ host: "192.168.68.40", port: 2001 });
async function monitorar(deviceName, deviceNo, intervalMs = 1000) {
console.log(`Monitorando ${deviceName}${deviceNo} a cada ${intervalMs}ms...`);
setInterval(async () => {
try {
const value = await client.readDevice(deviceName, deviceNo);
console.log(`[${new Date().toISOString()}] ${deviceName}${deviceNo} = ${value}`);
} catch (err) {
console.error(`Erro: ${err.message}`);
}
}, intervalMs);
}
monitorar("D", 0, 500);Leitura de múltiplos registradores em lote
const { Mc3eClient } = require("@egjr/mc3e");
const client = new Mc3eClient({ host: "192.168.68.40", port: 2001 });
async function lerProcesso() {
// Ler 10 registradores D consecutivos (D100..D109)
const dados = await client.readDevice("D", 100, 10);
// Ler temperatura como Float (D200 + D201)
const temperatura = await client.readFloat("D", 200);
// Ler contador como Double Word (D300 + D301)
const contador = await client.readDWord("D", 300);
// Ler status das entradas
const sensor1 = await client.readDevice("X", 0);
const sensor2 = await client.readDevice("X", 1);
return {
registradores: dados,
temperatura,
contador,
sensores: { sensor1, sensor2 },
};
}
lerProcesso().then(console.log).catch(console.error);Uso com Express (API própria)
const express = require("express");
const { Mc3eClient } = require("@egjr/mc3e");
const app = express();
const client = new Mc3eClient({ host: "192.168.68.40", port: 2001 });
app.get("/temperatura", async (req, res) => {
try {
const temp = await client.readFloat("D", 10);
res.json({ temperatura: temp });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(3000, () => console.log("API rodando na porta 3000"));Uso com TypeScript
import { Mc3eClient, DEVICES, getDevice } from "@egjr/mc3e";
const client = new Mc3eClient({
host: "192.168.68.40",
port: 2001,
timeoutMs: 5000,
});
async function main(): Promise<void> {
const valor: number = await client.readDevice("D", 0);
const bits: boolean = await client.readDevice("M", 0);
const temp: number = await client.readFloat("D", 10);
console.log({ valor, bits, temp });
}
main();Múltiplos PLCs
const { Mc3eClient } = require("@egjr/mc3e");
const plc1 = new Mc3eClient({ host: "192.168.68.40", port: 2001 });
const plc2 = new Mc3eClient({ host: "192.168.68.41", port: 2001 });
async function sincronizar() {
// Ler valor do PLC1 e escrever no PLC2
const valor = await plc1.readDevice("D", 0);
await plc2.writeDevice("D", 100, valor);
console.log(`Sincronizado: D0=${valor}`);
}
sincronizar().catch(console.error);Códigos de Erro MC
Quando o PLC retorna um erro, a biblioteca lança um Error com propriedades adicionais:
| Código | Mensagem | Descrição |
|--------|----------|-----------|
| 0x0050 | Erro de conversão ASCII/Binário | Communication Data Code da CPU não corresponde ao modo do cliente |
| 0x0051 | Quantidade de dados não corresponde | Número de palavras enviadas diverge do campo Number of device points |
| 0x0055 | Escrita recusada pelo PLC | Proteção de escrita remota, CPU em STOP, ou restrição no módulo Ethernet |
| 0xC050 | Erro no número de série da CPU | Número de série ASCII inválido na requisição |
| 0xC051 | Dados de escrita incompatíveis | Tipo de dispositivo incompatível com a operação |
| 0xC056 | Número de pontos excede o limite | Device points solicitados excedem o limite da CPU |
| 0xC058 | Comando não suportado pela CPU | Comando não suportado — verificar modelo e firmware |
| 0xC059 | Erro de tipo comando/subcomando | Par comando/subcomando não reconhecido |
| 0xC05B | CPU em modo incompatível | CPU em STOP/PAUSE — colocar em RUN |
| 0xC05C | Dispositivo inexistente | Dispositivo não existe nesta CPU |
| 0xC05D | Erro de senha remota | Senha remota necessária ou incorreta |
| 0xC05F | Requisição não pode ser executada | Módulo Ethernet não pode encaminhar a requisição |
| 0xC060 | Erro de número de dispositivo | Número do dispositivo fora da faixa |
| 0xC061 | Comprimento de dados inválido | Comprimento dos dados da requisição inválido |
Tratamento de Erros
const { Mc3eClient } = require("@egjr/mc3e");
const client = new Mc3eClient({ host: "192.168.68.40", port: 2001 });
try {
await client.writeDevice("D", 0, 1234);
} catch (error) {
console.error("Mensagem:", error.message);
// => "[0x0055] Escrita recusada pelo PLC"
if (error.endCode) {
console.error("Código:", `0x${error.endCode.toString(16).padStart(4, "0")}`);
// => "0x0055"
}
if (error.detail) {
console.error("Detalhe:", error.detail);
// => "O PLC recusou a operacao de escrita. Causas provaveis: ..."
}
}Erros comuns
| Erro | Causa | Solução |
|------|-------|---------|
| Timeout de comunicação com PLC | PLC inacessível ou IP/porta incorretos | Verificar rede, IP, porta e firewall |
| Erro de socket: ECONNREFUSED | Porta TCP não aberta no PLC | Verificar configuração do módulo Ethernet no GX Works2 |
| [0x0055] Escrita recusada | Proteção de escrita ativa | Desabilitar Remote Password ou liberar escrita no módulo Ethernet |
| [0xC05B] CPU em modo incompatível | CPU em STOP | Colocar CPU em modo RUN |
| [0xC05C] Dispositivo inexistente | Dispositivo não existe no modelo | Verificar manual da CPU |
Arquitetura do Protocolo
┌──────────────────────────────────────────────────────────┐
│ Aplicação Node.js │
│ │
│ const client = new Mc3eClient({ host, port }) │
│ await client.readDevice("D", 0) │
└──────────────┬───────────────────────────────────────────┘
│
│ TCP Socket (net.Socket)
│
┌──────────────▼───────────────────────────────────────────┐
│ MC Protocol 3E (Binary) │
│ │
│ Request Frame: │
│ ┌─────────┬────────┬───────┬─────────┬─────────┐ │
│ │Subheader│Access │Req. │Command │ Device │ │
│ │ 0x5000 │Route │Data │+ Sub. │ Data │ │
│ │ (2 B) │(5 B) │Len(2B)│ (4 B) │ (N B) │ │
│ └─────────┴────────┴───────┴─────────┴─────────┘ │
│ │
│ Response Frame: │
│ ┌─────────┬────────┬───────┬─────────┬─────────┐ │
│ │Subheader│Access │Resp. │End Code │Response │ │
│ │ 0xD000 │Route │Len(2B)│ (2 B) │ Data │ │
│ │ (2 B) │(5 B) │ │0=OK │ (N B) │ │
│ └─────────┴────────┴───────┴─────────┴─────────┘ │
└──────────────┬───────────────────────────────────────────┘
│
│ Ethernet / TCP
│
┌──────────────▼───────────────────────────────────────────┐
│ PLC Mitsubishi Série Q/L │
│ Módulo Ethernet (porta 2001) │
└──────────────────────────────────────────────────────────┘Comandos MC utilizados
| Comando | Código | Subcomando | Descrição |
|---------|--------|------------|-----------|
| Batch Read | 0x0401 | 0x0000 (word) | Leitura de dispositivos word |
| Batch Read | 0x0401 | 0x0001 (bit) | Leitura de dispositivos bit |
| Batch Write | 0x1401 | 0x0000 (word) | Escrita em dispositivos word |
| Batch Write | 0x1401 | 0x0001 (bit) | Escrita em dispositivos bit |
Observações de Segurança
⚠️ ATENÇÃO: Esta biblioteca comunica diretamente com equipamentos industriais. Escritas incorretas podem causar falhas de processo, danos a equipamentos ou riscos à segurança.
- Teste sempre em ambiente controlado antes de usar em produção.
- Valide os valores antes de escrever no PLC.
- Configure proteções no PLC (Remote Password, restrições no módulo Ethernet) conforme necessário.
- A biblioteca usa apenas
net(nativo do Node) — zero dependências externas.
