| | |
| | | * dtoverlay=i2c1,pins_2_3 |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | |
| | | #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 sht2x_s |
| | | { |
| | | char *dev; /* SHT20 connect I2C device, /dev/i2c-N */ |
| | | int addr; /* SHT20 7-bits slave address */ |
| | | int fd; /* File description */ |
| | | int mode; /* I2C device API use read/write or ioctl mode */ |
| | | } sht2x_t; |
| | | |
| | | int sht2x_init(sht2x_t *sht2x); |
| | | int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size); |
| | | int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh); |
| | | |
| | | static inline void msleep(unsigned long ms); |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size); |
| | | static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len); |
| | | static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size); |
| | | |
| | | static inline void banner(const char *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"); |
| | | |
| | |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | int fd, rv; |
| | | int rv; |
| | | float temp, rh; |
| | | char *dev = "/dev/i2c-1"; |
| | | char *progname=NULL; |
| | | uint8_t buf[4]; |
| | | uint8_t serialnumber[8]; |
| | | char *progname=NULL; |
| | | sht2x_t sht2x; |
| | | |
| | | 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]); |
| | | |
| | | memset(&sht2x, 0, sizeof(sht2x)); |
| | | sht2x.addr = SHT2X_CHIPADDR; |
| | | sht2x.dev = "/dev/i2c-1"; |
| | | |
| | | /* 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; |
| | | sht2x.dev = optarg; |
| | | break; |
| | | |
| | | case 'm': /* Set I2C API mode: 0,1 */ |
| | | sht2x.mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL; |
| | | break; |
| | | |
| | | case 'v': /* Get software version */ |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| open /dev/i2c-x device | |
| | | *+--------------------------------+*/ |
| | | if( (fd=open(dev, O_RDWR)) < 0) |
| | | if( sht2x_init(&sht2x) < 0 ) |
| | | { |
| | | printf("i2c device '%s' open failed: %s\n", dev, strerror(errno)); |
| | | printf("SHT2x initialize failure, maybe device not present!\n"); |
| | | return 1; |
| | | } |
| | | |
| | | if( sht2x_get_serialnumber(&sht2x, serialnumber, 8) < 0) |
| | | { |
| | | printf("SHT2x get serial number failure\n"); |
| | | return 3; |
| | | } |
| | | |
| | | if( sht2x_get_temp_humidity(&sht2x, &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(sht2x.fd); |
| | | } |
| | | |
| | | int sht2x_softreset(sht2x_t *sht2x) |
| | | { |
| | | uint8_t buf[1]; |
| | | |
| | | if( !sht2x || sht2x->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(sht2x, buf, 1) < 0 ) |
| | | { |
| | | return -2; |
| | | } |
| | | |
| | | msleep(50); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /*+--------------------------------+ |
| | | *| trigger temperature measure | |
| | | *+--------------------------------+*/ |
| | | int sht2x_init(sht2x_t *sht2x) |
| | | { |
| | | if( (sht2x->fd=open(sht2x->dev, O_RDWR)) < 0) |
| | | { |
| | | printf("i2c device open failed: %s\n", strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | buf[0] = 0xF3; |
| | | i2c_write(fd, 0x40, buf, 1); |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | { |
| | | /* set I2C mode and SHT2x slave address */ |
| | | ioctl(sht2x->fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */ |
| | | ioctl(sht2x->fd, I2C_SLAVE, SHT2X_CHIPADDR); /* set SHT2x slave address 0x40*/ |
| | | } |
| | | |
| | | if( sht2x_softreset(sht2x) < 0 ) |
| | | { |
| | | printf("SHT2x softreset failure\n"); |
| | | return -2; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh) |
| | | { |
| | | uint8_t buf[4]; |
| | | |
| | | if( !sht2x || !temp || !rh || sht2x->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(sht2x, buf, 1); |
| | | |
| | | msleep(85); /* datasheet: typ=66, max=85 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, 0x40, buf, 3); |
| | | i2c_read(sht2x, buf, 3); |
| | | dump_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(sht2x, buf, 1); |
| | | |
| | | msleep(29); /* datasheet: typ=22, max=29 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, 0x40, buf, 3); |
| | | |
| | | i2c_read(sht2x, buf, 3); |
| | | dump_buf("Relative humidity sample data: ", buf, 3); |
| | | *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | | printf("Relative humidity sample data CRC checksum failure.\n"); |
| | | goto cleanup; |
| | | } |
| | | |
| | | 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(sht2x_t *sht2x, 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( !sht2x || sht2x->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(sht2x, buf, 2); |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(sht2x, 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(sht2x, buf, 2); |
| | | |
| | | memset(buf, 0, sizeof(buf) ); |
| | | i2c_read(sht2x, 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; |
| | | } |
| | | |
| | | int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len) |
| | | static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | |
| | | if( !data || len<= 0) |
| | | { |
| | |
| | | return -1; |
| | | } |
| | | |
| | | /* I2C device program API: read()/write() */ |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | { |
| | | write(sht2x->fd, data, len); |
| | | return 0; |
| | | } |
| | | |
| | | /* I2C device program API: ioctl() */ |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = slave_addr; |
| | | i2cdata.msgs[0].addr = sht2x->addr; |
| | | i2cdata.msgs[0].flags = 0; //write |
| | | i2cdata.msgs[0].len = len; |
| | | i2cdata.msgs[0].buf = malloc(len); |
| | |
| | | memcpy(i2cdata.msgs[0].buf, data, len); |
| | | |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) |
| | | if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | |
| | | return rv; |
| | | } |
| | | |
| | | int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size) |
| | | static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | |
| | | if( !buf || size<= 0) |
| | | { |
| | |
| | | return -1; |
| | | } |
| | | |
| | | /* I2C device program API: read()/write() */ |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | { |
| | | read(sht2x->fd, buf, size); |
| | | return 0; |
| | | } |
| | | |
| | | /* I2C device program API: ioctl() */ |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = slave_addr; |
| | | i2cdata.msgs[0].addr = sht2x->addr; |
| | | i2cdata.msgs[0].flags = I2C_M_RD; //read |
| | | i2cdata.msgs[0].len = size; |
| | | i2cdata.msgs[0].buf = buf; |
| | | memset(buf, 0, size); |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) |
| | | if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | |
| | | free( i2cdata.msgs ); |
| | | return rv; |
| | | } |
| | | |
| | | /*+----------------+ |
| | | *| Misc functions | |
| | | *+----------------+*/ |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | return ; |
| | | } |
| | | |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size) |
| | |
| | | |
| | | if( prompt ) |
| | | { |
| | | printf("%-32s ", prompt); |
| | | printf("%s ", prompt); |
| | | } |
| | | |
| | | for(i=0; i<size; i++) |