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