{"id":20989803,"url":"https://github.com/robtillaart/sgp30","last_synced_at":"2025-05-14T18:32:10.939Z","repository":{"id":43669961,"uuid":"380207576","full_name":"RobTillaart/SGP30","owner":"RobTillaart","description":"Arduino library for SGP30 environment sensor","archived":false,"fork":false,"pushed_at":"2024-04-13T09:12:04.000Z","size":51,"stargazers_count":10,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-07T18:27:21.426Z","etag":null,"topics":["arduino","co2","tvoc"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RobTillaart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":"RobTillaart"}},"created_at":"2021-06-25T10:47:14.000Z","updated_at":"2024-04-24T18:54:18.000Z","dependencies_parsed_at":"2024-01-16T00:20:59.396Z","dependency_job_id":"d2d4c82e-b820-4edf-91e9-4f978d317149","html_url":"https://github.com/RobTillaart/SGP30","commit_stats":{"total_commits":25,"total_committers":2,"mean_commits":12.5,"dds":"0.040000000000000036","last_synced_commit":"342b04c694165cefc1e5defc21278fdf7ef617ed"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FSGP30","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FSGP30/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FSGP30/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FSGP30/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/SGP30/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225305673,"owners_count":17453424,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","co2","tvoc"],"created_at":"2024-11-19T06:26:12.249Z","updated_at":"2024-11-19T06:26:13.179Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart"],"categories":[],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/SGP30/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/SGP30/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/SGP30/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/SGP30/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/SGP30/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/SGP30.svg)](https://github.com/RobTillaart/SGP30/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/SGP30/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/SGP30.svg?maxAge=3600)](https://github.com/RobTillaart/SGP30/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/SGP30.svg)](https://registry.platformio.org/libraries/robtillaart/SGP30)\n\n\n# SGP30\n\nArduino library for SGP30 environment sensor\n\nWarning: experimental, library is not functional complete yet.\n\n\n## Description\n\nThe SGP30 from Sensirion is an environment sensor that measures H2 and Ethanol in the air. \nFrom these numbers an intern algorithm in the sensor derives an CO2 equivalent and a TVOC \nmeasurement. The library has an experimental conversion for H2 and Ethanol.\n\nThe CO2 units are ppm, the TVOC units are ppb. Units for H2 and Ethanol are ppm. \nNote that for larger concentrations the resolution of the measurements drops, see datasheet.\n\nThe library supports 2 types of interfaces, a synchronous and an asynchronous interface. \nThe sync interface is blocking for up to 40 milliseconds which was enough to trigger the \nimplementation of an async interface. \n\nNote: the sync interface is implemented with the async interface.\n\nNote: versions prior to 0.2.0 are obsolete due to a bug in **setBaseline()**.\n\n\n#### 0.3.0 Breaking change\n\nVersion 0.3.0 introduced a breaking change.\nYou cannot set the pins in **begin()** any more.\nThis reduces the dependency of processor dependent Wire implementations.\nThe user has to call **Wire.begin()** and can optionally set the Wire pins \nbefore calling **begin()**.\n\n\n#### Sample frequency\n\nThe CO2 and TVOC values can be read up to once per second (1 Hz). \nEthanol and H2, the raw data can be sampled up to 40 Hz.\n\nThe first 15 seconds the sensor needs to stabilize. Thereafter one gets real data.\n\n\n#### I2C performance\n\nThe SGP30 works with I2C bus at 100 KHz and 400 KHz. In a short test it worked well up to 500 KHz. \nA faster I2C clock does not give the sync interface much (relative) gain, \nhowever for the async interface the relative gain is much more.\n\n(indicative test run with UNO - IDE 1.8.13, CO2 and TVOC only, times in micros)\n\n|  I2C speed | measure() | request() | read() |\n|:----------:|:---------:|:---------:|:------:|\n|  100 kHz   |   12360   |    336    |   732  |\n|  200 kHz   |   12212   |    196    |   408  |\n|  300 kHz   |   12168   |    144    |   300  |\n|  400 kHz   |   12140   |    132    |   264  |\n|  500 kHz   |   12128   |    124    |   236  |\n\nNote the blocking of measure() takes 11 to 12 milliseconds extra.\n\n\n#### Multiple sensors.\n\nThe SGP30 sensor has a fixed I2C address 0x58 so only one sensor per I2C bus can be used. \nIf one needs more, one should use an I2C multiplexer or an MCU with multiple I2C buses \nor switch the VCC as a sort of ChipSelect signal.\n\n- https://github.com/RobTillaart/TCA9548  (I2C 8 channel multiplexer)\n\nSee **TCA9548_demo_SGP30.ino** example.\n\n\n#### Related\n\nCO2 sensors and more. \n\n- https://www.adafruit.com/product/3709 - the SGP30 sensor.\n- https://emariete.com/en/sensor-co2-mh-z19b/\n- https://emariete.com/en/sensor-co2-low-consumption-mh-z1311a-winsen/\n- https://revspace.nl/MHZ19\n- https://www.co2.earth/ - current outdoor CO2 level can be used for calibrating.\n- https://keelingcurve.ucsd.edu/ - historical outdoor CO2 level.\n- https://github.com/RobTillaart/ACD10\n- https://github.com/RobTillaart/MTP40C\n- https://github.com/RobTillaart/MTP40F\n- https://github.com/RobTillaart/Cozir\n\n\n## Interface\n\n```cpp\n#include \"SGP30.h\"\n```\n\n#### Constructor\n\n- **SGP30(TwoWire \\*wire = \u0026Wire)** Constructor with optional the Wire interface as parameter.\n- **bool begin()** initializes the library.\nReturns true if the (fixed) device address 0x58 is visible on the I2C bus.\n- **bool isConnected()** checks if the device address 0x58 is visible on the I2C bus.\n- **void GenericReset()** WARNING resets all I2C devices on the bus that support this call!\n\n\n#### Meta\n\n- **bool getID()** reads the sensor ID into 12 bytes. (needs rework).\n- **uint16_t getFeatureSet()** returns 0x0022, indicates that commands used in this library are supported.\n- **bool measureTest()** verify the chip is working.\n\n\n#### Synchronous measurements\n\n- **uint32_t lastMeasurement()** timestamp in milliseconds of the last sync measurement made. \nThis convenience function is useful to prevent reading the sensor too often.\n- **bool measure(bool all = false)** if all == false, only the TVOC and CO2 are updated (slow due to blocking), \nif all == true, also the H2 and the Ethanol values are updated (even slower). \nNote the measurement is slow as there is an active blocking until the sensor is done. \nIf the last measurement is less than a second ago, no measurement is made and the function returns false.\n\n\n#### Asynchronous measurements\n\nWith the async interface, the user should control that reads are at least one second apart. \nThe user should also take care not to mix up different requests. See examples.\n\n- **void request()** sends a request to the sensor to read CO2 and TVOC. \n- **bool read()** returns true if the last request is more than 12 milliseconds ago the \nCO2 and TVOC are read and updated. Otherwise false is returned.\n- **void requestRaw()** sends a request to the sensor to read H2 and Ethanol.\n- **bool readRaw()** returns true if the last request is more than 25 milliseconds ago the \nH2 and Ethanol are read and updated. Otherwise false is returned.\n\n\n#### Get the data\n\nThe library caches the last read values, and these are the functions to access them. \n\n- **uint16_t getTVOC()** gets the TVOC concentration (ppb)\n- **uint16_t getCO2()** gets the CO2 **equivalent** concentration (ppm)\n- **uint16_t getH2_raw()** gets the raw H2. Units unknown.\n- **uint16_t getEthanol_raw()** gets the raw Ethanol. Units unknown.\n\n\n#### Calibration\n\nCheck the datasheet for operating range, figure 7.\n\n- **float setRelHumidity(float T, float RH)** sets the compensation for temperature (5-55°C) and **relative** humidity (10-95%). \nThese values can be obtained e.g. from an SHT30, DHT22 or similar sensor. \nThe function returns the absolute humidity. \n- **void setAbsHumidity(float absoluteHumidity)** sets the compensation for **absolute** humidity. \nConcentration is in gram per cubic meter (g/m3)\n\n\n#### Baseline functions\n\nThe baseline functions give the sensor a reference value. \nAfter running in a known condition e.g. outside in open air, one can get the baseline values as a sort of calibration. \nPlease read the datasheet carefully before using these functions.\n\nNote: if the sensor has no reads done, these values tend to go to zero. \nThis is because the baselines are based upon recent reads.\n\n- **bool getBaseline(uint16_t \\*CO2, uint16_t \\*TVOC)** retrieves the baseline values from the sensor.\n- **void setBaseline(uint16_t CO2, uint16_t TVOC)** sets the baseline values.\n\nNote the outdoor calibration CO2 level differs per day and one should check \na local airport or weather station for a good reference.\n\nThe University of San Diego keeps track of CO2 for a long time now.\nSee - https://keelingcurve.ucsd.edu/ \n\n\nFor faster accurate results for the TVOC under bad air conditions, read **Inceptive Baseline for TVOC measurements**\n(not tested)\n- **bool getTVOCBaseline(uint16_t \\*TVOC)** retrieves the TVOC start value from the sensor.\n- **void setTVOCBaseline(uint16_t TVOC)** sets the TVOC start value.\n\n\n#### Miscellaneous\n\n- **int lastError()** returns last error. (needs rework)\n\n\n#### Experimental H2 Ethanol\n\nuse at own risk.\n\nSince 0.1.2 the library has experimental support for H2 and Ethanol concentration in ppm. \n\nOne should use these functions more as a relative indication than as an absolute measurement as it is definitely not calibrated.\nRuns with different temperature and humidity (different days) give very different values including overflow and infinity.\n\n- **float getH2()** gets the H2 concentration. Units ppm.\n- **float getEthanol()** gets the Ethanol concentration. Units ppm.\n\nThe used references are based upon   \n(1) averaging raw data in outside air at 22°C @ 1 meter and \n(2) the assumption that this is 0.4 resp 0.5 ppm. \n(Note only 1 significant digit) as mentioned in datasheet P2.\n\n- **void  setSrefH2(uint16_t s = 13119)**  13119 is my measurement.\n- **uint16_t getSrefH2()** returns value set.\n- **void setSrefEthanol(uint16_t s = 18472)** 18472 is my measurement.\n- **uint16_t getSrefEthanol()** returns value set.\n\n\n## Future\n\n#### Must\n\n- improve documentation\n\n#### Should\n\n- test \n  - different boards\n  - different gasses / atmosphere if possible.\n\n#### Could\n\n- move code from .h to .cpp\n- add/extend error handling\n- better name for **measureTest()**\n\nThe CRC checking + error handling (since 0.1.4) adds around 330 bytes PROGMEM on an UNO.\nThere might be a need for a minimal class that only reads CO2 and TVOC, no baselines etc.\nfor the smallest platforms e.g. tiny.\n\n#### Wont (unless)\n\n- make defines for the magic numbers (commands)\n  - only used once.\n- improve/merge the private **command()** function (made no difference)\n\n\n## Support\n\nIf you appreciate my libraries, you can support the development and maintenance.\nImprove the quality of the libraries by providing issues and Pull Requests, or\ndonate through PayPal or GitHub sponsors.\n\nThank you,\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtillaart%2Fsgp30","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobtillaart%2Fsgp30","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtillaart%2Fsgp30/lists"}