Open gigapod opened 8 months ago
I see the SGP40 showing as in stock: https://www.sparkfun.com/products/18345
Please let me know if they are in short supply as we can place an order immediately if needed.
Looks like our current stock is projected to last about 20 weeks, so no need to rush on an order.
This is working on the XRP, but not the XBee due to not enough memory:
File "/flash/lib/qwiic_sgp40/qwiic_sgp40.py", line 57, in <module>
MemoryError: memory allocation failed, allocating 136 bytes
The problem is importing this file. I'm not actually sure how to fix this - I've tried truncating names and removing unused code, but it always says 136 bytes. I'm no Python expert, so not sure what can be done here.
I'm happy to broker a conversation with the two Digi firmware engineers (Mike Wadsten and Travis Lubbers). But would it be better to bring them onboard this Project so we could all chat directly as needed?
@gigapod ^
@faludi - Yes, we can bring them up to speed on the project - happy to give a quick overview on this if you want me to.
Okay, I'll see if that works for them. In the meantime Mike already wrote back to me with some advice for @sfe-SparkFro:
Rob,
A memory allocation error, especially if it’s saying it can’t allocate a pretty-small number of bytes, is almost certainly a sign that the MicroPython app is holding on to “too much” stuff in memory. For example, if we do:
my_list = [“some big string…..”, “some other big string…”, “and another….”] … do_some_stuff_with(my_list)
and the code no longer needs
my_list
, then the code needs to be written in a way that lets it get garbage collected. (Or, you can do del my_list to delete the variable reference to it, then as long as nothing else is referencing it, it can get GC’ed).Each class or function definition also eats up memory (even if the class/function isn’t being used).
There are a couple of ways to get a readout of what’s using up MicroPython’s heap. I haven’t used/looked at them in a while, but micropython.mem_info(1) is one of them. (I’d have to go look at the MicroPython code to say what each thing means.)
The other trick he can use, at least on XBees, is to use os.bundle to relocate some code into internal flash, where they won’t take up as much RAM. (On other MicroPython ports, that is the “frozen code” feature, where modules are built into the firmware itself. Obviously we can’t do that with the XBee firmware, but we accomplish the same thing with os.bundle.)
-Mike
Aaaaaand he just sent a small update:
Small correction/clarification – either the code is allocating too much memory, or the things it’s allocating and keeping are fragmenting the heap. (Allocate a bit of memory, allocate a bit more, release the first, etc. – eventually there’s not much contiguous free space.) A cheap hack to try to work around that is to pepper the code with calls to gc.collect(). But if we look at the code, we might spot a cleaner way to avoid that.
-Mike
@gigapod Mike Wadsten, who is the lead on the firmware team for XBee MicroPython (and many other things) should be invited to this project. His GitHub username is "mikewadsten".
Thanks!
@gigapod please also add Travis Lubbers, who manages the firmware team. His GitHub username is "lubbet".
Thanks again!
Regarding Mike's response, I unfortunately don't think that will address the issue. I believe the root problem is this file, which has a lot going on, all of which is needed. I do wonder if this could be resolved with the MicroPython cross compiler, and compiling it into a .mpy file. However I just tried that, and it's unable to import due to it being an incompatible .mpy file
(I'm using v1.18 of the cross compiler); not sure what the problem is.
So I'm not sure how to fix this. Python and Python memory management aren't exactly my expertise, so if someone else is able to take a look and find a solution, that'd be great!
Here's some search results on cross-compiling from the Digi MicroPython Programming Guide that may be useful: https://www.digi.com/resources/documentation/digidocs/90002219/Default.htm#search-cross%20compiler
Thanks for the info, compiling it with the right flags seems to work!
mpy-cross -mno-unicode -msmall-int-bits=31 DFRobot_SGP40_VOCAlgorithm.py
And it can import the library and run the sensor:
>>> exec(open('ex/qwiic_sgp40_ex1_basic_readings.py', 'r').read())
SparkFun Qwiic Air Quality Sensor - SGP40, Example 1
Waiting 10 seconds for the SGP40 to warm-up.
SGP40 ready!
VOC Index is: 6
VOC Index is: 7
VOC Index is: 7
VOC Index is: 7
VOC Index is: 29 <- Me exhaling into the sensor
VOC Index is: 51
VOC Index is: 72
VOC Index is: 92
VOC Index is: 111
Confirmed working on my XBee as well. @sfe-SparkFro you are crushin' it.
Well, there is one minor issue but I still think stuff is getting crushed.
When I run the second example for function_test, it throws an exception at the soft reset. Not sure why, I checked the data sheet and everything looks correct. Not an issue for me since I don't plan to use that function but if you want to take a look, here's the output. Tried repeatedly on both SFE's and Digi's dev boards with same result. Note that every i2c interaction up to the soft reset works fine.
>>>
soft reboot
Parsing /flash/main.py...
Compiling /flash/main.py...
Running bytecode...
SparkFun Qwiic Air Quality Sensor - SGP40, Function Test
Waiting 10 seconds for the SGP40 to warm-up.
SGP40 ready!
Sensor self-test passed!
Traceback (most recent call last):
File "main", line 27, in <module>
File "main", line 23, in run_example
File "/flash/qwiic_sgp40/qwiic_sgp40.py", line 188, in soft_reset
File "/flash/lib/qwiic_i2c/micropython_i2c.py", line 123, in writeByte
OSError: [Errno 7019] ENODEV
MicroPython v1.18-1745-g7c2efb91b2 on 2023-05-30; XBC Global LTE-M/NB-IoT with EFR32MG
Type "help()" for more information.
>>>
Thanks Rob! Yes, I'm seeing the same behavior on the XBee, my apologies for not having tested that example on the XBee. The thing that's very curious to me is that this example works totally fine on a Raspberry Pi Pico W. I'll probe with a logic analyzer to see if I can figure out what's going on, and why these are behaving differently.
Turns out the SGP40 does not send back an ACK when the reset command is received. I tested this with 3 different platforms, this was true on all of them. For some reason, the Pico W just doesn't raise an exception in this situation, not sure why.
Regardless, I've pushed a fix that catches the exception and only raises it if an optional parameter of soft_reset()
is set. Seems to work fine on my end!
Board
SparkFun qwiic Air Quality Sensor - SGP40
Notes
Driver