From 6cae2a26c083e042496ca5ad3efe5a4e14a01c44 Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Fri, 31 Aug 2018 11:05:34 +0800
Subject: [PATCH] add sht20 source code
---
program/sht20/sht20.py | 143 +++++++++++++
program/sht20/sht20.c | 465 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 608 insertions(+), 0 deletions(-)
diff --git a/program/sht20/sht20.c b/program/sht20/sht20.c
new file mode 100644
index 0000000..0ff2101
--- /dev/null
+++ b/program/sht20/sht20.c
@@ -0,0 +1,465 @@
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+
+
+#define SOFTRESET 0xFE
+#define TRIGGER_TEMPERATURE_NO_HOLD 0xF3
+#define TRIGGER_HUMIDITY_NO_HOLD 0xF5
+
+//#define I2C_API_IOCTL /* Use I2C userspace driver ioctl API */
+#define I2C_API_RDWR /* Use I2C userspace driver read/write API */
+
+static inline void msleep(unsigned long ms);
+static inline void dump_buf(const char *prompt, uint8_t *buf, int size);
+int sht2x_init(void);
+int sht2x_softreset(int fd);
+int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size);
+int sht2x_get_temp_humidity(int fd, float *temp, float *rh);
+
+int main(int argc, char **argv)
+{
+ int fd;
+ float temp;
+ float rh;
+ uint8_t serialnumber[8];
+
+ fd = sht2x_init();
+ if(fd < 0)
+ {
+ printf("SHT2x initialize failure\n");
+ return 1;
+ }
+
+ if( sht2x_softreset(fd) < 0 )
+ {
+ printf("SHT2x softreset failure\n");
+ return 2;
+ }
+
+ if( sht2x_get_serialnumber(fd, serialnumber, 8) < 0)
+ {
+ printf("SHT2x get serial number failure\n");
+ return 3;
+ }
+
+ if( sht2x_get_temp_humidity(fd, &temp, &rh) < 0 )
+ {
+ printf("SHT2x get get temperature and relative humidity failure\n");
+ return 3;
+ }
+
+ printf("Temperature=%lf ℃ relative humidity=%lf%\n", temp, rh);
+
+ close(fd);
+}
+
+
+static inline void msleep(unsigned long ms)
+{
+ struct timespec cSleep;
+ unsigned long ulTmp;
+
+ cSleep.tv_sec = ms / 1000;
+ if (cSleep.tv_sec == 0)
+ {
+ ulTmp = ms * 10000;
+ cSleep.tv_nsec = ulTmp * 100;
+ }
+ else
+ {
+ cSleep.tv_nsec = 0;
+ }
+
+ nanosleep(&cSleep, 0);
+}
+
+static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
+{
+ int i;
+
+ if( !buf )
+ {
+ return ;
+ }
+
+ if( prompt )
+ {
+ printf("%s ", prompt);
+ }
+
+ for(i=0; i<size; i++)
+ {
+ printf("%02x ", buf[i]);
+ }
+ printf("\n");
+
+ return ;
+}
+
+
+
+#ifdef I2C_API_RDWR /* Use I2C userspace driver read/write API */
+
+int sht2x_init(void)
+{
+ int fd;
+
+ if( (fd=open("/dev/i2c-1", O_RDWR)) < 0)
+ {
+ printf("i2c device open failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* set I2C mode and SHT2x slave address */
+ ioctl(fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */
+ ioctl(fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40*/
+
+ return fd;
+}
+
+int sht2x_softreset(int fd)
+{
+ uint8_t buf[4];
+
+ if( fd<0 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+ /* software reset SHT2x */
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = SOFTRESET;
+ write(fd, buf, 1);
+
+ msleep(50);
+
+ return 0;
+}
+
+int sht2x_get_temp_humidity(int fd, float *temp, float *rh)
+{
+ uint8_t buf[4];
+
+ if( fd<0 || !temp || !rh )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+ /* send trigger temperature measure command and read the data */
+ memset(buf, 0, sizeof(buf));
+ buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
+ write(fd, buf, 1);
+
+ msleep(85); /* datasheet: typ=66, max=85 */
+
+ memset(buf, 0, sizeof(buf));
+ read(fd, buf, 3);
+ dump_buf("Temperature sample data: ", buf, 3);
+ *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+ /* send trigger humidity measure command and read the data */
+ memset(buf, 0, sizeof(buf));
+ buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
+ write(fd, buf, 1);
+
+ msleep(29); /* datasheet: typ=22, max=29 */
+ memset(buf, 0, sizeof(buf));
+
+ read(fd, buf, 3);
+ dump_buf("Relative humidity sample data: ", buf, 3);
+ *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+ return 0;
+}
+
+int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size)
+{
+ uint8_t buf[4];
+
+ if( fd<0 || !serialnumber || size!=8 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+ /* Read SerialNumber from Location 1 */
+ memset(buf, 0, sizeof(buf));
+ buf[0] = 0xfa; /* command for readout on-chip memory */
+ buf[1] = 0x0f; /* on-chip memory address */
+ write(fd, buf, 2);
+
+ memset(buf, 0, sizeof(buf));
+ read(fd, buf, 4);
+
+ serialnumber[5]=buf[0]; /* Read SNB_3 */
+ serialnumber[4]=buf[1]; /* Read SNB_2 */
+ serialnumber[3]=buf[2]; /* Read SNB_1 */
+ serialnumber[2]=buf[3]; /* Read SNB_0 */
+
+ /* Read SerialNumber from Location 2 */
+ memset(buf, 0, sizeof(buf) );
+ buf[0]=0xfc; /* command for readout on-chip memory */
+ buf[1]=0xc9; /* on-chip memory address */
+ write(fd, buf, 2);
+
+ memset(buf, 0, sizeof(buf) );
+ read(fd, buf, 4);
+
+ serialnumber[1]=buf[0]; /* Read SNC_1 */
+ serialnumber[0]=buf[1]; /* Read SNC_0 */
+ serialnumber[7]=buf[2]; /* Read SNA_1 */
+ serialnumber[6]=buf[3]; /* Read SNA_0 */
+
+ dump_buf("SHT2x Serial number: ", serialnumber, 8);
+
+ return 0;
+}
+
+#elif (defined I2C_API_IOCTL) /* Use I2C userspace driver read/write API */
+
+int sht2x_init(void)
+{
+ int fd;
+
+ if( (fd=open("/dev/i2c-1", O_RDWR)) < 0)
+ {
+ printf("i2c device open failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
+
+
+int sht2x_softreset(int fd)
+{
+ struct i2c_msg msg;
+ struct i2c_rdwr_ioctl_data sht2x_data;
+ uint8_t buf[2];
+
+
+ if( fd<0 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+ msg.addr= 0x40;
+ msg.flags=0; //write
+ msg.len= 1;
+ msg.buf= buf;
+ msg.buf[0]=SOFTRESET;
+
+ sht2x_data.nmsgs= 1;
+ sht2x_data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ msleep(50);
+
+ return 0;
+}
+
+
+
+int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size)
+{
+ struct i2c_msg msgs[2];
+ struct i2c_rdwr_ioctl_data sht2x_data;
+ uint8_t sbuf[2];
+ uint8_t rbuf[4];
+
+ if( fd<0 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+
+ /*+------------------------------------------+
+ *| Read SerialNumber from Location 1 |
+ *+------------------------------------------+*/
+
+ msgs[0].addr= 0x40;
+ msgs[0].flags=0; //write
+ msgs[0].len= 2;
+ msgs[0].buf= sbuf;
+ msgs[0].buf[0]=0xfa; /* command for readout on-chip memory */
+ msgs[0].buf[1]=0x0f; /* on-chip memory address */
+
+ msgs[1].addr=0x40;
+ msgs[1].flags=I2C_M_RD; //write
+ msgs[1].len= 4;
+ msgs[1].buf= rbuf;
+
+ sht2x_data.nmsgs= 2;
+ sht2x_data.msgs= msgs;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ serialnumber[5]=rbuf[0]; /* Read SNB_3 */
+ serialnumber[4]=rbuf[1]; /* Read SNB_2 */
+ serialnumber[3]=rbuf[2]; /* Read SNB_1 */
+ serialnumber[2]=rbuf[3]; /* Read SNB_0 */
+
+
+ /*+------------------------------------------+
+ *| Read SerialNumber from Location 2 |
+ *+------------------------------------------+*/
+
+ msgs[0].addr= 0x40;
+ msgs[0].flags=0; //write
+ msgs[0].len= 2;
+ msgs[0].buf= sbuf;
+ msgs[0].buf[0]=0xfc; /* command for readout on-chip memory */
+ msgs[0].buf[1]=0xc9; /* on-chip memory address */
+
+ msgs[1].addr=0x40;
+ msgs[1].flags=I2C_M_RD; //write
+ msgs[1].len= 4;
+ msgs[1].buf= rbuf;
+
+ sht2x_data.nmsgs= 2;
+ sht2x_data.msgs= msgs;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ serialnumber[1]=rbuf[0]; /* Read SNC_1 */
+ serialnumber[0]=rbuf[1]; /* Read SNC_0 */
+ serialnumber[7]=rbuf[2]; /* Read SNA_1 */
+ serialnumber[6]=rbuf[3]; /* Read SNA_0 */
+
+ dump_buf("SHT2x Serial number: ", serialnumber, 8);
+
+ return 0;
+}
+
+
+
+int sht2x_get_temp_humidity(int fd, float *temp, float *rh)
+{
+ struct i2c_msg msg;
+ struct i2c_rdwr_ioctl_data sht2x_data;
+ uint8_t buf[4];
+
+ if( fd<0 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+
+ /*+------------------------------------------+
+ *| measure and get temperature |
+ *+------------------------------------------+*/
+
+ msg.addr= 0x40;
+ msg.flags=0; //write
+ msg.len= 1;
+ msg.buf= buf;
+ msg.buf[0]=TRIGGER_TEMPERATURE_NO_HOLD; /* trigger temperature without hold I2C bus */
+
+ sht2x_data.nmsgs= 1;
+ sht2x_data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ msleep(85);
+
+ memset(buf, 0, sizeof(buf));
+ msg.addr=0x40;
+ msg.flags=I2C_M_RD; //write
+ msg.len= 3;
+ msg.buf= buf;
+
+ sht2x_data.nmsgs= 1;
+ sht2x_data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ //dump_buf("Temperature sample data: ", buf, 3);
+ *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+
+ /*+------------------------------------------+
+ *| measure and get relative humidity |
+ *+------------------------------------------+*/
+
+ msg.addr= 0x40;
+ msg.flags=0; //write
+ msg.len= 1;
+ msg.buf= buf;
+ msg.buf[0]=TRIGGER_HUMIDITY_NO_HOLD; /* trigger humidity without hold I2C bus */
+
+ sht2x_data.nmsgs= 1;
+ sht2x_data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ msleep(29);
+
+ memset(buf, 0, sizeof(buf));
+ msg.addr=0x40;
+ msg.flags=I2C_M_RD; //write
+ msg.len= 3;
+ msg.buf= buf;
+
+ sht2x_data.nmsgs= 1;
+ sht2x_data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -2;
+ }
+
+ //dump_buf("Relative humidity sample data: ", buf, 3);
+ *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+ return 0;
+}
+
+#endif
+
diff --git a/program/sht20/sht20.py b/program/sht20/sht20.py
new file mode 100644
index 0000000..a618a19
--- /dev/null
+++ b/program/sht20/sht20.py
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+import fcntl
+import time
+import unittest
+
+
+class SHT21:
+ """Class to read temperature and humidity from SHT21, much of class was
+ derived from:
+ http://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity/Sensirion_Humidity_SHT21_Datasheet_V3.pdf
+ and Martin Steppuhn's code from http://www.emsystech.de/raspi-sht21"""
+
+ # control constants
+ _SOFTRESET = 0xFE
+ _I2C_ADDRESS = 0x40
+ _TRIGGER_TEMPERATURE_NO_HOLD = 0xF3
+ _TRIGGER_HUMIDITY_NO_HOLD = 0xF5
+ _STATUS_BITS_MASK = 0xFFFC
+
+ # From: /linux/i2c-dev.h
+ I2C_SLAVE = 0x0703
+ I2C_SLAVE_FORCE = 0x0706
+
+ # datasheet (v4), page 9, table 7, thanks to Martin Milata
+ # for suggesting the use of these better values
+ # code copied from https://github.com/mmilata/growd
+ _TEMPERATURE_WAIT_TIME = 0.086 # (datasheet: typ=66, max=85)
+ _HUMIDITY_WAIT_TIME = 0.030 # (datasheet: typ=22, max=29)
+
+ def __init__(self, device_number=0):
+ """Opens the i2c device (assuming that the kernel modules have been
+ loaded). Note that this has only been tested on first revision
+ raspberry pi where the device_number = 0, but it should work
+ where device_number=1"""
+ device_number = 1
+ self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
+ fcntl.ioctl(self.i2c, self.I2C_SLAVE, 0x40)
+ self.i2c.write(chr(self._SOFTRESET))
+ time.sleep(0.050)
+
+ def read_temperature(self):
+ """Reads the temperature from the sensor. Not that this call blocks
+ for ~86ms to allow the sensor to return the data"""
+ self.i2c.write(chr(self._TRIGGER_TEMPERATURE_NO_HOLD))
+ time.sleep(self._TEMPERATURE_WAIT_TIME)
+ data = self.i2c.read(3)
+ if self._calculate_checksum(data, 2) == ord(data[2]):
+ return self._get_temperature_from_buffer(data)
+
+ def read_humidity(self):
+ """Reads the humidity from the sensor. Not that this call blocks
+ for ~30ms to allow the sensor to return the data"""
+ self.i2c.write(chr(self._TRIGGER_HUMIDITY_NO_HOLD))
+ time.sleep(self._HUMIDITY_WAIT_TIME)
+ data = self.i2c.read(3)
+ if self._calculate_checksum(data, 2) == ord(data[2]):
+ return self._get_humidity_from_buffer(data)
+
+ def close(self):
+ """Closes the i2c connection"""
+ self.i2c.close()
+
+ def __enter__(self):
+ """used to enable python's with statement support"""
+ return self
+
+ def __exit__(self, type, value, traceback):
+ """with support"""
+ self.close()
+
+ @staticmethod
+ def _calculate_checksum(data, number_of_bytes):
+ """5.7 CRC Checksum using the polynomial given in the datasheet"""
+ # CRC
+ POLYNOMIAL = 0x131 # //P(x)=x^8+x^5+x^4+1 = 100110001
+ crc = 0
+ # calculates 8-Bit checksum with given polynomial
+ for byteCtr in range(number_of_bytes):
+ crc ^= (ord(data[byteCtr]))
+ for bit in range(8, 0, -1):
+ if crc & 0x80:
+ crc = (crc << 1) ^ POLYNOMIAL
+ else:
+ crc = (crc << 1)
+ return crc
+
+ @staticmethod
+ def _get_temperature_from_buffer(data):
+ """This function reads the first two bytes of data and
+ returns the temperature in C by using the following function:
+ T = -46.85 + (175.72 * (ST/2^16))
+ where ST is the value from the sensor
+ """
+ unadjusted = (ord(data[0]) << 8) + ord(data[1])
+ unadjusted &= SHT21._STATUS_BITS_MASK # zero the status bits
+ unadjusted *= 175.72
+ unadjusted /= 1 << 16 # divide by 2^16
+ unadjusted -= 46.85
+ return unadjusted
+
+ @staticmethod
+ def _get_humidity_from_buffer(data):
+ """This function reads the first two bytes of data and returns
+ the relative humidity in percent by using the following function:
+ RH = -6 + (125 * (SRH / 2 ^16))
+ where SRH is the value read from the sensor
+ """
+ unadjusted = (ord(data[0]) << 8) + ord(data[1])
+ unadjusted &= SHT21._STATUS_BITS_MASK # zero the status bits
+ unadjusted *= 125.0
+ unadjusted /= 1 << 16 # divide by 2^16
+ unadjusted -= 6
+ return unadjusted
+
+
+class SHT21Test(unittest.TestCase):
+ """simple sanity test. Run from the command line with
+ python -m unittest sht21 to check they are still good"""
+
+ def test_temperature(self):
+ """Unit test to check the checksum method"""
+ calc_temp = SHT21._get_temperature_from_buffer([chr(99), chr(172)])
+ self.failUnless(abs(calc_temp - 21.5653979492) < 0.1)
+
+ def test_humidity(self):
+ """Unit test to check the humidity computation using example
+ from the v4 datasheet"""
+ calc_temp = SHT21._get_humidity_from_buffer([chr(99), chr(82)])
+ self.failUnless(abs(calc_temp - 42.4924) < 0.001)
+
+ def test_checksum(self):
+ """Unit test to check the checksum method. Uses values read"""
+ self.failUnless(SHT21._calculate_checksum([chr(99), chr(172)], 2) == 249)
+ self.failUnless(SHT21._calculate_checksum([chr(99), chr(160)], 2) == 132)
+
+if __name__ == "__main__":
+ try:
+ with SHT21(0) as sht21:
+ print "Temperature: %s" % sht21.read_temperature()
+ print "Humidity: %s" % sht21.read_humidity()
+ except IOError, e:
+ print e
+ print "Error creating connection to i2c. This must be run as root"
--
Gitblit v1.9.1