← index #12842Issue #4227
Related · high · value 1.060
QUERY · ISSUE

esp32: UDP sendto causes memory leaks/ENOMEM error for most IP addresses

openby tangentmongeropened 2023-10-31updated 2024-02-17
bug

I'm trying to send UDP packets using socket.sendto(). I would expect this to succeed regardless of the destination IP address, but for most addresses this fails after exactly four packets with OSError ENOMEM. I do not see this exception when sending to the board's own IP or the broadcast address.

Hardware: ESP32 on an Olimex ESP32-PoE-ISO board, connected to a switch that has nothing else connected to it (as a minimal test setup).

ESPtool identifies the chip as ESP32D0WDQ5 (revision 3).

Firmware: OLIMEX_ESP32_POE-20231005-v1.21.0.bin

Version in REPL: MicroPython v1.21.0 on 2023-10-06; Olimex ESP32 ETH with ESP32

#4029 is a similar issue but uses WiFi and instead affects broadcast only. I'm seeing the opposite behavior.

This code example attempts to send 100 packets to IPs from 192.168.4.0 through to 192.168.4.255. This succeeds for IPs 192.168.4.250 (the board's own IP) and 192.168.4.255 (broadcast) but fails for every other address after the fourth attempt with OSError 12, ENOMEM.

import socket
import network
import machine
import time

lan = network.LAN(mdc=machine.Pin(23), mdio=machine.Pin(18), power=machine.Pin(12),
                  phy_type=network.PHY_LAN8720, phy_addr=0)
try:
    lan.active(True)
except OSError as ex:
    if ex.value == "ethernet enable failed":
        print("Failed to enable ethernet - waiting a few seconds and doing a hardware reset.")
        time.sleep(2)
        machine.reset()
    raise

my_ip = '192.168.4.250'
lan.ifconfig((my_ip, '255.255.255.0', '192.168.4.1', '192.168.4.1'))

sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock1.bind((my_ip, 10005))

buf = "hello"
port = 10000

for x in range(0, 256):
    try:
        for test in range(0, 100):
            sock1.sendto(buf, (f"192.168.4.{x}", port))
        print(f"192.168.4.{x}:{port}: PASS")
    except OSError as e:
        print(f"192.168.4.{x}:{port}: FAIL after {test+1} attempts with {e}")

Output:

192.168.4.0:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.1:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.2:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.3:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.4:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
...
192.168.4.248:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.249:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.250:10000: PASS
192.168.4.251:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.252:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.253:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.254:10000: FAIL after 4 attempts with [Errno 12] ENOMEM
192.168.4.255:10000: PASS

My next step is to dig into the MicroPython and IDF code, would appreciate any advice on tests to try.

CANDIDATE · ISSUE

ESP32 cannot send and receive UDP packets to itself

openby goatchurchprimeopened 2018-10-10updated 2022-03-30
port-esp32

It seems you can a UDP packet on a PC to itself, but you can't send a UDP packet from an ESP32 to itself:

import network, socket

# Create access point for the phone to log onto
si = network.WLAN(network.AP_IF) 
si.active(True)       
print("Wifi is: ESP_%X%X%X" % tuple(si.config("mac")[-3:]))
print(si.ifconfig())

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(3)
port = 9099
ipnum = "192.168.4.1"
s.bind((ipnum, port))   

s.sendto(b"An ESP32 UDP Packet", (ipnum, port))
print(s.recvfrom(100))  # <--- this times out

response is:

Wifi is: ESP_8C1FB2
('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')

Traceback (most recent call last):
File "<stdin>", line 16, in <module>
OSError: [Errno 110] ETIMEDOUT

However, when I connect my PC to the ESP_8C1FB2 hotspot then I can run the following:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(3)
port = 9099
ipnum = "192.168.4.2"
s.bind((ipnum, port))   

s.sendto(b"A PC UDP Packet", (ipnum, port))
print(s.recvfrom(100))

response is:

(b'A PC UDP Packet', ('192.168.4.2', 9099))

I am able to send and receive UDP packets between the PC and the ESP32

  • PC: s.sendto(b"A PC UDP Packet", ("192.168.4.1", port)); print(s.recvfrom(100)))
  • ESP32: s.sendto(b"An ESP UDP Packet", ("192.168.4.2", port)); print(s.recvfrom(100)))

The socket.bind function seems to have to be to the device's own IPnumber. Fortunately this is quite predictable when working from the ESP's hotspot.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied