From 282c85e544f209c6c43d9d0aff2d61a489c17f17 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 27 May 2021 15:11:43 -0500 Subject: [PATCH 01/24] Fixed spelling error in comment, added a generate_crc function, as well as a raw measurement that takes temp and humidity as inputs for humidity compensation raw measurements on the sgp40 --- adafruit_sgp40.py | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 30e6f08..5f387ef 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -9,6 +9,7 @@ * Author(s): Bryan Siepert + Keith Murray Implementation Notes -------------------- @@ -36,7 +37,7 @@ _WORD_LEN = 2 # no point in generating this each time -_READ_CMD = [0x26, 0x0F, 0x7F, 0xFF, 0x8F, 0x66, 0x66, 0x93] +_READ_CMD = [0x26, 0x0F, 0x7F, 0xFF, 0x8F, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 49.999% class SGP40: @@ -80,6 +81,7 @@ def __init__(self, i2c, address=0x59): self.initialize() + def initialize(self): """Reset the sensor to it's initial unconfigured state and configure it with sensible defaults so it can be used""" @@ -153,7 +155,7 @@ def _read_word_from_command( return None readdata_buffer = [] - # The number of bytes to rad back, based on the number of words to read + # The number of bytes to read back, based on the number of words to read replylen = readlen * (_WORD_LEN + 1) # recycle buffer for read/write w/length replybuffer = bytearray(replylen) @@ -182,3 +184,89 @@ def _check_crc8(crc_buffer, crc_value): else: crc = crc << 1 return crc_value == (crc & 0xFF) # check against the bottom 8 bits + + def _generate_crc(crc_buffer): + crc = 0xFF + for byte in crc_buffer: + crc ^= byte + for _ in range(8): + if crc & 0x80: + crc = (crc << 1) ^ 0x31 # 0x31 is the Seed for SGP40's CRC polynomial + else: + crc = crc << 1 + return crc & 0xFF # Returns only bottom 8 bits + + + + + def _temp_c_to_ticks(self, temperature): + ''' + tests : From SGP40 Datasheet Table 10 + temp (C) | Hex Code (Check Sum/CRC Hex Code) + 25 | 0x6666 (CRC 0x93) + -45 | 0x0000 (CRC 0x81) + 130 | 0xFFFF (CRC 0xAC) + + ''' + rt = int(((temperature + 45) * 65535) / 175) & 0xFFFF + least_sig_temp_ticks = rt & 0xFF + most_sig_temp_ticks = (rt >> 8) & 0xFF + + return [most_sig_temp_ticks, least_sig_temp_ticks] + + def _relative_humidity_to_ticks(self, humidity): + ''' + tests : From SGP40 Datasheet Table 10 + Humidity (%) | Hex Code (Check Sum/CRC Hex Code) + 50 | 0x8000 (CRC 0xA2) + 0 | 0x0000 (CRC 0x81) + 100 | 0xFFFF (CRC 0xAC) + + ''' + rh = int((humidity * 65535) / 100 + 0.5) & 0xFFFF + least_sig_rhumidity_ticks = rh & 0xFF + most_sig_rhumidity_ticks = (rh >> 8) & 0xFF + + return [most_sig_rhumidity_ticks, least_sig_rhumidity_ticks] + + def measure_raw_humidity_compensation(self, t=25, h=50): + ''' + The raw gas value adjusted for the current temperature (c) and humidity (%) + ''' + # recycle a single buffer + _compensated_read_cmd = [0x26, 0x0F] + humidity_ticks = self._relative_humidity_to_ticks(h) + humidity_ticks.append(self._generate_crc(humidity_ticks)) + temp_ticks = self._temp_c_to_ticks(t) + temp_ticks.append(self._generate_crc(temp_ticks)) + _cmd = _compensated_read_cmd + humidity_ticks + temp_ticks + self._command_buffer = bytearray(_cmd) + print(self._command_buffer) + read_value = self._read_word_from_command(delay_ms=250) + self._command_buffer = bytearray(2) + return read_value[0] + + + + def measureVocIndex(self, t, h): + ''' + + + + + Notes + ----- + Based on figure one, + https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9_Gas_Sensors/Datasheets/Sensirion_Gas_Sensors_Datasheet_SGP40.pdf + the SGP40 handles the air quality signal processing on chip + and requires temperature and humidity to be sent to it over the i2c interface + + + Step one: + Take temp t, split it into two bytes using Ticks equation on table 10 + + + ''' + # Attempting to keep api close to the c library for ease of use + pass + From bf3ed0fa41632625cf98f9757c39b615b953b774 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 27 May 2021 15:37:40 -0500 Subject: [PATCH 02/24] Adjusted the default _READ_CMD bytearray to reflect the sample in the datasheet table 9 where the default humidity is 50, not 49.999. It's a small change but it might make refering to the datasheet more clear. --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 5f387ef..671deb6 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -37,7 +37,7 @@ _WORD_LEN = 2 # no point in generating this each time -_READ_CMD = [0x26, 0x0F, 0x7F, 0xFF, 0x8F, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 49.999% +_READ_CMD = [0x26, 0x0F, 0x80, 0x00, 0xA2, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 49.999% class SGP40: From 2f379a2f79f9162dd932d6a5f50edab7d09f110a Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 27 May 2021 19:07:53 -0500 Subject: [PATCH 03/24] Removed duplicate code between check crc and generate crc. _check_crc8 now calls generate_crc --- adafruit_sgp40.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 671deb6..1872701 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -173,18 +173,11 @@ def _read_word_from_command( return readdata_buffer - @staticmethod - def _check_crc8(crc_buffer, crc_value): - crc = 0xFF - for byte in crc_buffer: - crc ^= byte - for _ in range(8): - if crc & 0x80: - crc = (crc << 1) ^ 0x31 - else: - crc = crc << 1 - return crc_value == (crc & 0xFF) # check against the bottom 8 bits + def _check_crc8(self, crc_buffer, crc_value): + return crc_value == self._generate_crc(crc_buffer) + + @staticmethod def _generate_crc(crc_buffer): crc = 0xFF for byte in crc_buffer: From dfa6d8fb56cee748546d97232c93d48b9ad742e1 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 27 May 2021 21:30:11 -0500 Subject: [PATCH 04/24] Adjusted raw to pull from self._measure_command so either the preset _READ_CMD or the calculated command can be used. measure_raw takes in temp and humidity and prepares the measure command and calls raw allowing for humidity compensation on the raw reading --- adafruit_sgp40.py | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 1872701..152aa3d 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -37,7 +37,7 @@ _WORD_LEN = 2 # no point in generating this each time -_READ_CMD = [0x26, 0x0F, 0x80, 0x00, 0xA2, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 49.999% +_READ_CMD = [0x26, 0x0F, 0x80, 0x00, 0xA2, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 50% class SGP40: @@ -78,6 +78,7 @@ class SGP40: def __init__(self, i2c, address=0x59): self.i2c_device = i2c_device.I2CDevice(i2c, address) self._command_buffer = bytearray(2) + self._measure_command = _READ_CMD self.initialize() @@ -127,7 +128,7 @@ def _reset(self): def raw(self): """The raw gas value""" # recycle a single buffer - self._command_buffer = bytearray(_READ_CMD) + self._command_buffer = self._measure_command read_value = self._read_word_from_command(delay_ms=250) self._command_buffer = bytearray(2) return read_value[0] @@ -222,44 +223,21 @@ def _relative_humidity_to_ticks(self, humidity): return [most_sig_rhumidity_ticks, least_sig_rhumidity_ticks] - def measure_raw_humidity_compensation(self, t=25, h=50): + def measure_raw(self, temperature=25, relative_humidity=50): ''' The raw gas value adjusted for the current temperature (c) and humidity (%) ''' # recycle a single buffer _compensated_read_cmd = [0x26, 0x0F] - humidity_ticks = self._relative_humidity_to_ticks(h) + humidity_ticks = self._relative_humidity_to_ticks(relative_humidity) humidity_ticks.append(self._generate_crc(humidity_ticks)) - temp_ticks = self._temp_c_to_ticks(t) + temp_ticks = self._temp_c_to_ticks(temperature) temp_ticks.append(self._generate_crc(temp_ticks)) _cmd = _compensated_read_cmd + humidity_ticks + temp_ticks - self._command_buffer = bytearray(_cmd) - print(self._command_buffer) - read_value = self._read_word_from_command(delay_ms=250) - self._command_buffer = bytearray(2) - return read_value[0] - - - - def measureVocIndex(self, t, h): - ''' - + self._measure_command = bytearray(_cmd) + # print(self._command_buffer) + # read_value = self._read_word_from_command(delay_ms=250) + # self._command_buffer = bytearray(2) + return self.raw - - Notes - ----- - Based on figure one, - https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9_Gas_Sensors/Datasheets/Sensirion_Gas_Sensors_Datasheet_SGP40.pdf - the SGP40 handles the air quality signal processing on chip - and requires temperature and humidity to be sent to it over the i2c interface - - - Step one: - Take temp t, split it into two bytes using Ticks equation on table 10 - - - ''' - # Attempting to keep api close to the c library for ease of use - pass - From 1696e3c55d84a79fef555d91dcd4b50f00f50ddd Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 27 May 2021 21:58:37 -0500 Subject: [PATCH 05/24] Adjusted for sgp40 program structure for precommit --- adafruit_sgp40.py | 74 +++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 152aa3d..aef6716 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -37,7 +37,16 @@ _WORD_LEN = 2 # no point in generating this each time -_READ_CMD = [0x26, 0x0F, 0x80, 0x00, 0xA2, 0x66, 0x66, 0x93] # Generated from temp 25c, humidity 50% +_READ_CMD = [ + 0x26, + 0x0F, + 0x80, + 0x00, + 0xA2, + 0x66, + 0x66, + 0x93, +] # Generated from temp 25c, humidity 50% class SGP40: @@ -82,7 +91,6 @@ def __init__(self, i2c, address=0x59): self.initialize() - def initialize(self): """Reset the sensor to it's initial unconfigured state and configure it with sensible defaults so it can be used""" @@ -174,59 +182,45 @@ def _read_word_from_command( return readdata_buffer - def _check_crc8(self, crc_buffer, crc_value): return crc_value == self._generate_crc(crc_buffer) @staticmethod - def _generate_crc(crc_buffer): - crc = 0xFF - for byte in crc_buffer: - crc ^= byte - for _ in range(8): - if crc & 0x80: - crc = (crc << 1) ^ 0x31 # 0x31 is the Seed for SGP40's CRC polynomial - else: - crc = crc << 1 - return crc & 0xFF # Returns only bottom 8 bits - - - - - def _temp_c_to_ticks(self, temperature): - ''' + def _temp_c_to_ticks(temperature): + """ tests : From SGP40 Datasheet Table 10 temp (C) | Hex Code (Check Sum/CRC Hex Code) 25 | 0x6666 (CRC 0x93) -45 | 0x0000 (CRC 0x81) 130 | 0xFFFF (CRC 0xAC) - - ''' - rt = int(((temperature + 45) * 65535) / 175) & 0xFFFF - least_sig_temp_ticks = rt & 0xFF - most_sig_temp_ticks = (rt >> 8) & 0xFF + + """ + temp_ticks = int(((temperature + 45) * 65535) / 175) & 0xFFFF + least_sig_temp_ticks = temp_ticks & 0xFF + most_sig_temp_ticks = (temp_ticks >> 8) & 0xFF return [most_sig_temp_ticks, least_sig_temp_ticks] - def _relative_humidity_to_ticks(self, humidity): - ''' + @staticmethod + def _relative_humidity_to_ticks(humidity): + """ tests : From SGP40 Datasheet Table 10 Humidity (%) | Hex Code (Check Sum/CRC Hex Code) 50 | 0x8000 (CRC 0xA2) 0 | 0x0000 (CRC 0x81) 100 | 0xFFFF (CRC 0xAC) - - ''' - rh = int((humidity * 65535) / 100 + 0.5) & 0xFFFF - least_sig_rhumidity_ticks = rh & 0xFF - most_sig_rhumidity_ticks = (rh >> 8) & 0xFF + + """ + humidity_ticks = int((humidity * 65535) / 100 + 0.5) & 0xFFFF + least_sig_rhumidity_ticks = humidity_ticks & 0xFF + most_sig_rhumidity_ticks = (humidity_ticks >> 8) & 0xFF return [most_sig_rhumidity_ticks, least_sig_rhumidity_ticks] def measure_raw(self, temperature=25, relative_humidity=50): - ''' + """ The raw gas value adjusted for the current temperature (c) and humidity (%) - ''' + """ # recycle a single buffer _compensated_read_cmd = [0x26, 0x0F] humidity_ticks = self._relative_humidity_to_ticks(relative_humidity) @@ -240,4 +234,16 @@ def measure_raw(self, temperature=25, relative_humidity=50): # self._command_buffer = bytearray(2) return self.raw - + @staticmethod + def _generate_crc(crc_buffer): + crc = 0xFF + for byte in crc_buffer: + crc ^= byte + for _ in range(8): + if crc & 0x80: + crc = ( + crc << 1 + ) ^ 0x31 # 0x31 is the Seed for SGP40's CRC polynomial + else: + crc = crc << 1 + return crc & 0xFF # Returns only bottom 8 bits From db971d29b5b05bdf8f0e8d9f3afcecd7ff8ede10 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 3 Jun 2021 15:52:54 -0500 Subject: [PATCH 06/24] Added some explaination to measure_raw, reordered the functions to keep similar functions near each other, removed debug prints, and added some comments to clarify how to use the measure_raw class function. Readme has been updated with an example usage, and the simpletest.py now reflects the measure_raw sample, though it's commented out due to external dependencies --- README.rst | 24 ++++++ adafruit_sgp40.py | 146 ++++++++++++++++++++++------------- examples/sgp40_simpletest.py | 9 +++ 3 files changed, 127 insertions(+), 52 deletions(-) diff --git a/README.rst b/README.rst index 8dac5df..3c84a8b 100644 --- a/README.rst +++ b/README.rst @@ -65,6 +65,7 @@ Usage Example import time import board import busio + import adafruit_sgp40 i2c = busio.I2C(board.SCL, board.SDA) sgp = adafruit_sgp40(i2c) @@ -74,6 +75,29 @@ Usage Example print("") sleep(1) +For humidity compensated raw gas readings, we'll need a secondary sensor such as the bme280 + +.. code-block:: python3 + + import time + import board + import busio + import adafruit_sgp40 + import adafruit_bme280 + + i2c = busio.I2C(board.SCL, board.SDA) + sgp = adafruit_sgp40.SGP40(i2c) + bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) + + while True: + temperature = bme280.temperature + humidity = bme280.relative_humidity + compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) + print(compensated_raw_gas) + print("") + time.sleep(1) + + Contributing ============ diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index aef6716..ec0a02d 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -17,6 +17,9 @@ **Hardware:** * Adafruit SGP40 Air Quality Sensor Breakout - VOC Index +* In order to use the `measure_raw` function, a temperature and humidity sensor which + updates at at least 1Hz is needed (BME280, BME688, SHT31-D, SHT40, etc. For more, see: + https://www.adafruit.com/category/66) **Software and Dependencies:** @@ -25,7 +28,6 @@ * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice - * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register """ from time import sleep @@ -51,7 +53,7 @@ class SGP40: """ - Class to use the SGP40 Ambient Light and UV sensor + Class to use the SGP40 Air Quality Sensor Breakout :param ~busio.I2C i2c: The I2C bus the SGP40 is connected to. :param int address: The I2C address of the device. Defaults to :const:`0x59` @@ -67,6 +69,8 @@ class SGP40: import busio import board import adafruit_sgp40 + # If you have a temperature sensor, like the bme280, import that here as well + # import adafruit_bme280 Once this is done you can define your `busio.I2C` object and define your sensor object @@ -74,14 +78,37 @@ class SGP40: i2c = busio.I2C(board.SCL, board.SDA) sgp = adafruit_sgp40.SGP40(i2c) + # And if you have a temp/humidity sensor, define the sensor here as well + # bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) - Now you have access to the raw gas value using the :attr:`raw` attribute + Now you have access to the raw gas value using the :attr:`raw` attribute. + And with a temperature and humidity value, you can access the class function + `sgp.measure_raw` for a humidity compensated raw reading .. code-block:: python raw_gas_value = sgp.raw + # Lets quickly grab the humidity and temperature + # temperature = bme280.temperature + # humidity = bme280.relative_humidity + # compensated_raw_gas = sgp.measure_raw( + # temperature = temperature, relative_humidity = humidity) + + .. note:: + The operational range of temperatures for the SGP40 is -10 to 50 degrees C + and the operational range of relative humidity for the SGP40 is 0 to 90 % + (assuming that humidity is non-condensing). + + Humidity compensation is further optimized for a subset of the temperature + and relative humidity readings. See Figure 3 of the Sensirion datasheet for + the SGP40. At 25 deg C, the optimal range for relative humidity is 8% to 90%. + At 50% relative humidity, the optimal range for temperature is -7 to 42 deg C. + + Prolonged exposures outside of these ranges may reduce sensor performance, and + the sensor must not be exposed towards condensing conditions at any time. + """ def __init__(self, i2c, address=0x59): @@ -128,10 +155,48 @@ def _reset(self): try: self._read_word_from_command(delay_ms=50) except OSError: - # print("\tGot expected OSError from reset") + # Got expected OSError from reset pass sleep(1) + @staticmethod + def _temp_c_to_ticks(temperature): + """ + Converts Temperature in Celsius to 'ticks' which are an input parameter + the sgp40 can use + + Temperature to Ticks : From SGP40 Datasheet Table 10 + temp (C) | Hex Code (Check Sum/CRC Hex Code) + 25 | 0x6666 (CRC 0x93) + -45 | 0x0000 (CRC 0x81) + 130 | 0xFFFF (CRC 0xAC) + + """ + temp_ticks = int(((temperature + 45) * 65535) / 175) & 0xFFFF + least_sig_temp_ticks = temp_ticks & 0xFF + most_sig_temp_ticks = (temp_ticks >> 8) & 0xFF + + return [most_sig_temp_ticks, least_sig_temp_ticks] + + @staticmethod + def _relative_humidity_to_ticks(humidity): + """ + Converts Relative Humidity in % to 'ticks' which are an input parameter + the sgp40 can use + + Relative Humidity to Ticks : From SGP40 Datasheet Table 10 + Humidity (%) | Hex Code (Check Sum/CRC Hex Code) + 50 | 0x8000 (CRC 0xA2) + 0 | 0x0000 (CRC 0x81) + 100 | 0xFFFF (CRC 0xAC) + + """ + humidity_ticks = int((humidity * 65535) / 100 + 0.5) & 0xFFFF + least_sig_rhumidity_ticks = humidity_ticks & 0xFF + most_sig_rhumidity_ticks = (humidity_ticks >> 8) & 0xFF + + return [most_sig_rhumidity_ticks, least_sig_rhumidity_ticks] + @property def raw(self): """The raw gas value""" @@ -141,6 +206,23 @@ def raw(self): self._command_buffer = bytearray(2) return read_value[0] + def measure_raw(self, temperature=25, relative_humidity=50): + """ + A humidity and temperature compensated raw gas value which helps + address fluctuations in readings due to changing humidity. + + The raw gas value adjusted for the current temperature (c) and humidity (%) + """ + # recycle a single buffer + _compensated_read_cmd = [0x26, 0x0F] + humidity_ticks = self._relative_humidity_to_ticks(relative_humidity) + humidity_ticks.append(self._generate_crc(humidity_ticks)) + temp_ticks = self._temp_c_to_ticks(temperature) + temp_ticks.append(self._generate_crc(temp_ticks)) + _cmd = _compensated_read_cmd + humidity_ticks + temp_ticks + self._measure_command = bytearray(_cmd) + return self.raw + def _read_word_from_command( self, delay_ms=10, @@ -172,9 +254,6 @@ def _read_word_from_command( with self.i2c_device as i2c: i2c.readinto(replybuffer, end=replylen) - # print("Buffer:") - # print(["0x{:02X}".format(i) for i in replybuffer]) - for i in range(0, replylen, 3): if not self._check_crc8(replybuffer[i : i + 2], replybuffer[i + 2]): raise RuntimeError("CRC check failed while reading data") @@ -183,59 +262,22 @@ def _read_word_from_command( return readdata_buffer def _check_crc8(self, crc_buffer, crc_value): - return crc_value == self._generate_crc(crc_buffer) - - @staticmethod - def _temp_c_to_ticks(temperature): """ - tests : From SGP40 Datasheet Table 10 - temp (C) | Hex Code (Check Sum/CRC Hex Code) - 25 | 0x6666 (CRC 0x93) - -45 | 0x0000 (CRC 0x81) - 130 | 0xFFFF (CRC 0xAC) - + Checks that the 8 bit CRC Checksum value from the sensor matches the + received data """ - temp_ticks = int(((temperature + 45) * 65535) / 175) & 0xFFFF - least_sig_temp_ticks = temp_ticks & 0xFF - most_sig_temp_ticks = (temp_ticks >> 8) & 0xFF - - return [most_sig_temp_ticks, least_sig_temp_ticks] + return crc_value == self._generate_crc(crc_buffer) @staticmethod - def _relative_humidity_to_ticks(humidity): - """ - tests : From SGP40 Datasheet Table 10 - Humidity (%) | Hex Code (Check Sum/CRC Hex Code) - 50 | 0x8000 (CRC 0xA2) - 0 | 0x0000 (CRC 0x81) - 100 | 0xFFFF (CRC 0xAC) - + def _generate_crc(crc_buffer): """ - humidity_ticks = int((humidity * 65535) / 100 + 0.5) & 0xFFFF - least_sig_rhumidity_ticks = humidity_ticks & 0xFF - most_sig_rhumidity_ticks = (humidity_ticks >> 8) & 0xFF + Generates an 8 bit CRC Checksum from the input buffer. - return [most_sig_rhumidity_ticks, least_sig_rhumidity_ticks] + This checksum algorithm is outlined in Table 7 of the SGP40 datasheet. - def measure_raw(self, temperature=25, relative_humidity=50): + Checksums are only generated for 2-byte data packets. Command codes already + contain 3 bits of CRC and therefore do not need an added checksum. """ - The raw gas value adjusted for the current temperature (c) and humidity (%) - """ - # recycle a single buffer - _compensated_read_cmd = [0x26, 0x0F] - humidity_ticks = self._relative_humidity_to_ticks(relative_humidity) - humidity_ticks.append(self._generate_crc(humidity_ticks)) - temp_ticks = self._temp_c_to_ticks(temperature) - temp_ticks.append(self._generate_crc(temp_ticks)) - _cmd = _compensated_read_cmd + humidity_ticks + temp_ticks - self._measure_command = bytearray(_cmd) - # print(self._command_buffer) - # read_value = self._read_word_from_command(delay_ms=250) - # self._command_buffer = bytearray(2) - return self.raw - - @staticmethod - def _generate_crc(crc_buffer): crc = 0xFF for byte in crc_buffer: crc ^= byte diff --git a/examples/sgp40_simpletest.py b/examples/sgp40_simpletest.py index 04b7546..5b272ed 100644 --- a/examples/sgp40_simpletest.py +++ b/examples/sgp40_simpletest.py @@ -6,10 +6,19 @@ import busio import adafruit_sgp40 +# If you have a temperature sensor, like the bme280, import that here as well +# import adafruit_bme280 + i2c = busio.I2C(board.SCL, board.SDA) sgp = adafruit_sgp40.SGP40(i2c) +# And if you have a temp/humidity sensor, define the sensor here as well +# bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) while True: print("Raw Gas: ", sgp.raw) + # Lets quickly grab the humidity and temperature + # temperature = bme280.temperature + # humidity = bme280.relative_humidity + # compensated_raw_gas = sgp.measure_raw(temperature = temperature, relative_humidity = humidity) print("") time.sleep(1) From 6806081c3eed30813b984060acac71368fc6aa49 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Thu, 3 Jun 2021 17:38:52 -0500 Subject: [PATCH 07/24] Adjusted the reference to the method measure_raw for sphinx documentation --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index ec0a02d..39a5afc 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -83,7 +83,7 @@ class SGP40: Now you have access to the raw gas value using the :attr:`raw` attribute. And with a temperature and humidity value, you can access the class function - `sgp.measure_raw` for a humidity compensated raw reading + :meth:`measure_raw` for a humidity compensated raw reading .. code-block:: python From 1ef98368015a2d0b9d1e91fedea905cac7e5fe62 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:26:25 -0500 Subject: [PATCH 08/24] Update README.rst to remove busio Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3c84a8b..13d63de 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ Usage Example import busio import adafruit_sgp40 - i2c = busio.I2C(board.SCL, board.SDA) + i2c = board.I2C() # uses board.SCL and board.SDA sgp = adafruit_sgp40(i2c) while True: From b8e4e9345ff40f2448662dce08f6b2c24ba4b0c2 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:31:57 -0500 Subject: [PATCH 09/24] Update adafruit_sgp40.py to replace busio reference with board Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 39a5afc..c50cc83 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -72,7 +72,7 @@ class SGP40: # If you have a temperature sensor, like the bme280, import that here as well # import adafruit_bme280 - Once this is done you can define your `busio.I2C` object and define your sensor object + Once this is done you can define your `board.I2C` object and define your sensor object .. code-block:: python From 1b84c71372ac65552dc630ee7e2676439e7b51b3 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:32:38 -0500 Subject: [PATCH 10/24] Removing Busio from readme example Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index 13d63de..11e39db 100644 --- a/README.rst +++ b/README.rst @@ -81,7 +81,6 @@ For humidity compensated raw gas readings, we'll need a secondary sensor such as import time import board - import busio import adafruit_sgp40 import adafruit_bme280 From 2f52d8bf768a58bcd8f3400abb4bc083ee1599cf Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:33:15 -0500 Subject: [PATCH 11/24] Defining i2c with board instead of busio Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 11e39db..cd89185 100644 --- a/README.rst +++ b/README.rst @@ -84,7 +84,7 @@ For humidity compensated raw gas readings, we'll need a secondary sensor such as import adafruit_sgp40 import adafruit_bme280 - i2c = busio.I2C(board.SCL, board.SDA) + i2c = board.I2C() # uses board.SCL and board.SDA sgp = adafruit_sgp40.SGP40(i2c) bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) From 425014dc5053ecb15d161976ad046ebc3cf93bcb Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:33:44 -0500 Subject: [PATCH 12/24] Defining i2c with board instead of busio Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index c50cc83..8cd80bd 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -76,7 +76,7 @@ class SGP40: .. code-block:: python - i2c = busio.I2C(board.SCL, board.SDA) + i2c = board.I2C() # uses board.SCL and board.SDA sgp = adafruit_sgp40.SGP40(i2c) # And if you have a temp/humidity sensor, define the sensor here as well # bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) From 94ad780cc75addf68e900cd5cc35f7c2355c788f Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:35:14 -0500 Subject: [PATCH 13/24] Reformating varible assignment in the example using measure_raw Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 8cd80bd..5788b8d 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -91,7 +91,7 @@ class SGP40: # Lets quickly grab the humidity and temperature # temperature = bme280.temperature # humidity = bme280.relative_humidity - # compensated_raw_gas = sgp.measure_raw( + # compensated_raw_gas = sgp.measure_raw(temperature=temperature, relative_humidity=humidity) # temperature = temperature, relative_humidity = humidity) From 473ad2fb0989af132e952da7e50ba8a931da39a5 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:35:46 -0500 Subject: [PATCH 14/24] Clarifying Temperature in Celsius Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 5788b8d..a09bdd8 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -97,7 +97,7 @@ class SGP40: .. note:: - The operational range of temperatures for the SGP40 is -10 to 50 degrees C + The operational range of temperatures for the SGP40 is -10 to 50 degrees Celsius and the operational range of relative humidity for the SGP40 is 0 to 90 % (assuming that humidity is non-condensing). From 711e16103803a6df326c81c9dc8d3e4325c18de1 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:36:02 -0500 Subject: [PATCH 15/24] Clarifying Temperature in Celsius Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index a09bdd8..1713a92 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -103,7 +103,7 @@ class SGP40: Humidity compensation is further optimized for a subset of the temperature and relative humidity readings. See Figure 3 of the Sensirion datasheet for - the SGP40. At 25 deg C, the optimal range for relative humidity is 8% to 90%. + the SGP40. At 25 degrees Celsius, the optimal range for relative humidity is 8% to 90%. At 50% relative humidity, the optimal range for temperature is -7 to 42 deg C. Prolonged exposures outside of these ranges may reduce sensor performance, and From 7d8fae6140f9d10222d27d989d44f795262626a8 Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:36:57 -0500 Subject: [PATCH 16/24] Clarifying Temperature in Celsius Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- adafruit_sgp40.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 1713a92..0d0e87d 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -104,7 +104,7 @@ class SGP40: Humidity compensation is further optimized for a subset of the temperature and relative humidity readings. See Figure 3 of the Sensirion datasheet for the SGP40. At 25 degrees Celsius, the optimal range for relative humidity is 8% to 90%. - At 50% relative humidity, the optimal range for temperature is -7 to 42 deg C. + At 50% relative humidity, the optimal range for temperature is -7 to 42 degrees Celsius. Prolonged exposures outside of these ranges may reduce sensor performance, and the sensor must not be exposed towards condensing conditions at any time. From 28773c0c27177ce24679be01377202ac87a735ef Mon Sep 17 00:00:00 2001 From: Keith Murray Date: Fri, 4 Jun 2021 01:37:43 -0500 Subject: [PATCH 17/24] Removing Busio from readme example Co-authored-by: jposada202020 <34255413+jposada202020@users.noreply.github.com> --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index cd89185..f6ddeb7 100644 --- a/README.rst +++ b/README.rst @@ -64,7 +64,6 @@ Usage Example import time import board - import busio import adafruit_sgp40 i2c = board.I2C() # uses board.SCL and board.SDA From cc919c2faa0c02385e4f6187b16c240cd06c6b77 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 02:40:01 -0500 Subject: [PATCH 18/24] Removed Register library from the requirements since it's not in use --- docs/conf.py | 4 ---- requirements.txt | 1 - setup.py | 1 - 3 files changed, 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 391d742..915ed39 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,10 +34,6 @@ "https://circuitpython.readthedocs.io/projects/busdevice/en/latest/", None, ), - "Register": ( - "https://circuitpython.readthedocs.io/projects/register/en/latest/", - None, - ), "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), } diff --git a/requirements.txt b/requirements.txt index cc92861..3207dbb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,3 @@ Adafruit-Blinka adafruit-circuitpython-busdevice -adafruit-circuitpython-register diff --git a/setup.py b/setup.py index ab09ad0..ea6ee59 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,6 @@ install_requires=[ "Adafruit-Blinka", "adafruit-circuitpython-busdevice", - "adafruit-circuitpython-register", ], # Choose your license license="MIT", From bedde22d66cf8364c61f9fdfc9d1b95df9cc973b Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 02:46:52 -0500 Subject: [PATCH 19/24] Added link to datasheet and learn guide, fixed some formatting, and clarified the temperature type for the celsius to ticks conversion --- adafruit_sgp40.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 0d0e87d..fccb80b 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -91,7 +91,8 @@ class SGP40: # Lets quickly grab the humidity and temperature # temperature = bme280.temperature # humidity = bme280.relative_humidity - # compensated_raw_gas = sgp.measure_raw(temperature=temperature, relative_humidity=humidity) + # compensated_raw_gas = sgp.measure_raw(temperature=temperature, + # relative_humidity=humidity) # temperature = temperature, relative_humidity = humidity) @@ -109,6 +110,11 @@ class SGP40: Prolonged exposures outside of these ranges may reduce sensor performance, and the sensor must not be exposed towards condensing conditions at any time. + For more information see: + https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9_Gas_Sensors/Datasheets/Sensirion_Gas_Sensors_Datasheet_SGP40.pdf + and + https://learn.adafruit.com/adafruit-sgp40 + """ def __init__(self, i2c, address=0x59): @@ -160,7 +166,7 @@ def _reset(self): sleep(1) @staticmethod - def _temp_c_to_ticks(temperature): + def _celsius_to_ticks(temperature): """ Converts Temperature in Celsius to 'ticks' which are an input parameter the sgp40 can use @@ -217,7 +223,7 @@ def measure_raw(self, temperature=25, relative_humidity=50): _compensated_read_cmd = [0x26, 0x0F] humidity_ticks = self._relative_humidity_to_ticks(relative_humidity) humidity_ticks.append(self._generate_crc(humidity_ticks)) - temp_ticks = self._temp_c_to_ticks(temperature) + temp_ticks = self._celsius_to_ticks(temperature) temp_ticks.append(self._generate_crc(temp_ticks)) _cmd = _compensated_read_cmd + humidity_ticks + temp_ticks self._measure_command = bytearray(_cmd) From 3c044df294cf5d50a7b780e0559a4b8783b27392 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 02:56:10 -0500 Subject: [PATCH 20/24] Documented the parameter inputs for measure_raw --- adafruit_sgp40.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index fccb80b..629b483 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -217,6 +217,12 @@ def measure_raw(self, temperature=25, relative_humidity=50): A humidity and temperature compensated raw gas value which helps address fluctuations in readings due to changing humidity. + + :param float temperature: The temperature in degrees Celsius, defaults + to :const:`25` + :param float relative_humidity: The relative humidity in percentage, defaults + to :const:`50` + The raw gas value adjusted for the current temperature (c) and humidity (%) """ # recycle a single buffer From 94aded3586f134f031b7bbd0ceace242172aadf6 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 12:18:13 -0500 Subject: [PATCH 21/24] Removed references to busio in comments --- adafruit_sgp40.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index 629b483..f9c1a48 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -27,8 +27,6 @@ https://github.com/adafruit/circuitpython/releases - * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice - """ from time import sleep from struct import unpack_from @@ -55,7 +53,6 @@ class SGP40: """ Class to use the SGP40 Air Quality Sensor Breakout - :param ~busio.I2C i2c: The I2C bus the SGP40 is connected to. :param int address: The I2C address of the device. Defaults to :const:`0x59` @@ -66,7 +63,6 @@ class SGP40: .. code-block:: python - import busio import board import adafruit_sgp40 # If you have a temperature sensor, like the bme280, import that here as well From b93f0dec102f5b8434aa5b443a04d95fd1f9402e Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 12:19:13 -0500 Subject: [PATCH 22/24] Removed references to busio in comments --- adafruit_sgp40.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index f9c1a48..cd3ef91 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -27,6 +27,8 @@ https://github.com/adafruit/circuitpython/releases + * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice + """ from time import sleep from struct import unpack_from From e1c811484adf1f33ff88daaa9e0500bd2013ebe5 Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 12:21:18 -0500 Subject: [PATCH 23/24] Returned reference to the Bus Device Library --- adafruit_sgp40.py | 1 - 1 file changed, 1 deletion(-) diff --git a/adafruit_sgp40.py b/adafruit_sgp40.py index cd3ef91..a9ecf10 100644 --- a/adafruit_sgp40.py +++ b/adafruit_sgp40.py @@ -26,7 +26,6 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases - * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ From b9ea15dd68e448d8b8c615fc01d3928954fdaffc Mon Sep 17 00:00:00 2001 From: CrakeNotSnowman Date: Fri, 4 Jun 2021 12:29:59 -0500 Subject: [PATCH 24/24] Removed busio reference from simpletest example --- examples/sgp40_simpletest.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/sgp40_simpletest.py b/examples/sgp40_simpletest.py index 5b272ed..5d9d9d3 100644 --- a/examples/sgp40_simpletest.py +++ b/examples/sgp40_simpletest.py @@ -3,13 +3,12 @@ # SPDX-License-Identifier: Unlicense import time import board -import busio import adafruit_sgp40 # If you have a temperature sensor, like the bme280, import that here as well # import adafruit_bme280 -i2c = busio.I2C(board.SCL, board.SDA) +i2c = board.I2C() # uses board.SCL and board.SDA sgp = adafruit_sgp40.SGP40(i2c) # And if you have a temp/humidity sensor, define the sensor here as well # bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)