/********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio * All rights reserved. * * Filename: sht20.c * Description: This file is temperature and relative humidity sensor SHT20 code * * Version: 1.0.0(10/08/23) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" * * Pin connection: * SHT20 Raspberry Pi 40Pin * VCC <-----> #Pin1(3.3V) * SDA <-----> #Pin3(SDA, BCM GPIO2) * SCL <-----> #Pin5(SCL, BCM GPIO3) * GND <-----> GND * * /boot/config.txt: * dtoverlay=i2c1,pins_2_3 * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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); 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) { printf("%s program Version v1.0.0\n", progname); printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); } static void program_usage(const char *progname) { printf("Usage: %s [OPTION]...\n", progname); 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"); printf("\n"); banner(progname); return; } int main(int argc, char **argv) { char *dev="/dev/i2c-1"; char *progname=NULL; 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} }; progname = basename(argv[0]); /* Parser the command line parameters */ 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 */ banner(progname); return EXIT_SUCCESS; case 'h': /* Get help information */ program_usage(progname); return 0; default: break; } } if( i2c_init(&i2c, mode, dev, SHT2X_CHIPADDR) < 0 ) { 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 SHT2x */ buf[0] = SOFTRESET; if( i2c_write(i2c, buf, 1) < 0 ) { return -2; } msleep(50); return 0; } int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh) { uint8_t buf[4]; 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(i2c, buf, 3); print_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; i2c_write(i2c, buf, 1); msleep(29); /* datasheet: typ=22, max=29 */ memset(buf, 0, sizeof(buf)); i2c_read(i2c, buf, 3); print_buf("Relative humidity sample data: ", buf, 3); *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; return 0; } int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size) { uint8_t buf[4]={0x0}; if( !i2c || i2c->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 */ 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; } /*+----------------------+ *| I2C API functions | *+----------------------+*/ int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr) { 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) { printf("%s() invalid input arguments!\n", __func__); return -1; } /* 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; } 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(tr.msgs[0].buf, data, len); if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 ) { printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); rv = -4; goto cleanup; } cleanup: if( tr.msgs[0].buf ) free(tr.msgs[0].buf); if( tr.msgs ) free(tr.msgs); return rv; } int i2c_read(i2c_t *i2c, uint8_t *buf, int size) { struct i2c_rdwr_ioctl_data tr; int rv = 0; if( !buf || size<= 0) { printf("%s() invalid input arguments!\n", __func__); return -1; } /* 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; } 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(i2c->fd, I2C_RDWR, &tr)<0 ) { printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); rv = -4; } free( tr.msgs ); return rv; } /*+----------------------+ *| Misc functions | *+----------------------+*/ 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); } void print_buf(const char *prompt, uint8_t *buf, int size) { int i; if( !buf ) { return ; } if( prompt ) { printf("%s ", prompt); } for(i=0; i= 32 && c <= 126) ? c : '.'); } else { printf(" "); } } printf("\n"); } }