Skip to content

Commit 5c619da

Browse files
committed
add example for SGP40 & SHTC3 with humidity compensation and VOC algo
1 parent 3e179dd commit 5c619da

File tree

4 files changed

+1273
-0
lines changed

4 files changed

+1273
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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 <Wire.h>
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+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 <stdlib.h>
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 <stdint.h>
52+
53+
/**
54+
* Typedef section for types commonly defined in <stdint.h>
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 <stdbool.h>
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 */

0 commit comments

Comments
 (0)