guowenxue
2024-07-18 b8d02950d8c50611c2784c7a40e0b3003acf8d49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  ds18b20.c
 *    Description:  This file is temperature sensor DS18B20 source code
 *
 *        Version:  1.0.0(2023/8/10)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2023/8/10 12:13:26"
 *
 * Pin connection:
 *
 *               DS18B20 Module          IGKBoard
 *                   VCC      <----->      3.3V
 *                   DQ       <----->      #Pin7(GPIO1_IO18)
 *                   GND      <----->      GND
 *
 * /run/media/mmcblk1p1/config.txt:
 *
 *          # Eanble 1-Wire overlay
 *          dtoverlay_w1=yes
 *
 ********************************************************************************/
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
 
int ds18b20_get_temperature(float *temp);
 
int main(int argc, char *argv[])
{
    float       temp; /* 温度值有小数位,所以使用浮点数 */
 
    if( ds18b20_get_temperature(&temp) < 0 )
    {
        printf("ERROR: ds18b20 get temprature failure\n");
        return 1;
    }
 
    /* 打印DS18B20的采样温度值,因为℃是非ASCII打印字符,所以这里用 'C 代替 */
    printf("DS18B20 get temperature: %f 'C\n", temp);
 
    return 0;
}
 
/*
 * 函数说明: 该函数用来使用 DS18B20 温度传感器采样获取当前的温度值;
 * 参数说明: $temp: 通过指针返回DS18B20的采样温度
 * 返回说明: ==0 表示成功, <0 表示失败
 */
int ds18b20_get_temperature(float *temp)
{
    const char     *w1_path = "/sys/bus/w1/devices/";
    char            ds_path[50];
    char            chip[20];
    char            buf[128];
    DIR            *dirp;
    struct dirent  *direntp;
    int             fd =-1;
    char           *ptr;
    int             found = 0;
    int             rv = 0;
 
 
    /* 在C语言编程时,进入函数的第一件事应该进行函数参数的合法性检测,检查参数非法输入。
     * 否则调用者"不小心"通过 $temp 传入一个空指针,下面的代码就有可能出现段错误。
     */
    if( !temp )
    {
        return -1;
    }
 
    /* 打开 "/sys/bus/w1/devices/" 文件夹,如果打开失败则打印错误信息并退出。 */
    if((dirp = opendir(w1_path)) == NULL)
    {
        printf("opendir error: %s\n", strerror(errno));
        return -2;
    }
 
    /* 1, 因为文件夹下可能有很多文件,所以这里使用while()循环读取/sys/bus/w1/devices/
     * 文件夹下的所有目录项,其中 direntp->d_name 就是目录里的每个文件/文件夹的文件名。
     *
     * 2, 接下来我们使用 strstr() 函数判断文件名中是否包含 "28-",如果找到则将完整的
     * 文件名通过strcpy()函数保存到 chip 中;并设置 found 标志为1,跳出循环。
     */
    while((direntp = readdir(dirp)) != NULL)
    {
        if(strstr(direntp->d_name,"28-"))
        {
            /* find and get the chipset SN filename */
            strcpy(chip,direntp->d_name);
            found = 1;
            break;
        }
    }
 
    /* 文件夹打开用完后,要记得第一时间关闭 */
    closedir(dirp);
 
    /* found在定义时初始化为0,如果上面没有找到 "28-" 文件则其值依然为0,否则将被置为1 */
    if( !found )
    {
        printf("Can not find ds18b20 in %s\n", w1_path);
        return -3;
    }
 
    /* 使用snprintf() 生成完整路径/sys/bus/w1/devices/28-xxxxx/w1_slave */
    snprintf(ds_path, sizeof(ds_path), "%s/%s/w1_slave", w1_path, chip);
 
    /* 接下来打开 DS18B20 的采样文件 */
    if( (fd=open(ds_path, O_RDONLY)) < 0 )
    {
        printf("open %s error: %s\n", ds_path, strerror(errno));
        return -4;
    }
 
    /* 读取文件中的内容将会触发 DS18B20温度传感器采样,这里读取文件内容保存到buf中 */
    if(read(fd, buf, sizeof(buf)) < 0)
    {
        printf("read %s error: %s\n", ds_path, strerror(errno));
 
        /* 1, 这里不能直接调用 return直接返回,否则的话前面open()打开的文件描述符就没有关闭。
         * 这里设置 rv 为错误码-5,通过 goto 语句跳转到函数后面统一进行错误处理。
         *
         * 2, 在C语言编程时我们应该慎用goto语句进行"随意"的跳转,因为它会降低代码的可读性。但这里是
         * goto语句的一个非常典型应用,我们经常会用它来对错误进行统一的处理。
         */
        rv = -5;
        goto cleanup;
    }
 
    /* 采样温度值是在字符串"t="后面,这里我们从buf中找到"t="字符串的位置并保存到ptr指针中 */
    ptr = strstr(buf, "t=");
    if( !ptr )
    {
        printf("ERROR: Can not get temperature\n");
        rv = -6;
        goto cleanup;
    }
 
    /* 因为此时ptr是指向 "t="字符串的地址(即't'的地址),那跳过2个字节(t=)后面的就是采样温度值 */
    ptr+=2;
 
    /* 接下来我们使用 atof() 函数将采样温度值字符串形式,转化成 float 类型。*/
    *temp = atof(ptr)/1000;
 
    /* 1,在这里我们对函数返回进行集中处理,其中 cleanup 为 goto 语句的标号;
     * 2,在函数退出时,我们应该考虑清楚在前面的代码中做了哪些事,这些事是否需要进行反向操作。如
     *    打开的文件或文件夹是否需要关闭,malloc()分配的内存是否需要free()等。
     * 3, 在最开始我们定义rv并赋初值为0(表示成功)是有原因的,如果前面的代码任何一个地方出现错误,
     *    则会将rv的值修改为相应的错误码,否则rv的值将始终为0(即没有错误发生),这里将统一返回。
     */
cleanup:
    close(fd);
    return rv;
}