guowenxue
2018-08-31 6cae2a26c083e042496ca5ad3efe5a4e14a01c44
add sht20 source code
2 files added
608 ■■■■■ changed files
program/sht20/sht20.c 465 ●●●●● patch | view | raw | blame | history
program/sht20/sht20.py 143 ●●●●● patch | view | raw | blame | history
program/sht20/sht20.c
New file
@@ -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
program/sht20/sht20.py
New file
@@ -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"