Memory is tight with v1.29.0 on ESP8266 #19720
|
I upgraded one of my ESP8266's from v1.28.0 to v1.29.0, and stuff that worked before, won't work anymore. I get unable to allocate memory errors with v1.29.0 while v1.28.0 is still OK. So I reverted to v1.28.0. I'm not sure there's any solution to this, other than just remaining on the older version. |
Replies: 5 comments 8 replies
|
Depends on what functionality your code needs. Or pre compiling your code to .mpy (to avoid fragmenting memory by the parser & compiler) Or further optimizing your code. Without knowing anything about your code it is hard to give you more specific guidance. |
|
Are the 2 reduced flash variants the 512kiB and 1MiB versions? I have enough flash space. Its RAM that's the issue. Would they help for that? I did try compiling to .mpy, but that didn't seem to help. The specific thing that fails is this OTA updater that I use: https://github.com/RangerDigital/senko I did add some gc.collect()'s to it, which helped a little, but not enough for v1.29.0. I think its loading the files into memory to compare them. |
|
The test script: import wifi # connect to my wifi
import gc, time, sys, requests
urls = ("http://httpbin.org/get",
"https://httpbin.org/get",
"https://micropython.org",
"https://google.com",
"https://github.com",
"https://raw.githubusercontent.com/shariltumin/bit-and-pieces/master/README.md")
print(sys.implementation)
gc.collect()
for url in urls:
print('----------------------------')
# print('Mem free:', gc.mem_free())
print(url)
try:
res = requests.get(url, headers={})
print(res.status_code)
chunk = res.raw.read(80)
print(chunk)
except Exception as e:
print('ERROR:', e)
finally:
res.close()
gc.collect()
time.sleep(5)A run log: It seems that the results depend on the server we were trying to connect to and the TLS handshake enforced by the server. |
|
senko compares each file by loading all of it into RAM, and that's what breaks on v1.29. Streaming the file in 512-byte chunks and comparing SHA-1 hashes keeps peak use to one small buffer, whatever the file size: import requests, hashlib, os
BUF = bytearray(512)
MV = memoryview(BUF)
def file_sha1(path):
h = hashlib.sha1()
try:
with open(path, "rb") as f:
while n := f.readinto(BUF):
h.update(MV[:n])
except OSError:
return None # no local copy yet
return h.digest()
def update(url, path):
"""Download url to path only if it differs. Returns True if replaced."""
tmp = path + ".new"
h = hashlib.sha1()
r = requests.get(url)
try:
if r.status_code != 200:
raise OSError("HTTP %d for %s" % (r.status_code, url))
with open(tmp, "wb") as f:
while n := r.raw.readinto(BUF):
f.write(MV[:n])
h.update(MV[:n])
finally:
r.close()
if h.digest() == file_sha1(path):
os.remove(tmp)
return False
os.rename(tmp, path)
return True
Over HTTPS, axTLS also needs about 5 KB of contiguous heap for the handshake, as @shariltumin found. Call |
|
I think @enkhbold470's reply above is a good overview of this specific problem. I also want to point out the bigger picture, which is that MicroPython on ESP8266 will only get less viable in future releases. We spend a lot of effort trying to keep code size and memory usage low, but bugfixes tend to add special cases not remove them and as a result code size very slowly creeps up over time. In addition, our ESP8266 port is based on esp-open-sdk which hasn't been maintained for over eight years. Even the newer version of the same ESP8266 NONOS SDK was deprecated by Espressif in 2019. The axTLS library is also abandonware. We'll keep the wheels on for as long as we can, for anyone who isn't bothered by being stuck with old TLS and known-vulnerable Wi-Fi. However, if you are reading this and you are in a position to migrate to any ESP32 chip (ESP32-C3 for example): a lot of things will become easier immediately, and you should have a better time updating to new MicroPython releases for the foreseeable future. |
senko compares each file by loading all of it into RAM, and that's what breaks on v1.29.
urequests.get(url).textholds the response bytes, then a decodedstrcopy, then the local file for the comparison. Each of those needs its own contiguous block of heap, so an 8 KBmain.pyneeds roughly three 8 KB blocks at once.Streaming the file in 512-byte chunks and comparing SHA-1 hashes keeps peak use to one small buffer, whatever the file size: