Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions docs/jwplc/JWPLC_NATIVE_DEVICE_DISCOVERY_PHASE2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# JWPLC Native Device Discovery - Fase 2

## Estado

Cerrado y validado.

Esta fase mejora el JWPLC Device Discovery para que el editor no dependa únicamente de detectar equipos con el puerto Modbus TCP 502 abierto.

Ahora el JWPLC Basic responde con identidad propia mediante discovery nativo UDP y el editor puede diferenciar entre:

- JWPLC confirmado.
- Equipo Modbus TCP genérico detectado por fallback.

## Versión

- Editor: OpenPLC Editor - JWPLC Edition 4.2.8-jwplc.2
- Hardware validado: JWPLC Basic v2.0.0
- Board: JWPLC BASIC [2.0.0]
- VPP compatible: com.jwcontrol.jwplc-basic v2.1.0-alpha.11

## Instalador validado

Archivo:

OpenPLC Editor - JWPLC Edition_4.2.8-jwplc.2.exe

SHA256:

0952C753DC11C1AD34FDB3BD6A92E596A1929E024A8AD094A5D1203D4F74EFDA

## Cambios principales

### Discovery nativo JWPLC

Se agregó un responder UDP en el runtime Baremetal para JWPLC Basic.

Solicitud:

JWPLC_DISCOVER_V1

Respuesta:

JWPLC_DEVICE_V1

La respuesta incluye:

- Vendor.
- Modelo.
- IP asignada.
- MAC.
- Puerto Modbus TCP.
- Modo de red: DHCP o STATIC.

### Discovery del lado del editor

El backend del editor ahora intenta primero discovery nativo JWPLC.

Si no obtiene respuesta, mantiene fallback por escaneo de puerto TCP 502.

### UI mejorada

La pantalla Modbus TCP ahora muestra una lista de dispositivos encontrados con:

- Nombre/modelo.
- Estado JWPLC confirmado.
- IP y puerto.
- Tipo de discovery.
- MAC.
- Modo DHCP/static.
- Interfaz de red detectada.

### Comportamiento de Usar para Debug

Con DHCP activo:

- Actualiza Debug IP Address.

Con DHCP desactivado:

- Actualiza Debug IP Address.
- Actualiza IP Address.

## Validación realizada

- [x] Build main OK.
- [x] Build renderer OK.
- [x] Instalador generado.
- [x] Editor abre correctamente.
- [x] Modbus TCP activo con Ethernet W5500.
- [x] DHCP activo.
- [x] Discovery detecta JWPLC Basic.
- [x] UI muestra JWPLC confirmado.
- [x] UI muestra Discovery nativo.
- [x] UI muestra MAC, DHCP e interfaz.
- [x] Usar para Debug actualiza Debug IP Address.
- [x] Debugger TCP probado con IP detectada.

## No incluido

- No se cambia la IP del JWPLC desde el editor.
- No se implementa protocolo industrial tipo DCP.
- No se modifica OTA.
- No se modifica el package Arduino base.
- No se modifica el VPP en esta fase.

## Resultado

JWPLC Device Discovery pasa de ser un escaneo genérico de Modbus TCP a una identificación nativa de JWPLC Basic, manteniendo compatibilidad con fallback TCP 502.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "open-plc-editor-jwplc",
"description": "OpenPLC Editor - JWPLC Edition, build maintained by JW Control for JWPLC Basic integration",
"version": "4.2.8-jwplc.1",
"version": "4.2.8-jwplc.2",
"license": "GPL-3.0",
"author": {
"name": "Autonomy Logic"
Expand Down
2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "open-plc-editor-jwplc",
"version": "4.2.8-jwplc.1",
"version": "4.2.8-jwplc.2",
"description": "OpenPLC Editor - JWPLC Edition, build maintained by JW Control for JWPLC Basic integration",
"license": "GPL-3.0",
"author": {
Expand Down
91 changes: 91 additions & 0 deletions resources/sources/Baremetal/ModbusSlave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Copyright (C) 2022 OpenPLC - Thiago Alves

#if defined(JWPLC_BASIC)
#include <JWPLC_Ethernet.h>
#if defined(MBTCP_ETHERNET)
#include <EthernetUdp.h>
#endif
#endif

//Global Modbus vars
Expand All @@ -29,6 +32,90 @@ uint16_t mb_t35; // frame delay
#if defined(JWPLC_BASIC)
EthernetServer mb_server(502);
static bool jwplc_mbtcp_ready = false;

static EthernetUDP jwplc_discovery_udp;
static bool jwplc_discovery_ready = false;
static const uint16_t JWPLC_DISCOVERY_PORT = 54880;
static const char JWPLC_DISCOVERY_REQUEST[] = "JWPLC_DISCOVER_V1";

static void jwplc_format_ip(char *buffer, size_t size, IPAddress ip)
{
snprintf(buffer, size, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
}

static void jwplc_format_mac(char *buffer, size_t size, const uint8_t *mac)
{
if (mac == NULL)
{
snprintf(buffer, size, "00:00:00:00:00:00");
return;
}

snprintf(
buffer,
size,
"%02X:%02X:%02X:%02X:%02X:%02X",
mac[0],
mac[1],
mac[2],
mac[3],
mac[4],
mac[5]);
}

static void jwplc_discovery_begin()
{
jwplc_discovery_ready = (jwplc_discovery_udp.begin(JWPLC_DISCOVERY_PORT) == 1);
}

static void jwplc_discovery_task()
{
if (!jwplc_discovery_ready)
return;

int packetSize = jwplc_discovery_udp.parsePacket();
if (packetSize <= 0)
return;

char request[40];
int len = jwplc_discovery_udp.read(request, sizeof(request) - 1);
if (len <= 0)
return;

request[len] = '\0';

for (int i = 0; i < len; i++)
{
if (request[i] == '\r' || request[i] == '\n')
{
request[i] = '\0';
break;
}
}

if (strncmp(request, JWPLC_DISCOVERY_REQUEST, strlen(JWPLC_DISCOVERY_REQUEST)) != 0)
return;

char ipBuffer[16];
char macBuffer[18];
jwplc_format_ip(ipBuffer, sizeof(ipBuffer), JWPLC_Ethernet.localIP());
jwplc_format_mac(macBuffer, sizeof(macBuffer), JWPLC_Ethernet.mac());

const char *mode = (JWPLC_Ethernet.mode() == JWPLC_ETH_MODE_DHCP) ? "DHCP" : "STATIC";

char response[220];
snprintf(
response,
sizeof(response),
"JWPLC_DEVICE_V1;vendor=JW Control;model=JWPLC BASIC [2.0.0];ip=%s;mac=%s;modbusTcp=1;port=502;mode=%s",
ipBuffer,
macBuffer,
mode);

jwplc_discovery_udp.beginPacket(jwplc_discovery_udp.remoteIP(), jwplc_discovery_udp.remotePort());
jwplc_discovery_udp.write((const uint8_t *)response, strlen(response));
jwplc_discovery_udp.endPacket();
}
#elif defined(BOARD_ESP32)
WiFiServer mb_server(502);
WiFiClient mb_serverClients[MAX_SRV_CLIENTS];
Expand Down Expand Up @@ -180,6 +267,7 @@ void mbconfig_ethernet_iface(uint8_t *mac, uint8_t *ip, uint8_t *dns, uint8_t *g
// Ethernet.begin() directo: JWPLC_Ethernet inicializa CS, SPI,
// timeouts, DHCP/IP estatica, link y estado del W5500.
jwplc_mbtcp_ready = false;
jwplc_discovery_ready = false;

bool ethOk = false;

Expand Down Expand Up @@ -231,6 +319,7 @@ void mbconfig_ethernet_iface(uint8_t *mac, uint8_t *ip, uint8_t *dns, uint8_t *g
if (ethOk || hasIp)
{
mb_server.begin();
jwplc_discovery_begin();
jwplc_mbtcp_ready = true;
#if defined(JWPLC_MBTCP_DEBUG)
Serial.println("[JWPLC][MBTCP] Server started on port 502");
Expand All @@ -239,6 +328,7 @@ void mbconfig_ethernet_iface(uint8_t *mac, uint8_t *ip, uint8_t *dns, uint8_t *g
else
{
jwplc_mbtcp_ready = false;
jwplc_discovery_ready = false;
#if defined(JWPLC_MBTCP_DEBUG)
Serial.println("[JWPLC][MBTCP] Server NOT started");
#endif
Expand Down Expand Up @@ -324,6 +414,7 @@ void mbtask()
if (jwplc_mbtcp_ready)
{
handle_tcp();
jwplc_discovery_task();

static uint32_t lastMaintainMs = 0;
uint32_t nowMs = millis();
Expand Down
Loading
Loading