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