Este projeto utiliza um EVSE simulado em Node.js para testar a comunicação com um servidor OCPP 1.6. O simulador estabelece uma conexão WSS, executa uma sequência de mensagens OCPP, acompanha as respostas, encaminha eventos para uma interface Web e permite consultar dados do MongoDB.

1. Visão geral da arquitetura
O sistema é dividido em quatro elementos: o EVSE simulado, o servidor OCPP 1.6(que não faz parte do post), o MongoDB e a interface Web. No ambiente analisado, o simulador utiliza WSS para chegar ao servidor OCPP na porta 3001. O módulo web-server.js utiliza a porta 3002 para a comunicação com o navegador.
EVSE virtual
teste-ocpp.js
Representa o carregador. Abre a conexão WSS, envia requisições OCPP, recebe respostas e executa a sequência de teste.
Interface
web-server.js
Recebe eventos TX/RX do simulador e os encaminha para o navegador por WebSocket. Também importa o leitor do MongoDB.
Banco
mongo-reader.js
Conecta ao MongoDB, lista coleções e lê até 100 documentos de cada coleção.
2. Como um EVSE funciona
Um EVSE (Electric Vehicle Supply Equipment) é o equipamento que controla e disponibiliza energia para o veículo, fazendo também o monitoramento das condições da sessão. O OCPP atua na comunicação entre o ponto de carga e o sistema central de gerenciamento; ele não é o protocolo que controla diretamente todos os sinais elétricos entre o EVSE e o veículo.

EVSE → conexão segura → servidor OCPP → processamento/autorização → resposta ao EVSE → atualização da sessão → armazenamento dos dados.
No projeto de teste, o comportamento do EVSE físico é representado pelo arquivo teste-ocpp.js. Isso permite testar a camada de comunicação antes de conectar um carregador físico.
3. Arquivo teste-ocpp.js
Este é o núcleo do simulador. O arquivo utiliza ws, fs e EventEmitter. Ele cria o objeto ocppEvents, utilizado para informar ao servidor Web as mensagens transmitidas e recebidas.
Configuração e identificação do EVSE
const WebSocket = require("ws");
const fs = require("fs");
const EventEmitter = require("events");
const ocppEvents = new EventEmitter();
const URL = "wss://192.168.68.114:3001";
const CERT_FILE = "/app/certs/cert.pem";
const CHARGER_ID = "EVSE1";
const ID_TAG = "TAG123";
let ws = null;
let transactionId = null;
function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
/[xy]/g,
function (c) {
const r = Math.random() * 16 | 0;
const v = c === "x"
? r
: (r & 0x3 | 0x8);
return v.toString(16);
}
);
}Entre os parâmetros definidos estão o endereço WSS, o certificado, o identificador do carregador, o idTag, o número de série, modelo, firmware, conector, veículo e valor do medidor.
3.1 Conexão WSS
A função connect() cria o WebSocket seguro e utiliza o certificado CA. O endereço configurado no arquivo é wss://192.168.68.114:3001.
3.2 Envio das mensagens OCPP
A função sendOcpp(action, payload) gera um UUID, monta a mensagem e a envia pelo WebSocket. O projeto utiliza uma mensagem JSON estruturada com tipo, identificador, ação e payload.
sendOcpp()
function sendOcpp(action, payload) {
return new Promise((resolve, reject) => {
const messageId = generateUUID();
const ocppMessage = [
2,
messageId,
action,
payload
];
const message = JSON.stringify(ocppMessage);
ocppEvents.emit("tx", {
action: action,
message: message
});
ws.send(message, (err) => {
if (err) {
reject(err);
return;
}
resolve(messageId);
});
});
}3.3 Recebimento das respostas
A função receiveOcpp() aguarda uma resposta, converte o JSON recebido e verifica o identificador da mensagem esperada. O código considera messageType === 3 como resposta da requisição correspondente.
4. Comandos OCPP 1.6 usados no simulador
| Comando | Função no teste | Rotina |
|---|---|---|
BootNotification | Apresenta o carregador ao servidor e verifica a aceitação. | bootNotification() |
Heartbeat | Solicita ao servidor o horário atual e mantém o acompanhamento da conexão. | heartbeat() |
StatusNotification | Informa o estado configurado no simulador. | statusNotification() |
Authorize | Envia o idTag para autorização. | authorize() |
StartTransaction | Inicia a sessão e recebe o transactionId. | startTransaction() |
MeterValues | Envia o valor de energia simulado. | meterValues() |
StopTransaction | Finaliza a sessão. | stopTransaction() |
teste-ocpp.js. Ela não representa toda a lista de funcionalidades existentes no OCPP 1.6.5. Sequência completa do teste
A função main() executa a sequência principal:
Connect
↓
BootNotification
↓
Heartbeat
↓
StatusNotification
↓
Authorize
↓
StartTransaction
↓
MeterValues
↓
aguarda 2 segundos
↓
StopTransaction
↓
fecha conexãoTrecho da sequência principal
async function main() {
try {
await connect();
const bootOk = await bootNotification();
if (!bootOk) {
throw new Error("BootNotification rejeitado");
}
await heartbeat();
await statusNotification();
const authorizeOk = await authorize();
if (!authorizeOk) {
throw new Error("Authorize rejeitado");
}
const startOk = await startTransaction();
if (!startOk) {
throw new Error("StartTransaction rejeitado");
}
await meterValues();
await new Promise(resolve =>
setTimeout(resolve, 2000)
);
const stopOk = await stopTransaction();
if (!stopOk) {
throw new Error("StopTransaction não aceito");
}
ws.close();
} catch (err) {
console.error("ERRO NO TESTE:", err);
if (ws) {
try { ws.close(); } catch (e) {}
}
}
}6. O transactionId
Um ponto importante do teste é o transactionId. Depois de StartTransaction, o simulador extrai o identificador retornado pelo servidor e o guarda em uma variável. Esse valor é então utilizado nas mensagens relacionadas à transação, especialmente em MeterValues e StopTransaction.
7. O arquivo web-server.js
O web-server.js funciona como uma ponte entre o simulador e o navegador. Ele importa teste-ocpp.js e mongo-reader.js, define a porta 3002 e escuta os eventos tx e rx produzidos pelo EventEmitter.
Recepção dos eventos TX/RX
const http = require("http");
const WebSocket = require("ws");
const ocpp = require("./teste-ocpp");
const mongoReader = require("./mongo-reader");
const PORT = 3002;
ocpp.ocppEvents.on("tx", (data) => {
console.log("OCPP TX:", data.message);
if (browserSocket &&
browserSocket.readyState === WebSocket.OPEN) {
browserSocket.send(JSON.stringify({
type: "OCPP_TX",
action: data.action,
message: data.message
}));
}
});
ocpp.ocppEvents.on("rx", (data) => {
console.log("OCPP RX:", data.message);
if (browserSocket &&
browserSocket.readyState === WebSocket.OPEN) {
browserSocket.send(JSON.stringify({
type: "OCPP_RX",
message: data.message
}));
}
});Quando ocorre um evento TX, o módulo envia ao navegador um objeto contendo type: "OCPP_TX", a ação e a mensagem. Para RX, envia type: "OCPP_RX" e a mensagem recebida.
8. O arquivo mongo-reader.js
O mongo-reader.js é responsável exclusivamente pela leitura do MongoDB. A conexão é montada a partir das variáveis de ambiente MONGO_HOST, MONGO_PORT, MONGO_USER, MONGO_PASSWORD e MONGO_DATABASE.
Conexão e leitura das coleções
const { MongoClient } = require("mongodb");
const MONGO_HOST = process.env.MONGO_HOST || "mongodb";
const MONGO_PORT = process.env.MONGO_PORT || "27017";
const MONGO_USER = process.env.MONGO_USER;
const MONGO_PASSWORD = process.env.MONGO_PASSWORD;
const MONGO_DATABASE = process.env.MONGO_DATABASE || "ocpp_2";
const MONGO_URI =
`mongodb://${encodeURIComponent(MONGO_USER)}:` +
`${encodeURIComponent(MONGO_PASSWORD)}@` +
`${MONGO_HOST}:${MONGO_PORT}/` +
`${MONGO_DATABASE}?authSource=admin`;
async function readCollections() {
const client = new MongoClient(MONGO_URI);
try {
await client.connect();
const db = client.db(MONGO_DATABASE);
const collections =
await db.listCollections().toArray();
const result = {};
for (const collection of collections) {
const documents =
await db
.collection(collection.name)
.find({})
.limit(100)
.toArray();
result[collection.name] = documents;
}
return result;
} finally {
await client.close();
}
}A rotina readCollections() conecta ao banco, lista as coleções, percorre cada uma delas, consulta até 100 documentos e devolve o resultado em um objeto JavaScript. Ao terminar, fecha a conexão.
9. Como o EVSE interage com o servidor OCPP
Em uma sessão típica, o EVSE inicia a comunicação com o servidor e executa uma sequência de mensagens. No simulador analisado, o fluxo implementado é:
| Etapa | EVSE / simulador | Servidor OCPP |
|---|---|---|
| 1 | BootNotification | Recebe os dados do carregador e responde. |
| 2 | Heartbeat | Responde com currentTime. |
| 3 | StatusNotification | Recebe o estado informado. |
| 4 | Authorize | Analisa o idTag e retorna o status. |
| 5 | StartTransaction | Responde com o transactionId. |
| 6 | MeterValues | Recebe a medição da sessão. |
| 7 | StopTransaction | Processa o encerramento da transação. |
10. O que acontece em um EVSE real?
Em um EVSE físico, o controlador do carregador acompanha o estado do conector, as condições de segurança, a disponibilidade de energia e os valores do medidor. A comunicação OCPP permite que essas informações sejam compartilhadas com o sistema central.
Assim, podemos separar conceitualmente duas camadas:
Camada elétrica
- Conexão com o veículo
- Controle da entrega de energia
- Monitoramento elétrico
- Proteções e estados do carregador
- Medição de energia
Camada OCPP
- Identificação do carregador
- Autorização
- Transações
- Medições
- Status
- Comunicação com o sistema central
11. Estrutura do projeto para download
O pacote deste artigo contém o HTML, os três arquivos JavaScript utilizados pelo simulador e as duas figuras. Os links abaixo funcionam quando todos esses arquivos permanecem no mesmo diretório.
12. Conclusão
O simulador transforma o computador em uma bancada de testes para o servidor OCPP 1.6. O teste-ocpp.js representa o EVSE, o web-server.js acompanha a comunicação e conecta o teste à interface Web, enquanto o mongo-reader.js permite consultar os dados armazenados.
O ponto central é separar a função do EVSE da função do servidor: o EVSE representa o equipamento de campo, enquanto o servidor OCPP coordena a comunicação e as informações da sessão. O simulador permite reproduzir essa comunicação de maneira controlada, tornando mais simples testar, depurar e evoluir o servidor antes da utilização de um carregador físico.

Deixe um comentário