Open gsexton opened 5 days ago
Looking at adafruit_connectionmanager(), I'm not understanding how this will work without a call to gc.collect()
try:
socket = self._get_connected_socket(
addr_info, host, port, timeout, is_ssl, ssl_context
)
self._register_connected_socket(key, socket)
return socket
except (MemoryError, OSError, RuntimeError):
# Could not get a new socket (or two, if SSL).
# If there are any available sockets, free them all and try again.
if self.available_socket_count:
self._free_sockets()
socket = self._get_connected_socket(
addr_info, host, port, timeout, is_ssl, ssl_context
)
self._register_connected_socket(key, socket)
return socket
# Re-raise exception if no sockets could be freed.
raise
Python will free memory when it can't find enough. When the sockets are freed, they can be gc'd when the system needs it.
Once everything is loaded into memory, you should be able to constantly make calls, unless you are holding onto data locally (like keeping the weather data). Are you making sure everything is freed?
Also, you could look at the json streamer as it can pull data in chunks (vs the whole string and the json conversation).
@justmobilize Thanks for replying.
I changed the openweather code to save just the keys out of the response dict that I'm using. It's about 6 or so. This didn't make any difference. I'm still seeing the memory error on every other request to influx. I never got any memory error while parsing the OpenWeather API.
The influx writer doesn't allocate any memory. It formats a string and calls .post(). You can see on the first run-through that the memory allocation ratchets up on the first call. to the .post() call.
[06/29/2024 14:42:25] DEBUG Top of while Loop. GC: Allocated: 61952 Free: 61440
[06/29/2024 14:42:28] DEBUG Post InfluxWrite and gc.collect() GC: Allocated: 93760 Free: 29632
So, calling request.post() immediately consumes 1/4th of the RAM of the system and doesn't free it.
If you want to make sure the sockets are fully cleared you can call:
adafruit_connection_manager.connection_manager_close_all()
Which will release all the open sockets. You could then do a gc.collect()
as well.
The other issue is you need continuous memory. So if you have fragmentation it could slowly build until it's unusable.
I often do a soft reset when I get to a point of not needing anything that's loaded.
9.x did change how it managed memory, and for most it was helpful, but seems to have made the pico worse.
Please try the "Absolute Newest" build via the link on the board page. We turned off some things that will free up some RAM.
@dhalbert I tried with 9.1.0-beta.3-55-g95dbb18d22 and the issue still occurs.
Did you try adding the adafruit_connection_manager.connection_manager_close_all()
?
I have a Raspberry Pi Pico W running CircuitPython 9.0.5. I'm using the current version (HEAD) of Adafruit_CircuitPython_Requests, and the current HEAD of Adafruit_CircuitPython_ConnectionManager. The application files are compiled using mpy-cross 9.0.5. The size of the .mpy files is approximately 22K bytes and code.py is about 9K bytes.
My application is a thermometer with a LCD display. I'm using a TMP102, and an Adafruit ST7789 240x320 display. The display shows the current temp and time. Temperature readings are taken once a second, averaged, and once a minute, the program does a POST request to place the temperature data into an InfluxDB on a remote host. I'm also using Adafruit_CircuitPython_NTP at startup to set the RTC. Finally, I'm making requests to the OpenWeather API. The OpenWeather API call is made once per 10 minutes. I ran into problems with SSL certificates. If I tried to make requests to more than one SSL site, I immediately ran out of memory. To work around that, I made a proxy for the OpenWeather API on the same server that hosts Influx. Also, I'm using a GoDaddy cert, so I load the GoDaddy CA certificate using this code:
so I'm only calling one SSL http host. The requests session is created:
This worked pretty well for a year or so using CircuitPython 8.x.
What's happening is that about 7 minutes after startup, I start getting memory errors.
About every other call results in a MemoryError. After some time, failures become more prevalent.
I added some dumps of gc value, and can see when I enter the loop there's some 60K bytes free. After the first http requests, free memory reduces to about 20 K Bytes:
After a MemoryError, it looks like:
You can see that AFTER a memory error, a great deal more of memory is freed. I've noticed that free memory and allocated memory are unchanged UNLESS I use the requests library. IOW, reading the TMP102 and updating the display DON'T change the free memory.
Observations
So what seems to be happening is that calling the API library to make a request consumes around 40 K Bytes. When an error occurs, around 30KB is freed giving about 50K bytes free. A subsequent call consumes around 30K bytes. The call after that fails with out of memory.
Does anyone have any ideas on how to work around this? I feel like trying to follow conventional memory reduction techniques is just going to be useless since evidence shows the requests library consuming most of it.