Introduction
In
this blog post, we will explore how to setup a simple application using Ethernet UDP communication between any device and an
ESP32 PLC.
We will explain how to create an EthernetUDP instance on
your ESP32 PLC which will be accessible by all devices on the same
sub-net. Reading and using the following code, you will be able to easily learn
how to send messages to your ESP32 PLC from another device, using one of the simplest communication protocols used in industrial data transfer.
Simple UDP application
We have written the following code:
// 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);
}
}
This code is fully scalable, allowing for easy addition of new UPC orders and responses. All incoming messages are parsed in the responseUdp() function, which returns a response string which can be later sent through the same port it was received or a fixed one (default).
Example
There are many ways to send an UDP packet to a device, but we'll be using the following command:
$ echo "msg" > /dev/udp/10.42.0.20/8888
To listen for a response we'll be relying on this other command:
$ netcat -ul 8889
Notice 8888 and 8889 are the ports where the communication takes place through, as declared in the code. If we send a few messages through port 8888:

We get the following responses on port 8889, while blinking the Q0_0 LED:
