├── SEN44_UART_minimal_example ├── sps_git_version.c ├── sps_git_version.h ├── SEN44_UART_minimal_example.ino ├── sensirion_arch_config.h ├── sensirion_uart.h ├── sensirion_uart_implementation.cpp ├── sensirion_shdlc.h ├── sen44.h ├── sensirion_shdlc.c └── sen44.c ├── SFM3x00_I2C_minimal_example ├── README.md └── SFM3x00_minimal_example.ino ├── LICENSE ├── SHTC3_I2C_minimal_example └── SHTC3_I2C_minimal_example.ino ├── SDP8xx_I2C_minimal_example └── SDP8xx_I2C_minimal_example.ino ├── SHT4x_I2C_minimal_example └── SHT4x_I2C_minimal_example.ino ├── SHT3x_I2C_minimal_example └── SHT3x_I2C_minimal_example.ino ├── SGP40_SHTC3_I2C_voc_algo_minimal_example ├── sensirion_arch_config.h ├── SGP40_SHTC3_I2C_voc_algo_minimal_example.ino └── sensirion_voc_algorithm.h ├── SFA30_I2C_minimal_example └── SFA30_I2C_minimal_example.ino ├── SCD4x_I2C_minimal_example └── SCD4x_I2C_minimal_example.ino ├── SVM40_I2C_minimal_example └── SVM40_I2C_minimal_example.ino ├── SEN5x_I2C_read_raw └── SEN5x_I2C_read_raw.ino ├── SEN44_I2C_minimal_example └── SEN44_I2C_minimal_example.ino ├── SEN63C_I2C_minimal_example └── SEN63C_I2C_minimal_example.ino ├── SEN5x_I2C_minimal_example └── SEN5x_I2C_minimal_example.ino ├── SEN44_I2C_raw_signal_mode └── SEN44_I2C_raw_signal_mode.ino ├── SEN44_I2C_pm_values_floating_point └── SEN44_I2C_pm_values_floating_point.ino ├── README.md ├── SEN44_SCD40_I2C_example └── SEN44_SCD40_I2C_example.ino ├── SVM40_I2C_change_T_offset_example └── SVM40_I2C_change_T_offset_example.ino ├── SEN44_I2C_change_T_offset_example └── SEN44_I2C_change_T_offset_example.ino ├── SCD4x_I2C_FRC_Forced_Recalibration_Example └── SCD4x_I2C_FRC_Forced_Recalibration_Example.ino ├── SEN5x_I2C_config_warmstart_example └── SEN5x_I2C_config_warmstart_example.ino ├── SEN5x_I2C_switch_measurement_mode └── SEN5x_I2C_switch_measurement_mode.ino ├── SEN5x_I2C_config_STAR_example └── SEN5x_I2C_config_STAR_example.ino ├── SVM40_I2C_change_VOC_parameters_example └── SVM40_I2C_change_VOC_parameters_example.ino ├── SEN5x_I2C_config_coldstart_example └── SEN5x_I2C_config_coldstart_example.ino ├── SEN44_I2C_change_VOC_parameters_example └── SEN44_I2C_change_VOC_parameters_example.ino ├── SEN5x_I2C_change_NOx_parameters_example └── SEN5x_I2C_change_NOx_parameters_example.ino └── SEN5x_I2C_change_VOC_parameters_example └── SEN5x_I2C_change_VOC_parameters_example.ino /SEN44_UART_minimal_example/sps_git_version.c: -------------------------------------------------------------------------------- 1 | /* THIS FILE IS AUTOGENERATED */ 2 | #include "sps_git_version.h" 3 | const char * SPS_DRV_VERSION_STR = "3.0.0+sen44-alpha1"; 4 | -------------------------------------------------------------------------------- /SFM3x00_I2C_minimal_example/README.md: -------------------------------------------------------------------------------- 1 | # SFM3x00 Minimal Example 2 | 3 | ## Configuration 4 | 5 | Please note that the model selection is done manually, line 55 of [SFM3x00_minimal_example.ino](SFM3x00_minimal_example.ino#55) 6 | 7 | ```c++ 8 | 54: // ACTION: select your component here from the enum above: 9 | 55: const uint8_t MODEL = SFM3200; 10 | ``` 11 | 12 | The available sensors can be found on line 36 of [SFM3x00_minimal_example.ino](SFM3x00_minimal_example.ino#36) 13 | 14 | ```c++ 15 | 35: // supported sensors 16 | 36: enum SFM_MODEL { 17 | 37: SFM3000 = 0, 18 | 38: SFM3200, 19 | 39: SFM3300, 20 | 40: SFM3400, 21 | 41: SFM_MODEL_LENGTH //< Note: this is not a valid value for 'MODEL' below 22 | 42: }; 23 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Sensirion AG 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sps_git_version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SPS_GIT_VERSION_H 33 | #define SPS_GIT_VERSION_H 34 | 35 | extern const char *SPS_DRV_VERSION_STR; 36 | 37 | #endif /* SPS_GIT_VERSION_H */ 38 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/SEN44_UART_minimal_example.ino: -------------------------------------------------------------------------------- 1 | #include "sensirion_uart.h" 2 | #include "sen44.h" 3 | 4 | // used for sensor measurement return values 5 | struct sen44_measurement m; 6 | 7 | char serial[SEN44_MAX_SERIAL_LEN]; 8 | 9 | void setup() { 10 | int16_t ret; 11 | 12 | Serial.begin(115200); 13 | // wait for serial connection from PC 14 | // comment the following line if you'd like the output 15 | // without waiting for the interface being ready 16 | while(!Serial); 17 | 18 | // init serial interface to communicate with sensor 19 | while (sensirion_uart_open() != 0) { 20 | Serial.println("UART init failed\n"); 21 | // sleep for 1 s 22 | sensirion_sleep_usec(1000000); 23 | } 24 | delay(1000); 25 | 26 | // get sensor serial id 27 | ret = sen44_get_serial(serial); 28 | 29 | Serial.print("SEN44 Serial: "); 30 | Serial.println(serial); 31 | 32 | // start the measurement mode 33 | // fan and laser will be activated 34 | // TVOC and RH,T sensors set to measure mode 35 | ret = sen44_start_measurement(); 36 | if (ret < 0) 37 | Serial.println("error starting measurement\n"); 38 | 39 | //wait for startup of the fan 40 | delay(1000); 41 | 42 | Serial.println("PM_1.0\tPM_2.5\tPM_4.0\tPM_10.0\tVOCT\tRH\tT"); 43 | } 44 | 45 | void loop() { 46 | int16_t ret; 47 | 48 | // read sensor data 49 | ret = sen44_read_measurement(&m); 50 | if (ret < 0) { 51 | Serial.println("error reading measurement\n"); 52 | Serial.println(ret); 53 | } 54 | 55 | Serial.print("" + String(m.mc_1p0)); //serial print pm value 56 | Serial.print("\t" + String(m.mc_2p5)); //serial print pm value 57 | Serial.print("\t" + String(m.mc_4p0)); //serial print pm value 58 | Serial.print("\t" + String(m.mc_10p0)); //serial print pm value 59 | Serial.print("\t" + String(float(m.voc_index)/10)); //serial print NO2 value in ppb 60 | Serial.print("\t" + String(float(m.ambient_humidity)/100)); //serial print RH value 61 | Serial.print("\t" + String(float(m.ambient_temperature)/200)); //serial print T value 62 | Serial.println(); 63 | 64 | delay(1000); 65 | } 66 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sensirion_arch_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SENSIRION_ARCH_CONFIG_H 33 | #define SENSIRION_ARCH_CONFIG_H 34 | 35 | /** 36 | * If your platform does not provide the library stdint.h you have to 37 | * define the integral types yourself (see below). 38 | */ 39 | #include 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /** 46 | * Typedef section for types commonly defined in 47 | * If your system does not provide stdint headers, please define them 48 | * accordingly. 49 | */ 50 | /* typedef unsigned long long int uint64_t; 51 | * typedef long long int int64_t; 52 | * typedef long int32_t; 53 | * typedef unsigned long uint32_t; 54 | * typedef short int16_t; 55 | * typedef unsigned short uint16_t; 56 | * typedef char int8_t; 57 | * typedef unsigned char uint8_t; */ 58 | 59 | /* Types not typically provided by */ 60 | typedef float float32_t; 61 | 62 | /** 63 | * Define the endianness of your architecture: 64 | * 0: little endian, 1: big endian 65 | * Use the following code to determine if unsure: 66 | * ```c 67 | * #include 68 | * 69 | * int is_big_endian(void) { 70 | * union { 71 | * unsigned int u; 72 | * char c[sizeof(unsigned int)]; 73 | * } e = { 0 }; 74 | * e.c[0] = 1; 75 | * 76 | * return (e.i != 1); 77 | * } 78 | * 79 | * int main(void) { 80 | * printf("Use #define SENSIRION_BIG_ENDIAN %d\n", is_big_endian()); 81 | * 82 | * return 0; 83 | * } 84 | * ``` 85 | */ 86 | #define SENSIRION_BIG_ENDIAN 0 87 | 88 | #ifndef NULL 89 | #define NULL ((void *)0) 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif /* SENSIRION_ARCH_CONFIG_H */ 97 | -------------------------------------------------------------------------------- /SHTC3_I2C_minimal_example/SHTC3_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SHTC3 35 | const int16_t SHT_ADDRESS = 0x70; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // wait for serial connection from PC 40 | // comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // output format 45 | Serial.println("RelativeHumidity(percent)\tTemperature(degC)"); 46 | 47 | // init I2C 48 | Wire.begin(); 49 | 50 | // wait until sensors are ready, < 1 ms according to datasheet 51 | delay(1); 52 | } 53 | 54 | void loop() { 55 | float temperature, humidity; 56 | uint8_t data[6], counter; 57 | 58 | // start sht measurement in normal mode with clock stretching disabled, reading T first 59 | Wire.beginTransmission(SHT_ADDRESS); 60 | Wire.write(0x78); 61 | Wire.write(0x66); 62 | Wire.endTransmission(); 63 | 64 | // wait for measurement has finished according to datasheet > 12.1 ms 65 | delay(13); 66 | 67 | // read measurement data sht: 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC 68 | Wire.requestFrom(SHT_ADDRESS, 6); 69 | counter = 0; 70 | while (Wire.available()) { 71 | data[counter++] = Wire.read(); 72 | } 73 | 74 | // floating point conversion according to datasheet 75 | // convert T in degC 76 | temperature = -45 + 175 * (float)((uint16_t)data[0] << 8 | data[1]) / 65536; 77 | // convert RH in % 78 | humidity = 100 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536; 79 | 80 | Serial.print(humidity); 81 | Serial.print("\t"); 82 | Serial.print(temperature); 83 | Serial.println(); 84 | 85 | // wait 1 s for next measurement 86 | delay(1000); 87 | } 88 | -------------------------------------------------------------------------------- /SDP8xx_I2C_minimal_example/SDP8xx_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SDP8xx with I2C address = 0x25 35 | // depending on the sensor type this could be alternatively 0x26 36 | const int16_t SDP8xx_ADDRESS = 0x25; 37 | 38 | void setup() { 39 | Serial.begin(115200); 40 | // wait for serial connection from PC 41 | // comment the following line if you'd like the output 42 | // without waiting for the interface being ready 43 | while(!Serial); 44 | 45 | // output format 46 | Serial.println("DP\tT"); 47 | 48 | // init I2C 49 | Wire.begin(); 50 | 51 | // wait until sensors startup, > 25 ms according to datasheet 52 | delay(25); 53 | 54 | // start up sensor, sensor will go to continuous measurement mode 55 | // differential pressure mode with averaging till read 56 | Wire.beginTransmission(SDP8xx_ADDRESS); 57 | Wire.write(0x36); 58 | Wire.write(0x15); 59 | Wire.endTransmission(); 60 | 61 | // wait until first data is ready, > 8 ms 62 | delay(10); 63 | } 64 | 65 | void loop() { 66 | 67 | uint16_t scaling; 68 | int16_t dp, temperature; 69 | uint8_t data[9], counter; 70 | 71 | // read measurement data, after two bytes a CRC follows 72 | Wire.requestFrom(SDP8xx_ADDRESS, 9); 73 | counter = 0; 74 | while (Wire.available()) { 75 | data[counter++] = Wire.read(); 76 | } 77 | 78 | dp = (uint16_t)data[0] << 8 | data[1]; 79 | temperature = (uint16_t)data[3] << 8 | data[4]; 80 | scaling = (uint16_t)data[6] << 8 | data[7]; 81 | 82 | 83 | Serial.print(String(float(dp) / scaling)); 84 | Serial.print("\t"); 85 | Serial.print(String(float(temperature) / 200)); 86 | Serial.println(); 87 | 88 | // wait 100 ms for next measurement 89 | delay(100); 90 | } 91 | -------------------------------------------------------------------------------- /SHT4x_I2C_minimal_example/SHT4x_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SHT4x with I2C address A (SHT4x-AD1B) = 0x44 35 | // SHT40 with I2C address A (SHT40-BD1B) = 0x45 36 | const int16_t SHT_ADDRESS = 0x44; 37 | 38 | void setup() { 39 | Serial.begin(115200); 40 | // wait for serial connection from PC 41 | // comment the following line if you'd like the output 42 | // without waiting for the interface being ready 43 | while(!Serial); 44 | 45 | // output format 46 | Serial.println("RelativeHumidity(percent)\tTemperature(degC)"); 47 | 48 | // init I2C 49 | Wire.begin(); 50 | 51 | // wait until sensors are ready, < 1 ms according to datasheet 52 | delay(1); 53 | } 54 | 55 | void loop() { 56 | float temperature, humidity; 57 | uint8_t data[6], counter; 58 | 59 | // start sht measurement in high prescision 60 | Wire.beginTransmission(SHT_ADDRESS); 61 | Wire.write(0xFD); 62 | Wire.endTransmission(); 63 | 64 | // wait for measurement has finished according to datasheet > 8.2 ms 65 | delay(9); 66 | 67 | // read measurement data sht: 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC 68 | Wire.requestFrom(SHT_ADDRESS, 6); 69 | counter = 0; 70 | while (Wire.available()) { 71 | data[counter++] = Wire.read(); 72 | } 73 | 74 | // floating point conversion according to datasheet 75 | // convert T in degC 76 | temperature = -45 + 175 * (float)((uint16_t)data[0] << 8 | data[1]) / 65535; 77 | // convert RH in % 78 | humidity = -6 + 125 * (float)((uint16_t)data[3] << 8 | data[4]) / 65535; 79 | 80 | Serial.print(humidity); 81 | Serial.print("\t"); 82 | Serial.print(temperature); 83 | Serial.println(); 84 | 85 | // wait 1 s for next measurement 86 | delay(1000); 87 | } 88 | -------------------------------------------------------------------------------- /SFM3x00_I2C_minimal_example/SFM3x00_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | #include 34 | 35 | // supported sensors 36 | enum SFM_MODEL { 37 | SFM3000 = 0, 38 | SFM3200, 39 | SFM3300, 40 | SFM3400, 41 | SFM_MODEL_LENGTH //< Note: this is not a valid value for 'MODEL' below 42 | }; 43 | 44 | // from the datasheets: 45 | const int16_t SFM3X00_ADDRESS = 0x40; 46 | 47 | const int16_t FLOW_OFFSET[SFM_MODEL_LENGTH] = { 32000, 32768, 32768, 32768 }; 48 | const int16_t SCALE_FACTOR_AIR[SFM_MODEL_LENGTH] = { 140, 140, 120, 800 }; 49 | 50 | const byte CMD_START_MEASUREMENT[] = { 0x10, 0x00 }; 51 | const byte CMD_SOFT_RESET[] = { 0x20, 0x00 }; 52 | 53 | 54 | // ACTION: select your component here from the enum above: 55 | const uint8_t MODEL = SFM3200; 56 | 57 | void setup() 58 | { 59 | Serial.begin(115200); 60 | while(!Serial); 61 | 62 | Wire.begin(); 63 | 64 | /* 65 | // optional reset before use 66 | Wire.beginTransmission(SFM3X00_ADDRESS); 67 | Wire.write(CMD_SOFT_RESET, 2); 68 | Wire.endTransmission(); 69 | delay(100); 70 | */ 71 | 72 | Wire.beginTransmission(SFM3X00_ADDRESS); 73 | Wire.write(CMD_START_MEASUREMENT, 2); 74 | Wire.endTransmission(); 75 | 76 | delay(100); 77 | Serial.println("Flow rate [slm]"); 78 | } 79 | 80 | void loop() 81 | { 82 | Wire.requestFrom(SFM3X00_ADDRESS, 3); 83 | if (Wire.available() < 3) { 84 | Serial.println("No data received from sensor"); 85 | } else { 86 | int16_t flow; 87 | flow = (int16_t)Wire.read() << 8; 88 | flow |= Wire.read(); 89 | // CRC verification (third byte) left as an exercise for the reader 90 | 91 | flow = (flow - FLOW_OFFSET[MODEL]) / SCALE_FACTOR_AIR[MODEL]; 92 | Serial.println(flow); 93 | } 94 | 95 | delay(100); 96 | } -------------------------------------------------------------------------------- /SHT3x_I2C_minimal_example/SHT3x_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // all digital sensors of the SHT3x (SHT3x-DIS) family are sharing the same interface 35 | // SHT3x with I2C address A (Pin 2 connected to GND) = 0x44 36 | // if I2C address B is used (Pin 2 connected to VDD), change address to 0x45 37 | const int16_t SHT_ADDRESS = 0x44; 38 | 39 | void setup() { 40 | Serial.begin(115200); 41 | // wait for serial connection from PC 42 | // comment the following line if you'd like the output 43 | // without waiting for the interface being ready 44 | while(!Serial); 45 | 46 | // output format 47 | Serial.println("RelativeHumidity(percent)\tTemperature(degC)"); 48 | 49 | // init I2C 50 | Wire.begin(); 51 | 52 | // wait until sensors are ready, < 1 ms according to datasheet 53 | delay(1); 54 | } 55 | 56 | void loop() { 57 | float temperature, humidity; 58 | uint8_t data[6], counter; 59 | 60 | // start sht measurement in high repeatability with clock stretching disabled 61 | Wire.beginTransmission(SHT_ADDRESS); 62 | Wire.write(0x24); 63 | Wire.write(0x00); 64 | Wire.endTransmission(); 65 | 66 | // wait for measurement has finished according to datasheet > 15.5 ms 67 | delay(16); 68 | 69 | // read measurement data sht: 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC 70 | Wire.requestFrom(SHT_ADDRESS, 6); 71 | counter = 0; 72 | while (Wire.available()) { 73 | data[counter++] = Wire.read(); 74 | } 75 | 76 | // floating point conversion according to datasheet 77 | // convert T in degC 78 | temperature = -45 + 175 * (float)((uint16_t)data[0] << 8 | data[1]) / 65535; 79 | // convert RH in % 80 | humidity = 100 * (float)((uint16_t)data[3] << 8 | data[4]) / 65535; 81 | 82 | Serial.print(humidity); 83 | Serial.print("\t"); 84 | Serial.print(temperature); 85 | Serial.println(); 86 | 87 | // wait 1 s for next measurement 88 | delay(1000); 89 | } 90 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sensirion_uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SENSIRION_UART_H 33 | #define SENSIRION_UART_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include "sensirion_arch_config.h" 40 | 41 | /** 42 | * sensirion_uart_select_port() - select the UART port index to use 43 | * THE IMPLEMENTATION IS OPTIONAL ON SINGLE-PORT 44 | * SETUPS (only one SPS30) 45 | * 46 | * Return: 0 on success, an error code otherwise 47 | */ 48 | int16_t sensirion_uart_select_port(uint8_t port); 49 | 50 | /** 51 | * sensirion_uart_open() - initialize UART 52 | * 53 | * Return: 0 on success, an error code otherwise 54 | */ 55 | int16_t sensirion_uart_open(); 56 | 57 | /** 58 | * sensirion_uart_close() - release UART resources 59 | * 60 | * Return: 0 on success, an error code otherwise 61 | */ 62 | int16_t sensirion_uart_close(); 63 | 64 | /** 65 | * sensirion_uart_tx() - transmit data over UART 66 | * 67 | * @data_len: number of bytes to send 68 | * @data: data to send 69 | * Return: Number of bytes sent or a negative error code 70 | */ 71 | int16_t sensirion_uart_tx(uint16_t data_len, const uint8_t *data); 72 | 73 | /** 74 | * sensirion_uart_rx() - receive data over UART 75 | * 76 | * @data_len: max number of bytes to receive 77 | * @data: Memory where received data is stored 78 | * Return: Number of bytes received or a negative error code 79 | */ 80 | int16_t sensirion_uart_rx(uint16_t max_data_len, uint8_t *data); 81 | 82 | /** 83 | * Sleep for a given number of microseconds. The function should delay the 84 | * execution for at least the given time, but may also sleep longer. 85 | * 86 | * Despite the unit, a <10 millisecond precision is sufficient. 87 | * 88 | * @param useconds the sleep time in microseconds 89 | */ 90 | void sensirion_sleep_usec(uint32_t useconds); 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif /* SENSIRION_UART_H */ 97 | -------------------------------------------------------------------------------- /SGP40_SHTC3_I2C_voc_algo_minimal_example/sensirion_arch_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SENSIRION_ARCH_CONFIG_H 33 | #define SENSIRION_ARCH_CONFIG_H 34 | 35 | /** 36 | * If your platform does not provide the library stdlib.h you have to remove the 37 | * include and define NULL yourself (see below). 38 | */ 39 | #include 40 | 41 | /** 42 | * #ifndef NULL 43 | * #define NULL ((void *)0) 44 | * #endif 45 | */ 46 | 47 | /** 48 | * If your platform does not provide the library stdint.h you have to 49 | * define the integral types yourself (see below). 50 | */ 51 | #include 52 | 53 | /** 54 | * Typedef section for types commonly defined in 55 | * If your system does not provide stdint headers, please define them 56 | * accordingly. Please make sure to define int64_t and uint64_t. 57 | */ 58 | /* typedef unsigned long long int uint64_t; 59 | * typedef long long int int64_t; 60 | * typedef long int32_t; 61 | * typedef unsigned long uint32_t; 62 | * typedef short int16_t; 63 | * typedef unsigned short uint16_t; 64 | * typedef char int8_t; 65 | * typedef unsigned char uint8_t; */ 66 | 67 | #ifndef __cplusplus 68 | 69 | /** 70 | * If your platform doesn't define the bool type we define it as int. Depending 71 | * on your system update the definition below. 72 | */ 73 | #if __STDC_VERSION__ >= 199901L 74 | #include 75 | #else 76 | 77 | #ifndef bool 78 | #define bool int 79 | #define true 1 80 | #define false 0 81 | #endif /* bool */ 82 | 83 | #endif /* __STDC_VERSION__ */ 84 | 85 | #endif /* __cplusplus */ 86 | 87 | /** 88 | * The clock period of the i2c bus in microseconds. Increase this, if your GPIO 89 | * ports cannot support a 200 kHz output rate. (2 * 1 / 10usec == 200Khz) 90 | * 91 | * This is only relevant for the sw-i2c HAL (bit-banging on GPIO pins). The 92 | * pulse length is half the clock period, the number should thus be even. 93 | */ 94 | #define SENSIRION_I2C_CLOCK_PERIOD_USEC 10 95 | 96 | #endif /* SENSIRION_ARCH_CONFIG_H */ 97 | -------------------------------------------------------------------------------- /SFA30_I2C_minimal_example/SFA30_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SFA30 35 | const int16_t SFA_ADDRESS = 0x5D; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // wait for serial connection from PC 40 | // comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // init I2C 45 | Wire.begin(); 46 | 47 | // wait until sensor is ready 48 | delay(10); 49 | 50 | // start SFA measurement in periodic mode, will update every 0.5 s 51 | Wire.beginTransmission(SFA_ADDRESS); 52 | Wire.write(0x00); 53 | Wire.write(0x06); 54 | Wire.endTransmission(); 55 | 56 | // module is not outputing HCHO for the first 10 s after powering up 57 | delay(10000); 58 | 59 | } 60 | 61 | void loop() { 62 | float hcho, temperature, humidity; 63 | uint8_t data[9], counter; 64 | 65 | // send read data command 66 | Wire.beginTransmission(SFA_ADDRESS); 67 | Wire.write(0x03); 68 | Wire.write(0x27); 69 | Wire.endTransmission(); 70 | 71 | //wait time before reading for the values should be more than 2ms 72 | delay(10); 73 | 74 | // read measurement data: 75 | // 2 bytes formaldehyde, 1 byte CRC, scale factor 5 76 | // 2 bytes RH, 1 byte CRC, scale factor 100 77 | // 2 bytes T, 1 byte CRC, scale factor 200 78 | // stop reading after 9 bytes (not used) 79 | Wire.requestFrom(SFA_ADDRESS, 9); 80 | counter = 0; 81 | while (Wire.available()) { 82 | data[counter++] = Wire.read(); 83 | } 84 | 85 | // floating point conversion according to datasheet 86 | hcho = (float)((int16_t)data[0] << 8 | data[1])/5; 87 | // convert RH in % 88 | humidity = (float)((int16_t)data[3] << 8 | data[4])/100; 89 | // convert T in degC 90 | temperature = (float)((int16_t)data[6] << 8 | data[7])/200; 91 | 92 | Serial.print(hcho); 93 | Serial.print("\t"); 94 | Serial.print(temperature); 95 | Serial.print("\t"); 96 | Serial.print(humidity); 97 | Serial.println(); 98 | 99 | // wait 1 s for next measurement read out, sensor is averaging in between 100 | delay(1000); 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /SCD4x_I2C_minimal_example/SCD4x_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SCD4x 35 | const int16_t SCD_ADDRESS = 0x62; 36 | 37 | void setup() { 38 | // check in your settings that the right speed is selected 39 | Serial.begin(115200); 40 | // wait for serial connection from PC 41 | // comment the following line if you'd like the output 42 | // without waiting for the interface being ready 43 | while(!Serial); 44 | 45 | // output format 46 | Serial.println("CO2(ppm)\tTemperature(degC)\tRelativeHumidity(percent)"); 47 | 48 | // init I2C 49 | Wire.begin(); 50 | 51 | // wait until sensors are ready, > 1000 ms according to datasheet 52 | delay(1000); 53 | 54 | // start scd measurement in periodic mode, will update every 5 s 55 | Wire.beginTransmission(SCD_ADDRESS); 56 | Wire.write(0x21); 57 | Wire.write(0xb1); 58 | Wire.endTransmission(); 59 | 60 | // wait for first measurement to be finished 61 | delay(5000); 62 | } 63 | 64 | void loop() { 65 | float co2, temperature, humidity; 66 | uint8_t data[12], counter; 67 | 68 | // send read data command 69 | Wire.beginTransmission(SCD_ADDRESS); 70 | Wire.write(0xec); 71 | Wire.write(0x05); 72 | Wire.endTransmission(); 73 | 74 | // read measurement data: 2 bytes co2, 1 byte CRC, 75 | // 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC, 76 | // 2 bytes sensor status, 1 byte CRC 77 | // stop reading after 12 bytes (not used) 78 | // other data like ASC not included 79 | Wire.requestFrom(SCD_ADDRESS, 12); 80 | counter = 0; 81 | while (Wire.available()) { 82 | data[counter++] = Wire.read(); 83 | } 84 | 85 | // floating point conversion according to datasheet 86 | co2 = (float)((uint16_t)data[0] << 8 | data[1]); 87 | // convert T in degC 88 | temperature = -45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536; 89 | // convert RH in % 90 | humidity = 100 * (float)((uint16_t)data[6] << 8 | data[7]) / 65536; 91 | 92 | Serial.print(co2); 93 | Serial.print("\t"); 94 | Serial.print(temperature); 95 | Serial.print("\t"); 96 | Serial.print(humidity); 97 | Serial.println(); 98 | 99 | // wait 2 s for next measurement 100 | delay(5000); 101 | } 102 | -------------------------------------------------------------------------------- /SVM40_I2C_minimal_example/SVM40_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | 31 | */ 32 | 33 | #include 34 | 35 | // SVM40 36 | const int16_t SVM40_ADDRESS = 0x6A; 37 | 38 | void setup() { 39 | Serial.begin(115200); 40 | // wait for serial connection from PC 41 | // comment the following line if you'd like the output 42 | // without waiting for the interface being ready 43 | while(!Serial); 44 | 45 | // output format 46 | Serial.println("VOC_Index\tRH\tT"); 47 | 48 | // init I2C 49 | Wire.begin(); 50 | 51 | // wait until sensors startup, > 1 ms according to datasheet 52 | delay(1); 53 | 54 | // start up sensor, sensor will go to continous measurement mode 55 | // each second there will be new measurement values 56 | Wire.beginTransmission(SVM40_ADDRESS); 57 | Wire.write(0x00); 58 | Wire.write(0x10); 59 | Wire.endTransmission(); 60 | 61 | // wait until sensors is ready, fan is initialized 62 | delay(1000); 63 | } 64 | 65 | void loop() { 66 | 67 | int16_t voc, humidity, temperature; 68 | uint8_t data[9], counter; 69 | 70 | // read measurement data 71 | Wire.beginTransmission(SVM40_ADDRESS); 72 | Wire.write(0x03); 73 | Wire.write(0xA6); 74 | Wire.endTransmission(); 75 | 76 | // wait 5 ms to allow the sensor to fill the internal buffer 77 | delay(5); 78 | 79 | // read measurement data svm40, after two bytes a CRC follows 80 | Wire.requestFrom(SVM40_ADDRESS, 9); 81 | counter = 0; 82 | while (Wire.available()) { 83 | data[counter++] = Wire.read(); 84 | } 85 | 86 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 87 | // humidity is a signed int and scaled by 100 and need to be divided by 100 88 | // temperature is a signed int and scaled by 200 and need to be divided by 200 89 | voc = (uint16_t)data[0] << 8 | data[1]; 90 | humidity = (uint16_t)data[3] << 8 | data[4]; 91 | temperature = (uint16_t)data[6] << 8 | data[7]; 92 | 93 | Serial.print(String(float(voc) / 10)); 94 | Serial.print("\t"); 95 | Serial.print(String(float(humidity) / 100)); 96 | Serial.print("\t"); 97 | Serial.print(String(float(temperature) / 200)); 98 | Serial.println(); 99 | 100 | // wait 1 s for next measurement 101 | delay(1000); 102 | } 103 | -------------------------------------------------------------------------------- /SEN5x_I2C_read_raw/SEN5x_I2C_read_raw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN55 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // Wait for serial connection from PC 40 | // Comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // Output format 45 | Serial.println("VOC_Index\tNOx_Index\tRH\tT"); 46 | 47 | // wait until sensors startup, > 50 ms according to datasheet 48 | delay(50); 49 | 50 | // init I2C 51 | Wire.begin(); 52 | 53 | 54 | // start up sensor, sensor will go to continuous measurement mode 55 | // each second there will be new measurement values 56 | Wire.beginTransmission(SEN55_ADDRESS); 57 | Wire.write(0x00); 58 | Wire.write(0x21); 59 | Wire.endTransmission(); 60 | 61 | // wait until sensor is ready, fan is initialized 62 | delay(1000); 63 | } 64 | 65 | void loop() { 66 | 67 | int16_t voc, nox, humidity, temperature; 68 | uint8_t data[12], counter; 69 | 70 | // send read measurement data command (0x03D2)l 71 | Wire.beginTransmission(SEN55_ADDRESS); 72 | Wire.write(0x03); 73 | Wire.write(0xD2); 74 | Wire.endTransmission(); 75 | // wait 10 ms to allow the sensor to fill the internal buffer 76 | delay(10); 77 | 78 | // read measurement data SEN44, after two bytes a CRC follows 79 | Wire.requestFrom(SEN55_ADDRESS, 12); 80 | counter = 0; 81 | while (Wire.available()) { 82 | data[counter++] = Wire.read(); 83 | } 84 | 85 | // VOC level is a signed int 86 | // humidity is a signed int and scaled by 100 and need to be divided by 100 87 | // temperature is a signed int and scaled by 200 and need to be divided by 200 88 | humidity = (uint16_t)data[0] << 8 | data[1]; 89 | temperature = (uint16_t)data[3] << 8 | data[4]; 90 | voc = (uint16_t)data[6] << 8 | data[7]; 91 | nox = (uint16_t)data[9] << 8 | data[10]; 92 | 93 | Serial.print(String(int(voc))); 94 | Serial.print("\t"); 95 | Serial.print(String(int(nox))); 96 | Serial.print("\t"); 97 | Serial.print(String(float(humidity) / 100)); 98 | Serial.print("\t"); 99 | Serial.print(String(float(temperature) / 200)); 100 | Serial.println(); 101 | 102 | // wait 1 s for next measurement 103 | delay(1000); 104 | } 105 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sensirion_uart_implementation.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | // needed for delay() routine 33 | //#include "wiring_private.h" // pinPeripheral() function 34 | #include 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | #include "sensirion_arch_config.h" 41 | #include "sensirion_uart.h" 42 | 43 | #define BAUDRATE 115200 // baud rate of SPS30 44 | 45 | /** 46 | * sensirion_uart_select_port() - select the UART port index to use 47 | * THE IMPLEMENTATION IS OPTIONAL ON SINGLE-PORT 48 | * SETUPS (only one SPS30) 49 | * 50 | * Return: 0 on success, an error code otherwise 51 | */ 52 | int16_t sensirion_uart_select_port(uint8_t port) { 53 | return 0; 54 | } 55 | 56 | /** 57 | * sensirion_uart_open() - initialize UART 58 | * 59 | * Return: 0 on success, an error code otherwise 60 | */ 61 | int16_t sensirion_uart_open() { 62 | Serial1.begin(BAUDRATE); 63 | 64 | /*while (!Serial1) { 65 | delay(100); 66 | }*/ 67 | return 0; 68 | } 69 | 70 | /** 71 | * sensirion_uart_close() - release UART resources 72 | * 73 | * Return: 0 on success, an error code otherwise 74 | */ 75 | int16_t sensirion_uart_close() { 76 | Serial1.end(); 77 | return 0; 78 | } 79 | 80 | /** 81 | * sensirion_uart_tx() - transmit data over UART 82 | * 83 | * @data_len: number of bytes to send 84 | * @data: data to sendv v 85 | * Return: Number of bytes sent or a negative error code 86 | */ 87 | int16_t sensirion_uart_tx(uint16_t data_len, const uint8_t *data) { 88 | return Serial1.write(data, data_len); 89 | } 90 | 91 | /** 92 | * sensirion_uart_rx() - receive data over UART 93 | * 94 | * @data_len: max number of bytes to receive 95 | * @data: Memory where received data is stored 96 | * Return: Number of bytes received or a negative error code 97 | */ 98 | int16_t sensirion_uart_rx(uint16_t max_data_len, uint8_t *data) { 99 | int16_t i = 0; 100 | 101 | while (Serial1.available() > 0 && i < max_data_len) { 102 | data[i] = (uint8_t)Serial1.read(); 103 | i++; 104 | } 105 | 106 | return i; 107 | } 108 | 109 | /** 110 | * Sleep for a given number of microseconds. The function should delay the 111 | * execution for at least the given time, but may also sleep longer. 112 | * 113 | * Despite the unit, a <10 millisecond precision is sufficient. 114 | * 115 | * @param useconds the sleep time in microseconds 116 | */ 117 | void sensirion_sleep_usec(uint32_t useconds) { 118 | delay((useconds / 1000) + 1); 119 | } 120 | 121 | #ifdef __cplusplus 122 | } // extern "C" 123 | #endif 124 | -------------------------------------------------------------------------------- /SEN44_I2C_minimal_example/SEN44_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // wait for serial connection from PC 40 | // comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // output format 45 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT"); 46 | 47 | // init I2C 48 | Wire.begin(); 49 | 50 | // wait until sensors startup, > 1 ms according to datasheet 51 | delay(1); 52 | 53 | // start up sensor, sensor will go to continuous measurement mode 54 | // each second there will be new measurement values 55 | Wire.beginTransmission(SEN44_ADDRESS); 56 | Wire.write(0x00); 57 | Wire.write(0x21); 58 | Wire.endTransmission(); 59 | 60 | // wait until sensor is ready, fan is initialized 61 | delay(1000); 62 | } 63 | 64 | void loop() { 65 | 66 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 67 | int16_t voc, humidity, temperature; 68 | uint8_t data[21], counter; 69 | 70 | // send read measurement data command 71 | Wire.beginTransmission(SEN44_ADDRESS); 72 | Wire.write(0x03); 73 | Wire.write(0x74); 74 | Wire.endTransmission(); 75 | 76 | // wait 10 ms to allow the sensor to fill the internal buffer 77 | delay(10); 78 | 79 | // read measurement data SEN44, after two bytes a CRC follows 80 | Wire.requestFrom(SEN44_ADDRESS, 21); 81 | counter = 0; 82 | while (Wire.available()) { 83 | data[counter++] = Wire.read(); 84 | } 85 | 86 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 87 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 88 | // humidity is a signed int and scaled by 100 and need to be divided by 100 89 | // temperature is a signed int and scaled by 200 and need to be divided by 200 90 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 91 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 92 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 93 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 94 | voc = (uint16_t)data[12] << 8 | data[13]; 95 | humidity = (uint16_t)data[15] << 8 | data[16]; 96 | temperature = (uint16_t)data[18] << 8 | data[19]; 97 | 98 | Serial.print(pm1p0); 99 | Serial.print("\t"); 100 | Serial.print(pm2p5); 101 | Serial.print("\t"); 102 | Serial.print(pm4p0); 103 | Serial.print("\t"); 104 | Serial.print(pm10p0); 105 | Serial.print("\t"); 106 | Serial.print(String(float(voc) / 10)); 107 | Serial.print("\t"); 108 | Serial.print(String(float(humidity) / 100)); 109 | Serial.print("\t"); 110 | Serial.print(String(float(temperature) / 200)); 111 | Serial.println(); 112 | 113 | // wait 1 s for next measurement 114 | delay(1000); 115 | } 116 | -------------------------------------------------------------------------------- /SEN63C_I2C_minimal_example/SEN63C_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN63C 35 | const int16_t SEN63C_ADDRESS = 0x6B; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | 40 | // Wait for serial connection from PC 41 | // Comment the following line if you'd like the output 42 | // without waiting for the interface being ready 43 | while(!Serial); 44 | 45 | // Wait until sensors startup, > 50 ms according to datasheet 46 | delay(50); 47 | 48 | // Initiate I2C communication 49 | Wire.begin(); 50 | 51 | // Send command to start measurement (0x0021) 52 | Wire.beginTransmission(SEN63C_ADDRESS); 53 | Wire.write(0x00); 54 | Wire.write(0x21); 55 | Wire.endTransmission(); 56 | 57 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tRH\tT\tCO2"); 58 | 59 | // Wait until sensor is ready, fan is initialized 60 | delay(1000); 61 | } 62 | 63 | void loop() { 64 | // Initializing variables 65 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 66 | int16_t co2, humidity, temperature; 67 | uint8_t data[21], counter; 68 | 69 | // Send command to read measurement data (0x0471) 70 | Wire.beginTransmission(SEN63C_ADDRESS); 71 | Wire.write(0x04); 72 | Wire.write(0x71); 73 | Wire.endTransmission(); 74 | 75 | // Wait 20 ms to allow the sensor to fill the internal buffer 76 | delay(20); 77 | 78 | // Read measurement data of SEN55, after two bytes a CRC follows 79 | Wire.requestFrom(SEN63C_ADDRESS, 21); 80 | counter = 0; 81 | 82 | while (Wire.available()) { 83 | data[counter++] = Wire.read(); 84 | } 85 | 86 | // PM1.0 to PM10 are scaled unsigned integer values in ug / um3 and need to be divided by 10 87 | // humidity is a signed int and scaled by 100 and need to be divided by 100 88 | // temperature is a signed int and scaled by 200 and need to be divided by 200 89 | // CO2 concentration a signed int and not scaled 90 | // during CO2 sensor init, value is the first 30s 0x7fff/ 32767 91 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 92 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 93 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 94 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 95 | humidity = (uint16_t)data[12] << 8 | data[13]; 96 | temperature = (uint16_t)data[15] << 8 | data[16]; 97 | co2 = (uint16_t)data[18] << 8 | data[19]; 98 | 99 | // Print output 100 | Serial.print(String(float(pm1p0) / 10)); 101 | Serial.print("\t"); 102 | Serial.print(String(float(pm2p5) / 10)); 103 | Serial.print("\t"); 104 | Serial.print(String(float(pm4p0) / 10)); 105 | Serial.print("\t"); 106 | Serial.print(String(float(pm10p0) / 10)); 107 | Serial.print("\t"); 108 | Serial.print(String(float(humidity) / 100)); 109 | Serial.print("\t"); 110 | Serial.print(String(float(temperature) / 200)); 111 | Serial.print("\t"); 112 | Serial.print(String(float(co2))); 113 | Serial.println(); 114 | 115 | // Wait 1 s for next measurement 116 | delay(1000); 117 | } 118 | -------------------------------------------------------------------------------- /SEN5x_I2C_minimal_example/SEN5x_I2C_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN55 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // Wait for serial connection from PC 40 | // Comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // Wait until sensors startup, > 50 ms according to datasheet 45 | delay(50); 46 | 47 | // Initiate I2C communication 48 | Wire.begin(); 49 | 50 | // Send command to start measurement (0x0021) 51 | Wire.beginTransmission(SEN55_ADDRESS); 52 | Wire.write(0x00); 53 | Wire.write(0x21); 54 | Wire.endTransmission(); 55 | 56 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 57 | 58 | // Wait until sensor is ready, fan is initialized 59 | delay(1000); 60 | } 61 | 62 | 63 | 64 | void loop() { 65 | 66 | // Initializing variables 67 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 68 | int16_t voc, nox, humidity, temperature; 69 | uint8_t data[24], counter; 70 | 71 | // Send command to read measurement data (0x03C4) 72 | Wire.beginTransmission(SEN55_ADDRESS); 73 | Wire.write(0x03); 74 | Wire.write(0xC4); 75 | Wire.endTransmission(); 76 | // Wait 20 ms to allow the sensor to fill the internal buffer 77 | delay(20); 78 | 79 | // Read measurement data of SEN55, after two bytes a CRC follows 80 | Wire.requestFrom(SEN55_ADDRESS, 24); 81 | counter = 0; 82 | 83 | while (Wire.available()) { 84 | data[counter++] = Wire.read(); 85 | } 86 | 87 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 88 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 89 | // humidity is a signed int and scaled by 100 and need to be divided by 100 90 | // temperature is a signed int and scaled by 200 and need to be divided by 200 91 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 92 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 93 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 94 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 95 | humidity = (uint16_t)data[12] << 8 | data[13]; 96 | temperature = (uint16_t)data[15] << 8 | data[16]; 97 | voc = (uint16_t)data[18] << 8 | data[19]; 98 | nox = (uint16_t)data[21] << 8 | data[22]; 99 | 100 | 101 | // Print output 102 | Serial.print(String(float(pm1p0) / 10)); 103 | Serial.print("\t"); 104 | Serial.print(String(float(pm2p5) / 10)); 105 | Serial.print("\t"); 106 | Serial.print(String(float(pm4p0) / 10)); 107 | Serial.print("\t"); 108 | Serial.print(String(float(pm10p0) / 10)); 109 | Serial.print("\t"); 110 | Serial.print(String(float(voc) / 10)); 111 | Serial.print("\t\t"); 112 | Serial.print(String(float(nox) / 10)); 113 | Serial.print("\t\t"); 114 | Serial.print(String(float(humidity) / 100)); 115 | Serial.print("\t"); 116 | Serial.print(String(float(temperature) / 200)); 117 | Serial.println(); 118 | 119 | // Wait 1 s for next measurement 120 | delay(1000); 121 | } 122 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sensirion_shdlc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SENSIRION_SHDLC_H 33 | #define SENSIRION_SHDLC_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include "sensirion_arch_config.h" 40 | 41 | #define SENSIRION_SHDLC_ERR_NO_DATA -1 42 | #define SENSIRION_SHDLC_ERR_MISSING_START -2 43 | #define SENSIRION_SHDLC_ERR_MISSING_STOP -3 44 | #define SENSIRION_SHDLC_ERR_CRC_MISMATCH -4 45 | #define SENSIRION_SHDLC_ERR_ENCODING_ERROR -5 46 | #define SENSIRION_SHDLC_ERR_TX_INCOMPLETE -6 47 | 48 | #if SENSIRION_BIG_ENDIAN 49 | #define be16_to_cpu(s) (s) 50 | #define be32_to_cpu(s) (s) 51 | #define be64_to_cpu(s) (s) 52 | #else 53 | #define be16_to_cpu(s) (((uint16_t)(s) << 8) | (0xff & ((uint16_t)(s)) >> 8)) 54 | #define be32_to_cpu(s) \ 55 | (((uint32_t)be16_to_cpu(s) << 16) | (0xffff & (be16_to_cpu((s) >> 16)))) 56 | #define be64_to_cpu(s) \ 57 | (((uint64_t)be32_to_cpu(s) << 32) | \ 58 | (0xffffffff & ((uint64_t)be32_to_cpu((s) >> 32)))) 59 | #endif 60 | 61 | struct sensirion_shdlc_rx_header { 62 | uint8_t addr; 63 | uint8_t cmd; 64 | uint8_t state; 65 | uint8_t data_len; 66 | }; 67 | 68 | /** 69 | * sensirion_shdlc_tx() - transmit an SHDLC frame 70 | * 71 | * @addr: SHDLC recipient address 72 | * @cmd: command parameter 73 | * @data_len: data length to send 74 | * @data: data to send 75 | * Return: 0 on success, an error code otherwise 76 | */ 77 | int16_t sensirion_shdlc_tx(uint8_t addr, uint8_t cmd, uint8_t data_len, 78 | const uint8_t *data); 79 | 80 | /** 81 | * sensirion_shdlc_rx() - receive an SHDLC frame 82 | * 83 | * Note that the header and data must be discarded on failure 84 | * 85 | * @data_len: max data length to receive 86 | * @header: Memory where the SHDLC header containing the sender address, 87 | * command, sensor state and data length is stored 88 | * @data: Memory where received data is stored 89 | * Return: 0 on success, an error code otherwise 90 | */ 91 | int16_t sensirion_shdlc_rx(uint8_t max_data_len, 92 | struct sensirion_shdlc_rx_header *header, 93 | uint8_t *data); 94 | 95 | /** 96 | * sensirion_shdlc_xcv() - transceive (transmit then receive) an SHDLC frame 97 | * 98 | * Note that rx_header and rx_data must be discarded on failure 99 | * 100 | * @addr: recipient address 101 | * @cmd: parameter 102 | * @tx_data_len: data length to send 103 | * @tx_data: data to send 104 | * @rx_header: Memory where the SHDLC header containing the sender address, 105 | * command, sensor state and data length is stored 106 | * @rx_data: Memory where the received data is stored 107 | * Return: 0 on success, an error code otherwise 108 | */ 109 | int16_t sensirion_shdlc_xcv(uint8_t addr, uint8_t cmd, uint8_t tx_data_len, 110 | const uint8_t *tx_data, uint8_t max_rx_data_len, 111 | struct sensirion_shdlc_rx_header *rx_header, 112 | uint8_t *rx_data); 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif 117 | 118 | #endif /* SENSIRION_SHDLC_H */ 119 | -------------------------------------------------------------------------------- /SEN44_I2C_raw_signal_mode/SEN44_I2C_raw_signal_mode.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | Serial.begin(115200); // start serial for output 39 | // wait for serial connection from PC 40 | // comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // output format 45 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT\tVOC_Raw\tRH_Uncomp\tT_Uncomp"); 46 | 47 | // init I2C 48 | Wire.begin(); 49 | 50 | // wait until sensors startup, > 1 ms according to datasheet 51 | delay(1); 52 | 53 | // start up sensor, sensor will go to continous measurement mode 54 | // each second there will be new measurement values 55 | Wire.beginTransmission(SEN44_ADDRESS); 56 | Wire.write(0x00); 57 | Wire.write(0x21); 58 | Wire.endTransmission(); 59 | 60 | // wait until sensor is ready, fan is initialized 61 | delay(1000); 62 | } 63 | 64 | void loop() { 65 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0, voc_raw; 66 | int16_t voc, humidity, temperature; 67 | int16_t humidity_uncomp, temperature_uncomp; 68 | uint8_t data[30], counter; 69 | 70 | // read measurement data 71 | Wire.beginTransmission(SEN44_ADDRESS); 72 | Wire.write(0x03); 73 | Wire.write(0x81); 74 | Wire.endTransmission(); 75 | 76 | // wait 5 ms to allow the sensor to fill the internal buffer 77 | delay(10); 78 | 79 | // read measurement data sen4x, after two bytes a CRC follows 80 | Wire.requestFrom(SEN44_ADDRESS, 30); 81 | counter = 0; 82 | while (Wire.available()) { 83 | data[counter++] = Wire.read(); 84 | } 85 | 86 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 87 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 88 | // VOC raw value is an uint16_t and has no scaling 89 | // humidity is a signed int and scaled by 100 and need to be divided by 100 90 | // temperature is a signed int and scaled by 200 and need to be divided by 200 91 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 92 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 93 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 94 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 95 | voc = (uint16_t)data[12] << 8 | data[13]; 96 | humidity = (uint16_t)data[15] << 8 | data[16]; 97 | temperature = (uint16_t)data[18] << 8 | data[19]; 98 | voc_raw = (uint16_t)data[21] << 8 | data[22]; 99 | humidity_uncomp = (uint16_t)data[24] << 8 | data[25]; 100 | temperature_uncomp = (uint16_t)data[27] << 8 | data[28]; 101 | 102 | Serial.print(pm1p0); 103 | Serial.print("\t"); 104 | Serial.print(pm2p5); 105 | Serial.print("\t"); 106 | Serial.print(pm4p0); 107 | Serial.print("\t"); 108 | Serial.print(pm10p0); 109 | Serial.print("\t"); 110 | Serial.print(String(float(voc) / 10)); 111 | Serial.print("\t"); 112 | Serial.print(String(float(humidity) / 100)); 113 | Serial.print("\t"); 114 | Serial.print(String(float(temperature) / 200)); 115 | Serial.print("\t"); 116 | Serial.print(voc_raw); 117 | Serial.print("\t"); 118 | Serial.print(String(float(humidity_uncomp) / 100)); 119 | Serial.print("\t"); 120 | Serial.print(String(float(temperature_uncomp) / 200)); 121 | Serial.println(); 122 | 123 | // wait 1 s for next measurement 124 | delay(1000); 125 | } 126 | -------------------------------------------------------------------------------- /SEN44_I2C_pm_values_floating_point/SEN44_I2C_pm_values_floating_point.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | 37 | // union construct to help to convert byte array to float 38 | union u_tag { 39 | byte b[4]; 40 | float fval; 41 | } u; 42 | 43 | void setup() { 44 | Serial.begin(115200); 45 | // wait for serial connection from PC 46 | // comment the following line if you'd like the output 47 | // without waiting for the interface being ready 48 | while(!Serial); 49 | 50 | // output format 51 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT"); 52 | 53 | // init I2C 54 | Wire.begin(); 55 | 56 | // wait until sensors startup, > 1 ms according to datasheet 57 | delay(1); 58 | 59 | // start up sensor, sensor will go to continuous measurement mode 60 | // each second there will be new measurement values 61 | Wire.beginTransmission(SEN44_ADDRESS); 62 | Wire.write(0x00); 63 | Wire.write(0x21); 64 | Wire.endTransmission(); 65 | 66 | // wait until sensor is ready, fan is initialized 67 | delay(1000); 68 | } 69 | 70 | void loop() { 71 | 72 | float pm1p0_f, pm2p5_f, pm4p0_f, pm10p0_f; 73 | int16_t voc, humidity, temperature; 74 | uint8_t data[40], counter; 75 | 76 | // send read measurement data command to get pm values in floating point 77 | Wire.beginTransmission(SEN44_ADDRESS); 78 | Wire.write(0x03); 79 | Wire.write(0x31); 80 | Wire.endTransmission(); 81 | 82 | // wait 10 ms to allow the sensor to fill the internal buffer 83 | delay(10); 84 | 85 | // read measurement data SEN44, after two bytes a CRC follows 86 | Wire.requestFrom(SEN44_ADDRESS, 40); 87 | counter = 0; 88 | while (Wire.available()) { 89 | data[counter++] = Wire.read(); 90 | } 91 | 92 | // only the first four float values are used 93 | // rest will be ignored 94 | // convert byte arrays to float 95 | u.b[0] = data[4]; 96 | u.b[1] = data[3]; 97 | u.b[2] = data[1]; 98 | u.b[3] = data[0]; 99 | pm1p0_f = u.fval; 100 | 101 | u.b[0] = data[10]; 102 | u.b[1] = data[9]; 103 | u.b[2] = data[7]; 104 | u.b[3] = data[6]; 105 | pm2p5_f = u.fval; 106 | 107 | u.b[0] = data[16]; 108 | u.b[1] = data[15]; 109 | u.b[2] = data[13]; 110 | u.b[3] = data[12]; 111 | pm4p0_f = u.fval; 112 | 113 | u.b[0] = data[22]; 114 | u.b[1] = data[21]; 115 | u.b[2] = data[19]; 116 | u.b[3] = data[18]; 117 | pm10p0_f = u.fval; 118 | 119 | Serial.print(pm1p0_f); 120 | Serial.print("\t"); 121 | Serial.print(pm2p5_f); 122 | Serial.print("\t"); 123 | Serial.print(pm4p0_f); 124 | Serial.print("\t"); 125 | Serial.print(pm10p0_f); 126 | Serial.print("\t"); 127 | 128 | // send read measurement data command to get VOC, RH, T 129 | Wire.beginTransmission(SEN44_ADDRESS); 130 | Wire.write(0x03); 131 | Wire.write(0xA6); 132 | Wire.endTransmission(); 133 | 134 | // wait 10 ms to allow the sensor to fill the internal buffer 135 | delay(10); 136 | 137 | // read measurement data SEN44, after two bytes a CRC follows 138 | Wire.requestFrom(SEN44_ADDRESS, 9); 139 | counter = 0; 140 | while (Wire.available()) { 141 | data[counter++] = Wire.read(); 142 | } 143 | 144 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 145 | // humidity is a signed int and scaled by 100 and need to be divided by 100 146 | // temperature is a signed int and scaled by 200 and need to be divided by 200 147 | voc = (uint16_t)data[0] << 8 | data[1]; 148 | humidity = (uint16_t)data[3] << 8 | data[4]; 149 | temperature = (uint16_t)data[6] << 8 | data[7]; 150 | 151 | Serial.print(String(float(voc) / 10)); 152 | Serial.print("\t"); 153 | Serial.print(String(float(humidity) / 100)); 154 | Serial.print("\t"); 155 | Serial.print(String(float(temperature) / 200)); 156 | Serial.println(); 157 | 158 | delay(10); 159 | 160 | // wait 1 s for next measurement 161 | delay(1000); 162 | } 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample Code for Arduino 2 | Arduino code examples for raw sensor communication with many Sensirion sensors 3 | 4 | # Summary 5 | The Arduino Platform allows for easy prototyping with endless possibilities. In addition to the documentation in the datasheet and application notes this repository demonstrates the communication with several Sensirion AG sensors through I2C and UART interfaces. The examples are very basic and typically are a starting point for customer specific implementations. The code for the minimal examples only uses the standard Wire or Serial libraries. No additional external libraries are needed. 6 | 7 | The code for the I2C interface examples is written without the use of abstractions so it could be easily adapted to own projects. To keep the code simple usually no error handling like CRC check or I2C NAK checks are implemented. 8 | 9 | The UART examples typically are referring to the SHDLC driver by Sensirion which is included in each example folder. 10 | 11 | # How to use 12 | 13 | All samples in this directory share the same format; as such, you can follow the instructions below to get any of them up and running: 14 | 15 | 1. If you haven't done so yet, install the [Arduino IDE](https://www.arduino.cc/en/software) and run some [basic test programs](https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink) to ensure your system is configured properly 16 | 1. Unplug your Arduino from your computer 17 | 1. Connect your sensor to the Arduino board. If you're unsure how to do that, please consult the sensors datasheet and the Arduino pinout 18 | 1. Reconnect your Arduino to your computer 19 | 1. Create a new Sketch in the Arduino IDE 20 | 1. Copy the contents of the .ino file and replace the content of your newly created sketch 21 | 1. Save your project under a meaningful name 22 | 1. Compile and flash the sketch 23 | 1. Open the "Serial Monitor" from the "Tools" menu in the Arduino IDE to display the output data read from the sensor 24 | 25 | # Examples by sensor 26 | 27 | ## SCD4x 28 | 29 | |Name|Protocol|Description| 30 | |----|--------|-----------| 31 | |[SCD4x_I2C_minimal_example](/SCD4x_I2C_minimal_example)|I2C|Minimal example to get started| 32 | |[SCD4x_I2C_FRC_Forced_Recalibration_Example](SCD4x_I2C_FRC_Forced_Recalibration_Example)|I2C|Show forced recalibration| 33 | 34 | ## SEN44 35 | |Name|Protocol|Description| 36 | |----|--------|-----------| 37 | |[SEN44_I2C_minimal_example](/SEN44_I2C_minimal_example)|I2C|Basic example for I2C| 38 | |[SEN44_I2C_pm_values_floating_point](/SEN44_I2C_pm_values_floating_point)|I2C|Read out PM values as floating point| 39 | |[SEN44_I2C_change_T_offset_example](/SEN44_I2C_change_T_offset_example)|I2C|Change T offset over I2C| 40 | |[SEN44_I2C_change_VOC_parameters_example](/SEN44_I2C_change_VOC_parameters_example)|I2C|Change VOC parameters over I2C| 41 | |[SEN44_SCD40_I2C_example](/SEN44_SCD40_I2C_example)|I2C|Example using SEN44 with SCD4x| 42 | |[SEN44_UART_minimal_example](/SEN44_UART_minimal_example)|UART|Basic example for UART (Serial) interface| 43 | 44 | ## SEN5x 45 | |Name|Protocol|Description| 46 | |----|--------|-----------| 47 | |[SEN5x_I2C_minimal_example](/SEN5x_I2C_minimal_example)|I2C|Basic example for I2C| 48 | |[SEN5x_I2C_config_STAR_example](/SEN5x_I2C_config_STAR_example)|I2C|Example configuration of STAR| 49 | |[SEN5x_I2C_config_coldstart_example](/SEN5x_I2C_config_coldstart_example)|I2C|Change T offset for cold start compensation| 50 | |[SEN5x_I2C_config_warmstart_example](/SEN5x_I2C_config_warmstart_example)|I2C|Change T behaviour in warm start scenario| 51 | |[SEN5x_I2C_change_VOC_parameters_example](/SEN5x_I2C_change_VOC_parameters_example)|I2C|Change VOC parameters over I2C| 52 | |[SEN5x_I2C_change_NOx_parameters_example](/SEN5x_I2C_change_NOx_parameters_example)|I2C|Change NOx parameters over I2C| 53 | |[SEN5x_I2C_read_raw](/SEN5x_I2C_read_raw)|I2C|Example for reading raw VOC and NOX values from the sensor over I2C| 54 | |[SEN5x_I2C_switch_measurement_mode](/SEN5x_I2C_switch_measurement_mode)|I2C|Example for switching between gas only and full measurement mode (requires FW2.0) over I2C| 55 | 56 | 57 | ## SEN6x 58 | |Name|Protocol|Description| 59 | |----|--------|-----------| 60 | |[SEN63C_I2C_minimal_example](/SEN63C_I2C_minimal_example)|I2C|Basic example for I2C| 61 | 62 | ## SFA30 63 | |Name|Protocol|Description| 64 | |----|--------|-----------| 65 | |[SFA30_I2C_minimal_example](/SFA30_I2C_minimal_example)|I2C|Minimal example to get started| 66 | 67 | 68 | ## SGP40 69 | |Name|Protocol|Description| 70 | |----|--------|-----------| 71 | |[SGP40_SHTC3_I2C_voc_algo_minimal_example](/SGP40_SHTC3_I2C_voc_algo_minimal_example)|I2C|Minimal example to use SGP40 with VOC Index algorithm| 72 | 73 | ## SHT3x 74 | |Name|Protocol|Description| 75 | |----|--------|-----------| 76 | |[SHT3x_I2C_minimal_example](/SHT3x_I2C_minimal_example)|I2C|Minimal example to get started| 77 | 78 | ## SHT4x 79 | |Name|Protocol|Description| 80 | |----|--------|-----------| 81 | |[SHT4x_I2C_minimal_example](/SHT4x_I2C_minimal_example)|I2C|Minimal example to get started| 82 | 83 | ## SHTC3 84 | |Name|Protocol|Description| 85 | |----|--------|-----------| 86 | |[SHTC3_I2C_minimal_example](/SHTC3_I2C_minimal_example)|I2C|Minimal example to get started| 87 | 88 | 89 | ## SVM40 90 | |Name|Protocol|Description| 91 | |----|--------|-----------| 92 | |[SVM40_I2C_minimal_example](/SVM40_I2C_minimal_example)|I2C|Minimal example to get started| 93 | |[SVM40_I2C_change_T_offset_example](/SVM40_I2C_change_T_offset_example)|I2C|Change T offset| 94 | |[SVM40_I2C_change_VOC_parameters_example](/SVM40_I2C_change_VOC_parameters_example)|I2C|Change VOC index algorithm parameters| 95 | 96 | 97 | ## SDP8xx 98 | |Name|Protocol|Description| 99 | |----|--------|-----------| 100 | |[SDP8xx_I2C_minimal_example](/SDP8xx_I2C_minimal_example)|I2C|Minimal example to get started| 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /SEN44_SCD40_I2C_example/SEN44_SCD40_I2C_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | // SCD40 37 | const int16_t SCD_ADDRESS = 0x62; 38 | 39 | void setup() { 40 | Serial.begin(115200); 41 | // wait for serial connection from PC 42 | // comment the following line if you'd like the output 43 | // without waiting for the interface being ready 44 | while(!Serial); 45 | 46 | // output format 47 | Serial.println("CO2\tRH_SCD\tT_SCD\tPM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT"); 48 | 49 | // init I2C 50 | Wire.begin(); 51 | 52 | // wait until sensors startup, > 1 ms according to datasheet 53 | delay(1); 54 | 55 | // start scd measurement in periodic mode, will update every 2 s 56 | Wire.beginTransmission(SCD_ADDRESS); 57 | Wire.write(0x21); 58 | Wire.write(0xb1); 59 | Wire.endTransmission(); 60 | 61 | // start up sensor, sensor will go to continous measurement mode 62 | // each second there will be new measurement values 63 | Wire.beginTransmission(SEN44_ADDRESS); 64 | Wire.write(0x00); 65 | Wire.write(0x21); 66 | Wire.endTransmission(); 67 | 68 | // wait until sensors is ready, fan is initialized 69 | delay(2000); 70 | } 71 | 72 | void loop() { 73 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 74 | int16_t voc, humidity, temperature; 75 | float co2, temp_scd, rh_scd; 76 | uint8_t data[21], counter; 77 | 78 | // read measurement data SCD 79 | Wire.beginTransmission(SCD_ADDRESS); 80 | Wire.write(0xec); 81 | Wire.write(0x05); 82 | Wire.endTransmission(); 83 | 84 | // read measurement data: 2 bytes co2, 1 byte CRC, 85 | // 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC, 86 | // 2 bytes sensor status, 1 byte CRC 87 | // stop reading after 12 bytes (not used) 88 | // other data like ASC not included 89 | Wire.requestFrom(SCD_ADDRESS, 12); 90 | counter = 0; 91 | while (Wire.available()) { 92 | data[counter++] = Wire.read(); 93 | } 94 | 95 | // floating point conversion according to datasheet 96 | co2 = (float)((uint16_t)data[0] << 8 | data[1]); 97 | // convert T in degC 98 | temp_scd = -45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536; 99 | // convert RH in % 100 | rh_scd = 100 * (float)((uint16_t)data[6] << 8 | data[7]) / 65536; 101 | 102 | Serial.print(co2); 103 | Serial.print("\t"); 104 | Serial.print(rh_scd); 105 | Serial.print("\t"); 106 | Serial.print(temp_scd); 107 | Serial.print("\t"); 108 | 109 | // send read measurement data command 110 | Wire.beginTransmission(SEN44_ADDRESS); 111 | Wire.write(0x03); 112 | Wire.write(0x74); 113 | Wire.endTransmission(); 114 | 115 | // wait 10 ms to allow the sensor to fill the internal buffer 116 | delay(10); 117 | 118 | // read measurement data SEN44, after two bytes a CRC follows 119 | Wire.requestFrom(SEN44_ADDRESS, 21); 120 | counter = 0; 121 | while (Wire.available()) { 122 | data[counter++] = Wire.read(); 123 | } 124 | 125 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 126 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 127 | // humidity is a signed int and scaled by 100 and need to be divided by 100 128 | // temperature is a signed int and scaled by 200 and need to be divided by 200 129 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 130 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 131 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 132 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 133 | voc = (uint16_t)data[12] << 8 | data[13]; 134 | humidity = (uint16_t)data[15] << 8 | data[16]; 135 | temperature = (uint16_t)data[18] << 8 | data[19]; 136 | 137 | Serial.print(pm1p0); 138 | Serial.print("\t"); 139 | Serial.print(pm2p5); 140 | Serial.print("\t"); 141 | Serial.print(pm4p0); 142 | Serial.print("\t"); 143 | Serial.print(pm10p0); 144 | Serial.print("\t"); 145 | Serial.print(String(float(voc) / 10)); 146 | Serial.print("\t"); 147 | Serial.print(String(float(humidity) / 100)); 148 | Serial.print("\t"); 149 | Serial.print(String(float(temperature) / 200)); 150 | Serial.println(); 151 | 152 | // wait 2 s for next measurement, due to sampling frequency of SCD 153 | delay(2000); 154 | } 155 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sen44.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SEN44_H 33 | #define SEN44_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include "sensirion_arch_config.h" 40 | 41 | #define SEN44_MAX_SERIAL_LEN 80 42 | #define SEN44_ERR_NOT_ENOUGH_DATA (-1) 43 | #define SEN44_ERR_STATE_MASK (0x100) 44 | #define SEN44_IS_ERR_STATE(err_code) (((err_code) | 0xff) == 0x1ff) 45 | #define SEN44_GET_ERR_STATE(err_code) ((err_code)&0xff) 46 | 47 | struct sen44_measurement{ 48 | uint16_t mc_1p0; 49 | uint16_t mc_2p5; 50 | uint16_t mc_4p0; 51 | uint16_t mc_10p0; 52 | int16_t voc_index; 53 | int16_t ambient_humidity; 54 | int16_t ambient_temperature; 55 | }; 56 | 57 | 58 | /** 59 | * sen44_get_driver_version() - Return the driver version 60 | * Return: Driver version string 61 | */ 62 | const char *sen44_get_driver_version(void); 63 | 64 | /** 65 | * sen44_probe() - check if SEN44 sensor is available and initialize it 66 | * 67 | * Return: 0 on success, an error code otherwise 68 | */ 69 | int16_t sen44_probe(); 70 | 71 | /** 72 | * sen44_get_serial() - retrieve the serial number 73 | * 74 | * Note that serial must be discarded when the return code is non-zero. 75 | * 76 | * @serial: Memory where the serial number is written into as hex string (zero 77 | * terminated). Must be at least SPS_MAX_SERIAL_LEN long. 78 | * Return: 0 on success, an error code otherwise 79 | */ 80 | int16_t sen44_get_serial(char *serial); 81 | 82 | /** 83 | * sen44_start_measurement() - start measuring 84 | * 85 | * Once the measurement is started, measurements are retrievable once per second 86 | * with sen44_read_measurement. 87 | * 88 | * Return: 0 on success, an error code otherwise 89 | */ 90 | int16_t sen44_start_measurement(); 91 | 92 | /** 93 | * sen44_stop_measurement() - stop measuring 94 | * 95 | * Return: 0 on success, an error code otherwise 96 | */ 97 | int16_t sen44_stop_measurement(); 98 | 99 | /** 100 | * sen44_read_measurement() - read a measurement 101 | * 102 | * Read the last measurement. 103 | * 104 | * Return: 0 on success, an error code otherwise 105 | */ 106 | int16_t sen44_read_measurement(struct sen44_measurement *measurement); 107 | 108 | 109 | /** 110 | * sen44_get_fan_auto_cleaning_interval() - read the current auto-cleaning 111 | * interval 112 | * 113 | * Note that interval_seconds must be discarded when the return code is 114 | * non-zero. 115 | * 116 | * @interval_seconds: Memory where the interval in seconds is stored 117 | * Return: 0 on success, an error code otherwise 118 | */ 119 | int16_t sen44_get_fan_auto_cleaning_interval(uint32_t *interval_seconds); 120 | 121 | /** 122 | * sen44_set_fan_auto_cleaning_interval() - set the current auto-cleaning 123 | * interval 124 | * 125 | * @interval_seconds: Value in seconds used to sets the auto-cleaning interval 126 | * Return: 0 on success, an error code otherwise 127 | */ 128 | int16_t sen44_set_fan_auto_cleaning_interval(uint32_t interval_seconds); 129 | 130 | /** 131 | * sen44_get_fan_auto_cleaning_interval_days() - convenience function to read 132 | * the current auto-cleaning interval in days 133 | * 134 | * note that the value is simply cut, not rounded or calculated nicely, thus 135 | * using this method is not precise when used in conjunction with 136 | * sen44_set_fan_auto_cleaning_interval instead of 137 | * sen44_set_fan_auto_cleaning_interval_days 138 | * 139 | * Note that interval_days must be discarded when the return code is non-zero. 140 | * 141 | * @interval_days: Memory where the interval in days is stored 142 | * Return: 0 on success, an error code otherwise 143 | */ 144 | int16_t sen44_get_fan_auto_cleaning_interval_days(uint8_t *interval_days); 145 | 146 | /** 147 | * sen44_set_fan_auto_cleaning_interval_days() - convenience function to set the 148 | * current auto-cleaning interval in days 149 | * 150 | * @interval_days: Value in days used to sets the auto-cleaning interval 151 | * Return: 0 on success, an error code otherwise 152 | */ 153 | int16_t sen44_set_fan_auto_cleaning_interval_days(uint8_t interval_days); 154 | 155 | /** 156 | * sen44_start_manual_fan_cleaning() - Immediately trigger the fan cleaning 157 | * 158 | * Note that this command can only be run when the sensor is in measurement 159 | * mode, i.e. after sen44_start_measurement() without subsequent 160 | * sen44_stop_measurement(). 161 | * 162 | * Return: 0 on success, an error code otherwise 163 | */ 164 | int16_t sen44_start_manual_fan_cleaning(); 165 | 166 | /** 167 | * sen44_reset() - reset the SEN44 168 | * 169 | * Return: 0 on success, an error code otherwise 170 | */ 171 | int16_t sen44_reset(); 172 | 173 | #ifdef __cplusplus 174 | } 175 | #endif 176 | 177 | #endif /* SEN44_H */ 178 | -------------------------------------------------------------------------------- /SGP40_SHTC3_I2C_voc_algo_minimal_example/SGP40_SHTC3_I2C_voc_algo_minimal_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include "sensirion_voc_algorithm.h" 34 | 35 | // SGP4x 36 | const int16_t SGP_ADDRESS = 0x59; 37 | // SHTC3 38 | const int16_t SHT_ADDRESS = 0x70; 39 | 40 | VocAlgorithmParams voc_algorithm_params; 41 | 42 | void setup() { 43 | Serial.begin(115200); // start serial for output 44 | // wait for serial connection from PC 45 | // comment the following line if you'd like the output 46 | // without waiting for the interface being ready 47 | while(!Serial); 48 | 49 | // output format 50 | Serial.println("VOC(level)\tVOC_raw(a.u.)\tRelativeHumidity(percent)\tTemperature(degC)"); 51 | 52 | // init I2C 53 | Wire.begin(); 54 | 55 | // init VOC engine 56 | VocAlgorithm_init(&voc_algorithm_params); 57 | 58 | // wait until sensors are ready, < 1 ms according to datasheet 59 | delay(1); 60 | } 61 | 62 | void loop() { 63 | int32_t voc_raw, voc_index, temperature, humidity; 64 | uint16_t rh_sgp, t_sgp; 65 | uint8_t data[6], counter; 66 | 67 | // start sht measurement in normal mode with clock stretching disabled 68 | Wire.beginTransmission(SHT_ADDRESS); 69 | Wire.write(0x78); 70 | Wire.write(0x66); 71 | Wire.endTransmission(); 72 | 73 | // wait for measurement has finished according to datasheet > 12.1 ms 74 | delay(13); 75 | 76 | // read measurement data sht: 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC 77 | Wire.requestFrom(SHT_ADDRESS, 6); 78 | counter = 0; 79 | while (Wire.available()) { 80 | data[counter++] = Wire.read(); 81 | } 82 | 83 | // floating point conversion according to datasheet 84 | // convert T 85 | // float temperature; 86 | // temperature = -45 + 175 * (float)((uint16_t)data_receive[0]<<8 | data_receive[1])/65536; 87 | // convert RH 88 | // float humidity; 89 | // humidity = 100 * (float)((uint16_t)data_receive[3]<<8 | data_receive[4])/65536; 90 | 91 | /** 92 | * formulas for conversion of the sensor signals, optimized for fixed point 93 | * algebra: Temperature = 175 * S_T / 2^16 - 45 94 | * Relative Humidity = * 100 * S_RH / 2^16 95 | * https://github.com/Sensirion/embedded-sht/blob/master/sht3x/sht3x.c 96 | * result is in 1/1000 deg C and 1/1000 % RH 97 | */ 98 | temperature = (21875 * ((int32_t)data[0] << 8 | data[1]) >> 13) - 45000; 99 | humidity = (12500 * ((int32_t)data[3] << 8 | data[4]) >> 13); 100 | 101 | // calculate rh, t for voc on chip compensation; 102 | // this is basically the reverse conversion 103 | // to get the raw data from the rht sensor, 104 | // another option is to directly use the 105 | // rht sensor output 106 | t_sgp = (temperature / 1000 + 45) * 65536 / 175; 107 | rh_sgp = (humidity / 1000) * 65536 / 100; 108 | 109 | // prepare buffer with rht compensation data 110 | // calculate CRC for each 2 bytes of data 111 | data[0] = (rh_sgp & 0xff00) >> 8; 112 | data[1] = rh_sgp & 0x00ff; 113 | data[2] = CalcCrc(data); 114 | data[3] = (t_sgp & 0xff00) >> 8; 115 | data[4] = t_sgp & 0x00ff; 116 | data[5] = CalcCrc(data + 3); 117 | 118 | // start sgp measurement in mode sgp40_measure_raw 119 | Wire.beginTransmission(SGP_ADDRESS); 120 | Wire.write(0x26); 121 | Wire.write(0x0F); 122 | // append data for rh and t for compensation 123 | // as in chapter 4.7 of the datasheet 124 | // 2 bytes rh, CRC, 2 bytes t, CRC 125 | Wire.write(data[0]); 126 | Wire.write(data[1]); 127 | Wire.write(data[2]); 128 | Wire.write(data[3]); 129 | Wire.write(data[4]); 130 | Wire.write(data[5]); 131 | Wire.endTransmission(); 132 | 133 | // wait for measurement has finished according to datasheet > 30 ms 134 | delay(30); 135 | 136 | //read measurement data sgp: 2 bytes voc raw signal, CRC 137 | Wire.requestFrom(SGP_ADDRESS, 3); 138 | counter = 0; 139 | while (Wire.available()) { 140 | data[counter++] = Wire.read(); 141 | } 142 | 143 | // convert 2 bytes to one word 144 | voc_raw = (int16_t)data[0] << 8 | data[1]; 145 | // convert raw signal to voc index 146 | VocAlgorithm_process(&voc_algorithm_params, voc_raw, &voc_index); 147 | 148 | Serial.print(voc_raw); 149 | Serial.print("\t"); 150 | Serial.print(voc_index); 151 | Serial.print("\t"); 152 | Serial.print(humidity); 153 | Serial.print("\t"); 154 | Serial.print(temperature); 155 | Serial.println(); 156 | 157 | // wait 1 s for next measurement 158 | delay(1000); 159 | } 160 | 161 | // calculate CRC according to datasheet section 4.6 162 | uint8_t CalcCrc(uint8_t data[2]) { 163 | uint8_t crc = 0xFF; 164 | for(int i = 0; i < 2; i++) { 165 | crc ^= data[i]; 166 | for(uint8_t bit = 8; bit > 0; --bit) { 167 | if(crc & 0x80) { 168 | crc = (crc << 1) ^ 0x31u; 169 | } else { 170 | crc = (crc << 1); 171 | } 172 | } 173 | } 174 | return crc; 175 | } 176 | -------------------------------------------------------------------------------- /SVM40_I2C_change_T_offset_example/SVM40_I2C_change_T_offset_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SVM40 35 | const int16_t SVM40_ADDRESS = 0x6A; 36 | 37 | void setup() { 38 | int16_t t_offset; 39 | uint8_t data[3], counter; 40 | 41 | Serial.begin(115200); 42 | // wait for serial connection from PC 43 | // comment the following line if you'd like the output 44 | // without waiting for the interface being ready 45 | while(!Serial); 46 | 47 | // output format 48 | Serial.println("VOC_Index\tRH\tT"); 49 | 50 | // init I2C 51 | Wire.begin(); 52 | 53 | // wait until sensors startup, > 1 ms according to datasheet 54 | delay(100); 55 | 56 | // read t offset from flash memory 57 | Wire.beginTransmission(SVM40_ADDRESS); 58 | Wire.write(0x60); 59 | Wire.write(0x14); 60 | Wire.endTransmission(); 61 | 62 | // wait 10 ms to allow the sensor to fill the internal buffer 63 | delay(10); 64 | 65 | // read offset data, after two bytes a CRC follows 66 | Wire.requestFrom(SVM40_ADDRESS, 3); 67 | counter = 0; 68 | while (Wire.available()) { 69 | data[counter++] = Wire.read(); 70 | } 71 | 72 | t_offset = (uint16_t)data[0] << 8 | data[1]; 73 | 74 | // print value, as all temperature values, this one is also scaled by 200 75 | Serial.print("Default T Offset: "); 76 | Serial.print(String(float(t_offset)/200)); 77 | Serial.println(); 78 | 79 | // set t offset to new value (-5 degC) and scale it accordingly by a factor of 200 80 | t_offset = -5 * 200; 81 | 82 | // prepare buffer with t offset data 83 | // calculate CRC for each 2 bytes of data 84 | data[0] = (t_offset & 0xff00) >> 8; 85 | data[1] = t_offset & 0x00ff; 86 | data[2] = CalcCrc(data); 87 | 88 | // send new value for t offset to sensor (will be hold in RAM, not persistent) 89 | Wire.beginTransmission(SVM40_ADDRESS); 90 | Wire.write(0x60); 91 | Wire.write(0x14); 92 | Wire.write(data[0]); 93 | Wire.write(data[1]); 94 | Wire.write(data[2]); 95 | Wire.endTransmission(); 96 | 97 | // wait 10 ms to allow the sensor to fill the internal buffer 98 | delay(10); 99 | 100 | // now send command to save parameter in the flash (NVM memory) of the sensor to have persistence 101 | Wire.beginTransmission(SVM40_ADDRESS); 102 | Wire.write(0x60); 103 | Wire.write(0x02); 104 | Wire.endTransmission(); 105 | 106 | // wait 20 ms to allow the sensor to write the data into the flash 107 | delay(20); 108 | 109 | // repeat read offset data to make sure that the values are applied correctly 110 | Wire.beginTransmission(SVM40_ADDRESS); 111 | Wire.write(0x60); 112 | Wire.write(0x14); 113 | Wire.endTransmission(); 114 | 115 | // wait 10 ms to allow the sensor to fill the internal buffer 116 | delay(10); 117 | 118 | // read offset data, after two bytes a CRC follows 119 | Wire.requestFrom(SVM40_ADDRESS, 3); 120 | counter = 0; 121 | while (Wire.available()) { 122 | data[counter++] = Wire.read(); 123 | } 124 | 125 | t_offset = (uint16_t)data[0] << 8 | data[1]; 126 | 127 | Serial.print("New T Offset: "); 128 | Serial.print(String(float(t_offset)/200)); 129 | Serial.println(); 130 | 131 | // start up sensor, sensor will go to continous measurement mode 132 | // each second there will be new measurement values 133 | Wire.beginTransmission(SVM40_ADDRESS); 134 | Wire.write(0x00); 135 | Wire.write(0x10); 136 | Wire.endTransmission(); 137 | 138 | // wait until sensors is ready, fan is initialized 139 | delay(2000); 140 | } 141 | 142 | void loop() { 143 | int16_t voc, humidity, temperature; 144 | uint8_t data[9], counter; 145 | 146 | // read measurement data 147 | Wire.beginTransmission(SVM40_ADDRESS); 148 | Wire.write(0x03); 149 | Wire.write(0xA6); 150 | Wire.endTransmission(); 151 | 152 | // wait 10 ms to allow the sensor to fill the internal buffer 153 | delay(10); 154 | 155 | // read measurement data sen44, after two bytes a CRC follows 156 | Wire.requestFrom(SVM40_ADDRESS, 9); 157 | counter = 0; 158 | while (Wire.available()) { 159 | data[counter++] = Wire.read(); 160 | } 161 | 162 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 163 | // VOC raw value is an uint16_t and has no scaling 164 | // humidity is a signed int and scaled by 100 and need to be divided by 100 165 | // temperature is a signed int and scaled by 200 and need to be divided by 200 166 | voc = (uint16_t)data[0] << 8 | data[1]; 167 | humidity = (uint16_t)data[3] << 8 | data[4]; 168 | temperature = (uint16_t)data[6] << 8 | data[7]; 169 | 170 | Serial.print(String(float(voc) / 10)); 171 | Serial.print("\t"); 172 | Serial.print(String(float(humidity) / 100)); 173 | Serial.print("\t"); 174 | Serial.print(String(float(temperature) / 200)); 175 | Serial.println(); 176 | 177 | // wait 1 s for next measurement 178 | delay(1000); 179 | } 180 | 181 | // calculate CRC according to datasheet 182 | uint8_t CalcCrc(uint8_t data[2]) { 183 | uint8_t crc = 0xFF; 184 | for(int i = 0; i < 2; i++) { 185 | crc ^= data[i]; 186 | for(uint8_t bit = 8; bit > 0; --bit) { 187 | if(crc & 0x80) { 188 | crc = (crc << 1) ^ 0x31u; 189 | } else { 190 | crc = (crc << 1); 191 | } 192 | } 193 | } 194 | return crc; 195 | } 196 | -------------------------------------------------------------------------------- /SEN44_I2C_change_T_offset_example/SEN44_I2C_change_T_offset_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t t_offset; 39 | uint8_t data[3], counter; 40 | 41 | Serial.begin(115200); 42 | // wait for serial connection from PC 43 | // comment the following line if you'd like the output 44 | // without waiting for the interface being ready 45 | while(!Serial); 46 | 47 | // output format 48 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT"); 49 | 50 | // init I2C 51 | Wire.begin(); 52 | 53 | // wait until sensors startup, > 1 ms according to datasheet 54 | delay(100); 55 | 56 | // read t offset from flash memory 57 | Wire.beginTransmission(SEN44_ADDRESS); 58 | Wire.write(0x60); 59 | Wire.write(0x14); 60 | Wire.endTransmission(); 61 | 62 | // wait 10 ms to allow the sensor to fill the internal buffer 63 | delay(10); 64 | 65 | // read offset data, after two bytes a CRC follows 66 | Wire.requestFrom(SEN44_ADDRESS, 3); 67 | counter = 0; 68 | while (Wire.available()) { 69 | data[counter++] = Wire.read(); 70 | } 71 | 72 | t_offset = (uint16_t)data[0] << 8 | data[1]; 73 | 74 | // print value, as all temperature values, this one is also scaled by 200 75 | Serial.print("Default T Offset: "); 76 | Serial.print(String(float(t_offset)/200)); 77 | Serial.println(); 78 | 79 | // set t offset to new value (-5 degC) and scale it accordingly by a factor of 200 80 | t_offset = -5 * 200; 81 | 82 | // prepare buffer with t offset data 83 | // calculate CRC for each 2 bytes of data 84 | data[0] = (t_offset & 0xff00) >> 8; 85 | data[1] = t_offset & 0x00ff; 86 | data[2] = CalcCrc(data); 87 | 88 | // send new value for t offset to sensor (will be hold in RAM, not persistent) 89 | Wire.beginTransmission(SEN44_ADDRESS); 90 | Wire.write(0x60); 91 | Wire.write(0x14); 92 | Wire.write(data[0]); 93 | Wire.write(data[1]); 94 | Wire.write(data[2]); 95 | Wire.endTransmission(); 96 | 97 | // wait 10 ms to allow the sensor to fill the internal buffer 98 | delay(10); 99 | 100 | // now send command to save parameter in the flash (NVM memory) of the sensor to have persistence 101 | Wire.beginTransmission(SEN44_ADDRESS); 102 | Wire.write(0x60); 103 | Wire.write(0x02); 104 | Wire.endTransmission(); 105 | 106 | // wait 20 ms to allow the sensor to write the data into the flash 107 | delay(20); 108 | 109 | // repeat read offset data to make sure that the values are applied correctly 110 | Wire.beginTransmission(SEN44_ADDRESS); 111 | Wire.write(0x60); 112 | Wire.write(0x14); 113 | Wire.endTransmission(); 114 | 115 | // wait 10 ms to allow the sensor to fill the internal buffer 116 | delay(10); 117 | 118 | // read offset data, after two bytes a CRC follows 119 | Wire.requestFrom(SEN44_ADDRESS, 3); 120 | counter = 0; 121 | while (Wire.available()) { 122 | data[counter++] = Wire.read(); 123 | } 124 | 125 | t_offset = (uint16_t)data[0] << 8 | data[1]; 126 | 127 | Serial.print("New T Offset: "); 128 | Serial.print(String(float(t_offset)/200)); 129 | Serial.println(); 130 | 131 | // start up sensor, sensor will go to continous measurement mode 132 | // each second there will be new measurement values 133 | Wire.beginTransmission(SEN44_ADDRESS); 134 | Wire.write(0x00); 135 | Wire.write(0x21); 136 | Wire.endTransmission(); 137 | 138 | // wait until sensors is ready, fan is initialized 139 | delay(2000); 140 | } 141 | 142 | void loop() { 143 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 144 | int16_t voc, humidity, temperature; 145 | uint8_t data[21], counter; 146 | 147 | // read measurement data 148 | Wire.beginTransmission(SEN44_ADDRESS); 149 | Wire.write(0x03); 150 | Wire.write(0x74); 151 | Wire.endTransmission(); 152 | 153 | // wait 10 ms to allow the sensor to fill the internal buffer 154 | delay(10); 155 | 156 | // read measurement data sen44, after two bytes a CRC follows 157 | Wire.requestFrom(SEN44_ADDRESS, 21); 158 | counter = 0; 159 | while (Wire.available()) { 160 | data[counter++] = Wire.read(); 161 | } 162 | 163 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 164 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 165 | // VOC raw value is an uint16_t and has no scaling 166 | // humidity is a signed int and scaled by 100 and need to be divided by 100 167 | // temperature is a signed int and scaled by 200 and need to be divided by 200 168 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 169 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 170 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 171 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 172 | voc = (uint16_t)data[12] << 8 | data[13]; 173 | humidity = (uint16_t)data[15] << 8 | data[16]; 174 | temperature = (uint16_t)data[18] << 8 | data[19]; 175 | 176 | Serial.print(pm1p0); 177 | Serial.print("\t"); 178 | Serial.print(pm2p5); 179 | Serial.print("\t"); 180 | Serial.print(pm4p0); 181 | Serial.print("\t"); 182 | Serial.print(pm10p0); 183 | Serial.print("\t"); 184 | Serial.print(String(float(voc) / 10)); 185 | Serial.print("\t"); 186 | Serial.print(String(float(humidity) / 100)); 187 | Serial.print("\t"); 188 | Serial.print(String(float(temperature) / 200)); 189 | Serial.println(); 190 | 191 | // wait 1 s for next measurement 192 | delay(1000); 193 | } 194 | 195 | // calculate CRC according to datasheet 196 | uint8_t CalcCrc(uint8_t data[2]) { 197 | uint8_t crc = 0xFF; 198 | for(int i = 0; i < 2; i++) { 199 | crc ^= data[i]; 200 | for(uint8_t bit = 8; bit > 0; --bit) { 201 | if(crc & 0x80) { 202 | crc = (crc << 1) ^ 0x31u; 203 | } else { 204 | crc = (crc << 1); 205 | } 206 | } 207 | } 208 | return crc; 209 | } 210 | -------------------------------------------------------------------------------- /SCD4x_I2C_FRC_Forced_Recalibration_Example/SCD4x_I2C_FRC_Forced_Recalibration_Example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SCD4x 35 | const int16_t SCD_ADDRESS = 0x62; 36 | 37 | void setup() { 38 | float co2, temperature, humidity; 39 | uint16_t calibration; 40 | uint8_t data[12], counter, repetition; 41 | uint8_t ret; 42 | 43 | Serial.begin(115200); 44 | // wait for serial connection from PC 45 | // comment the following line if you'd like the output 46 | // without waiting for the interface being ready 47 | while(!Serial); 48 | 49 | // init I2C 50 | Wire.begin(); 51 | 52 | // wait until sensors are ready, > 1000 ms according to datasheet 53 | delay(1000); 54 | 55 | // start scd measurement in periodic mode, will update every 5 s 56 | Wire.beginTransmission(SCD_ADDRESS); 57 | Wire.write(0x21); 58 | Wire.write(0xb1); 59 | Wire.endTransmission(); 60 | 61 | // wait for 5 minutes to equilibrate sensor to ambient 62 | Serial.println("# Waiting 5 minutes for equilibration"); 63 | delay((unsigned long)5 * 60 * 1000); 64 | 65 | Serial.println("# CO2 values before calibration"); 66 | 67 | // measure 5 times 68 | for(repetition = 0; repetition < 5; repetition++) { 69 | // read measurement data: 2 bytes co2, 1 byte CRC, 70 | // 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC, 71 | // 2 bytes sensor status, 1 byte CRC 72 | // stop reading after 12 bytes (not used) 73 | // other data like ASC not included 74 | Wire.requestFrom(SCD_ADDRESS, 12); 75 | counter = 0; 76 | while (Wire.available()) { 77 | data[counter++] = Wire.read(); 78 | } 79 | 80 | // floating point conversion according to datasheet 81 | co2 = (float)((uint16_t)data[0] << 8 | data[1]); 82 | // convert T in degC 83 | temperature = -45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536; 84 | // convert RH in % 85 | humidity = 100 * (float)((uint16_t)data[6] << 8 | data[7]) / 65536; 86 | 87 | Serial.print("# "); 88 | Serial.print(co2); 89 | Serial.print("\t"); 90 | Serial.print(temperature); 91 | Serial.print("\t"); 92 | Serial.print(humidity); 93 | Serial.println(); 94 | 95 | delay(2000); 96 | } 97 | 98 | 99 | // stop scd measurement 100 | Wire.beginTransmission(SCD_ADDRESS); 101 | Wire.write(0x3f); 102 | Wire.write(0x86); 103 | ret = Wire.endTransmission(); 104 | Serial.println(ret); 105 | 106 | // wait for sensor 107 | delay(20); 108 | 109 | // assuming an external reference shows 650 ppm 110 | calibration = 650; 111 | Serial.print("# Calibrating with reference value [ppm]: "); 112 | Serial.println(calibration); 113 | 114 | // prepare buffer with data for calibration 115 | // calculate CRC for each 2 bytes of data 116 | data[0] = (calibration & 0xff00) >> 8; 117 | data[1] = calibration & 0x00ff; 118 | data[2] = CalcCrc(data); 119 | 120 | // send command for perform_forced_recalibration 121 | Wire.beginTransmission(SCD_ADDRESS); 122 | Wire.write(0x36); 123 | Wire.write(0x2F); 124 | // append data for calibration 125 | // 2 bytes calibraion, CRC 126 | Wire.write(data[0]); 127 | Wire.write(data[1]); 128 | Wire.write(data[2]); 129 | ret = Wire.endTransmission(); 130 | Serial.println(ret); 131 | 132 | delay(400); 133 | 134 | // read data: 2 bytes correction, 1 byte CRC 135 | Wire.requestFrom(SCD_ADDRESS, 3); 136 | counter = 0; 137 | while (Wire.available()) { 138 | data[counter++] = Wire.read(); 139 | } 140 | 141 | if(CalcCrc(data) != data[2]) 142 | Serial.println("# ERROR: recalibration CRC return value"); 143 | 144 | calibration = ((uint16_t)data[0] << 8 | data[1]); 145 | 146 | Serial.print("# Value after recalibration\n# "); 147 | Serial.println(calibration-32768); 148 | 149 | // output format 150 | Serial.println("CO2(ppm)\tTemperature(degC)\tRelativeHumidity(percent)"); 151 | 152 | // start scd measurement again in periodic mode, will update every 2 s 153 | Wire.beginTransmission(SCD_ADDRESS); 154 | Wire.write(0x21); 155 | Wire.write(0xb1); 156 | Wire.endTransmission(); 157 | 158 | // wait for first measurement to be finished (> 5 s) 159 | delay(10000); 160 | } 161 | 162 | void loop() { 163 | float co2, temperature, humidity; 164 | uint8_t data[12], counter; 165 | 166 | // send read data command 167 | Wire.beginTransmission(SCD_ADDRESS); 168 | Wire.write(0xec); 169 | Wire.write(0x05); 170 | Wire.endTransmission(); 171 | 172 | // read measurement data: 2 bytes co2, 1 byte CRC, 173 | // 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC, 174 | // 2 bytes sensor status, 1 byte CRC 175 | // stop reading after 12 bytes (not used) 176 | // other data like ASC not included 177 | Wire.requestFrom(SCD_ADDRESS, 12); 178 | counter = 0; 179 | while (Wire.available()) { 180 | data[counter++] = Wire.read(); 181 | } 182 | 183 | // floating point conversion according to datasheet 184 | co2 = (float)((uint16_t)data[0] << 8 | data[1]); 185 | // convert T in degC 186 | temperature = -45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536; 187 | // convert RH in % 188 | humidity = 100 * (float)((uint16_t)data[6] << 8 | data[7]) / 65536; 189 | 190 | Serial.print(co2); 191 | Serial.print("\t"); 192 | Serial.print(temperature); 193 | Serial.print("\t"); 194 | Serial.print(humidity); 195 | Serial.println(); 196 | 197 | // wait 5 s for next measurement 198 | delay(5000); 199 | } 200 | 201 | // calculate CRC according to datasheet section 5.17 202 | uint8_t CalcCrc(uint8_t data[2]) { 203 | uint8_t crc = 0xFF; 204 | for(int i = 0; i < 2; i++) { 205 | crc ^= data[i]; 206 | for(uint8_t bit = 8; bit > 0; --bit) { 207 | if(crc & 0x80) { 208 | crc = (crc << 1) ^ 0x31u; 209 | } else { 210 | crc = (crc << 1); 211 | } 212 | } 213 | } 214 | return crc; 215 | } 216 | -------------------------------------------------------------------------------- /SEN5x_I2C_config_warmstart_example/SEN5x_I2C_config_warmstart_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN5x 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t warm_start; 39 | uint8_t data[3], counter; 40 | 41 | Serial.begin(115200); 42 | 43 | // Wait for serial connection from PC 44 | // Comment the following line if you'd like the output 45 | // without waiting for the interface being ready 46 | while(!Serial); 47 | 48 | // Wait until sensors startup, > 50 ms according to datasheet 49 | delay(50); 50 | 51 | // Initiate I2C communication 52 | Wire.begin(); 53 | 54 | // Send command to read/write warm start parameters (0x60C6) 55 | Wire.beginTransmission(SEN55_ADDRESS); 56 | Wire.write(0x60); 57 | Wire.write(0xC6); 58 | Wire.endTransmission(); 59 | 60 | // Wait for 20 ms to allow the sensor to fill the internal buffer 61 | delay(20); 62 | 63 | // Read preset temperature compensation parameters from SEN55 64 | Wire.requestFrom(SEN55_ADDRESS, 3); 65 | counter = 0; 66 | while (Wire.available()) { 67 | data[counter++] = Wire.read(); 68 | } 69 | 70 | // Processing preset warm start parameter data 71 | // CRC byte (every 3rd byte) is excluded from processing 72 | warm_start = (uint16_t)data[0] << 8 | data[1]; 73 | 74 | // print value 75 | Serial.println(); 76 | Serial.print("Preset Warm Start Parameter: "); 77 | Serial.print(String(float(warm_start)/1)); 78 | Serial.println(); 79 | 80 | // Set new value for warm start parameter (1000) 81 | warm_start = 1000; 82 | 83 | // Parsing 84 | data[0] = (warm_start & 0xff00) >> 8; 85 | data[1] = warm_start & 0x00ff; 86 | data[2] = CalcCrc(data); 87 | 88 | // Send new warm start parameter to sensor (will be held in RAM, not persistent) 89 | Wire.beginTransmission(SEN55_ADDRESS); 90 | Wire.write(0x60); 91 | Wire.write(0xC6); 92 | Wire.write(data[0]); 93 | Wire.write(data[1]); 94 | Wire.write(data[2]); 95 | Wire.endTransmission(); 96 | 97 | // Wait 20 ms to allow the sensor to fill the internal buffer 98 | delay(20); 99 | 100 | // Send command to read new warm start parameter (for confirmation) 101 | Wire.beginTransmission(SEN55_ADDRESS); 102 | Wire.write(0x60); 103 | Wire.write(0xC6); 104 | Wire.endTransmission(); 105 | 106 | // Wait 20 ms to allow the sensor to fill the internal buffer 107 | delay(20); 108 | 109 | // Read warm start parameters from SEN55 110 | Wire.requestFrom(SEN55_ADDRESS, 3); 111 | counter = 0; 112 | while (Wire.available()) { 113 | data[counter++] = Wire.read(); 114 | } 115 | 116 | // Parse data to make sure that new warm start parameters are correct 117 | warm_start = (uint16_t)data[0] << 8 | data[1]; 118 | 119 | // Print new warm start parameter 120 | Serial.println(); 121 | Serial.print("New Warm Start Parameter: "); 122 | Serial.print(String(float(warm_start)/1)); 123 | Serial.println(); 124 | 125 | // Send command to start measurement (0x0021) 126 | Wire.beginTransmission(SEN55_ADDRESS); 127 | Wire.write(0x00); 128 | Wire.write(0x21); 129 | Wire.endTransmission(); 130 | 131 | // Wait until sensors is ready, fan is initialized 132 | delay(2000); 133 | 134 | // Output measurement value format 135 | Serial.println(); 136 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 137 | 138 | } 139 | 140 | void loop() { 141 | 142 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 143 | int16_t voc, nox, humidity, temperature; 144 | uint8_t data[24], counter; 145 | 146 | // Send read measurement data command (0x03C4) 147 | Wire.beginTransmission(SEN55_ADDRESS); 148 | Wire.write(0x03); 149 | Wire.write(0xC4); 150 | Wire.endTransmission(); 151 | // Wait 20 ms to allow the sensor to fill the internal buffer 152 | delay(20); 153 | 154 | // Read measurement data SEN55 155 | Wire.requestFrom(SEN55_ADDRESS, 24); 156 | counter = 0; 157 | while (Wire.available()) { 158 | data[counter++] = Wire.read(); 159 | } 160 | 161 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 162 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 163 | // humidity is a signed int and scaled by 100 and need to be divided by 100 164 | // temperature is a signed int and scaled by 200 and need to be divided by 200 165 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 166 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 167 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 168 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 169 | humidity = (uint16_t)data[12] << 8 | data[13]; 170 | temperature = (uint16_t)data[15] << 8 | data[16]; 171 | voc = (uint16_t)data[18] << 8 | data[19]; 172 | nox = (uint16_t)data[21] << 8 | data[22]; 173 | 174 | Serial.print(String(float(pm1p0) / 10)); 175 | Serial.print("\t"); 176 | Serial.print(String(float(pm2p5) / 10)); 177 | Serial.print("\t"); 178 | Serial.print(String(float(pm4p0) / 10)); 179 | Serial.print("\t"); 180 | Serial.print(String(float(pm10p0) / 10)); 181 | Serial.print("\t"); 182 | Serial.print(String(float(voc) / 10)); 183 | Serial.print("\t\t"); 184 | Serial.print(String(float(nox) / 10)); 185 | Serial.print("\t\t"); 186 | Serial.print(String(float(humidity) / 100)); 187 | Serial.print("\t"); 188 | Serial.print(String(float(temperature) / 200)); 189 | Serial.println(); 190 | 191 | // Wait 1 s for next measurement 192 | delay(1000); 193 | } 194 | 195 | // Calculate CRC according to datasheet 196 | uint8_t CalcCrc(uint8_t data[2]) { 197 | uint8_t crc = 0xFF; 198 | for(int i = 0; i < 2; i++) { 199 | crc ^= data[i]; 200 | for(uint8_t bit = 8; bit > 0; --bit) { 201 | if(crc & 0x80) { 202 | crc = (crc << 1) ^ 0x31u; 203 | } else { 204 | crc = (crc << 1); 205 | } 206 | } 207 | } 208 | return crc; 209 | } 210 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sensirion_shdlc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "sensirion_shdlc.h" 33 | #include "sensirion_arch_config.h" 34 | #include "sensirion_uart.h" 35 | 36 | #define SHDLC_START 0x7e 37 | #define SHDLC_STOP 0x7e 38 | 39 | #define SHDLC_MIN_TX_FRAME_SIZE 6 40 | /** start/stop + (4 header + 255 data) * 2 because of byte stuffing */ 41 | #define SHDLC_FRAME_MAX_TX_FRAME_SIZE (2 + (4 + 255) * 2) 42 | 43 | /** start/stop + (5 header + 255 data) * 2 because of byte stuffing */ 44 | #define SHDLC_FRAME_MAX_RX_FRAME_SIZE (2 + (5 + 255) * 2) 45 | 46 | #define RX_DELAY_US 20000 47 | 48 | static uint8_t sensirion_shdlc_crc(uint8_t header_sum, uint8_t data_len, 49 | const uint8_t *data) { 50 | header_sum += data_len; 51 | 52 | while (data_len--) 53 | header_sum += *(data++); 54 | 55 | return ~header_sum; 56 | } 57 | 58 | static uint16_t sensirion_shdlc_stuff_data(uint8_t data_len, 59 | const uint8_t *data, 60 | uint8_t *stuffed_data) { 61 | uint16_t output_data_len = 0; 62 | uint8_t c; 63 | 64 | while (data_len--) { 65 | c = *(data++); 66 | switch (c) { 67 | case 0x11: 68 | case 0x13: 69 | case 0x7d: 70 | case 0x7e: 71 | // byte stuffing is done by inserting 0x7d and inverting bit 5 72 | *(stuffed_data++) = 0x7d; 73 | *(stuffed_data++) = c ^ (1 << 5); 74 | output_data_len += 2; 75 | break; 76 | default: 77 | *(stuffed_data++) = c; 78 | output_data_len += 1; 79 | } 80 | } 81 | return output_data_len; 82 | } 83 | 84 | static uint8_t sensirion_shdlc_check_unstuff(uint8_t data) { 85 | return data == 0x7d; 86 | } 87 | 88 | static uint8_t sensirion_shdlc_unstuff_byte(uint8_t data) { 89 | switch (data) { 90 | case 0x31: 91 | return 0x11; 92 | case 0x33: 93 | return 0x13; 94 | case 0x5d: 95 | return 0x7d; 96 | case 0x5e: 97 | return 0x7e; 98 | default: 99 | return data; 100 | } 101 | } 102 | 103 | int16_t sensirion_shdlc_xcv(uint8_t addr, uint8_t cmd, uint8_t tx_data_len, 104 | const uint8_t *tx_data, uint8_t max_rx_data_len, 105 | struct sensirion_shdlc_rx_header *rx_header, 106 | uint8_t *rx_data) { 107 | int16_t ret; 108 | 109 | ret = sensirion_shdlc_tx(addr, cmd, tx_data_len, tx_data); 110 | if (ret != 0) 111 | return ret; 112 | 113 | sensirion_sleep_usec(RX_DELAY_US); 114 | return sensirion_shdlc_rx(max_rx_data_len, rx_header, rx_data); 115 | } 116 | 117 | int16_t sensirion_shdlc_tx(uint8_t addr, uint8_t cmd, uint8_t data_len, 118 | const uint8_t *data) { 119 | uint16_t len = 0; 120 | int16_t ret; 121 | uint8_t crc; 122 | uint8_t tx_frame_buf[SHDLC_FRAME_MAX_TX_FRAME_SIZE]; 123 | 124 | crc = sensirion_shdlc_crc(addr + cmd, data_len, data); 125 | 126 | tx_frame_buf[len++] = SHDLC_START; 127 | len += sensirion_shdlc_stuff_data(1, &addr, tx_frame_buf + len); 128 | len += sensirion_shdlc_stuff_data(1, &cmd, tx_frame_buf + len); 129 | len += sensirion_shdlc_stuff_data(1, &data_len, tx_frame_buf + len); 130 | len += sensirion_shdlc_stuff_data(data_len, data, tx_frame_buf + len); 131 | len += sensirion_shdlc_stuff_data(1, &crc, tx_frame_buf + len); 132 | tx_frame_buf[len++] = SHDLC_STOP; 133 | 134 | ret = sensirion_uart_tx(len, tx_frame_buf); 135 | if (ret < 0) 136 | return ret; 137 | if (ret != len) 138 | return SENSIRION_SHDLC_ERR_TX_INCOMPLETE; 139 | return 0; 140 | } 141 | 142 | int16_t sensirion_shdlc_rx(uint8_t max_data_len, 143 | struct sensirion_shdlc_rx_header *rxh, 144 | uint8_t *data) { 145 | int16_t len; 146 | uint16_t i; 147 | uint8_t rx_frame[SHDLC_FRAME_MAX_RX_FRAME_SIZE]; 148 | uint8_t *rx_header = (uint8_t *)rxh; 149 | uint8_t j; 150 | uint8_t crc; 151 | uint8_t unstuff_next; 152 | 153 | len = sensirion_uart_rx(2 + (5 + (uint16_t)max_data_len) * 2, rx_frame); 154 | if (len < 1 || rx_frame[0] != SHDLC_START) 155 | return SENSIRION_SHDLC_ERR_MISSING_START; 156 | 157 | for (unstuff_next = 0, i = 1, j = 0; j < sizeof(*rxh) && i < len - 2; ++i) { 158 | if (unstuff_next) { 159 | rx_header[j++] = sensirion_shdlc_unstuff_byte(rx_frame[i]); 160 | unstuff_next = 0; 161 | } else { 162 | unstuff_next = sensirion_shdlc_check_unstuff(rx_frame[i]); 163 | if (!unstuff_next) 164 | rx_header[j++] = rx_frame[i]; 165 | } 166 | } 167 | if (j != sizeof(*rxh) || unstuff_next) 168 | return SENSIRION_SHDLC_ERR_ENCODING_ERROR; 169 | 170 | if (max_data_len < rxh->data_len) 171 | return SENSIRION_SHDLC_ERR_MISSING_STOP; 172 | 173 | for (unstuff_next = 0, j = 0; j < rxh->data_len && i < len - 2; ++i) { 174 | if (unstuff_next) { 175 | data[j++] = sensirion_shdlc_unstuff_byte(rx_frame[i]); 176 | unstuff_next = 0; 177 | } else { 178 | unstuff_next = sensirion_shdlc_check_unstuff(rx_frame[i]); 179 | if (!unstuff_next) 180 | data[j++] = rx_frame[i]; 181 | } 182 | } 183 | 184 | if (unstuff_next) 185 | return SENSIRION_SHDLC_ERR_ENCODING_ERROR; 186 | 187 | if (j < rxh->data_len) 188 | return SENSIRION_SHDLC_ERR_ENCODING_ERROR; 189 | 190 | crc = rx_frame[i++]; 191 | if (sensirion_shdlc_check_unstuff(crc)) { 192 | crc = sensirion_shdlc_unstuff_byte(rx_frame[i++]); 193 | if (i >= len) 194 | return SENSIRION_SHDLC_ERR_MISSING_STOP; 195 | } 196 | if (sensirion_shdlc_crc(rxh->addr + rxh->cmd + rxh->state, rxh->data_len, 197 | data) != crc) 198 | return SENSIRION_SHDLC_ERR_CRC_MISMATCH; 199 | 200 | if (i >= len || rx_frame[i] != SHDLC_STOP) 201 | return SENSIRION_SHDLC_ERR_MISSING_STOP; 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /SEN5x_I2C_switch_measurement_mode/SEN5x_I2C_switch_measurement_mode.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN5x 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | // wait for serial connection from PC 40 | // comment the following line if you'd like the output 41 | // without waiting for the interface being ready 42 | while(!Serial); 43 | 44 | // wait until sensors startup, > 50 ms according to datasheet 45 | delay(50); 46 | 47 | // Initiate I2C communication 48 | Wire.begin(); 49 | 50 | // Send command to start measurement (0x0021) 51 | Wire.beginTransmission(SEN55_ADDRESS); 52 | Wire.write(0x00); 53 | Wire.write(0x21); 54 | Wire.endTransmission(); 55 | 56 | // Wait until sensor is ready, fan is initialized 57 | delay(1000); 58 | 59 | } 60 | 61 | void loop() { 62 | 63 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 64 | int16_t voc, nox, humidity, temperature; 65 | uint8_t data[24], counter, cycles; 66 | 67 | // Output format 68 | Serial.println("PM measurement mode"); 69 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 70 | 71 | // Send command to read measurements with PM included (0x0021) 72 | Wire.beginTransmission(SEN55_ADDRESS); 73 | Wire.write(0x00); 74 | Wire.write(0x21); 75 | Wire.endTransmission(); 76 | 77 | // Wait until sensor is ready, fan is initialized 78 | delay(2000); 79 | 80 | // Initialize cycles 81 | cycles = 0; 82 | 83 | // Read full data for 120s, 60 cycles 84 | while (cycles<60){ 85 | 86 | // Send read measurement data command (0x03C4) 87 | Wire.beginTransmission(SEN55_ADDRESS); 88 | Wire.write(0x03); 89 | Wire.write(0xC4); 90 | Wire.endTransmission(); 91 | 92 | // Wait 20 ms to allow the sensor to fill the internal buffer 93 | delay(20); 94 | 95 | // Read measurement data from SEN55 96 | Wire.requestFrom(SEN55_ADDRESS, 24); 97 | counter = 0; 98 | while (Wire.available()) { 99 | data[counter++] = Wire.read(); 100 | } 101 | 102 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 103 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 104 | // humidity is a signed int and scaled by 100 and need to be divided by 100 105 | // temperature is a signed int and scaled by 200 and need to be divided by 200 106 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 107 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 108 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 109 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 110 | humidity = (uint16_t)data[12] << 8 | data[13]; 111 | temperature = (uint16_t)data[15] << 8 | data[16]; 112 | voc = (uint16_t)data[18] << 8 | data[19]; 113 | nox = (uint16_t)data[21] << 8 | data[22]; 114 | 115 | Serial.print(String(float(pm1p0) / 10)); 116 | Serial.print("\t"); 117 | Serial.print(String(float(pm2p5) / 10)); 118 | Serial.print("\t"); 119 | Serial.print(String(float(pm4p0) / 10)); 120 | Serial.print("\t"); 121 | Serial.print(String(float(pm10p0) / 10)); 122 | Serial.print("\t"); 123 | Serial.print(String(float(voc) / 10)); 124 | Serial.print("\t\t"); 125 | Serial.print(String(float(nox) / 10)); 126 | Serial.print("\t\t"); 127 | Serial.print(String(float(humidity) / 100)); 128 | Serial.print("\t"); 129 | Serial.print(String(float(temperature) / 200)); 130 | Serial.println(); 131 | 132 | // Wait 2s for next measurement 133 | delay(2000); 134 | cycles++; 135 | } 136 | 137 | 138 | // Send command to switch to gas only measurement mode (0x0037) 139 | Wire.beginTransmission(SEN55_ADDRESS); 140 | Wire.write(0x00); 141 | Wire.write(0x37); 142 | Wire.endTransmission(); 143 | delay(2000); 144 | 145 | // Output format 146 | Serial.println("Gas only measurement mode"); 147 | Serial.println("VOC_Index\tNOx_Index\tRH\tT"); 148 | 149 | cycles = 0; 150 | 151 | // Read gas only measurement data for 120s, 60 cycles 152 | while (cycles<60){ 153 | 154 | // Send read measurement data command (0x03C4) 155 | Wire.beginTransmission(SEN55_ADDRESS); 156 | Wire.write(0x03); 157 | Wire.write(0xC4); 158 | Wire.endTransmission(); 159 | 160 | // Wait 20 ms to allow the sensor to fill the internal buffer 161 | delay(20); 162 | 163 | // Read measurement data SEN55, after two bytes a CRC follows 164 | Wire.requestFrom(SEN55_ADDRESS, 24); 165 | counter = 0; 166 | while (Wire.available()) { 167 | data[counter++] = Wire.read(); 168 | } 169 | 170 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 171 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 172 | // humidity is a signed int and scaled by 100 and need to be divided by 100 173 | // temperature is a signed int and scaled by 200 and need to be divided by 200 174 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 175 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 176 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 177 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 178 | humidity = (uint16_t)data[12] << 8 | data[13]; 179 | temperature = (uint16_t)data[15] << 8 | data[16]; 180 | voc = (uint16_t)data[18] << 8 | data[19]; 181 | nox = (uint16_t)data[21] << 8 | data[22]; 182 | 183 | Serial.print(String(float(voc) / 10)); 184 | Serial.print("\t\t"); 185 | Serial.print(String(float(nox) / 10)); 186 | Serial.print("\t\t"); 187 | Serial.print(String(float(humidity) / 100)); 188 | Serial.print("\t"); 189 | Serial.print(String(float(temperature) / 200)); 190 | Serial.println(); 191 | 192 | // Wait 2 s for next measurement 193 | delay(2000); 194 | cycles++; 195 | } 196 | 197 | } 198 | 199 | // Calculate CRC according to datasheet 200 | uint8_t CalcCrc(uint8_t data[2]) { 201 | uint8_t crc = 0xFF; 202 | for(int i = 0; i < 2; i++) { 203 | crc ^= data[i]; 204 | for(uint8_t bit = 8; bit > 0; --bit) { 205 | if(crc & 0x80) { 206 | crc = (crc << 1) ^ 0x31u; 207 | } else { 208 | crc = (crc << 1); 209 | } 210 | } 211 | } 212 | return crc; 213 | } 214 | -------------------------------------------------------------------------------- /SEN44_UART_minimal_example/sen44.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "sen44.h" 33 | #include "sensirion_shdlc.h" 34 | #include "sps_git_version.h" 35 | 36 | #define SEN44_ADDR 0x00 37 | #define SEN44_CMD_START_MEASUREMENT 0x00 38 | #define SEN44_CMD_STOP_MEASUREMENT 0x01 39 | #define SEN44_SUBCMD_MEASUREMENT_START_PMSGP { 0x02 } 40 | #define SEN44_CMD_READ_MEASUREMENT 0x03 41 | 42 | #define SEN44_SUBCMD_READ_MEASUREMENT_DEFAULT { 0x07 } 43 | #define SEN44_READ_MEASUREMENT_MODE_DEFAULT 0x00 44 | #define SEN44_READ_MEASUREMENT_SIZE_DEFAULT 7 45 | 46 | #define SEN44_CMD_READ_FAN_SPEED 0x40 47 | #define SEN44_CMD_FAN_CLEAN_INTV 0x80 48 | #define SEN44_CMD_FAN_CLEAN_INTV_LEN 5 49 | #define SEN44_SUBCMD_READ_FAN_CLEAN_INTV 0x00 50 | #define SEN44_CMD_START_FAN_CLEANING 0x56 51 | #define SEN44_CMD_DEV_INFO 0xd0 52 | #define SEN44_CMD_DEV_INFO_SUBCMD_GET_SERIAL { 0x03 } 53 | #define SEN44_CMD_RESET 0xd3 54 | #define SEN44_ERR_STATE(state) (SEN44_ERR_STATE_MASK | (state)) 55 | 56 | const char *sen44_get_driver_version() { 57 | return SPS_DRV_VERSION_STR; 58 | } 59 | 60 | int16_t sen44_probe() { 61 | char serial[SEN44_MAX_SERIAL_LEN]; 62 | int16_t ret = sen44_get_serial(serial); 63 | 64 | return ret; 65 | } 66 | 67 | int16_t sen44_get_serial(char *serial) { 68 | struct sensirion_shdlc_rx_header header; 69 | uint8_t param_buf[] = SEN44_CMD_DEV_INFO_SUBCMD_GET_SERIAL; 70 | int16_t ret; 71 | 72 | ret = sensirion_shdlc_xcv(SEN44_ADDR, SEN44_CMD_DEV_INFO, sizeof(param_buf), 73 | param_buf, SEN44_MAX_SERIAL_LEN, &header, 74 | (uint8_t *)serial); 75 | if (ret < 0) 76 | return ret; 77 | 78 | if (header.state) 79 | return SEN44_ERR_STATE(header.state); 80 | 81 | return 0; 82 | } 83 | 84 | int16_t sen44_start_measurement() { 85 | struct sensirion_shdlc_rx_header header; 86 | uint8_t param_buf[] = SEN44_SUBCMD_MEASUREMENT_START_PMSGP; 87 | 88 | return sensirion_shdlc_xcv(SEN44_ADDR, SEN44_CMD_START_MEASUREMENT, 89 | sizeof(param_buf), param_buf, 0, &header, NULL); 90 | } 91 | 92 | int16_t sen44_stop_measurement() { 93 | struct sensirion_shdlc_rx_header header; 94 | 95 | return sensirion_shdlc_xcv(SEN44_ADDR, SEN44_CMD_STOP_MEASUREMENT, 0, NULL, 96 | 0, &header, NULL); 97 | } 98 | 99 | int16_t sen44_read_measurement(struct sen44_measurement *measurement) { 100 | struct sensirion_shdlc_rx_header header; 101 | int i; 102 | int16_t ret; 103 | uint16_t idx; 104 | int8_t datasize; 105 | 106 | datasize = SEN44_READ_MEASUREMENT_SIZE_DEFAULT; 107 | 108 | union { 109 | uint16_t u16_value; 110 | int16_t s16_value; 111 | } val, data[datasize]; 112 | 113 | uint8_t param_buf[] = SEN44_SUBCMD_READ_MEASUREMENT_DEFAULT; 114 | 115 | ret = sensirion_shdlc_xcv(SEN44_ADDR, SEN44_CMD_READ_MEASUREMENT, sizeof(param_buf), param_buf, sizeof(data), &header, (uint8_t *)data); 116 | if (ret) 117 | return ret; 118 | 119 | if (header.data_len != sizeof(data)) 120 | return SEN44_ERR_NOT_ENOUGH_DATA; 121 | 122 | idx = 0; 123 | val.u16_value = be16_to_cpu(data[idx].u16_value); 124 | measurement->mc_1p0 = val.u16_value; 125 | ++idx; 126 | val.u16_value = be16_to_cpu(data[idx].u16_value); 127 | measurement->mc_2p5 = val.u16_value; 128 | ++idx; 129 | val.u16_value = be16_to_cpu(data[idx].u16_value); 130 | measurement->mc_4p0 = val.u16_value; 131 | ++idx; 132 | val.u16_value = be16_to_cpu(data[idx].u16_value); 133 | measurement->mc_10p0 = val.u16_value; 134 | ++idx; 135 | val.s16_value = be16_to_cpu(data[idx].u16_value); 136 | measurement->voc_index = val.s16_value; 137 | ++idx; 138 | val.s16_value = be16_to_cpu(data[idx].u16_value); 139 | measurement->ambient_humidity = val.s16_value; 140 | ++idx; 141 | val.s16_value = be16_to_cpu(data[idx].u16_value); 142 | measurement->ambient_temperature = val.s16_value; 143 | ++idx; 144 | 145 | if (header.state) 146 | return SEN44_ERR_STATE(header.state); 147 | 148 | return 0; 149 | } 150 | 151 | int16_t sen44_get_fan_auto_cleaning_interval(uint32_t *interval_seconds) { 152 | struct sensirion_shdlc_rx_header header; 153 | uint8_t tx_data[] = {SEN44_SUBCMD_READ_FAN_CLEAN_INTV}; 154 | int16_t ret; 155 | 156 | ret = sensirion_shdlc_xcv( 157 | SEN44_ADDR, SEN44_CMD_FAN_CLEAN_INTV, sizeof(tx_data), tx_data, 158 | sizeof(*interval_seconds), &header, (uint8_t *)interval_seconds); 159 | if (ret < 0) 160 | return ret; 161 | 162 | *interval_seconds = be32_to_cpu(*interval_seconds); 163 | 164 | if (header.state) 165 | return SEN44_ERR_STATE(header.state); 166 | 167 | return 0; 168 | } 169 | 170 | int16_t sen44_set_fan_auto_cleaning_interval(uint32_t interval_seconds) { 171 | struct sensirion_shdlc_rx_header header; 172 | uint8_t ix; 173 | uint8_t cleaning_command[SEN44_CMD_FAN_CLEAN_INTV_LEN]; 174 | uint32_t value = be32_to_cpu(interval_seconds); 175 | 176 | cleaning_command[0] = SEN44_SUBCMD_READ_FAN_CLEAN_INTV; 177 | for (ix = 0; ix < sizeof(uint32_t); ix++) 178 | cleaning_command[ix + 1] = (uint8_t)(value >> (8 * ix)); 179 | return sensirion_shdlc_xcv( 180 | SEN44_ADDR, SEN44_CMD_FAN_CLEAN_INTV, sizeof(cleaning_command), 181 | (const uint8_t *)cleaning_command, sizeof(interval_seconds), &header, 182 | (uint8_t *)&interval_seconds); 183 | } 184 | 185 | int16_t sen44_get_fan_auto_cleaning_interval_days(uint8_t *interval_days) { 186 | int16_t ret; 187 | uint32_t interval_seconds; 188 | 189 | ret = sen44_get_fan_auto_cleaning_interval(&interval_seconds); 190 | if (ret < 0) 191 | return ret; 192 | 193 | *interval_days = interval_seconds / (24 * 60 * 60); 194 | return ret; 195 | } 196 | 197 | int16_t sen44_set_fan_auto_cleaning_interval_days(uint8_t interval_days) { 198 | return sen44_set_fan_auto_cleaning_interval((uint32_t)interval_days * 24 * 199 | 60 * 60); 200 | } 201 | 202 | int16_t sen44_start_manual_fan_cleaning() { 203 | struct sensirion_shdlc_rx_header header; 204 | 205 | return sensirion_shdlc_xcv(SEN44_ADDR, SEN44_CMD_START_FAN_CLEANING, 0, 206 | NULL, 0, &header, NULL); 207 | } 208 | 209 | int16_t sen44_reset() { 210 | return sensirion_shdlc_tx(SEN44_ADDR, SEN44_CMD_RESET, 0, NULL); 211 | } 212 | -------------------------------------------------------------------------------- /SEN5x_I2C_config_STAR_example/SEN5x_I2C_config_STAR_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // STAR: Sensirion Temperature Acceleration Routine 35 | 36 | // SEN5x address 37 | const int16_t SEN55_ADDRESS = 0x69; 38 | 39 | void setup() { 40 | int16_t star_mode; 41 | uint8_t data[3], counter; 42 | 43 | Serial.begin(115200); 44 | // Wait for serial connection from PC 45 | // Comment the following line if you'd like the output 46 | // without waiting for the interface being ready 47 | while(!Serial); 48 | 49 | // Wait for sensor to startup before initiation of I2C communication (50 ms according to datasheet) 50 | delay(50); 51 | 52 | // Initiate I2C communication 53 | Wire.begin(); 54 | 55 | // Wait until sensor is ready, fan is initialized 56 | delay(1000); 57 | 58 | // Read acceleration mode from previous measurement 59 | // Send command to read RH/T acceleration mode (0x60F7) 60 | Wire.beginTransmission(SEN55_ADDRESS); 61 | Wire.write(0x60); 62 | Wire.write(0xF7); 63 | Wire.endTransmission(); 64 | 65 | // Wait 20 ms to allow the sensor to fill the internal buffer 66 | delay(20); 67 | 68 | // Read measurement data SEN55, after two bytes a CRC follows 69 | Wire.requestFrom(SEN55_ADDRESS, 3); 70 | counter = 0; 71 | while (Wire.available()) { 72 | data[counter++] = Wire.read(); 73 | } 74 | 75 | // Prepare buffer with t offset data 76 | // calculate CRC for each 2 bytes of data 77 | star_mode = (uint16_t)data[0] << 8 | data[1]; 78 | 79 | // Print value for previous acceleration mode 80 | Serial.println(); 81 | Serial.print("Preset STAR mode: "); 82 | Serial.print(String(float(star_mode)/1)); 83 | Serial.println(); 84 | 85 | // Setting new acceleration mode (0: low, 1:high, 2:medium) 86 | star_mode = 2; 87 | 88 | // Parsing new acceleration mode into SEN55 instructions 89 | data[0] = (star_mode & 0xff00) >> 8; 90 | data[1] = star_mode & 0x00ff; 91 | data[2] = CalcCrc(data); 92 | 93 | // Send new acceleration mode value to SEN5x (acceleration mode value will be held in RAM, which will be erased when powered off and on) 94 | Wire.beginTransmission(SEN55_ADDRESS); 95 | Wire.write(0x60); 96 | Wire.write(0xF7); 97 | Wire.write(data[0]); 98 | Wire.write(data[1]); 99 | Wire.write(data[2]); 100 | Wire.endTransmission(); 101 | 102 | // Wait 20 ms to allow the sensor to fill the internal buffer 103 | delay(20); 104 | 105 | // Read newly set acceleration mode 106 | Wire.beginTransmission(SEN55_ADDRESS); 107 | Wire.write(0x60); 108 | Wire.write(0xF7); 109 | Wire.endTransmission(); 110 | 111 | // Wait 20 ms to allow the sensor to fill the internal buffer 112 | delay(20); 113 | 114 | // Read accleration mode data of SEN55, after two bytes a CRC follows 115 | Wire.requestFrom(SEN55_ADDRESS, 3); 116 | counter = 0; 117 | while (Wire.available()) { 118 | data[counter++] = Wire.read(); 119 | } 120 | 121 | // Prepare buffer with T offset data 122 | // Calculate CRC for each 2 bytes of data 123 | star_mode = (uint16_t)data[0] << 8 | data[1]; 124 | 125 | // Print value for new acceleration mode 126 | Serial.println(); 127 | Serial.print("New STAR mode: "); 128 | Serial.print(String(float(star_mode)/1)); 129 | Serial.println(); 130 | 131 | // Wait until sensors is ready and fan is initialized 132 | delay(2000); 133 | 134 | // Send command to start measurement 135 | Wire.beginTransmission(SEN55_ADDRESS); 136 | Wire.write(0x00); 137 | Wire.write(0x21); 138 | Wire.endTransmission(); 139 | 140 | // Wait for 20 ms to allow the sensor to fill the internal buffer 141 | delay(20); 142 | 143 | // Output measurement value format 144 | Serial.println(); 145 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 146 | } 147 | 148 | void loop() { 149 | 150 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 151 | int16_t voc, nox, humidity, temperature; 152 | uint8_t data[24], counter; 153 | 154 | // Send read measurement data command; measurement data will be returned every second 155 | Wire.beginTransmission(SEN55_ADDRESS); 156 | Wire.write(0x03); 157 | Wire.write(0xC4); 158 | Wire.endTransmission(); 159 | 160 | // Wait 20 ms to allow the sensor to fill the internal buffer 161 | delay(20); 162 | 163 | // Read measurement data SEN55, after two bytes a CRC follows 164 | Wire.requestFrom(SEN55_ADDRESS, 24); 165 | counter = 0; 166 | while (Wire.available()) { 167 | data[counter++] = Wire.read(); 168 | } 169 | 170 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 171 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 172 | // humidity is a signed int and scaled by 100 and need to be divided by 100 173 | // temperature is a signed int and scaled by 200 and need to be divided by 200 174 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 175 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 176 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 177 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 178 | humidity = (uint16_t)data[12] << 8 | data[13]; 179 | temperature = (uint16_t)data[15] << 8 | data[16]; 180 | voc = (uint16_t)data[18] << 8 | data[19]; 181 | nox = (uint16_t)data[21] << 8 | data[22]; 182 | 183 | // Print measurement values 184 | Serial.print(String(float(pm1p0) / 10)); 185 | Serial.print("\t"); 186 | Serial.print(String(float(pm2p5) / 10)); 187 | Serial.print("\t"); 188 | Serial.print(String(float(pm4p0) / 10)); 189 | Serial.print("\t"); 190 | Serial.print(String(float(pm10p0) / 10)); 191 | Serial.print("\t"); 192 | Serial.print(String(float(voc) / 10)); 193 | Serial.print("\t\t"); 194 | Serial.print(String(float(nox) / 10)); 195 | Serial.print("\t\t"); 196 | Serial.print(String(float(humidity) / 100)); 197 | Serial.print("\t"); 198 | Serial.print(String(float(temperature) / 200)); 199 | Serial.println(); 200 | 201 | // Wait 1 s for next measurement 202 | delay(1000); 203 | } 204 | 205 | // Calculate CRC according to datasheet 206 | uint8_t CalcCrc(uint8_t data[2]) { 207 | uint8_t crc = 0xFF; 208 | for(int i = 0; i < 2; i++) { 209 | crc ^= data[i]; 210 | for(uint8_t bit = 8; bit > 0; --bit) { 211 | if(crc & 0x80) { 212 | crc = (crc << 1) ^ 0x31u; 213 | } else { 214 | crc = (crc << 1); 215 | } 216 | } 217 | } 218 | return crc; 219 | } 220 | -------------------------------------------------------------------------------- /SVM40_I2C_change_VOC_parameters_example/SVM40_I2C_change_VOC_parameters_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SVM40 35 | const int16_t SVM40_ADDRESS = 0x6A; 36 | 37 | void setup() { 38 | int16_t voc_offset,voc_learning, voc_gating, voc_initial; 39 | uint8_t data[12], counter; 40 | 41 | Serial.begin(115200); 42 | // wait for serial connection from PC 43 | // comment the following line if you'd like the output 44 | // without waiting for the interface being ready 45 | while(!Serial); 46 | 47 | // output format 48 | Serial.println("VOC_Index\tRH\tT"); 49 | 50 | // init I2C 51 | Wire.begin(); 52 | 53 | // wait until sensors startup, > 1 ms according to datasheet 54 | delay(100); 55 | 56 | // make sure sensor is stopped to change parameters 57 | Wire.beginTransmission(SVM40_ADDRESS); 58 | Wire.write(0x01); 59 | Wire.write(0x04); 60 | Wire.endTransmission(); 61 | 62 | // wait until sensor is ready 63 | delay(100); 64 | 65 | // read current VOC parameters from flash memory 66 | Wire.beginTransmission(SVM40_ADDRESS); 67 | Wire.write(0x60); 68 | Wire.write(0x83); 69 | Wire.endTransmission(); 70 | 71 | // wait 10 ms to allow the sensor to fill the internal buffer 72 | delay(10); 73 | 74 | // read offset data, after two bytes a CRC follows 75 | Wire.requestFrom(SVM40_ADDRESS, 12); 76 | counter = 0; 77 | while (Wire.available()) { 78 | data[counter++] = Wire.read(); 79 | } 80 | 81 | // values are unscaled 82 | // offset is arbitrary 83 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 84 | // learning time is in hours 85 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 86 | // gating time is in minutes 87 | voc_gating = (uint16_t)data[6] << 8 | data[7]; 88 | // standard initial is arbitrary 89 | voc_initial = (uint16_t)data[9] << 8 | data[10]; Serial.println(); 90 | 91 | Serial.println("default parameters (offset, learning, gating, initial): "); 92 | Serial.println(voc_offset); 93 | Serial.println(voc_learning); 94 | Serial.println(voc_gating); 95 | Serial.println(voc_initial); 96 | Serial.println(); 97 | 98 | // Set new algorithm parameters 99 | // Offset 200 instead of 100 100 | voc_offset = 200; 101 | // Learning 6 h instead of 12 h 102 | voc_learning = 12; 103 | // gating 60 min instead of 180 min 104 | voc_gating = 60; 105 | // initial same as default with 50 106 | voc_initial = 50; 107 | 108 | // prepare buffer with algorithm parameter data 109 | // calculate CRC for each 2 bytes of data 110 | data[0] = (voc_offset & 0xff00) >> 8; 111 | data[1] = voc_offset & 0x00ff; 112 | data[2] = CalcCrc(data); 113 | data[3] = (voc_learning & 0xff00) >> 8; 114 | data[4] = voc_learning & 0x00ff; 115 | data[5] = CalcCrc(data+3); 116 | data[6] = (voc_gating & 0xff00) >> 8; 117 | data[7] = voc_gating & 0x00ff; 118 | data[8] = CalcCrc(data+6); 119 | data[9] = (voc_initial & 0xff00) >> 8; 120 | data[10] = voc_initial & 0x00ff; 121 | data[11] = CalcCrc(data+9); 122 | 123 | // send new value for voc parameters to sensor (will be hold in RAM, not persistent) 124 | Wire.beginTransmission(SVM40_ADDRESS); 125 | Wire.write(0x60); 126 | Wire.write(0x83); 127 | Wire.write(data[0]); 128 | Wire.write(data[1]); 129 | Wire.write(data[2]); 130 | Wire.write(data[3]); 131 | Wire.write(data[4]); 132 | Wire.write(data[5]); 133 | Wire.write(data[6]); 134 | Wire.write(data[7]); 135 | Wire.write(data[8]); 136 | Wire.write(data[9]); 137 | Wire.write(data[10]); 138 | Wire.write(data[11]); 139 | Wire.endTransmission(); 140 | 141 | // wait 10 ms to allow the sensor to fill the internal buffer 142 | delay(10); 143 | 144 | // now send command to save parameter in the flash (NVM memory) of the sensor to have persistence 145 | Wire.beginTransmission(SVM40_ADDRESS); 146 | Wire.write(0x60); 147 | Wire.write(0x02); 148 | Wire.endTransmission(); 149 | 150 | // wait 30 ms to allow the sensor to write the data into the flash 151 | delay(30); 152 | 153 | // repeat read data to make sure that the values are applied correctly 154 | Wire.beginTransmission(SVM40_ADDRESS); 155 | Wire.write(0x60); 156 | Wire.write(0x83); 157 | Wire.endTransmission(); 158 | 159 | // wait 10 ms to allow the sensor to fill the internal buffer 160 | delay(10); 161 | 162 | // read offset data, after two bytes a CRC follows 163 | Wire.requestFrom(SVM40_ADDRESS, 12); 164 | counter = 0; 165 | while (Wire.available()) { 166 | data[counter++] = Wire.read(); 167 | } 168 | // values are unscaled 169 | // offset is arbitrary 170 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 171 | // learning time is in hours 172 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 173 | // gating time is in minutes 174 | voc_gating = (uint16_t)data[6] << 8 | data[7]; 175 | // standard initial is arbitrary 176 | voc_initial = (uint16_t)data[9] << 8 | data[10]; Serial.println(); 177 | 178 | Serial.println("new parameters (offset, learning, gating, initial): "); 179 | Serial.println(voc_offset); 180 | Serial.println(voc_learning); 181 | Serial.println(voc_gating); 182 | Serial.println(voc_initial); 183 | Serial.println(); 184 | 185 | // wait 10 ms to allow the sensor to be ready again 186 | delay(10); 187 | 188 | // start up sensor, sensor will go to continous measurement mode 189 | // each second there will be new measurement values 190 | Wire.beginTransmission(SVM40_ADDRESS); 191 | Wire.write(0x00); 192 | Wire.write(0x10); 193 | Wire.endTransmission(); 194 | 195 | // wait until sensors is ready, fan is initialized 196 | delay(2000); 197 | } 198 | 199 | void loop() { 200 | int16_t voc, humidity, temperature; 201 | uint8_t data[9], counter; 202 | 203 | // read measurement data 204 | Wire.beginTransmission(SVM40_ADDRESS); 205 | Wire.write(0x03); 206 | Wire.write(0xA6); 207 | Wire.endTransmission(); 208 | 209 | // wait 10 ms to allow the sensor to fill the internal buffer 210 | delay(10); 211 | 212 | // read measurement data sen44, after two bytes a CRC follows 213 | Wire.requestFrom(SVM40_ADDRESS, 9); 214 | counter = 0; 215 | while (Wire.available()) { 216 | data[counter++] = Wire.read(); 217 | } 218 | 219 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 220 | // VOC raw value is an uint16_t and has no scaling 221 | // humidity is a signed int and scaled by 100 and need to be divided by 100 222 | // temperature is a signed int and scaled by 200 and need to be divided by 200 223 | voc = (uint16_t)data[0] << 8 | data[1]; 224 | humidity = (uint16_t)data[3] << 8 | data[4]; 225 | temperature = (uint16_t)data[6] << 8 | data[7]; 226 | 227 | Serial.print(String(float(voc) / 10)); 228 | Serial.print("\t"); 229 | Serial.print(String(float(humidity) / 100)); 230 | Serial.print("\t"); 231 | Serial.print(String(float(temperature) / 200)); 232 | Serial.println(); 233 | 234 | // wait 1 s for next measurement 235 | delay(1000); 236 | } 237 | 238 | // calculate CRC according to datasheet 239 | uint8_t CalcCrc(uint8_t data[2]) { 240 | uint8_t crc = 0xFF; 241 | for(int i = 0; i < 2; i++) { 242 | crc ^= data[i]; 243 | for(uint8_t bit = 8; bit > 0; --bit) { 244 | if(crc & 0x80) { 245 | crc = (crc << 1) ^ 0x31u; 246 | } else { 247 | crc = (crc << 1); 248 | } 249 | } 250 | } 251 | return crc; 252 | } 253 | -------------------------------------------------------------------------------- /SGP40_SHTC3_I2C_voc_algo_minimal_example/sensirion_voc_algorithm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef VOCALGORITHM_H_ 33 | #define VOCALGORITHM_H_ 34 | 35 | #include "sensirion_arch_config.h" 36 | 37 | /* The fixed point arithmetic parts of this code were originally created by 38 | * https://github.com/PetteriAimonen/libfixmath 39 | */ 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | typedef int32_t fix16_t; 46 | 47 | #define F16(x) \ 48 | ((fix16_t)(((x) >= 0) ? ((x)*65536.0 + 0.5) : ((x)*65536.0 - 0.5))) 49 | 50 | #define VocAlgorithm_SAMPLING_INTERVAL (1.) 51 | #define VocAlgorithm_INITIAL_BLACKOUT (45.) 52 | #define VocAlgorithm_VOC_INDEX_GAIN (230.) 53 | #define VocAlgorithm_SRAW_STD_INITIAL (50.) 54 | #define VocAlgorithm_SRAW_STD_BONUS (220.) 55 | #define VocAlgorithm_TAU_MEAN_VARIANCE_HOURS (12.) 56 | #define VocAlgorithm_TAU_INITIAL_MEAN (20.) 57 | #define VocAlgorithm_INIT_DURATION_MEAN ((3600. * 0.75)) 58 | #define VocAlgorithm_INIT_TRANSITION_MEAN (0.01) 59 | #define VocAlgorithm_TAU_INITIAL_VARIANCE (2500.) 60 | #define VocAlgorithm_INIT_DURATION_VARIANCE ((3600. * 1.45)) 61 | #define VocAlgorithm_INIT_TRANSITION_VARIANCE (0.01) 62 | #define VocAlgorithm_GATING_THRESHOLD (340.) 63 | #define VocAlgorithm_GATING_THRESHOLD_INITIAL (510.) 64 | #define VocAlgorithm_GATING_THRESHOLD_TRANSITION (0.09) 65 | #define VocAlgorithm_GATING_MAX_DURATION_MINUTES ((60. * 3.)) 66 | #define VocAlgorithm_GATING_MAX_RATIO (0.3) 67 | #define VocAlgorithm_SIGMOID_L (500.) 68 | #define VocAlgorithm_SIGMOID_K (-0.0065) 69 | #define VocAlgorithm_SIGMOID_X0 (213.) 70 | #define VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT (100.) 71 | #define VocAlgorithm_LP_TAU_FAST (20.0) 72 | #define VocAlgorithm_LP_TAU_SLOW (500.0) 73 | #define VocAlgorithm_LP_ALPHA (-0.2) 74 | #define VocAlgorithm_PERSISTENCE_UPTIME_GAMMA ((3. * 3600.)) 75 | #define VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING (64.) 76 | #define VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX (32767.) 77 | 78 | /** 79 | * Struct to hold all the states of the VOC algorithm. 80 | */ 81 | typedef struct { 82 | fix16_t mVoc_Index_Offset; 83 | fix16_t mTau_Mean_Variance_Hours; 84 | fix16_t mGating_Max_Duration_Minutes; 85 | fix16_t mSraw_Std_Initial; 86 | fix16_t mUptime; 87 | fix16_t mSraw; 88 | fix16_t mVoc_Index; 89 | fix16_t m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes; 90 | bool m_Mean_Variance_Estimator___Initialized; 91 | fix16_t m_Mean_Variance_Estimator___Mean; 92 | fix16_t m_Mean_Variance_Estimator___Sraw_Offset; 93 | fix16_t m_Mean_Variance_Estimator___Std; 94 | fix16_t m_Mean_Variance_Estimator___Gamma; 95 | fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Mean; 96 | fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Variance; 97 | fix16_t m_Mean_Variance_Estimator__Gamma_Mean; 98 | fix16_t m_Mean_Variance_Estimator__Gamma_Variance; 99 | fix16_t m_Mean_Variance_Estimator___Uptime_Gamma; 100 | fix16_t m_Mean_Variance_Estimator___Uptime_Gating; 101 | fix16_t m_Mean_Variance_Estimator___Gating_Duration_Minutes; 102 | fix16_t m_Mean_Variance_Estimator___Sigmoid__L; 103 | fix16_t m_Mean_Variance_Estimator___Sigmoid__K; 104 | fix16_t m_Mean_Variance_Estimator___Sigmoid__X0; 105 | fix16_t m_Mox_Model__Sraw_Std; 106 | fix16_t m_Mox_Model__Sraw_Mean; 107 | fix16_t m_Sigmoid_Scaled__Offset; 108 | fix16_t m_Adaptive_Lowpass__A1; 109 | fix16_t m_Adaptive_Lowpass__A2; 110 | bool m_Adaptive_Lowpass___Initialized; 111 | fix16_t m_Adaptive_Lowpass___X1; 112 | fix16_t m_Adaptive_Lowpass___X2; 113 | fix16_t m_Adaptive_Lowpass___X3; 114 | } VocAlgorithmParams; 115 | 116 | /** 117 | * Initialize the VOC algorithm parameters. Call this once at the beginning or 118 | * whenever the sensor stopped measurements. 119 | * @param params Pointer to the VocAlgorithmParams struct 120 | */ 121 | void VocAlgorithm_init(VocAlgorithmParams* params); 122 | 123 | /** 124 | * Get current algorithm states. Retrieved values can be used in 125 | * VocAlgorithm_set_states() to resume operation after a short interruption, 126 | * skipping initial learning phase. This feature can only be used after at least 127 | * 3 hours of continuous operation. 128 | * @param params Pointer to the VocAlgorithmParams struct 129 | * @param state0 State0 to be stored 130 | * @param state1 State1 to be stored 131 | */ 132 | void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0, 133 | int32_t* state1); 134 | 135 | /** 136 | * Set previously retrieved algorithm states to resume operation after a short 137 | * interruption, skipping initial learning phase. This feature should not be 138 | * used after inerruptions of more than 10 minutes. Call this once after 139 | * VocAlgorithm_init() and the optional VocAlgorithm_set_tuning_parameters(), if 140 | * desired. Otherwise, the algorithm will start with initial learning phase. 141 | * @param params Pointer to the VocAlgorithmParams struct 142 | * @param state0 State0 to be restored 143 | * @param state1 State1 to be restored 144 | */ 145 | void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0, 146 | int32_t state1); 147 | 148 | /** 149 | * Set parameters to customize the VOC algorithm. Call this once after 150 | * VocAlgorithm_init(), if desired. Otherwise, the default values will be used. 151 | * 152 | * @param params Pointer to the VocAlgorithmParams struct 153 | * @param voc_index_offset VOC index representing typical (average) 154 | * conditions. Range 1..250, default 100 155 | * @param learning_time_hours Time constant of long-term estimator. 156 | * Past events will be forgotten after about 157 | * twice the learning time. 158 | * Range 1..72 [hours], default 12 [hours] 159 | * @param gating_max_duration_minutes Maximum duration of gating (freeze of 160 | * estimator during high VOC index signal). 161 | * 0 (no gating) or range 1..720 [minutes], 162 | * default 180 [minutes] 163 | * @param std_initial Initial estimate for standard deviation. 164 | * Lower value boosts events during initial 165 | * learning period, but may result in larger 166 | * device-to-device variations. 167 | * Range 10..500, default 50 168 | */ 169 | void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params, 170 | int32_t voc_index_offset, 171 | int32_t learning_time_hours, 172 | int32_t gating_max_duration_minutes, 173 | int32_t std_initial); 174 | 175 | /** 176 | * Calculate the VOC index value from the raw sensor value. 177 | * 178 | * @param params Pointer to the VocAlgorithmParams struct 179 | * @param sraw Raw value from the SGP40 sensor 180 | * @param voc_index Calculated VOC index value from the raw sensor value. Zero 181 | * during initial blackout period and 1..500 afterwards 182 | */ 183 | void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw, 184 | int32_t* voc_index); 185 | 186 | #ifdef __cplusplus 187 | } 188 | #endif 189 | 190 | #endif /* VOCALGORITHM_H_ */ 191 | -------------------------------------------------------------------------------- /SEN5x_I2C_config_coldstart_example/SEN5x_I2C_config_coldstart_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN5x 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t t_offset, t_slope, t_time; 39 | uint8_t data[9], counter; 40 | 41 | Serial.begin(115200); 42 | 43 | // Wait for serial connection from PC 44 | // Comment the following line if you'd like the output 45 | // without waiting for the interface being ready 46 | while(!Serial); 47 | 48 | // Wait until sensors startup, > 50 ms according to datasheet 49 | delay(50); 50 | 51 | // Initiate I2C communication 52 | Wire.begin(); 53 | 54 | // Send command to read/write temperature compensation parameters (0x60B2) 55 | Wire.beginTransmission(SEN55_ADDRESS); 56 | Wire.write(0x60); 57 | Wire.write(0xB2); 58 | Wire.endTransmission(); 59 | 60 | // Wait for 20 ms to allow the sensor to fill the internal buffer 61 | delay(20); 62 | 63 | // Read preset temperature compensation parameters from SEN55 64 | Wire.requestFrom(SEN55_ADDRESS, 9); 65 | counter = 0; 66 | while (Wire.available()) { 67 | data[counter++] = Wire.read(); 68 | } 69 | 70 | // Processing preset t offset, t slope and t time data respectively 71 | // CRC byte (every 3rd byte) is excluded from processing 72 | t_offset = (uint16_t)data[0] << 8 | data[1]; 73 | t_slope = (uint16_t)data[3] << 8 | data[4]; 74 | t_time = (uint16_t)data[6] << 8 | data[7]; 75 | 76 | // Print t values (divided by scale factor) 77 | Serial.println(); 78 | Serial.print("Preset T Offset: "); 79 | Serial.print(String(float(t_offset)/200)); 80 | Serial.println(); 81 | 82 | Serial.print("Preset Slope: "); 83 | Serial.print(String(float(t_slope)/10000)); 84 | Serial.println(); 85 | 86 | Serial.print("Preset Time constant: "); 87 | Serial.print(String(float(t_time)/1)); 88 | Serial.println(); 89 | 90 | // Set new values for temperature compensation parameters 91 | // Set t offset to new value (e.g. -5 degC) and scale it accordingly by a factor of 200 92 | t_offset = -5 * 200; 93 | // Set t slope to new value (e.g. 0.01 degC/degC) and scale it accordingly by a factor of 10000 94 | t_slope = 0.01 * 10000; 95 | // Set t time constant to new value (in seconds) 96 | t_time = 600; 97 | 98 | // Parsing 99 | data[0] = (t_offset & 0xff00) >> 8; 100 | data[1] = t_offset & 0x00ff; 101 | data[2] = CalcCrc(data); 102 | data[3] = (t_slope & 0xff00) >> 8; 103 | data[4] = t_slope & 0x00ff; 104 | data[5] = CalcCrc(data+3); 105 | data[6] = (t_time & 0xff00) >> 8; 106 | data[7] = t_time & 0x00ff; 107 | data[8] = CalcCrc(data+6); 108 | 109 | // Send new temperature compensation parameters to sensor (will be held in RAM, not persistent) 110 | Wire.beginTransmission(SEN55_ADDRESS); 111 | Wire.write(0x60); 112 | Wire.write(0xB2); 113 | Wire.write(data[0]); 114 | Wire.write(data[1]); 115 | Wire.write(data[2]); 116 | Wire.write(data[3]); 117 | Wire.write(data[4]); 118 | Wire.write(data[5]); 119 | Wire.write(data[6]); 120 | Wire.write(data[7]); 121 | Wire.write(data[8]); 122 | Wire.endTransmission(); 123 | 124 | // Wait 20 ms to allow the sensor to fill the internal buffer 125 | delay(20); 126 | 127 | // Send command to read new temperature compensation parameters (for confirmation) 128 | Wire.beginTransmission(SEN55_ADDRESS); 129 | Wire.write(0x60); 130 | Wire.write(0xB2); 131 | Wire.endTransmission(); 132 | 133 | // Wait 20 ms to allow the sensor to fill the internal buffer 134 | delay(20); 135 | 136 | // Read temperature compensation parameters from SEN55 137 | Wire.requestFrom(SEN55_ADDRESS, 9); 138 | counter = 0; 139 | while (Wire.available()) { 140 | data[counter++] = Wire.read(); 141 | } 142 | 143 | // Parse data to make sure that new temperature compensation parameters are correct 144 | t_offset = (uint16_t)data[0] << 8 | data[1]; 145 | t_slope = (uint16_t)data[3] << 8 | data[4]; 146 | t_time = (uint16_t)data[6] << 8 | data[7]; 147 | 148 | // Print new temperature compensation parameters 149 | Serial.println(); 150 | Serial.print("New T Offset: "); 151 | Serial.print(String(float(t_offset)/200)); 152 | Serial.println(); 153 | 154 | Serial.print("New Slope: "); 155 | Serial.print(String(float(t_slope)/10000)); 156 | Serial.println(); 157 | 158 | Serial.print("New Time Constant: "); 159 | Serial.print(String(float(t_time)/1)); 160 | Serial.println(); 161 | 162 | // Send command to start measurement (0x0021) 163 | Wire.beginTransmission(SEN55_ADDRESS); 164 | Wire.write(0x00); 165 | Wire.write(0x21); 166 | Wire.endTransmission(); 167 | 168 | // Wait until sensors is ready, fan is initialized 169 | delay(2000); 170 | 171 | // Output measurement value format 172 | Serial.println(); 173 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 174 | } 175 | 176 | void loop() { 177 | 178 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 179 | int16_t voc, nox, humidity, temperature; 180 | uint8_t data[24], counter; 181 | 182 | // Send read measurement data command (0x03C4) 183 | Wire.beginTransmission(SEN55_ADDRESS); 184 | Wire.write(0x03); 185 | Wire.write(0xC4); 186 | Wire.endTransmission(); 187 | 188 | // Wait 20 ms to allow the sensor to fill the internal buffer 189 | delay(20); 190 | 191 | // Read measurement data from SEN55 192 | Wire.requestFrom(SEN55_ADDRESS, 24); 193 | counter = 0; 194 | while (Wire.available()) { 195 | data[counter++] = Wire.read(); 196 | } 197 | 198 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 199 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 200 | // humidity is a signed int and scaled by 100 and need to be divided by 100 201 | // temperature is a signed int and scaled by 200 and need to be divided by 200 202 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 203 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 204 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 205 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 206 | humidity = (uint16_t)data[12] << 8 | data[13]; 207 | temperature = (uint16_t)data[15] << 8 | data[16]; 208 | voc = (uint16_t)data[18] << 8 | data[19]; 209 | nox = (uint16_t)data[21] << 8 | data[22]; 210 | 211 | // Begin measurement output 212 | Serial.print(String(float(pm1p0) / 10)); 213 | Serial.print("\t"); 214 | Serial.print(String(float(pm2p5) / 10)); 215 | Serial.print("\t"); 216 | Serial.print(String(float(pm4p0) / 10)); 217 | Serial.print("\t"); 218 | Serial.print(String(float(pm10p0) / 10)); 219 | Serial.print("\t"); 220 | Serial.print(String(float(voc) / 10)); 221 | Serial.print("\t\t"); 222 | Serial.print(String(float(nox) / 10)); 223 | Serial.print("\t\t"); 224 | Serial.print(String(float(humidity) / 100)); 225 | Serial.print("\t"); 226 | Serial.print(String(float(temperature) / 200)); 227 | Serial.println(); 228 | 229 | // Wait 1 s for next measurement 230 | delay(1000); 231 | } 232 | 233 | // Calculate CRC according to datasheet 234 | uint8_t CalcCrc(uint8_t data[2]) { 235 | uint8_t crc = 0xFF; 236 | for(int i = 0; i < 2; i++) { 237 | crc ^= data[i]; 238 | for(uint8_t bit = 8; bit > 0; --bit) { 239 | if(crc & 0x80) { 240 | crc = (crc << 1) ^ 0x31u; 241 | } else { 242 | crc = (crc << 1); 243 | } 244 | } 245 | } 246 | return crc; 247 | } 248 | -------------------------------------------------------------------------------- /SEN44_I2C_change_VOC_parameters_example/SEN44_I2C_change_VOC_parameters_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN44 35 | const int16_t SEN44_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t voc_offset,voc_learning, voc_gating, voc_initial; 39 | uint8_t data[12], counter; 40 | 41 | Serial.begin(115200); 42 | // wait for serial connection from PC 43 | // comment the following line if you'd like the output 44 | // without waiting for the interface being ready 45 | while(!Serial); 46 | 47 | // output format 48 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tRH\tT"); 49 | 50 | // init I2C 51 | Wire.begin(); 52 | 53 | // wait until sensors startup, > 1 ms according to datasheet 54 | delay(100); 55 | 56 | // make sure sensor is stopped to change parameters 57 | Wire.beginTransmission(SEN44_ADDRESS); 58 | Wire.write(0x01); 59 | Wire.write(0x04); 60 | Wire.endTransmission(); 61 | 62 | // wait until sensor is ready 63 | delay(100); 64 | 65 | // read current VOC parameters from flash memory 66 | Wire.beginTransmission(SEN44_ADDRESS); 67 | Wire.write(0x60); 68 | Wire.write(0x83); 69 | Wire.endTransmission(); 70 | 71 | // wait 10 ms to allow the sensor to fill the internal buffer 72 | delay(10); 73 | 74 | // read offset data, after two bytes a CRC follows 75 | Wire.requestFrom(SEN44_ADDRESS, 12); 76 | counter = 0; 77 | while (Wire.available()) { 78 | data[counter++] = Wire.read(); 79 | } 80 | 81 | // values are unscaled 82 | // offset is arbitrary 83 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 84 | // learning time is in hours 85 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 86 | // gating time is in minutes 87 | voc_gating = (uint16_t)data[6] << 8 | data[7]; 88 | // standard initial is arbitrary 89 | voc_initial = (uint16_t)data[9] << 8 | data[10]; Serial.println(); 90 | 91 | Serial.println("default parameters (offset, learning, gating, initial): "); 92 | Serial.println(voc_offset); 93 | Serial.println(voc_learning); 94 | Serial.println(voc_gating); 95 | Serial.println(voc_initial); 96 | Serial.println(); 97 | 98 | // Set new algorithm parameters 99 | // Offset 200 instead of 100 100 | voc_offset = 200; 101 | // Learning 6 h instead of 12 h 102 | voc_learning = 12; 103 | // gating 60 min instead of 180 min 104 | voc_gating = 60; 105 | // initial same as default with 50 106 | voc_initial = 50; 107 | 108 | // prepare buffer with algorithm parameter data 109 | // calculate CRC for each 2 bytes of data 110 | data[0] = (voc_offset & 0xff00) >> 8; 111 | data[1] = voc_offset & 0x00ff; 112 | data[2] = CalcCrc(data); 113 | data[3] = (voc_learning & 0xff00) >> 8; 114 | data[4] = voc_learning & 0x00ff; 115 | data[5] = CalcCrc(data+3); 116 | data[6] = (voc_gating & 0xff00) >> 8; 117 | data[7] = voc_gating & 0x00ff; 118 | data[8] = CalcCrc(data+6); 119 | data[9] = (voc_initial & 0xff00) >> 8; 120 | data[10] = voc_initial & 0x00ff; 121 | data[11] = CalcCrc(data+9); 122 | 123 | // send new value for voc parameters to sensor (will be hold in RAM, not persistent) 124 | Wire.beginTransmission(SEN44_ADDRESS); 125 | Wire.write(0x60); 126 | Wire.write(0x83); 127 | Wire.write(data[0]); 128 | Wire.write(data[1]); 129 | Wire.write(data[2]); 130 | Wire.write(data[3]); 131 | Wire.write(data[4]); 132 | Wire.write(data[5]); 133 | Wire.write(data[6]); 134 | Wire.write(data[7]); 135 | Wire.write(data[8]); 136 | Wire.write(data[9]); 137 | Wire.write(data[10]); 138 | Wire.write(data[11]); 139 | Wire.endTransmission(); 140 | 141 | // wait 10 ms to allow the sensor to fill the internal buffer 142 | delay(10); 143 | 144 | // now send command to save parameter in the flash (NVM memory) of the sensor to have persistence 145 | Wire.beginTransmission(SEN44_ADDRESS); 146 | Wire.write(0x60); 147 | Wire.write(0x02); 148 | Wire.endTransmission(); 149 | 150 | // wait 30 ms to allow the sensor to write the data into the flash 151 | delay(30); 152 | 153 | // repeat read data to make sure that the values are applied correctly 154 | Wire.beginTransmission(SEN44_ADDRESS); 155 | Wire.write(0x60); 156 | Wire.write(0x83); 157 | Wire.endTransmission(); 158 | 159 | // wait 10 ms to allow the sensor to fill the internal buffer 160 | delay(10); 161 | 162 | // read offset data, after two bytes a CRC follows 163 | Wire.requestFrom(SEN44_ADDRESS, 12); 164 | counter = 0; 165 | while (Wire.available()) { 166 | data[counter++] = Wire.read(); 167 | } 168 | // values are unscaled 169 | // offset is arbitrary 170 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 171 | // learning time is in hours 172 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 173 | // gating time is in minutes 174 | voc_gating = (uint16_t)data[6] << 8 | data[7]; 175 | // standard initial is arbitrary 176 | voc_initial = (uint16_t)data[9] << 8 | data[10]; Serial.println(); 177 | 178 | Serial.println("new parameters (offset, learning, gating, initial): "); 179 | Serial.println(voc_offset); 180 | Serial.println(voc_learning); 181 | Serial.println(voc_gating); 182 | Serial.println(voc_initial); 183 | Serial.println(); 184 | 185 | // wait 10 ms to allow the sensor to be ready again 186 | delay(10); 187 | 188 | // start up sensor, sensor will go to continous measurement mode 189 | // each second there will be new measurement values 190 | Wire.beginTransmission(SEN44_ADDRESS); 191 | Wire.write(0x00); 192 | Wire.write(0x21); 193 | Wire.endTransmission(); 194 | 195 | // wait until sensors is ready, fan is initialized 196 | delay(2000); 197 | } 198 | 199 | void loop() { 200 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 201 | int16_t voc, humidity, temperature; 202 | uint8_t data[21], counter; 203 | 204 | // read measurement data 205 | Wire.beginTransmission(SEN44_ADDRESS); 206 | Wire.write(0x03); 207 | Wire.write(0x74); 208 | Wire.endTransmission(); 209 | 210 | // wait 10 ms to allow the sensor to fill the internal buffer 211 | delay(10); 212 | 213 | // read measurement data sen44, after two bytes a CRC follows 214 | Wire.requestFrom(SEN44_ADDRESS, 21); 215 | counter = 0; 216 | while (Wire.available()) { 217 | data[counter++] = Wire.read(); 218 | } 219 | 220 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 221 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 222 | // VOC raw value is an uint16_t and has no scaling 223 | // humidity is a signed int and scaled by 100 and need to be divided by 100 224 | // temperature is a signed int and scaled by 200 and need to be divided by 200 225 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 226 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 227 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 228 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 229 | voc = (uint16_t)data[12] << 8 | data[13]; 230 | humidity = (uint16_t)data[15] << 8 | data[16]; 231 | temperature = (uint16_t)data[18] << 8 | data[19]; 232 | 233 | Serial.print(pm1p0); 234 | Serial.print("\t"); 235 | Serial.print(pm2p5); 236 | Serial.print("\t"); 237 | Serial.print(pm4p0); 238 | Serial.print("\t"); 239 | Serial.print(pm10p0); 240 | Serial.print("\t"); 241 | Serial.print(String(float(voc) / 10)); 242 | Serial.print("\t"); 243 | Serial.print(String(float(humidity) / 100)); 244 | Serial.print("\t"); 245 | Serial.print(String(float(temperature) / 200)); 246 | Serial.println(); 247 | 248 | // wait 1 s for next measurement 249 | delay(1000); 250 | } 251 | 252 | // calculate CRC according to datasheet 253 | uint8_t CalcCrc(uint8_t data[2]) { 254 | uint8_t crc = 0xFF; 255 | for(int i = 0; i < 2; i++) { 256 | crc ^= data[i]; 257 | for(uint8_t bit = 8; bit > 0; --bit) { 258 | if(crc & 0x80) { 259 | crc = (crc << 1) ^ 0x31u; 260 | } else { 261 | crc = (crc << 1); 262 | } 263 | } 264 | } 265 | return crc; 266 | } 267 | -------------------------------------------------------------------------------- /SEN5x_I2C_change_NOx_parameters_example/SEN5x_I2C_change_NOx_parameters_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN5x 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t nox_offset, nox_learning, nox_learning_gain, nox_gating, nox_initial, nox_gain; 39 | uint8_t data[18], counter; 40 | 41 | Serial.begin(115200); 42 | 43 | // Wait for serial connection from PC 44 | // comment the following line if you'd like the output 45 | // without waiting for the interface being ready 46 | while(!Serial); 47 | 48 | // Initiate I2C communication 49 | Wire.begin(); 50 | 51 | // Wait until sensors startup, > 50 ms according to datasheet 52 | delay(50); 53 | 54 | // Send command to NOx algorithm tuning parameters (0x60E1) 55 | Wire.beginTransmission(SEN55_ADDRESS); 56 | Wire.write(0x60); 57 | Wire.write(0xE1); 58 | Wire.endTransmission(); 59 | 60 | // Wait 20 ms to allow the sensor to fill the internal buffer 61 | delay(20); 62 | 63 | // Read preset NOx algorithm tuning parameters from SEN55 64 | Wire.requestFrom(SEN55_ADDRESS, 18); 65 | counter = 0; 66 | while (Wire.available()) { 67 | data[counter++] = Wire.read(); 68 | } 69 | 70 | // Processing preset NOx algorithm tuning parameters 71 | // CRC byte (every 3rd byte) is excluded from processing 72 | // offset is arbitrary 73 | nox_offset = (uint16_t)data[0] << 8 | data[1]; 74 | // learning time is in hours 75 | nox_learning = (uint16_t)data[3] << 8 | data[4]; 76 | // learning time gain in hours 77 | nox_learning_gain = (uint16_t)data[6] << 8 | data[7]; 78 | // gating time is in minutes 79 | nox_gating = (uint16_t)data[9] << 8 | data[10]; 80 | // standard initial is arbitrary 81 | nox_initial = (uint16_t)data[12] << 8 | data[13]; 82 | // standard initial is arbitrary 83 | nox_gain = (uint16_t)data[15] << 8 | data[16]; 84 | 85 | Serial.println(); 86 | Serial.println("Default parameters: "); 87 | Serial.print("Index offset: "); 88 | Serial.println(float(nox_offset)); 89 | Serial.print("Learning time offset hours: "); 90 | Serial.println(float(nox_learning)); 91 | Serial.print("Learning time gain hours: "); 92 | Serial.println(nox_learning_gain); 93 | Serial.print("Gating max duration minutes: "); 94 | Serial.println(nox_gating); 95 | Serial.print("Std initial: "); 96 | Serial.println(nox_initial); 97 | Serial.print("Gain factor: "); 98 | Serial.println(nox_gain); 99 | Serial.println(); 100 | 101 | // Set new NOx algorithm tuning parameters 102 | // Offset 100 instead of 1 103 | nox_offset = 100; 104 | // Learning 6 h instead of 12 h 105 | nox_learning = 6; 106 | // gating 1500 min instead of 720 min 107 | nox_gating = 1500; 108 | // gain 250 instead of 230 109 | nox_gain = 250; 110 | 111 | // prepare buffer with algorithm parameter data 112 | // calculate CRC for each 2 bytes of data 113 | data[0] = (nox_offset & 0xff00) >> 8; 114 | data[1] = nox_offset & 0x00ff; 115 | data[2] = CalcCrc(data); 116 | data[3] = (nox_learning & 0xff00) >> 8; 117 | data[4] = nox_learning & 0x00ff; 118 | data[5] = CalcCrc(data+3); 119 | data[6] = (nox_learning_gain & 0xff00) >> 8; 120 | data[7] = nox_learning_gain & 0x00ff; 121 | data[8] = CalcCrc(data+6); 122 | data[9] = (nox_gating & 0xff00) >> 8; 123 | data[10] = nox_gating & 0x00ff; 124 | data[11] = CalcCrc(data+9); 125 | data[12] = (nox_initial & 0xff00) >> 8; 126 | data[13] = nox_initial & 0x00ff; 127 | data[14] = CalcCrc(data+12); 128 | data[15] = (nox_gain & 0xff00) >> 8; 129 | data[16] = nox_gain & 0x00ff; 130 | data[17] = CalcCrc(data+15); 131 | 132 | // Send new value for NOx parameters to sensor (will be hold in RAM, not persistent) 133 | Wire.beginTransmission(SEN55_ADDRESS); 134 | Wire.write(0x60); 135 | Wire.write(0xE1); 136 | Wire.write(data[0]); 137 | Wire.write(data[1]); 138 | Wire.write(data[2]); 139 | Wire.write(data[3]); 140 | Wire.write(data[4]); 141 | Wire.write(data[5]); 142 | Wire.write(data[6]); 143 | Wire.write(data[7]); 144 | Wire.write(data[8]); 145 | Wire.write(data[9]); 146 | Wire.write(data[10]); 147 | Wire.write(data[11]); 148 | Wire.write(data[12]); 149 | Wire.write(data[13]); 150 | Wire.write(data[14]); 151 | Wire.write(data[15]); 152 | Wire.write(data[16]); 153 | Wire.write(data[17]); 154 | Wire.endTransmission(); 155 | 156 | // Wait 20 ms to allow the sensor to fill the internal buffer 157 | delay(20); 158 | 159 | // Send command to read NOx algortihm tuning parameters (0x60E1) 160 | Wire.beginTransmission(SEN55_ADDRESS); 161 | Wire.write(0x60); 162 | Wire.write(0xE1); 163 | Wire.endTransmission(); 164 | 165 | // Wait 20 ms to allow the sensor to fill the internal buffer 166 | delay(20); 167 | 168 | // Read NOx algorithm tuning parameters from SEN55 169 | Wire.requestFrom(SEN55_ADDRESS, 18); 170 | counter = 0; 171 | while (Wire.available()) { 172 | data[counter++] = Wire.read(); 173 | } 174 | 175 | // Parse data to make sure that new NOx algorithm tuning parameters are correct 176 | // offset is arbitrary 177 | nox_offset = (uint16_t)data[0] << 8 | data[1]; 178 | // learning time is in hours 179 | nox_learning = (uint16_t)data[3] << 8 | data[4]; 180 | // learning time gain in hours 181 | nox_learning_gain = (uint16_t)data[6] << 8 | data[7]; 182 | // gating time is in minutes 183 | nox_gating = (uint16_t)data[9] << 8 | data[10]; 184 | // standard initial is arbitrary 185 | nox_initial = (uint16_t)data[12] << 8 | data[13]; 186 | // standard initial is arbitrary 187 | nox_gain = (uint16_t)data[15] << 8 | data[16]; 188 | 189 | // Print new NOx algorithm tuning parameters 190 | Serial.println("default parameters (offset, learning, learning gain, gating, initial, gain): "); 191 | Serial.println(nox_offset); 192 | Serial.println(nox_learning); 193 | Serial.println(nox_learning_gain); 194 | Serial.println(nox_gating); 195 | Serial.println(nox_initial); 196 | Serial.println(nox_gain); 197 | Serial.println(); 198 | 199 | // Send command to start measurement (0x0021) 200 | Wire.beginTransmission(SEN55_ADDRESS); 201 | Wire.write(0x00); 202 | Wire.write(0x21); 203 | Wire.endTransmission(); 204 | 205 | // Wait until command is executed, sensors are ready and fan is initialized 206 | delay(2000); 207 | 208 | // Output measurement value format 209 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 210 | } 211 | 212 | void loop() { 213 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 214 | int16_t voc, nox, humidity, temperature; 215 | uint8_t data[24], counter; 216 | 217 | // Send read measurement data command (0x03C4) 218 | Wire.beginTransmission(SEN55_ADDRESS); 219 | Wire.write(0x03); 220 | Wire.write(0xC4); 221 | Wire.endTransmission(); 222 | 223 | // Wait 20 ms for command execution 224 | delay(20); 225 | 226 | // Read measurement data SEN55, after two bytes a CRC follows 227 | Wire.requestFrom(SEN55_ADDRESS, 24); 228 | counter = 0; 229 | while (Wire.available()) { 230 | data[counter++] = Wire.read(); 231 | } 232 | 233 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 234 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 235 | // humidity is a signed int and scaled by 100 and need to be divided by 100 236 | // temperature is a signed int and scaled by 200 and need to be divided by 200 237 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 238 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 239 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 240 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 241 | humidity = (uint16_t)data[12] << 8 | data[13]; 242 | temperature = (uint16_t)data[15] << 8 | data[16]; 243 | voc = (uint16_t)data[18] << 8 | data[19]; 244 | nox = (uint16_t)data[21] << 8 | data[22]; 245 | 246 | Serial.print(String(float(pm1p0) / 10)); 247 | Serial.print("\t"); 248 | Serial.print(String(float(pm2p5) / 10)); 249 | Serial.print("\t"); 250 | Serial.print(String(float(pm4p0) / 10)); 251 | Serial.print("\t"); 252 | Serial.print(String(float(pm10p0) / 10)); 253 | Serial.print("\t"); 254 | Serial.print(String(float(voc) / 10)); 255 | Serial.print("\t\t"); 256 | Serial.print(String(float(nox) / 10)); 257 | Serial.print("\t\t"); 258 | Serial.print(String(float(humidity) / 100)); 259 | Serial.print("\t"); 260 | Serial.print(String(float(temperature) / 200)); 261 | Serial.println(); 262 | 263 | // Wait 1 s for next measurement 264 | delay(1000); 265 | } 266 | 267 | // Calculate CRC according to datasheet 268 | uint8_t CalcCrc(uint8_t data[2]) { 269 | uint8_t crc = 0xFF; 270 | for(int i = 0; i < 2; i++) { 271 | crc ^= data[i]; 272 | for(uint8_t bit = 8; bit > 0; --bit) { 273 | if(crc & 0x80) { 274 | crc = (crc << 1) ^ 0x31u; 275 | } else { 276 | crc = (crc << 1); 277 | } 278 | } 279 | } 280 | return crc; 281 | } 282 | -------------------------------------------------------------------------------- /SEN5x_I2C_change_VOC_parameters_example/SEN5x_I2C_change_VOC_parameters_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Sensirion AG 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Sensirion AG nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | // SEN5x 35 | const int16_t SEN55_ADDRESS = 0x69; 36 | 37 | void setup() { 38 | int16_t voc_offset, voc_learning, voc_learning_gain, voc_gating, voc_initial, voc_gain; 39 | uint8_t data[18], counter; 40 | 41 | Serial.begin(115200); 42 | 43 | // Wait for serial connection from PC 44 | // Comment the following line if you'd like the output 45 | // without waiting for the interface being ready 46 | while(!Serial); 47 | 48 | // Wait until sensors startup, > 50 ms according to datasheet 49 | delay(50); 50 | 51 | // Initiate I2C communication 52 | Wire.begin(); 53 | 54 | // Send command to read preset VOC algorithm tuning parameters (0x60D0) 55 | Wire.beginTransmission(SEN55_ADDRESS); 56 | Wire.write(0x60); 57 | Wire.write(0xD0); 58 | Wire.endTransmission(); 59 | 60 | // Wait 20 ms to allow for command execution 61 | delay(20); 62 | 63 | // Read preset VOC algorithm tuning parameters from SEN55 64 | Wire.requestFrom(SEN55_ADDRESS, 18); 65 | counter = 0; 66 | while (Wire.available()) { 67 | data[counter++] = Wire.read(); 68 | } 69 | 70 | // Processing preset VOC algorithm tuning parameters 71 | // CRC byte (every 3rd byte) is excluded from processing 72 | // offset is arbitrary 73 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 74 | // Learning time is in hours 75 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 76 | // Learning time gain in hours 77 | voc_learning_gain = (uint16_t)data[6] << 8 | data[7]; 78 | // Gating time is in minutes 79 | voc_gating = (uint16_t)data[9] << 8 | data[10]; 80 | // Standard initial is arbitrary 81 | voc_initial = (uint16_t)data[12] << 8 | data[13]; 82 | // Standard initial is arbitrary 83 | voc_gain = (uint16_t)data[15] << 8 | data[16]; 84 | 85 | Serial.println(); 86 | Serial.println("Default parameter values "); 87 | Serial.print("Index offset: "); 88 | Serial.println(voc_offset); 89 | Serial.print("Learning time offset hours: "); 90 | Serial.println(voc_learning); 91 | Serial.print("Learning time gain hours: "); 92 | Serial.println(voc_learning_gain); 93 | Serial.print("Gating max duration minutes: "); 94 | Serial.println(voc_gating); 95 | Serial.print("Std initial: "); 96 | Serial.println(voc_initial); 97 | Serial.print("Gain factor: "); 98 | Serial.println(voc_gain); 99 | Serial.println(); 100 | 101 | // Set new VOC algorithm tuning parameters 102 | // Offset 250 instead of 100 103 | voc_offset = 250; 104 | // Learning 6 h instead of 12 h 105 | voc_learning = 6; 106 | // Learning gain 6 h instead of 12 h 107 | voc_learning_gain = 6; 108 | // Gating 60 min instead of 180 min 109 | voc_gating = 60; 110 | // Initial to 60 instead of 50 111 | voc_initial = 60; 112 | // Gain 200 instead of 230 113 | voc_gain = 200; 114 | 115 | // Parsing 116 | data[0] = (voc_offset & 0xff00) >> 8; 117 | data[1] = voc_offset & 0x00ff; 118 | data[2] = CalcCrc(data); 119 | data[3] = (voc_learning & 0xff00) >> 8; 120 | data[4] = voc_learning & 0x00ff; 121 | data[5] = CalcCrc(data+3); 122 | data[6] = (voc_learning_gain & 0xff00) >> 8; 123 | data[7] = voc_learning_gain & 0x00ff; 124 | data[8] = CalcCrc(data+6); 125 | data[9] = (voc_gating & 0xff00) >> 8; 126 | data[10] = voc_gating & 0x00ff; 127 | data[11] = CalcCrc(data+9); 128 | data[12] = (voc_initial & 0xff00) >> 8; 129 | data[13] = voc_initial & 0x00ff; 130 | data[14] = CalcCrc(data+12); 131 | data[15] = (voc_gain & 0xff00) >> 8; 132 | data[16] = voc_gain & 0x00ff; 133 | data[17] = CalcCrc(data+15); 134 | 135 | // Send new VOC parameters values to sensor (will be held in RAM, not persistent) 136 | Wire.beginTransmission(SEN55_ADDRESS); 137 | Wire.write(0x60); 138 | Wire.write(0xD0); 139 | Wire.write(data[0]); 140 | Wire.write(data[1]); 141 | Wire.write(data[2]); 142 | Wire.write(data[3]); 143 | Wire.write(data[4]); 144 | Wire.write(data[5]); 145 | Wire.write(data[6]); 146 | Wire.write(data[7]); 147 | Wire.write(data[8]); 148 | Wire.write(data[9]); 149 | Wire.write(data[10]); 150 | Wire.write(data[11]); 151 | Wire.write(data[12]); 152 | Wire.write(data[13]); 153 | Wire.write(data[14]); 154 | Wire.write(data[15]); 155 | Wire.write(data[16]); 156 | Wire.write(data[17]); 157 | Wire.endTransmission(); 158 | 159 | // Wait 20 ms to allow for command execution 160 | delay(20); 161 | 162 | 163 | // Send command to read new VOC algorithm tuning parameters 164 | Wire.beginTransmission(SEN55_ADDRESS); 165 | Wire.write(0x60); 166 | Wire.write(0xD0); 167 | Wire.endTransmission(); 168 | 169 | // Wait 20 ms to allow for command execution 170 | delay(20); 171 | 172 | // Read VOC algorithm tuning parameters from SEN55 173 | Wire.requestFrom(SEN55_ADDRESS, 18); 174 | counter = 0; 175 | while (Wire.available()) { 176 | data[counter++] = Wire.read(); 177 | } 178 | 179 | 180 | // Parse data to make sure that new VOC algorithm tuning parameters are correct 181 | // offset is arbitrary 182 | voc_offset = (uint16_t)data[0] << 8 | data[1]; 183 | // learning time is in hours 184 | voc_learning = (uint16_t)data[3] << 8 | data[4]; 185 | // learning time gain in hours 186 | voc_learning_gain = (uint16_t)data[6] << 8 | data[7]; 187 | // gating time is in minutes 188 | voc_gating = (uint16_t)data[9] << 8 | data[10]; 189 | // standard initial is arbitrary 190 | voc_initial = (uint16_t)data[12] << 8 | data[13]; 191 | // standard initial is arbitrary 192 | voc_gain = (uint16_t)data[15] << 8 | data[16]; 193 | 194 | // Print new VOC algorithm tuning parameters 195 | Serial.println(); 196 | Serial.println("New parameter values"); 197 | Serial.print("Index offset: "); 198 | Serial.println(voc_offset); 199 | Serial.print("Learning time offset hours: "); 200 | Serial.println(voc_learning); 201 | Serial.print("Learning time gain hours: "); 202 | Serial.println(voc_learning_gain); 203 | Serial.print("Gating max duration minutes: "); 204 | Serial.println(voc_gating); 205 | Serial.print("Std initial: " ); 206 | Serial.println(voc_initial); 207 | Serial.print("Gain factor: "); 208 | Serial.println(voc_gain); 209 | Serial.println(); 210 | 211 | // Send command to start measurement (0x0021) 212 | Wire.beginTransmission(SEN55_ADDRESS); 213 | Wire.write(0x00); 214 | Wire.write(0x21); 215 | Wire.endTransmission(); 216 | 217 | // Wait until command is executed, sensors are ready and fan is initialized 218 | delay(2000); 219 | 220 | // Output measurement value format 221 | Serial.println(); 222 | Serial.println("PM1.0\tPM2.5\tPM4.0\tPM10.0\tVOC_Index\tNOx_Index\tRH\tT"); 223 | } 224 | 225 | void loop() { 226 | 227 | uint16_t pm1p0, pm2p5, pm4p0, pm10p0; 228 | int16_t voc, nox, humidity, temperature; 229 | uint8_t data[24], counter; 230 | 231 | // Send command to read measurement data (0x03C4) 232 | Wire.beginTransmission(SEN55_ADDRESS); 233 | Wire.write(0x03); 234 | Wire.write(0xC4); 235 | Wire.endTransmission(); 236 | 237 | // Wait 20 ms for command execution 238 | delay(20); 239 | 240 | // Read measurement data from SEN55 241 | Wire.requestFrom(SEN55_ADDRESS, 24); 242 | counter = 0; 243 | while (Wire.available()) { 244 | data[counter++] = Wire.read(); 245 | } 246 | 247 | // PM1.0 to PM10 are unscaled unsigned integer values in ug / um3 248 | // VOC level is a signed int and scaled by a factor of 10 and needs to be divided by 10 249 | // humidity is a signed int and scaled by 100 and need to be divided by 100 250 | // temperature is a signed int and scaled by 200 and need to be divided by 200 251 | pm1p0 = (uint16_t)data[0] << 8 | data[1]; 252 | pm2p5 = (uint16_t)data[3] << 8 | data[4]; 253 | pm4p0 = (uint16_t)data[6] << 8 | data[7]; 254 | pm10p0 = (uint16_t)data[9] << 8 | data[10]; 255 | humidity = (uint16_t)data[12] << 8 | data[13]; 256 | temperature = (uint16_t)data[15] << 8 | data[16]; 257 | voc = (uint16_t)data[18] << 8 | data[19]; 258 | nox = (uint16_t)data[21] << 8 | data[22]; 259 | 260 | // Begin measurement output 261 | Serial.print(String(float(pm1p0) / 10)); 262 | Serial.print("\t"); 263 | Serial.print(String(float(pm2p5) / 10)); 264 | Serial.print("\t"); 265 | Serial.print(String(float(pm4p0) / 10)); 266 | Serial.print("\t"); 267 | Serial.print(String(float(pm10p0) / 10)); 268 | Serial.print("\t"); 269 | Serial.print(String(float(voc) / 10)); 270 | Serial.print("\t\t"); 271 | Serial.print(String(float(nox) / 10)); 272 | Serial.print("\t\t"); 273 | Serial.print(String(float(humidity) / 100)); 274 | Serial.print("\t"); 275 | Serial.print(String(float(temperature) / 200)); 276 | Serial.println(); 277 | 278 | // Wait 1 s for next measurement 279 | delay(1000); 280 | } 281 | 282 | // Calculate CRC according to datasheet 283 | uint8_t CalcCrc(uint8_t data[2]) { 284 | uint8_t crc = 0xFF; 285 | for(int i = 0; i < 2; i++) { 286 | crc ^= data[i]; 287 | for(uint8_t bit = 8; bit > 0; --bit) { 288 | if(crc & 0x80) { 289 | crc = (crc << 1) ^ 0x31u; 290 | } else { 291 | crc = (crc << 1); 292 | } 293 | } 294 | } 295 | return crc; 296 | } 297 | --------------------------------------------------------------------------------