esp8266-protocolo-udp

How to Communicate with ESP32 via UDP

  • 5 min

UDP is a lightweight, connectionless communication protocol that we can use on ESP32 when we want to send data quickly with little overhead.

The example is oriented to ESP32. In many cases it can also be adapted to ESP8266 by changing libraries and a few pin details.

We have seen several ways to communicate the ESP8266 with the client. We have seen web forms as a simple (and somewhat obsolete) solution, and the more modern Ajax connections, websockets, and asynchronous websockets.

All these solutions work via HTTP over TCP. But sometimes we forget that UDP communications also exist. Very briefly, UDP (Universal Datagram Protocol) does away with some of the packets needed to establish the connection and error verification.

In a UDP communication, the server sends packets without waiting for acknowledgment from the client. If a packet is lost, the client cannot request it to be resent. Therefore, TCP is useful for applications that require communication reliability. While UDP is useful for fast transmissions, even faster than a Websocket.

And in the case of a microprocessor like the ESP32, a UDP communication fits many cases. Furthermore, UDP communications mean less load for the server as they avoid a good part of the required packets.

For example, if we are sending an animation to a series of LEDs, or controlling a robot’s position. In these cases of “almost continuous” communication, I don’t care as much about acknowledgment, but about speed. If a packet is lost, it will be immediately replaced by the next one.

Code Example

Fortunately, implementing UDP communication on the ESP32 is very simple thanks to the ‘WiFiUDP.h’ library. Let’s see it with an example.

First, the main program loop looks like this.

We will see that the only relevant points are that we have included the necessary files, and the functions ‘ConnectUDP()’ and ‘GetUDP_Packet()’ that we will see next.

We also have the ‘SendUDP_Packet(“abcde”)’ function commented out, which would illustrate sending a String via UDP. We won’t use it in this example, but it’s there.

#include <WiFi.h>
#include <WiFiUDP.h>

#include "config.h"  // Replace with your network data
#include "UDP.hpp"
#include "ESP32_Utils.hpp"
#include "ESP32_Utils_UDP.hpp"

void setup() 
{
  Serial.begin(115200);

  ConnectWiFi_STA();
  ConnectUDP();
}

void loop() 
{
  GetUDP_Packet();
  
  //SendUDP_Packet("abcde");
}
Copied!

On the other hand, we have the ‘UDP.hpp’ file, where we have placed all the functions related to UDP in our program.

In this file, we have instantiated a ‘WiFiUDP’ object, the ports on which the connection will operate, and defined the ‘ProcessPacket(String response)’ function that handles the response we want to give to a UDP request.

// UDP variables
WiFiUDP UDP;

unsigned int localPort = 8888;
unsigned int remotePort = 8889;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

void ProcessPacket(String response)
{
   Serial.println(response);
}
Copied!

Finally, we have the ESP32_Utils_UDP.hpp file, where we have defined a series of common functions that we can reuse between programs.

Here we have the ‘ConnectUDP()’ function that establishes the UDP connection, the functions ‘SendUDP_ACK()’ that sends an acknowledgment, ‘SendUDP_Packet(String content)’ that sends a String via UDP, and the function ‘GetUDP_Packet(bool sendACK = true)’ that receives a UDP packet and processes it with the function we defined in the previous file.

boolean ConnectUDP() {
  Serial.println();
  Serial.println("Starting UDP");

  // in UDP error, block execution
  if (UDP.begin(localPort) != 1) 
  {
    Serial.println("Connection failed");
    while (true) { delay(1000); } 
  }

  Serial.println("UDP successful");
}

void SendUDP_ACK()
{
  UDP.beginPacket(UDP.remoteIP(), remotePort);
  UDP.write("ACK");
  UDP.endPacket();
}

void SendUDP_Packet(String content)
{
  UDP.beginPacket(UDP.remoteIP(), remotePort);
  UDP.write(content.c_str());
  UDP.endPacket();
}

void GetUDP_Packet(bool sendACK = true)
{
  int packetSize = UDP.parsePacket();
  if (packetSize)
  {
    // read the packet into packetBufffer
    UDP.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);

    Serial.println();
    Serial.print("Received packet of size ");
    Serial.print(packetSize);
    Serial.print(" from ");
    Serial.print(UDP.remoteIP());
    Serial.print(":");
    Serial.println(UDP.remotePort());
    Serial.print("Payload: ");
    Serial.write((uint8_t*)packetBuffer, (size_t)packetSize);
    Serial.println();
    ProcessPacket(String(packetBuffer));

    //// send a reply, to the IP address and port that sent us the packet we received
    if(sendACK) SendUDP_ACK();
  }
  delay(10);
}
Copied!

We upload everything to our ESP32, and we are going to test our UDP communication. For this, we provide two small Python scripts.

The ‘recieveUDP.py’ script that receives a UDP packet and displays it on the screen.

import socket

UDP_PORT = 8889

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', UDP_PORT))

while True:
  data, addr = sock.recvfrom(1024)
  print("received message:")
  print(data.decode('utf-8'))  
Copied!

The ‘sendUDP.py’ script file that sends an example packet via UDP.

import socket

UDP_IP = '192.168.43.237'
UDP_PORT = 8888
UDP_PAYLOAD = 'abcdef'

def yes_or_no(question):
    reply = str(input(question)).lower().strip()
    if reply[0] == 'y':
        return 0
    elif reply[0] == 'n':
        return 1
    else:
        return yes_or_no("Please Enter (y/n) ")
    
while True:
  try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(bytes(UDP_PAYLOAD, "utf-8"), (UDP_IP, UDP_PORT))    
    sock.close()
    print("UDP target IP:", UDP_IP)
    print("UDP target port:", UDP_PORT)
    print("message:", UDP_PAYLOAD)
    if(yes_or_no('Send again (y/n): ')):
      break
  except:
    pass
Copied!

Result

We first run ‘recieveUDP.py’, which listens, and then ‘sendUDP.py’. We see in the console that the packet is sent correctly.

arduino-esp8266-udp-send

In the Arduino serial port, we can check that, indeed, the ESP32 receives the packet.

arduino-esp8266-udp-serial-port

Finally, since in the ESP32 receive function we indicated that we want it to send an ACK signal, we see that the Python script receives the acknowledgment message.

arduino-esp8266-udp-ack

Everything works correctly! Easy, very useful, and often forgotten, UDP connections are another way to communicate a client with the ESP32.

Of course, instead of a simple String with “abced”, we will normally work with a JSON file containing the information we want, as we saw in this post.

With this we already have a working UDP communication. It will not always be the right option, but when it fits, it is a very useful tool for sending fast messages between devices.

Download the Code

All the code from this post is available for download on Github.

github-full

Version for ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Version for ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples