sparkfun / pdp-qwiic-micro-python

Repo to manage the logistics of micro python qwiic driver development
0 stars 0 forks source link

SGP40 Air Quality Sensor #2

Open gigapod opened 8 months ago

gigapod commented 8 months ago

Board

SparkFun qwiic Air Quality Sensor - SGP40

Notes

Driver

faludi commented 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.

sfe-SparkFro commented 8 months ago

Looks like our current stock is projected to last about 20 weeks, so no need to rush on an order.

sfe-SparkFro commented 7 months ago

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.

faludi commented 7 months ago

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?

sfe-SparkFro commented 7 months ago

@gigapod ^

gigapod commented 7 months ago

@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.

faludi commented 7 months ago

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

faludi commented 7 months ago

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

faludi commented 7 months ago

@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!

faludi commented 7 months ago

@gigapod please also add Travis Lubbers, who manages the firmware team. His GitHub username is "lubbet".

Thanks again!

sfe-SparkFro commented 7 months ago

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!

faludi commented 7 months ago

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

sfe-SparkFro commented 7 months ago

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
faludi commented 7 months ago

Confirmed working on my XBee as well. @sfe-SparkFro you are crushin' it.

faludi commented 7 months ago

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.
>>> 
sfe-SparkFro commented 7 months ago

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.

sfe-SparkFro commented 7 months ago

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!