-
Notifications
You must be signed in to change notification settings - Fork 4
VOC Index #9
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
VOC Index #9
Conversation
Fixed Issues in build tests |
adafruit_sgp40/__init__.py
Outdated
@@ -31,7 +31,8 @@ | |||
""" | |||
from time import sleep | |||
from struct import unpack_from | |||
import adafruit_bus_device.i2c_device as i2c_device | |||
from adafruit_bus_device import i2c_device | |||
from adafruit_sgp40.voc_algorithm import VOCAlgorithm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may want to have this only be imported if the voc algorithm is requested, since its a large py file
adafruit_sgp40/voc_algorithm.py
Outdated
VOCALGORITHM_TAU_INITIAL_MEAN = 20.0 | ||
VOCALGORITHM_INITI_DURATION_MEAN = 3600.0 * 0.75 | ||
VOCALGORITHM_INITI_TRANSITION_MEAN = 0.01 | ||
VOCALGORITHM_TAU_INITIAL_VARIANCE = 2500.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can make these const()
have the name start with _
as well - this will reduce the memory usage
So the Dfrobot implementation swaps all math to fixed point math in a 1 to 1 copy of the sension c implementation--which I believe is the primary reason it's so large (to be fair, the algorithm is large in it's own right). Would the size be enough of a justification to explore a floating point implementation to avoid the overhead of defining fixed point math? |
no, its fine to just have it be a non-immediate import |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work, just a couple of small things but really great job with this PR!
I have a meeting but I should be able to test this in full on my sensor in a couple of hours.
Thank you! The VOC algorithm was giving me a massive knot of trouble.
README.rst
Outdated
compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) | ||
|
||
# For compensated raw gas readings | ||
compensated_raw_gas = sgp.raw(temperature = temperature, relative_humidity = humidity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgp.raw
doesn't take in any inputs, you'll need measure_raw
in it's place
# For compensated raw gas readings | ||
compensated_raw_gas = sgp.raw(temperature = temperature, relative_humidity = humidity) | ||
|
||
# For Compensated voc index readings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might also need to be a time.sleep(1) here, or we can comment out one of the examples (in this case compensated_raw_gas
would be better to comment out) because I think the sensor needs full second between readings, and both the measure_raw
and measure_index
make a call to the sensor over i2c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, datasheet says 1s typical, .5s min
compensated_raw_gas = sgp.measure_raw( | ||
temperature=temperature, relative_humidity=humidity | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The time.sleep(1) or commenting out of the codeblock brought up in the readme applies here
Tested with example loops |
Thanks for those adjustments! Now for an issue outside of your PR, which either I can handle or you can if you're up for it. It looks like the
to
Again, I can handle that or you can, either option works. Now I've got another question for you. So I think the sgp40 voc algorithm is time dependent. The source c library has tuning parameters based off of the number of minute the device has been online. Do you know how the dfrobot implementation knows the duration the device has been running? I can't find it, and I'm wondering if the algorithm goes wonky if the sensor is sampled at a rate other than once a second. As an aside, I've been running it with a sample once a second for about two hours and it seems to be working well. It's responsive when I put it next to a sour dough starter as it should be. |
Found the variable! It looks like it defined a constant,
as a once per second sample rate. I think this means if the sampling is at a different rate, the sampling interval needs to be adjusted accordingly for a proper use of the algorithm. I'm not the best person to make a suggestion as to what to do going forward though. Maybe in What are your thoughts? |
Dug deeper into it--it looks like the algorithm is required to sample at once per second and not valid otherwise. So feel free to ignore my previous comments. It might be worthwhile to add a note about the sample rate in the example and readme though. I can take care of it or you can, up to you. Thanks for all of your work on this! This is fantastic and I think it's ready to go now that I understand the sampling rate better :D |
@AVanVlack @KeithTheEE I tested this as far as basic functionality and it seems OK. It doesn't appear to break any existing use and the From your testing, do you consider the values returned by Here's my simple test program: import time
import board
import adafruit_sgp40
sgp = adafruit_sgp40.SGP40(board.I2C())
while True:
print(sgp.measure_index(20, 40))
time.sleep(1) |
From my testing and from reading the code, the initial startup time will return 0's because there isn't any history to draw a known variance in sensor values from. After the warm up phase to settles in on a better value. One that seems more or less accurate for my place. To test it's responsiveness I brought the sensor above my sour dough starter, which off-gasses things like alcohol and co2 as the yeast ferments, and it spiked above 200 as I'd expect it to before dropping off back to around 120. The AC is running in my apartment so it make sense that the gas would dissipate. It's pretty responsive, with the numbers rising in a very short interval of time, like a sample or two. It's step increase is around 10 indices per sample give or take. It might not seem like much but the value is pretty constant when left at my desk, hovering at around 100 and jittering only one or two values above that from time to time. This responsiveness is good as the sgp40 sensor can't return an actual count of VOCs, and instead returns changes in it. I've only been running this code for a few hours, and it has a burn in period as well before the algorithm switches to 'heating element has fully heated' mode (I think that's 2 hours in?), so it's possible after a while I'll notice something's amiss, but I don't expect that to be the case from what I've seen so far. My code is mostly identical to your test, with the exception of including the bme280 to get a humidity and temperature response as well. From my usage, the pull request itself is fantastic, I need to follow up with a second pull request to adjust the humidity and temperature compensated example as it is now out of date since the bme280 requires an additional bit, But those aren't really about this pull request, they're separate issues. |
The sensor has been running for more than 24 hours and seems somewhat responsive as well today. It still responds to the sour dough starter, but if I smell a subtle odor or have a bag of fruit next to it, it doesn't spike up as I'd expect it to. That might be my fault though for subjecting it to multiple bursts of sour dough starter--it keeps on internal model of how much the odors vary, and I might have made it think my home is more wildly variable with smells than it really is. The values still seem within reason, and responded to being near the sour dough as I'd expect. I'd say it looks good to me after running for so long. I forgot to check how much memory the full algorithm occupies, maybe going forward I should and I can report that back, but for the time being I'm going to put my home air quality sensor back together and add this into it unless you have another aspect you want me to test. |
@KeithTheEE Most of the math in the algorithm is much above my math knowledge I appreciate your understating of it. In thinking about making the library straight forward to use, what about giving the user a loop method to call in their main loop (noted at a max of 1s intervals). The method would make sure to call the algorithm at proper 1s intravals, store the returned index in an instance variable and return when there is a new reading. Then the user can just pull the variable for the index and/or the loop method can return when there is new data. Of corse we would leave the measure_index method as is for someone that wants to manage scheduling themselves. Ideally some kind of async loop/interrupt timer would be better but it looks like that is still in early stages on circuitpython. |
Correct. This is generally not an option with CircuitPython. Also still wondering if this would better to keep separate from the current library. Or at least don't modify |
@caternuson That structure wouldn't be difficult, in an example omitting comments it would look something like import time
import board
import adafruit_bme280
import adafruit_sgp40
import adafruit_sgp40_VOC_Algorithm
i2c = board.I2C() # uses board.SCL and board.SDA
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
sgp = adafruit_sgp40.SGP40(i2c)
voc_algorithm = adafruit_sgp40_VOC_Algorithm.VOC_Algorithim()
while True:
temperature = bme280.temperature
humidity = bme280.relative_humidity
compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity)
voc_index = voc_algorithm.update(compensated_raw_gas)
print(compensated_raw_gas)
print(voc_index)
print("")
time.sleep(1) There is a downside and an upside to this. The upside being memory management. The downside is the @AVanVlack you've done a great job with this! I am not great on the math either, I've just been digging in the docs and code for a while trying to make sense of 'why' anything in the voc algorithm happens. |
Makes sense. Amazing how much it takes to go from It sounds like this code is generally working and useful. So let's go ahead and merge it in. Thanks! |
Hey @Curt-is-Pi Is this on a blinka setup? It looks like I missed the import statement for const, it seems to be automatically imported on microcontrollers. |
Hello Drew,
I am using blinka with this sensor. Am correct in assuming that the import
would be made in the '_inti_.py' file?
*From: *Drew VanVlack ***@***.***>
*Sent: *Sunday, September 5, 2021 5:09 PM
*To: *adafruit/Adafruit_CircuitPython_SGP40
***@***.***>
*Cc: *Curt-is-Pi ***@***.***>; Mention
***@***.***>
*Subject: *Re: [adafruit/Adafruit_CircuitPython_SGP40] VOC Index (#9)
Hey @Curt-is-Pi <https://github.com/Curt-is-Pi> Is this on a blinka setup?
It looks like I missed the import statement for const, it seems to be
automatically imported on microcontrollers. from micropython import const
should fix it. I will create a new pull request with that fix.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ATDN5G7LY3O3XGPGCSFULRLUAQBEPANCNFSM5C7EIPVA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
Updating https://github.com/adafruit/Adafruit_CircuitPython_SGP40 to 1.3.0 from 1.2.1: > Merge pull request adafruit/Adafruit_CircuitPython_SGP40#10 from AVanVlack/main > Merge pull request adafruit/Adafruit_CircuitPython_SGP40#9 from AVanVlack/main Updating https://github.com/adafruit/Adafruit_CircuitPython_ST7789 to 1.5.1 from 1.5.0: > Merge pull request adafruit/Adafruit_CircuitPython_ST7789#25 from makermelissa/master > Merge pull request adafruit/Adafruit_CircuitPython_ST7789#24 from makermelissa/master Updating https://github.com/adafruit/Adafruit_CircuitPython_turtle to 2.2.1 from 2.2.0: > Merge pull request adafruit/Adafruit_CircuitPython_turtle#26 from FoamyGuy/angle_docs
Resolves #2 and adds a voc index algorithm to measure_index method.
Changes
adafruit_sgp40.py
toadafruit_sgp40/__init__.py