-
Notifications
You must be signed in to change notification settings - Fork 495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use http instead of https to avoid memory errors #599
Conversation
@helgibbons My Pico W with pimoroni's uf2 will successfully fetch data one time from my Firebase Cloud Function api, which is accessed through HTTPS. However, subsequent fetches are failing with the error -29312, 'MBEDTLS_ERR_SSL_CONN_EOF'. I see that in this example you moved from https to http for memory errors. Can you describe what was going on? Were some fetches in this example successful and then subsequent fetches failed with an error like in my case? Is this error coming from a bug in the pimoroni uf2 and are there any plans to fix it? |
@jonbhanson unfortunately it's got nothing to do with our custom .uf2 and everything to do with SSL just needing more memory than we can reasonably expect from a 262k microcontroller that has ~200k allocated to MicroPython heap storage and flags. If you run the following minimal example: import gc
import rp2
import time
import network
import urequests
SSID = "your-ssid"
PSK = "your-psk"
TEST_URL = "https://api.open-meteo.com/v1/forecast?latitude=53.38609085276884&longitude=-1.4239983439328177¤t_weather=true&timezone=auto"
rp2.country("GB")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PSK)
while not wlan.isconnected():
print("Connecting...")
time.sleep(1.0)
rcount = 1
while True:
print(f"Requests: {rcount}")
r = urequests.get(TEST_URL)
del r
gc.collect()
time.sleep(1.0)
rcount += 1 It will fail at ~5 attempts on both our .uf2 and the Pico W nightly.
Though I'll usually see something like:
Or:
It looks like the underlying SSL wrapper is not being particularly conscientious about freeing memory and we're either chewing through the heap remaining to C (invisible and unmanageable to the user) or just fragmenting it until there's not a sufficient portion of RAM left. Also on Pico W nightly, using
But only intermittently, with the above errors being more prominent. It's possible something weird with the socket management is going on there, IE: a failure to explicitly I tend to prefer
ℹ️ The easy availability of import gc
import rp2
import time
import network
from urllib import urequest
SSID = "your-ssid"
PSK = "your-psk"
TEST_URL = "https://api.open-meteo.com/v1/forecast?latitude=53.38609085276884&longitude=-1.4239983439328177¤t_weather=true&timezone=auto"
rp2.country("GB")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PSK)
while not wlan.isconnected():
print("Connecting...")
time.sleep(1.0)
rcount = 1
while True:
print(f"Requests: {rcount}")
r = urequest.urlopen(TEST_URL)
r.close()
del r
gc.collect()
time.sleep(1.0)
rcount += 1 You can get Or if you have a WiFi connection on your Pico you can do: import mip
mip.install('urllib.urequest') I managed up to 25 requests using the above code with |
@Gadgetoid thank you for taking the time to write this informative and helpful response! I will give your suggestion a try and see how it goes. |
BTW, here is the project I'm working on, just in case you're interested: Boiler monitor. Now I'm working on the next revision of this project, which is a Pimoroni Display 2.0 screen to display my boiler's status: |
That little monitor setup looks awesome! Thank you for sharing. I spent a long time bashing my head against this stuff and needed a good excuse to formalise my reasons for preferring A fairer test on the latter case might be: import gc
import rp2
import time
import network
import urequest
SSID = "your-ssid"
PSK = "your-psk"
TEST_URL = "https://api.open-meteo.com/v1/forecast?latitude=53.38609085276884&longitude=-1.4239983439328177¤t_weather=true&timezone=auto"
rp2.country("GB")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PSK)
while not wlan.isconnected():
print("Connecting...")
time.sleep(1.0)
rcount = 1
while True:
print(f"Requests: {rcount}")
r = urequest.urlopen(TEST_URL)
data = r.read(1000)
print(data[0:10])
r.close()
del data
del r
gc.collect()
time.sleep(1.0)
rcount += 1 Since this actually reads back and prints data from the socket. If your API is pretty stable and has a known limit in the size of its responses then you can pre-allocate a bytearray and use Here's the above example modified to do that: import gc
import rp2
import time
import network
import urequest
SSID = "your-ssid"
PSK = "your-psk"
TEST_URL = "https://api.open-meteo.com/v1/forecast?latitude=53.38609085276884&longitude=-1.4239983439328177¤t_weather=true&timezone=auto"
rp2.country("GB")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PSK)
while not wlan.isconnected():
print("Connecting...")
time.sleep(1.0)
rcount = 1
data = bytearray(2048)
while True:
print(f"Requests: {rcount}")
r = urequest.urlopen(TEST_URL)
r.readinto(data)
print(data[0:10])
r.close()
del r
gc.collect()
time.sleep(1.0)
rcount += 1 It's normally pretty good practice to pre-allocate buffers on micro-controllers, but it feels very weird doing it in MicroPython. Nonetheless it can head off memory fragmentation and prevent unexpected server responses from chomping all your RAM since - IIRC - |
Once again, very helpful, thanks! My api response is pretty stable and under my control, so I may be able to make that work. 👍 |
In fact (I'm reminded via the MicroPython Discord) Edit: I have added some |
I tried your final example with my api endpoint (reserving a 6k byte array) and it worked fine. However, when I added the line to initialize the display # set up the hardware
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, rotate=0) it resumed failing again with the same error So maybe there is not enough memory onboard for the display code and the SSL overhead at the same time? |
Pico Display 2 is, by default, RGB232 at 320x240 or 75k. Definitely not small. You could try the 4-bit palette mode to get the size down to 37.5k using: display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, pen_type=PEN_P4, rotate=0) But that will give you just 16 colours to play with. How big is your API payload? |
Thanks for the palette suggestion, that could really help when I get to the bottom of this, but at the moment there seems to be a different problem: I created a "hello world" endpoint to see if the problem is payload size related. If I use your example above, adding the So, it seems something about my server response is causing this problem. I figured maybe it was related to encryption type, but it looks like both endpoints are using TLS 1.3. Do you have any thought on anything that could be changed in the server response to make it digestible? |
@Gadgetoid @helgibbons do you have any advice given the note above about different endpoints? |
No description provided.