RaspberrPi project source code
guowenxue
2024-03-12 8df7c2928393a804db45a685311df14bf8a76f45
commit | author | age
d6b4a7 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2023 LingYun IoT System Studio
3  *                  All rights reserved.
4  *
5  *       Filename:  sht20.c
6  *    Description:  This file is temperature and relative humidity sensor SHT20 code
7  *
8  *        Version:  1.0.0(10/08/23)
9  *         Author:  Guo Wenxue <guowenxue@gmail.com>
10  *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
11  *
12  * Pin connection:
13  *               STH20 Module            Raspberry Pi Board
14  *                   VCC      <----->      #Pin1(3.3V)
15  *                   SDA      <----->      #Pin3(SDA, BCM GPIO2)
16  *                   SCL      <----->      #Pin5(SCL, BCM GPIO3)
17  *                   GND      <----->      GND
18  *
19  * /boot/config.txt:
20  *                  dtoverlay=i2c1,pins_2_3
21  *
22  ********************************************************************************/
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <libgen.h>
30 #include <getopt.h>
31 #include <time.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <linux/types.h>
38 #include <linux/i2c.h>
39 #include <linux/i2c-dev.h>
40
c5e9e8 41 /* SHT2X 7-bit I2C slave address */
G 42 #define SHT2X_CHIPADDR                  0x40
43
44 /* SHT2X trigger command */
45 #define SOFTRESET                       0xFE
46 #define TRIGGER_TEMPERATURE_NO_HOLD     0xF3
47 #define TRIGGER_HUMIDITY_NO_HOLD        0xF5
48
49 /* Linux /dev/i2c-x device program API mode */
50 enum
51 {
52     MODE_IOCTL, /* I2C device API use ioctl() */
53     MODE_RDWR,  /* I2C device API use read()/write() */
54 };
55
56 typedef struct sht2x_s
57 {
58     char           *dev;    /* SHT20 connect I2C device, /dev/i2c-N */
59     int             addr;   /* SHT20 7-bits slave address */
60     int             fd;     /* File description */
61     int             mode;   /* I2C device API use read/write or ioctl mode */
62 } sht2x_t;
63
64 int sht2x_init(sht2x_t *sht2x);
65 int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size);
66 int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh);
67
d6b4a7 68 static inline void msleep(unsigned long ms);
G 69 static inline void dump_buf(const char *prompt, uint8_t *buf, int size);
c5e9e8 70 static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len);
G 71 static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size);
d6b4a7 72
G 73 static inline void banner(const char *progname)
74 {
75     printf("%s program Version v1.0.0\n", progname);
76     printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
77 }
78
79 static void program_usage(const char *progname)
80 {
81
82     printf("Usage: %s [OPTION]...\n", progname);
83     printf(" %s is SHT20 temperature and humidity sensor program. \n", progname);
84
85     printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-1\n");
c5e9e8 86     printf(" -m[mode    ]  Specify API mode, 0 for ioctl and 1 for read()/write()\n");
d6b4a7 87     printf(" -h[help    ]  Display this help information\n");
G 88     printf(" -v[version ]  Display the program version\n");
89
90     printf("\n");
91     banner(progname);
92     return;
93 }
94
95 int main(int argc, char **argv)
96 {
c5e9e8 97     int             rv;
d6b4a7 98     float           temp, rh;
c5e9e8 99     uint8_t         serialnumber[8];
G 100     char            *progname=NULL;
101     sht2x_t         sht2x;
d6b4a7 102
G 103     struct option long_options[] = {
104         {"device", required_argument, NULL, 'd'},
c5e9e8 105         {"mode", required_argument, NULL, 'm'},
d6b4a7 106         {"version", no_argument, NULL, 'v'},
G 107         {"help", no_argument, NULL, 'h'},
108         {NULL, 0, NULL, 0}
109     };
110
111     progname = basename(argv[0]);
112
c5e9e8 113     memset(&sht2x, 0, sizeof(sht2x));
G 114     sht2x.addr = SHT2X_CHIPADDR;
115     sht2x.dev = "/dev/i2c-1";
116
d6b4a7 117     /* Parser the command line parameters */
c5e9e8 118     while ((rv = getopt_long(argc, argv, "d:m:vh", long_options, NULL)) != -1)
d6b4a7 119     {
G 120         switch (rv)
121         {
122             case 'd': /*  Set I2C device path: /dev/i2c-1 */
c5e9e8 123                 sht2x.dev = optarg;
G 124                 break;
125
126             case 'm': /*  Set I2C API mode: 0,1 */
127                 sht2x.mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL;
d6b4a7 128                 break;
G 129
130             case 'v':  /*  Get software version */
131                 banner(progname);
132                 return EXIT_SUCCESS;
133
134             case 'h':  /*  Get help information */
135                 program_usage(progname);
136                 return 0;
137
138             default:
139                 break;
140         }
141     }
142
c5e9e8 143     if( sht2x_init(&sht2x) < 0 )
d6b4a7 144     {
c5e9e8 145         printf("SHT2x initialize failure, maybe device not present!\n");
G 146         return 1;
147     }
148
149     if( sht2x_get_serialnumber(&sht2x, serialnumber, 8) < 0)
150     {
151         printf("SHT2x get serial number failure\n");
152         return 3;
153     }
154
155     if( sht2x_get_temp_humidity(&sht2x, &temp, &rh) < 0 )
156     {
157         printf("SHT2x get get temperature and relative humidity failure\n");
158         return 3;
159     }
160
161     printf("Temperature=%lf ℃ relative humidity=%lf.\n", temp, rh);
162
163     close(sht2x.fd);
164 }
165
166 int sht2x_softreset(sht2x_t *sht2x)
167 {
168     uint8_t           buf[1];
169
170     if( !sht2x || sht2x->fd<0 )
171     {
172         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
d6b4a7 173         return -1;
G 174     }
175
c5e9e8 176     /* software reset SHT2x */
G 177     buf[0] = SOFTRESET;
178     if( i2c_write(sht2x, buf, 1) < 0 )
179     {
180         return -2;
181     }
d6b4a7 182
G 183     msleep(50);
184
c5e9e8 185     return 0;
G 186 }
d6b4a7 187
c5e9e8 188 int sht2x_init(sht2x_t *sht2x)
G 189 {
190     if( (sht2x->fd=open(sht2x->dev, O_RDWR)) < 0)
191     {
192         printf("i2c device open failed: %s\n", strerror(errno));
193         return -1;
194     }
d6b4a7 195
c5e9e8 196     if( MODE_RDWR == sht2x->mode )
G 197     {
198         /* set I2C mode and SHT2x slave address */
199         ioctl(sht2x->fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
200         ioctl(sht2x->fd, I2C_SLAVE, SHT2X_CHIPADDR); /* set SHT2x slave address 0x40*/
201     }
202
203     if( sht2x_softreset(sht2x) < 0 )
204     {
205         printf("SHT2x softreset failure\n");
206         return -2;
207     }
208
209     return 0;
210 }
211
212 int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh)
213 {
214     uint8_t           buf[4];
215
216     if( !sht2x || !temp || !rh || sht2x->fd<0 )
217     {
218         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
219         return -1;
220     }
221
222     /* send trigger temperature measure command and read the data */
223     memset(buf, 0, sizeof(buf));
224     buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
225     i2c_write(sht2x, buf, 1);
d6b4a7 226
G 227     msleep(85); /* datasheet: typ=66, max=85 */
228
229     memset(buf, 0, sizeof(buf));
c5e9e8 230     i2c_read(sht2x, buf, 3);
d6b4a7 231     dump_buf("Temperature sample data: ", buf, 3);
c5e9e8 232     *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
d6b4a7 233
c5e9e8 234     /* send trigger humidity measure command and read the data */
G 235     memset(buf, 0, sizeof(buf));
236     buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
237     i2c_write(sht2x, buf, 1);
d6b4a7 238
G 239     msleep(29); /* datasheet: typ=22, max=29 */
240     memset(buf, 0, sizeof(buf));
c5e9e8 241
G 242     i2c_read(sht2x, buf, 3);
d6b4a7 243     dump_buf("Relative humidity sample data: ", buf, 3);
c5e9e8 244     *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
d6b4a7 245
G 246     return 0;
247 }
248
c5e9e8 249 int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size)
d6b4a7 250 {
c5e9e8 251     uint8_t           buf[4]={0x0};
d6b4a7 252
c5e9e8 253     if( !sht2x || sht2x->fd<0 || !serialnumber || size!=8 )
d6b4a7 254     {
c5e9e8 255         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
G 256         return -1;
d6b4a7 257     }
G 258
c5e9e8 259     /* Read SerialNumber from Location 1 */
G 260     memset(buf, 0, sizeof(buf));
261     buf[0] = 0xfa;  /* command for readout on-chip memory */
262     buf[1] = 0x0f;  /* on-chip memory address */
263     i2c_write(sht2x, buf, 2);
264
265     memset(buf, 0, sizeof(buf));
266     i2c_read(sht2x, buf, 4);
267
268     if( !buf[0] && !buf[1] && !buf[2] && !buf[3] )
269     {
270         printf("ERROR: SHT2X device not detected!\n");
271         return -2;
272     }
273
274     serialnumber[5]=buf[0]; /* Read SNB_3 */
275     serialnumber[4]=buf[1]; /* Read SNB_2 */
276     serialnumber[3]=buf[2]; /* Read SNB_1 */
277     serialnumber[2]=buf[3]; /* Read SNB_0 */
278
279     /* Read SerialNumber from Location 2 */
280     memset(buf, 0, sizeof(buf) );
281     buf[0]=0xfc;  /* command for readout on-chip memory */
282     buf[1]=0xc9;  /* on-chip memory address */
283     i2c_write(sht2x, buf, 2);
284
285     memset(buf, 0, sizeof(buf) );
286     i2c_read(sht2x, buf, 4);
287
288     serialnumber[1]=buf[0]; /* Read SNC_1 */
289     serialnumber[0]=buf[1]; /* Read SNC_0 */
290     serialnumber[7]=buf[2]; /* Read SNA_1 */
291     serialnumber[6]=buf[3]; /* Read SNA_0 */
292
293     dump_buf("SHT2x Serial number: ", serialnumber, 8);
294
295     return 0;
d6b4a7 296 }
G 297
c5e9e8 298 static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len)
d6b4a7 299 {
c5e9e8 300     struct i2c_rdwr_ioctl_data      i2cdata;
G 301     int                             rv = 0;
d6b4a7 302
G 303     if( !data || len<= 0)
304     {
305         printf("%s() invalid input arguments!\n", __func__);
306         return -1;
307     }
308
c5e9e8 309     /* I2C device program API: read()/write() */
G 310     if( MODE_RDWR == sht2x->mode )
311     {
312         write(sht2x->fd, data, len);
313         return 0;
314     }
315
316     /* I2C device program API: ioctl() */
d6b4a7 317     i2cdata.nmsgs = 1;
G 318     i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
319     if ( !i2cdata.msgs )
320     {
321         printf("%s() msgs malloc failed!\n", __func__);
322         return -2;
323     }
324
c5e9e8 325     i2cdata.msgs[0].addr = sht2x->addr;
d6b4a7 326     i2cdata.msgs[0].flags = 0; //write
G 327     i2cdata.msgs[0].len = len;
328     i2cdata.msgs[0].buf = malloc(len);
329     if( !i2cdata.msgs[0].buf )
330     {
331         printf("%s() msgs malloc failed!\n", __func__);
332         rv = -3;
333         goto cleanup;
334     }
335     memcpy(i2cdata.msgs[0].buf, data, len);
336
337
c5e9e8 338     if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 )
d6b4a7 339     {
G 340         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
341         rv = -4;
342         goto cleanup;
343     }
344
345 cleanup:
346     if( i2cdata.msgs[0].buf )
347         free(i2cdata.msgs[0].buf);
348
349     if( i2cdata.msgs )
350         free(i2cdata.msgs);
351
352     return rv;
353 }
354
c5e9e8 355 static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size)
d6b4a7 356 {
c5e9e8 357     struct i2c_rdwr_ioctl_data      i2cdata;
G 358     int                             rv = 0;
d6b4a7 359
G 360     if( !buf || size<= 0)
361     {
362         printf("%s() invalid input arguments!\n", __func__);
363         return -1;
364     }
365
c5e9e8 366     /* I2C device program API: read()/write() */
G 367     if( MODE_RDWR == sht2x->mode )
368     {
369         read(sht2x->fd, buf, size);
370         return 0;
371     }
372
373     /* I2C device program API: ioctl() */
d6b4a7 374     i2cdata.nmsgs = 1;
G 375     i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
376     if ( !i2cdata.msgs )
377     {
378         printf("%s() msgs malloc failed!\n", __func__);
379         return -2;
380     }
381
c5e9e8 382     i2cdata.msgs[0].addr = sht2x->addr;
d6b4a7 383     i2cdata.msgs[0].flags = I2C_M_RD; //read
G 384     i2cdata.msgs[0].len = size;
385     i2cdata.msgs[0].buf = buf;
386     memset(buf, 0, size);
387
c5e9e8 388     if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 )
d6b4a7 389     {
G 390         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
391         rv = -4;
392     }
393
394     free( i2cdata.msgs );
395     return rv;
396 }
c5e9e8 397
G 398 /*+----------------+
399  *| Misc functions |
400  *+----------------+*/
d6b4a7 401
G 402 static inline void msleep(unsigned long ms)
403 {
404     struct timespec cSleep;
405     unsigned long ulTmp;
406
407     cSleep.tv_sec = ms / 1000;
408     if (cSleep.tv_sec == 0)
409     {
410         ulTmp = ms * 10000;
411         cSleep.tv_nsec = ulTmp * 100;
412     }
413     else
414     {
415         cSleep.tv_nsec = 0;
416     }
417
418     nanosleep(&cSleep, 0);
419 }
420
421 static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
422 {
423     int          i;
424
425     if( !buf )
426     {
427         return ;
428     }
429
430     if( prompt )
431     {
c5e9e8 432         printf("%s ", prompt);
d6b4a7 433     }
G 434
435     for(i=0; i<size; i++)
436     {
437         printf("%02x ", buf[i]);
438     }
439     printf("\n");
440
441     return ;
442 }