RaspberrPi project source code
Guo Wenxue
2024-12-29 8517494b239d652ea9a24457faa13f88f55979b9
modules/sht20.c
@@ -10,7 +10,7 @@
 *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
 *
 * Pin connection:
 *               STH20 Module            Raspberry Pi Board
 *                  SHT20                 Raspberry Pi 40Pin
 *                   VCC      <----->      #Pin1(3.3V)
 *                   SDA      <----->      #Pin3(SDA, BCM GPIO2)
 *                   SCL      <----->      #Pin5(SCL, BCM GPIO3)
@@ -39,11 +39,41 @@
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len);
int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size);
int sht20_checksum(uint8_t *data, int len, int8_t checksum);
/* SHT2X 7-bit I2C slave address */
#define SHT2X_CHIPADDR                  0x40
/* SHT2X trigger command */
#define SOFTRESET                       0xFE
#define TRIGGER_TEMPERATURE_NO_HOLD     0xF3
#define TRIGGER_HUMIDITY_NO_HOLD        0xF5
/* Linux /dev/i2c-x device program API mode */
enum
{
    MODE_IOCTL, /* I2C device API use ioctl() */
    MODE_RDWR,  /* I2C device API use read()/write() */
};
typedef struct i2c_s
{
    char           *dev;    /* I2C master device, /dev/i2c-N */
    int             addr;   /* 7-bits slave address */
    int             fd;     /* File description */
    int             mode;   /* I2C device API use read/write or ioctl mode */
} i2c_t;
int sht2x_softreset(i2c_t *i2c);
int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size);
int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh);
int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr);
int i2c_write(i2c_t *i2c, uint8_t *data, int len);
int i2c_read(i2c_t *i2c, uint8_t *buf, int size);
void i2c_term(i2c_t *i2c);
static inline void msleep(unsigned long ms);
static inline void dump_buf(const char *prompt, uint8_t *buf, int size);
void print_buf(const char *prompt, uint8_t *buf, int size);
void dump_buf(const char *prompt, char *buffer, size_t length);
static inline void banner(const char *progname)
{
@@ -58,6 +88,7 @@
    printf(" %s is SHT20 temperature and humidity sensor program. \n", progname);
    printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-1\n");
    printf(" -m[mode    ]  Specify API mode, 0 for ioctl and 1 for read()/write()\n");
    printf(" -h[help    ]  Display this help information\n");
    printf(" -v[version ]  Display the program version\n");
@@ -68,14 +99,17 @@
int main(int argc, char **argv)
{
    int             fd, rv;
    float           temp, rh;
    char           *dev = "/dev/i2c-1";
    char           *dev="/dev/i2c-1";
    char           *progname=NULL;
    uint8_t         buf[4];
    int             mode = MODE_IOCTL;
    int             rv;
    float           temp, rh;
    uint8_t         serialnumber[8];
    i2c_t           i2c;
    struct option long_options[] = {
        {"device", required_argument, NULL, 'd'},
        {"mode", required_argument, NULL, 'm'},
        {"version", no_argument, NULL, 'v'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
@@ -84,12 +118,16 @@
    progname = basename(argv[0]);
    /* Parser the command line parameters */
    while ((rv = getopt_long(argc, argv, "d:vh", long_options, NULL)) != -1)
    while ((rv = getopt_long(argc, argv, "d:m:vh", long_options, NULL)) != -1)
    {
        switch (rv)
        {
            case 'd': /*  Set I2C device path: /dev/i2c-1 */
                dev = optarg;
                break;
            case 'm': /*  Set I2C API mode: 0,1 */
                mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL;
                break;
            case 'v':  /*  Get software version */
@@ -105,108 +143,190 @@
        }
    }
    /*+--------------------------------+
     *|     open /dev/i2c-x device     |
     *+--------------------------------+*/
    if( (fd=open(dev, O_RDWR)) < 0)
    if( i2c_init(&i2c, mode, dev, SHT2X_CHIPADDR) < 0 )
    {
        printf("i2c device '%s' open failed: %s\n", dev, strerror(errno));
        printf("I2C master device initial failed.\n");
        return 1;
    }
    if( sht2x_softreset(&i2c) < 0 )
    {
        printf("SHT2x initialize failure, maybe device not present!\n");
        return 1;
    }
    if( sht2x_get_serialnumber(&i2c, serialnumber, 8) < 0)
    {
        printf("SHT2x get serial number failure\n");
        return 3;
    }
    if( sht2x_get_temp_humidity(&i2c, &temp, &rh) < 0 )
    {
        printf("SHT2x get get temperature and relative humidity failure\n");
        return 3;
    }
    printf("Temperature=%lf ℃ relative humidity=%lf.\n", temp, rh);
    i2c_term(&i2c);
    return 0;
}
int sht2x_softreset(i2c_t *i2c)
{
    uint8_t           buf[1];
    if( !i2c || i2c->fd<0 )
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }
    /*+--------------------------------+
     *|   software reset SHT20 sensor  |
     *+--------------------------------+*/
    buf[0] = 0xFE;
    i2c_write(fd, 0x40, buf, 1);
    /* software reset SHT2x */
    buf[0] = SOFTRESET;
    if( i2c_write(i2c, buf, 1) < 0 )
    {
        return -2;
    }
    msleep(50);
    return 0;
}
    /*+--------------------------------+
     *|   trigger temperature measure  |
     *+--------------------------------+*/
int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh)
{
    uint8_t           buf[4];
    buf[0] = 0xF3;
    i2c_write(fd, 0x40, buf, 1);
    if( !i2c || !temp || !rh || i2c->fd<0 )
    {
        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;
    i2c_write(i2c, buf, 1);
    msleep(85); /* datasheet: typ=66, max=85 */
    memset(buf, 0, sizeof(buf));
    i2c_read(fd, 0x40, buf, 3);
    dump_buf("Temperature sample data: ", buf, 3);
    i2c_read(i2c, buf, 3);
    print_buf("Temperature sample data: ", buf, 3);
    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
    if( !sht20_checksum(buf, 2, buf[2]) )
    {
        printf("Temperature sample data CRC checksum failure.\n");
        goto cleanup;
    }
    temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
    /*+--------------------------------+
     *|    trigger humidity measure    |
     *+--------------------------------+*/
    buf[0] = 0xF5;
    i2c_write(fd, 0x40, buf, 1);
    /* send trigger humidity measure command and read the data */
    memset(buf, 0, sizeof(buf));
    buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
    i2c_write(i2c, buf, 1);
    msleep(29); /* datasheet: typ=22, max=29 */
    memset(buf, 0, sizeof(buf));
    i2c_read(fd, 0x40, buf, 3);
    dump_buf("Relative humidity sample data: ", buf, 3);
    if( !sht20_checksum(buf, 2, buf[2]) )
    {
        printf("Relative humidity sample data CRC checksum failure.\n");
        goto cleanup;
    }
    i2c_read(i2c, buf, 3);
    print_buf("Relative humidity sample data: ", buf, 3);
    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
    rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
    /*+--------------------------------+
     *|    print the measure result    |
     *+--------------------------------+*/
    printf("Temperature=%lf 'C relative humidity=%lf%%\n", temp, rh);
cleanup:
    close(fd);
    return 0;
}
int sht20_checksum(uint8_t *data, int len, int8_t checksum)
int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size)
{
    int8_t crc = 0;
    int8_t bit;
    int8_t byteCtr;
    uint8_t           buf[4]={0x0};
    //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1
    for (byteCtr = 0; byteCtr < len; ++byteCtr)
    if( !i2c || i2c->fd<0 || !serialnumber || size!=8 )
    {
        crc ^= (data[byteCtr]);
        for ( bit = 8; bit > 0; --bit)
        {
            /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */
            if (crc & 0x80) crc = (crc << 1) ^ 0x131;
            else crc = (crc << 1);
        }
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }
    if (crc != checksum)
        return 0;
    else
        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 */
    i2c_write(i2c, buf, 2);
    memset(buf, 0, sizeof(buf));
    i2c_read(i2c, buf, 4);
    if( !buf[0] && !buf[1] && !buf[2] && !buf[3] )
    {
        printf("ERROR: SHT2X device not detected!\n");
        return -2;
    }
    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 */
    i2c_write(i2c, buf, 2);
    memset(buf, 0, sizeof(buf) );
    i2c_read(i2c, 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 */
    print_buf("SHT2x Serial number: ", serialnumber, 8);
    return 0;
}
int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len)
/*+----------------------+
 *|   I2C API functions  |
 *+----------------------+*/
int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr)
{
    struct i2c_rdwr_ioctl_data i2cdata;
    int rv = 0;
    if( !i2c || !i2cdev || !addr )
        return -1;
    memset(i2c, 0, sizeof(*i2c));
    i2c->addr = addr;
    i2c->dev = i2cdev;
    i2c->mode = mode;
    if( (i2c->fd=open(i2cdev, O_RDWR)) < 0)
    {
        printf("open i2c device %s failed: %s\n", i2cdev, strerror(errno));
        return -1;
    }
    if( MODE_RDWR == i2c->mode )
    {
        /* set I2C mode and SHT2x slave address */
        ioctl(i2c->fd, I2C_TENBIT, 0);          /* Not 10-bit but 7-bit mode */
        ioctl(i2c->fd, I2C_SLAVE, i2c->addr);   /* set SHT2x slave address */
    }
    return 0;
}
void i2c_term(i2c_t *i2c)
{
    if( !i2c )
        return;
    if( i2c->fd > 0)
        close(i2c->fd);
    return ;
}
int i2c_write(i2c_t *i2c, uint8_t *data, int len)
{
    struct i2c_rdwr_ioctl_data      tr;
    int                             rv = 0;
    if( !data || len<= 0)
    {
@@ -214,28 +334,36 @@
        return -1;
    }
    i2cdata.nmsgs = 1;
    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
    if ( !i2cdata.msgs )
    /* I2C device program API: read()/write() */
    if( MODE_RDWR == i2c->mode )
    {
        write(i2c->fd, data, len);
        return 0;
    }
    /* I2C device program API: ioctl() */
    tr.nmsgs = 1;
    tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
    if ( !tr.msgs )
    {
        printf("%s() msgs malloc failed!\n", __func__);
        return -2;
    }
    i2cdata.msgs[0].addr = slave_addr;
    i2cdata.msgs[0].flags = 0; //write
    i2cdata.msgs[0].len = len;
    i2cdata.msgs[0].buf = malloc(len);
    if( !i2cdata.msgs[0].buf )
    tr.msgs[0].addr = i2c->addr;
    tr.msgs[0].flags = 0; //write
    tr.msgs[0].len = len;
    tr.msgs[0].buf = malloc(len);
    if( !tr.msgs[0].buf )
    {
        printf("%s() msgs malloc failed!\n", __func__);
        rv = -3;
        goto cleanup;
    }
    memcpy(i2cdata.msgs[0].buf, data, len);
    memcpy(tr.msgs[0].buf, data, len);
    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
    if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
    {
        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
        rv = -4;
@@ -243,19 +371,19 @@
    }
cleanup:
    if( i2cdata.msgs[0].buf )
        free(i2cdata.msgs[0].buf);
    if( tr.msgs[0].buf )
        free(tr.msgs[0].buf);
    if( i2cdata.msgs )
        free(i2cdata.msgs);
    if( tr.msgs )
        free(tr.msgs);
    return rv;
}
int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size)
int i2c_read(i2c_t *i2c, uint8_t *buf, int size)
{
    struct i2c_rdwr_ioctl_data i2cdata;
    int rv = 0;
    struct i2c_rdwr_ioctl_data      tr;
    int                             rv = 0;
    if( !buf || size<= 0)
    {
@@ -263,29 +391,41 @@
        return -1;
    }
    i2cdata.nmsgs = 1;
    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
    if ( !i2cdata.msgs )
    /* I2C device program API: read()/write() */
    if( MODE_RDWR == i2c->mode )
    {
        read(i2c->fd, buf, size);
        return 0;
    }
    /* I2C device program API: ioctl() */
    tr.nmsgs = 1;
    tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
    if ( !tr.msgs )
    {
        printf("%s() msgs malloc failed!\n", __func__);
        return -2;
    }
    i2cdata.msgs[0].addr = slave_addr;
    i2cdata.msgs[0].flags = I2C_M_RD; //read
    i2cdata.msgs[0].len = size;
    i2cdata.msgs[0].buf = buf;
    tr.msgs[0].addr = i2c->addr;
    tr.msgs[0].flags = I2C_M_RD; //read
    tr.msgs[0].len = size;
    tr.msgs[0].buf = buf;
    memset(buf, 0, size);
    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
    if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
    {
        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
        rv = -4;
    }
    free( i2cdata.msgs );
    free( tr.msgs );
    return rv;
}
/*+----------------------+
 *|    Misc functions    |
 *+----------------------+*/
static inline void msleep(unsigned long ms)
{
@@ -304,10 +444,9 @@
    }
    nanosleep(&cSleep, 0);
    return ;
}
static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
void print_buf(const char *prompt, uint8_t *buf, int size)
{
    int          i;
@@ -318,7 +457,7 @@
    if( prompt )
    {
        printf("%-32s ", prompt);
        printf("%s ", prompt);
    }
    for(i=0; i<size; i++)
@@ -329,3 +468,48 @@
    return ;
}
void dump_buf(const char *prompt, char *buffer, size_t length)
{
    size_t i, j;
    if (prompt)
    {
        printf("%s\n", prompt);
    }
    for (i = 0; i < length; i += 16)
    {
        printf("%08zx: ", i);
        for (j = 0; j < 16; j++)
        {
            if (i + j < length)
            {
                printf("%02x ", buffer[i + j]);
            }
            else
            {
                printf("   ");
            }
        }
        printf(" ");
        for (j = 0; j < 16; j++)
        {
            if (i + j < length)
            {
                unsigned char c = buffer[i + j];
                printf("%c", (c >= 32 && c <= 126) ? c : '.');
            }
            else
            {
                printf(" ");
            }
        }
        printf("\n");
    }
}