/*********************************************************************************
|
* 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 <guowenxue@gmail.com>
|
* 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 <stdio.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <stdint.h>
|
#include <string.h>
|
#include <unistd.h>
|
#include <libgen.h>
|
#include <getopt.h>
|
#include <time.h>
|
#include <errno.h>
|
#include <fcntl.h>
|
#include <sys/ioctl.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#include <linux/types.h>
|
#include <linux/i2c.h>
|
#include <linux/i2c-dev.h>
|
|
/* 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<size; i++)
|
{
|
printf("%02x ", buf[i]);
|
}
|
printf("\n");
|
|
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");
|
}
|
}
|