Introducción
En
este artículo veremos cómo montar una aplicación sencilla con comunicación Ethernet UDP entre cualquier dispositivo y un
PLC ESP32.
Explicaremos cómo crear una instancia EthernetUDP en
tu PLC ESP32 a la que podrán acceder todos los dispositivos de la misma
subred. Leyendo y utilizando el código siguiente aprenderás fácilmente
a enviar mensajes a tu PLC ESP32 desde otro dispositivo, con uno de los protocolos de comunicación más sencillos que se usan en la transferencia de datos industriales.
Aplicación UDP sencilla
Hemos escrito el siguiente código:
// Simple UDP communication for controlling a LED
// Commands: Start, LedON, LedOFF
#include <ethernet.h>
#include <ethernetudp.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(10, 42, 0, 20);
unsigned int localPort = 8888; // local port to listen on
unsigned int replyPort = 8889; // port to send response to
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer for receiving incoming data
EthernetUDP Udp;// An EthernetUDP instance to let us send and receive packets over UDP
bool ledStatus = LOW;
// Response function
String responseUdp(String incoming_message){
incoming_message = incoming_message.substring(0,incoming_message.length()-1);
if (incoming_message=="Start"){ledStatus=HIGH; return "ACK0";}
else if (incoming_message=="LedOn"){ledStatus=HIGH; return "ACK1";}
else if (incoming_message == "LedOff"){ledStatus=LOW; return "ACK2";}
else return "Unrecognised Command";
}
void setup() {
// Serial
Serial.begin(9600);
while (!Serial) {;}
Serial.println("Serial ready");
// Ethernet
Ethernet.begin(mac, ip);
// Check for Ethernet hardware
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
Serial.println("Ethernet ready");
// Start UDP
Udp.begin(localPort);
Serial.println("UDP ready");
}
void loop() {
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize) {
// read the packet into packetBufffer
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
// Packet info
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remote = Udp.remoteIP();
for (int i=0; i < 4; i++) {
Serial.print(remote[i], DEC);
if (i < 3) {
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(Udp.remotePort());
Serial.print("Contents:");
Serial.println(packetBuffer); //*/
// Process packet and generate response
String reply = responseUdp(packetBuffer);
// send a reply to the IP address that sent us the packet we received to a port of our choice
int reply_port = replyPort; // Port to send msg back to. Switch to Udp.remotePort() to reply to the transmitting port
Udp.beginPacket(Udp.remoteIP(), reply_port);
Serial.println("Sending message \""+reply+"\" back to port: "+String(reply_port));
reply+="\n";
const char* udp_reply = reply.c_str();
Udp.write(udp_reply);
Udp.endPacket(); //*/
}
delay(10);
if (ledStatus) {
digitalWrite(Q0_0, HIGH);
}
else
{
digitalWrite(Q0_0, LOW);
}
}
// Simple UDP communication for controlling a LED
// Commands: Start, LedON, LedOFF
#include <ethernet.h>
#include <ethernetudp.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(10, 42, 0, 20);
unsigned int localPort = 8888; // local port to listen on
unsigned int replyPort = 8889; // port to send response to
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer for receiving incoming data
EthernetUDP Udp;// An EthernetUDP instance to let us send and receive packets over UDP
bool ledStatus = LOW;
// Response function
String responseUdp(String incoming_message){
incoming_message = incoming_message.substring(0,incoming_message.length()-1);
if (incoming_message=="Start"){ledStatus=HIGH; return "ACK0";}
else if (incoming_message=="LedOn"){ledStatus=HIGH; return "ACK1";}
else if (incoming_message == "LedOff"){ledStatus=LOW; return "ACK2";}
else return "Unrecognised Command";
}
void setup() {
// Serial
Serial.begin(9600);
while (!Serial) {;}
Serial.println("Serial ready");
// Ethernet
Ethernet.begin(mac, ip);
// Check for Ethernet hardware
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
Serial.println("Ethernet ready");
// Start UDP
Udp.begin(localPort);
Serial.println("UDP ready");
}
void loop() {
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize) {
// read the packet into packetBufffer
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
// Packet info
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remote = Udp.remoteIP();
for (int i=0; i < 4; i++) {
Serial.print(remote[i], DEC);
if (i < 3) {
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(Udp.remotePort());
Serial.print("Contents:");
Serial.println(packetBuffer); //*/
// Process packet and generate response
String reply = responseUdp(packetBuffer);
// send a reply to the IP address that sent us the packet we received to a port of our choice
int reply_port = replyPort; // Port to send msg back to. Switch to Udp.remotePort() to reply to the transmitting port
Udp.beginPacket(Udp.remoteIP(), reply_port);
Serial.println("Sending message \""+reply+"\" back to port: "+String(reply_port));
reply+="\n";
const char* udp_reply = reply.c_str();
Udp.write(udp_reply);
Udp.endPacket(); //*/
}
delay(10);
if (ledStatus) {
digitalWrite(Q0_0, HIGH);
}
else
{
digitalWrite(Q0_0, LOW);
}
}
Este código es totalmente escalable y permite añadir fácilmente nuevos comandos UDP y respuestas. Todos los mensajes entrantes se analizan en la función responseUdp(), que devuelve una cadena de respuesta que después se puede enviar por el mismo puerto por el que se recibió o por uno fijo (el predeterminado).
Ejemplo
Hay muchas formas de enviar un paquete UDP a un dispositivo, pero aquí usaremos el siguiente comando:
$ echo "msg" > /dev/udp/10.42.0.20/8888
Para escuchar la respuesta usaremos este otro comando:
$ netcat -ul 8889
Ten en cuenta que 8888 y 8889 son los puertos por los que se realiza la comunicación, tal como se declara en el código. Si enviamos algunos mensajes por el puerto 8888:

Obtenemos las siguientes respuestas en el puerto 8889, mientras parpadea el LED Q0_0:
