esp32: UDP broadcast via sendto causes memory leaks/ENOMEM error
I described this in the uP forum:
https://forum.micropython.org/viewtopic.php?f=18&t=5063&p=28953#p28953
on further investigation, I found that commenting out
https://github.com/micropython/micropython/blob/master/ports/esp32/modsocket.c#L485
made the problem disappear - by disabling the actual functionality of course. To my uninitiated eye, this looks like a SDK issue, but at the same time there seems to be some dependency to the gc calls as documented in the forum post. Any suggestions on to get to the bottom of this?
esp32: UDP sendto causes memory leaks/ENOMEM error for most IP addresses
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.