RaspberrPi project source code
guowenxue
2024-05-27 2c971f2fcf6c6322a0ea584b2af4c3cef20d3d63
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:
d973d6 13  *                  SHT20                 Raspberry Pi 40Pin
d6b4a7 14  *                   VCC      <----->      #Pin1(3.3V)
G 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  ********************************************************************************/
d973d6 23
d6b4a7 24 #include <stdio.h>
G 25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <libgen.h>
31 #include <getopt.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <linux/types.h>
39 #include <linux/i2c.h>
40 #include <linux/i2c-dev.h>
41
c5e9e8 42 /* SHT2X 7-bit I2C slave address */
G 43 #define SHT2X_CHIPADDR                  0x40
44
45 /* SHT2X trigger command */
46 #define SOFTRESET                       0xFE
47 #define TRIGGER_TEMPERATURE_NO_HOLD     0xF3
48 #define TRIGGER_HUMIDITY_NO_HOLD        0xF5
49
50 /* Linux /dev/i2c-x device program API mode */
51 enum
52 {
53     MODE_IOCTL, /* I2C device API use ioctl() */
54     MODE_RDWR,  /* I2C device API use read()/write() */
55 };
56
d973d6 57 typedef struct i2c_s
c5e9e8 58 {
d973d6 59     char           *dev;    /* I2C master device, /dev/i2c-N */
G 60     int             addr;   /* 7-bits slave address */
c5e9e8 61     int             fd;     /* File description */
G 62     int             mode;   /* I2C device API use read/write or ioctl mode */
d973d6 63 } i2c_t;
c5e9e8 64
d973d6 65 int sht2x_softreset(i2c_t *i2c);
G 66 int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size);
67 int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh);
68
69 int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr);
70 int i2c_write(i2c_t *i2c, uint8_t *data, int len);
71 int i2c_read(i2c_t *i2c, uint8_t *buf, int size);
72 void i2c_term(i2c_t *i2c);
c5e9e8 73
d6b4a7 74 static inline void msleep(unsigned long ms);
d973d6 75 void print_buf(const char *prompt, uint8_t *buf, int size);
G 76 void dump_buf(const char *prompt, char *buf, size_t len);
d6b4a7 77
G 78 static inline void banner(const char *progname)
79 {
80     printf("%s program Version v1.0.0\n", progname);
81     printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
82 }
83
84 static void program_usage(const char *progname)
85 {
86
87     printf("Usage: %s [OPTION]...\n", progname);
88     printf(" %s is SHT20 temperature and humidity sensor program. \n", progname);
89
90     printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-1\n");
c5e9e8 91     printf(" -m[mode    ]  Specify API mode, 0 for ioctl and 1 for read()/write()\n");
d6b4a7 92     printf(" -h[help    ]  Display this help information\n");
G 93     printf(" -v[version ]  Display the program version\n");
94
95     printf("\n");
96     banner(progname);
97     return;
98 }
99
100 int main(int argc, char **argv)
101 {
d973d6 102     char           *dev="/dev/i2c-1";
G 103     char           *progname=NULL;
104     int             mode = MODE_IOCTL;
c5e9e8 105     int             rv;
d6b4a7 106     float           temp, rh;
c5e9e8 107     uint8_t         serialnumber[8];
d973d6 108     i2c_t           i2c;
d6b4a7 109
G 110     struct option long_options[] = {
111         {"device", required_argument, NULL, 'd'},
c5e9e8 112         {"mode", required_argument, NULL, 'm'},
d6b4a7 113         {"version", no_argument, NULL, 'v'},
G 114         {"help", no_argument, NULL, 'h'},
115         {NULL, 0, NULL, 0}
116     };
117
118     progname = basename(argv[0]);
119
120     /* Parser the command line parameters */
c5e9e8 121     while ((rv = getopt_long(argc, argv, "d:m:vh", long_options, NULL)) != -1)
d6b4a7 122     {
G 123         switch (rv)
124         {
125             case 'd': /*  Set I2C device path: /dev/i2c-1 */
d973d6 126                 dev = optarg;
c5e9e8 127                 break;
G 128
129             case 'm': /*  Set I2C API mode: 0,1 */
d973d6 130                 mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL;
d6b4a7 131                 break;
G 132
133             case 'v':  /*  Get software version */
134                 banner(progname);
135                 return EXIT_SUCCESS;
136
137             case 'h':  /*  Get help information */
138                 program_usage(progname);
139                 return 0;
140
141             default:
142                 break;
143         }
144     }
145
d973d6 146     if( i2c_init(&i2c, mode, dev, SHT2X_CHIPADDR) < 0 )
G 147     {
148         printf("I2C master device initial failed.\n");
149         return 1;
150     }
151
152     if( sht2x_softreset(&i2c) < 0 )
d6b4a7 153     {
c5e9e8 154         printf("SHT2x initialize failure, maybe device not present!\n");
G 155         return 1;
156     }
157
d973d6 158     if( sht2x_get_serialnumber(&i2c, serialnumber, 8) < 0)
c5e9e8 159     {
G 160         printf("SHT2x get serial number failure\n");
161         return 3;
162     }
163
d973d6 164     if( sht2x_get_temp_humidity(&i2c, &temp, &rh) < 0 )
c5e9e8 165     {
G 166         printf("SHT2x get get temperature and relative humidity failure\n");
167         return 3;
168     }
169
170     printf("Temperature=%lf ℃ relative humidity=%lf.\n", temp, rh);
171
d973d6 172     i2c_term(&i2c);
G 173
174     return 0;
c5e9e8 175 }
G 176
d973d6 177 int sht2x_softreset(i2c_t *i2c)
c5e9e8 178 {
G 179     uint8_t           buf[1];
180
d973d6 181     if( !i2c || i2c->fd<0 )
c5e9e8 182     {
G 183         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
d6b4a7 184         return -1;
G 185     }
186
c5e9e8 187     /* software reset SHT2x */
G 188     buf[0] = SOFTRESET;
d973d6 189     if( i2c_write(i2c, buf, 1) < 0 )
c5e9e8 190     {
G 191         return -2;
192     }
d6b4a7 193
G 194     msleep(50);
195
c5e9e8 196     return 0;
G 197 }
d6b4a7 198
d973d6 199 int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh)
c5e9e8 200 {
G 201     uint8_t           buf[4];
202
d973d6 203     if( !i2c || !temp || !rh || i2c->fd<0 )
c5e9e8 204     {
G 205         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
206         return -1;
207     }
208
209     /* send trigger temperature measure command and read the data */
210     memset(buf, 0, sizeof(buf));
211     buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
d973d6 212     i2c_write(i2c, buf, 1);
d6b4a7 213
G 214     msleep(85); /* datasheet: typ=66, max=85 */
215
216     memset(buf, 0, sizeof(buf));
d973d6 217     i2c_read(i2c, buf, 3);
G 218     print_buf("Temperature sample data: ", buf, 3);
c5e9e8 219     *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
d6b4a7 220
c5e9e8 221     /* send trigger humidity measure command and read the data */
G 222     memset(buf, 0, sizeof(buf));
223     buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
d973d6 224     i2c_write(i2c, buf, 1);
d6b4a7 225
G 226     msleep(29); /* datasheet: typ=22, max=29 */
227     memset(buf, 0, sizeof(buf));
c5e9e8 228
d973d6 229     i2c_read(i2c, buf, 3);
G 230     print_buf("Relative humidity sample data: ", buf, 3);
c5e9e8 231     *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
d6b4a7 232
G 233     return 0;
234 }
235
d973d6 236 int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size)
d6b4a7 237 {
c5e9e8 238     uint8_t           buf[4]={0x0};
d6b4a7 239
d973d6 240     if( !i2c || i2c->fd<0 || !serialnumber || size!=8 )
d6b4a7 241     {
c5e9e8 242         printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
G 243         return -1;
d6b4a7 244     }
G 245
c5e9e8 246     /* Read SerialNumber from Location 1 */
G 247     memset(buf, 0, sizeof(buf));
248     buf[0] = 0xfa;  /* command for readout on-chip memory */
249     buf[1] = 0x0f;  /* on-chip memory address */
d973d6 250     i2c_write(i2c, buf, 2);
c5e9e8 251
G 252     memset(buf, 0, sizeof(buf));
d973d6 253     i2c_read(i2c, buf, 4);
c5e9e8 254
G 255     if( !buf[0] && !buf[1] && !buf[2] && !buf[3] )
256     {
257         printf("ERROR: SHT2X device not detected!\n");
258         return -2;
259     }
260
261     serialnumber[5]=buf[0]; /* Read SNB_3 */
262     serialnumber[4]=buf[1]; /* Read SNB_2 */
263     serialnumber[3]=buf[2]; /* Read SNB_1 */
264     serialnumber[2]=buf[3]; /* Read SNB_0 */
265
266     /* Read SerialNumber from Location 2 */
267     memset(buf, 0, sizeof(buf) );
268     buf[0]=0xfc;  /* command for readout on-chip memory */
269     buf[1]=0xc9;  /* on-chip memory address */
d973d6 270     i2c_write(i2c, buf, 2);
c5e9e8 271
G 272     memset(buf, 0, sizeof(buf) );
d973d6 273     i2c_read(i2c, buf, 4);
c5e9e8 274
G 275     serialnumber[1]=buf[0]; /* Read SNC_1 */
276     serialnumber[0]=buf[1]; /* Read SNC_0 */
277     serialnumber[7]=buf[2]; /* Read SNA_1 */
278     serialnumber[6]=buf[3]; /* Read SNA_0 */
279
d973d6 280     print_buf("SHT2x Serial number: ", serialnumber, 8);
c5e9e8 281
G 282     return 0;
d6b4a7 283 }
G 284
d973d6 285 /*+----------------------+
G 286  *|   I2C API functions  |
287  *+----------------------+*/
288
289 int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr)
d6b4a7 290 {
d973d6 291     if( !i2c || !i2cdev || !addr )
G 292         return -1;
293
294     memset(i2c, 0, sizeof(*i2c));
295     i2c->addr = addr;
296     i2c->dev = i2cdev;
297     i2c->mode = mode;
298
299     if( (i2c->fd=open(i2cdev, O_RDWR)) < 0)
300     {
301         printf("open i2c device %s failed: %s\n", i2cdev, strerror(errno));
302         return -1;
303     }
304
305     if( MODE_RDWR == i2c->mode )
306     {
307         /* set I2C mode and SHT2x slave address */
308         ioctl(i2c->fd, I2C_TENBIT, 0);          /* Not 10-bit but 7-bit mode */
309         ioctl(i2c->fd, I2C_SLAVE, i2c->addr);   /* set SHT2x slave address */
310     }
311
312     return 0;
313 }
314
315 void i2c_term(i2c_t *i2c)
316 {
317     if( !i2c )
318         return;
319
320     if( i2c->fd > 0)
321         close(i2c->fd);
322
323     return ;
324 }
325
326 int i2c_write(i2c_t *i2c, uint8_t *data, int len)
327 {
328     struct i2c_rdwr_ioctl_data      tr;
c5e9e8 329     int                             rv = 0;
d6b4a7 330
G 331     if( !data || len<= 0)
332     {
333         printf("%s() invalid input arguments!\n", __func__);
334         return -1;
335     }
336
c5e9e8 337     /* I2C device program API: read()/write() */
d973d6 338     if( MODE_RDWR == i2c->mode )
c5e9e8 339     {
d973d6 340         write(i2c->fd, data, len);
c5e9e8 341         return 0;
G 342     }
343
344     /* I2C device program API: ioctl() */
d973d6 345     tr.nmsgs = 1;
G 346     tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
347     if ( !tr.msgs )
d6b4a7 348     {
G 349         printf("%s() msgs malloc failed!\n", __func__);
350         return -2;
351     }
352
d973d6 353     tr.msgs[0].addr = i2c->addr;
G 354     tr.msgs[0].flags = 0; //write
355     tr.msgs[0].len = len;
356     tr.msgs[0].buf = malloc(len);
357     if( !tr.msgs[0].buf )
d6b4a7 358     {
G 359         printf("%s() msgs malloc failed!\n", __func__);
360         rv = -3;
361         goto cleanup;
362     }
d973d6 363     memcpy(tr.msgs[0].buf, data, len);
d6b4a7 364
G 365
d973d6 366     if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
d6b4a7 367     {
G 368         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
369         rv = -4;
370         goto cleanup;
371     }
372
373 cleanup:
d973d6 374     if( tr.msgs[0].buf )
G 375         free(tr.msgs[0].buf);
d6b4a7 376
d973d6 377     if( tr.msgs )
G 378         free(tr.msgs);
d6b4a7 379
G 380     return rv;
381 }
382
d973d6 383 int i2c_read(i2c_t *i2c, uint8_t *buf, int size)
d6b4a7 384 {
d973d6 385     struct i2c_rdwr_ioctl_data      tr;
c5e9e8 386     int                             rv = 0;
d6b4a7 387
G 388     if( !buf || size<= 0)
389     {
390         printf("%s() invalid input arguments!\n", __func__);
391         return -1;
392     }
393
c5e9e8 394     /* I2C device program API: read()/write() */
d973d6 395     if( MODE_RDWR == i2c->mode )
c5e9e8 396     {
d973d6 397         read(i2c->fd, buf, size);
c5e9e8 398         return 0;
G 399     }
400
401     /* I2C device program API: ioctl() */
d973d6 402     tr.nmsgs = 1;
G 403     tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
404     if ( !tr.msgs )
d6b4a7 405     {
G 406         printf("%s() msgs malloc failed!\n", __func__);
407         return -2;
408     }
409
d973d6 410     tr.msgs[0].addr = i2c->addr;
G 411     tr.msgs[0].flags = I2C_M_RD; //read
412     tr.msgs[0].len = size;
413     tr.msgs[0].buf = buf;
d6b4a7 414     memset(buf, 0, size);
G 415
d973d6 416     if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
d6b4a7 417     {
G 418         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
419         rv = -4;
420     }
421
d973d6 422     free( tr.msgs );
d6b4a7 423     return rv;
G 424 }
c5e9e8 425
d973d6 426 /*+----------------------+
G 427  *|    Misc functions    |
428  *+----------------------+*/
d6b4a7 429
G 430 static inline void msleep(unsigned long ms)
431 {
432     struct timespec cSleep;
433     unsigned long ulTmp;
434
435     cSleep.tv_sec = ms / 1000;
436     if (cSleep.tv_sec == 0)
437     {
438         ulTmp = ms * 10000;
439         cSleep.tv_nsec = ulTmp * 100;
440     }
441     else
442     {
443         cSleep.tv_nsec = 0;
444     }
445
446     nanosleep(&cSleep, 0);
447 }
448
d973d6 449 void print_buf(const char *prompt, uint8_t *buf, int size)
d6b4a7 450 {
G 451     int          i;
452
453     if( !buf )
454     {
455         return ;
456     }
457
458     if( prompt )
459     {
c5e9e8 460         printf("%s ", prompt);
d6b4a7 461     }
G 462
463     for(i=0; i<size; i++)
464     {
465         printf("%02x ", buf[i]);
466     }
467     printf("\n");
468
469     return ;
470 }
d973d6 471
G 472 #define LINELEN 81
473 #define CHARS_PER_LINE 16
474 static char *print_char =
475 "                "
476 "                "
477 " !\"#$%&'()*+,-./"
478 "0123456789:;<=>?"
479 "@ABCDEFGHIJKLMNO"
480 "PQRSTUVWXYZ[\\]^_"
481 "`abcdefghijklmno"
482 "pqrstuvwxyz{|}~ "
483 "                "
484 "                "
485 " ???????????????"
486 "????????????????"
487 "????????????????"
488 "????????????????"
489 "????????????????"
490 "????????????????";
491
492 void dump_buf(const char *prompt, char *buf, size_t len)
493 {
494     int rc;
495     int idx;
496     char prn[LINELEN];
497     char lit[CHARS_PER_LINE + 2];
498     char hc[4];
499     short line_done = 1;
500
501     if( prompt )
502         printf("%s", prompt);
503
504     rc = len;
505     idx = 0;
506     lit[CHARS_PER_LINE] = '\0';
507
508     while (rc > 0)
509     {
510         if (line_done)
511             snprintf(prn, LINELEN, "%08X: ", idx);
512
513         do
514         {
515             unsigned char c = buf[idx];
516             snprintf(hc, 4, "%02X ", c);
517             strncat(prn, hc, LINELEN);
518
519             lit[idx % CHARS_PER_LINE] = print_char[c];
520         }
521         while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
522
523         line_done = (idx % CHARS_PER_LINE) == 0;
524         if (line_done)
525         {
526             printf("%s  %s\r\n", prn, lit);
527         }
528     }
529
530     if (!line_done)
531     {
532         int ldx = idx % CHARS_PER_LINE;
533         lit[ldx++] = print_char[(int)buf[idx]];
534         lit[ldx] = '\0';
535
536         while ((++idx % CHARS_PER_LINE) != 0)
537             strncat(prn, "   ", sizeof(prn)-strlen(prn));
538
539         printf("%s  %s\r\n", prn, lit);
540
541     }
542 }