glenn20/micropython-espnow-images

ESPnow bug in sending

danish1963 opened this issue · 5 comments

I'm trying to use Espnow on esp8266. When i try to send str(7) or '7' the esp8266 reboot, I can send all other number with out failure, is there a bug in the firmware.
The firmware is : firmware-esp8266-GENERIC.bin ....MicroPython v1.17-12-g26e539a1b on 2021-09-03; ESP module with ESP8266

The code i use:
import network
import time
from esp import espnow

A WLAN interface must be active to send()/recv()

w0 = network.WLAN(network.STA_IF) # Or network.AP_IF
w0.active(True)

e = espnow.ESPNow()
e.init()

peer = b'\xf4\xcf\xa2\xd1\x10\x7b' # put your reciever mac adress
e.add_peer(peer)

e.send(peer,"Starting...",True) # Send to all peers

while True:
e.send(peer, "My Name", True)
e.send(peer, '7', True) # the string '7' reboot the esp8266? all other string (I try) works.
time.sleep(2)

Oooh - that's an interesting bug. I confirm it happens to me as well on esp8266, but not on esp32.

A work around while I investigate further: e.send(peer, b'7', True) works fine (ie, supply a bytes literal instead of str).

It seems like it might be a bug related to string encoding on esp8266. Eg.

x = b'7'
y = '7'.encode('utf8')
print(x, y, x == y)
print(e.send(peer, x))
print(e.send(peer, y))

will print:

b'7' b'7' True
True

then reboot on the e.send(peer, y). Which is bizarre and currently inexplicable behaviour.

I'll investigate further and report back (hopefully with a fix).

A little more debugging info... sending 'e', 'f', 'j', 'n', 'r', 's', 'w', 'y'' also causes a reboot.

These are all micropython 'interned' QSTR strings and the strings that all crash return buffer addresses in the same range (0x4028XXXX) and all others have an address in the range 0x3ffeXXXX, so it may be some memory mapping issue for the Espressif esp_now_send() call.
I'll keep investigating, but if anyone has a clue - please let me know.

Update:
Actually, passing any QSTR literal which has been pre-defined in the micropython image (see ports/esp8266/BUILD-GENERIC/genhdr/qstrdefs.generated.h) as the 2nd arg of e.send() will cause a reboot, eg: 'join', '<module>', ....
I suspect there is an issue passing memory addresses for these statically interned strings to Espressif's esp_now_send() function on the 8266.

Till I find a fix, it would be safest to always use byte string literals instead of str literals, eg. e.send(peer, b'join') or e.send(peer, bytearray('join')) to avoid the interning.

Update 2:
I have a fix which detects if a memory buffer address is outside the GC memory pool and makes a temporary copy on the stack to pass to esp_now_send(). This fixes the bug (with the overhead of an additional copy) - BUT now the code overflows the default rss size on the esp8266. I will have to try to squeeze the code elsewhere yet again - which will take a little time.

By tweaking my build environment I was able to fit the esp8266 code into the text size limit with the bug fix.

New ESP8266 images are available in the 20211020_espnow-g20-v1.17-g26f058583 folder. These images pass all micropython tests including my espnow multi-tests.

Please test and let me know if there are any problems. If not I will close this issue in a week or so.

Thanks again for the bug report - was a surprising bug. It seems that esp_now_send() is trying to write to the read-only memory passed to it - which it should not.

Hi Glenn20. Thank you for your fix. After installing the new image, now it works for me too..

Excellent. Closing now. Reopen if it resurfaces or there are any other related problems.