Linux系统编程:树莓派 ds18b20温度获取(文件IO相关操作)

文章目录

    • 问题
    • 解答
      • linux文件io操作文件夹
        • 1.opendir()
        • 2.readdir()
        • 3.closedir()
        • 4.open()
        • 5.read()函数

问题

当树莓派配置好ds18b20数字温度传感器后,该温度信息通常保存在下面路径中:

/sys/bus/w1/devices/28-04xxxxxxxxxx/w1_slave

这里的“28-04xxxxxxxxxx"是数字温度传感器的生产流水号,每一个传感器的都不相同。
当我们打开这个文件后,会读到下面内容:

ten@Public_RPi:/sys/bus/w1/devices/28-041731f7c0ff$ cat w1_slave 
dc 00 4b 46 7f ff 0c 10 78 : crc=78 YES
dc 00 4b 46 7f ff 0c 10 78 t=13750

t=13750 就是我们要获取的数据,那么,问题来了:
1.在linux系统中,如何通过代码,在程序中打开阅读这些文件?
2.每一个数字温度传感器的编号不一样,导致文件名不同,是不是意味着每更换一个ds18b20传感器或者移植代码到其他设备都要更改程序?
3.文件里的内容本没有我们想要的最终结果,而是很多我们不需要的数据,怎么定位到我们需要的数据处?
4.t = 13750 并不是标准的温度格式,如何获取到标准格式?例如t = 13.75℃.

下面一 一解答。

解答

话不多说,先贴代码再bb:

/*********************************************************************************
 *      Copyright:  (C) 2020 Xiao yang System Studio
 *                  All rights reserved.
 *
 *       Filename:  ds18b20.c
 *    Description:  This file is how to get the temperature from ds18b20
 *                 
 *        Version:  1.0.0(03/07/2020)
 *         Author:  Lu Xiaoyang <[email protected]>
 *      ChangeLog:  1, Release initial version on "03/07/2020 04:25:03 PM"
 *                 
 ********************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int temperature_get(float *temp);
int main(int argc,char *argv[])
{
    float            temp;    

    printf("Hello,I'll give you the temperature from ds18b20.\n");
    temperature_get(&temp);   //传入地址,操作地址!
    printf("The temperature is:%.2f℃ now!\n",temp);

    return 0;

}

int temperature_get(float *temp)
{
    char             w1_path[100] = "/sys/bus/w1/devices/"; //文件路径
    char             chip[30];   //用来存储“28-”的全名
    char             *ptr = NULL;
    char             buf[1024];
    int              fd = -1;
    int              rv = -1;
    int              found = 0;
    DIR              *dirp = NULL;
    struct dirent    *direntp;

    if(!temp)
    {
        printf("Can not start work:%s\n",strerror(errno));
        return -1;
    }

    if((dirp = opendir(w1_path)) == 0)
    {
        printf("Opendir %s failure,check you path or check your hardware!\n",w1_path);
        return -2;
    }
    while((direntp = readdir(dirp)) != NULL)
    {
        if(strstr(direntp->d_name,"28-"))
        {
            strcat(chip,direntp->d_name);    //将找到的全名存储来数组中
            found =1;     //设置一个标志位,若不满足则说明寻找失败
            break;      //跳出循环
        }
    }

    closedir(dirp);
   
    if(!found)
    {
        printf("Can't find \"28-\" in the path\n");
        return -3;
    }
    
    strncat(w1_path,chip,sizeof(w1_path) - strlen(w1_path));  //将“28-”的全名追加到w1_path这个路径下
    strncat(w1_path,"/w1_slave",sizeof(w1_path) - strlen(w1_path));//"28-"下还有一个文件w1_slave,这是最终路径
     
    if(( fd = open(w1_path,O_RDONLY)) < 0)
    {
        printf("open %s failure:%s\n",w1_path,strerror(errno));
        return -4;
    }
    if((rv = read(fd,buf,sizeof(buf))) < 0)
    {
        printf("read fd failure:%s\n",strerror(errno));
        return -5;
    }

    close(fd);

    if(!(ptr = strstr(buf,"t=")))
    {
        printf("Can't find \"t=\"\n");
        return -6;
        
    }

    ptr+=2;

    *temp = atof(ptr)/1000.0;   //1000也要取float类型1000.0

    return 0;
}

linux文件io操作文件夹

1.opendir()

#include 
#include 

DIR *opendir(const char *name);

opendir() 用来打开指定的目录文件,并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值,该返回值是一个指向DIR结构体的指针
,失败返回NULL;

DIR结构体:

struct __dirstream
   
   {   
    void *__fd;
    char *__data; 
    int __entry_data;   
    char *__ptr;   
    int __entry_ptr;    
    size_t __allocation;    
    size_t __size;    
    __libc_lock_define (, __lock)    
   };     
typedef struct __dirstream DIR;  

这里,我们使用代码:

 char             w1_path[100] = "/sys/bus/w1/devices/";
 if((dirp = opendir(w1_path)) == 0)
    {
        printf("Opendir %s failure,check you path or check your hardware!\n",w1_path);
        return -2;
    }
   

使用opendir()函数。打开了w1_path这个路径下的目录文件:
在这里插入图片描述
在**/devices** 这个文件中,就包含了”28-04xxxxxxxx“这个文件,我们并不清楚他具体的全名,但也不需要知道,因为我们使用到readdir这个函数!

2.readdir()

 #include     
 struct dirent *readdir(DIR *dirp);

readdir()函数参数为opendir()返回的DIR结构体指针,通过这个结构体,readdir()就可以获取到文件夹的相关信息,例如文件名,长度等,并返回一个指向dirent 结构体的指针,该结构体就用来存放这些信息,失败返回NULL;

dirent结构体

struct dirent{   

long d_ino; /* inode number 索引节点号 */   
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */   
unsigned short d_reclen; /* length of this d_name 文件名长 */   
unsigned char d_type; /* the type of d_name 文件类型 */   
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */

}

因为我们要进入到“28-04xxxxxxxxxx”这个文件夹下,但是我们又不知道他具体的名字,但是,所有的这个文件都是以“28-”打头的,所以,我们通过opendir()返回的结构体指针,再调用reaaddir()函数循环遍历存储了文件名信息的DIR结构体的目录流,使用strstr() 函数找到一个文件名字为 “28-”打头的,这时,readdir()函数 返回的direntp指针就会指向这个结构体成员中d_name为“28-”的文件,使用strcat() 函数,
将direntp->d_name(即28-的全名)添加到chip这个数组中,当然,你也可以直接追加到w1_path中,这样就需用在追加chip到w1_path了;

若成功,则found 置为1;就可以读文件了;

3.closedir()

关闭打开的目录流;

4.open()

#include 
#include 
#include 

int open(const char *pathname, int flags);

找到了最终路径,我们要做的就是打开文件,获取有用信息,第一步,使用open()
打开我们已经找到了的最终文件,第二个参数表示只读,该函数成功调用会返回一个文件描述符(当前未被占用文件描述符中最小的一个)指向该文件,失败则返回 -1;
关于open()函数相关参数及更更多描述可参考:C语言open()函数

5.read()函数

 #include 
ssize_t read(int fd, void *buf, size_t count);

返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0;

在open()函数成功返回一个文件描述符后,调用read()函数,读取文件中的内容存储在指定的buf中,但文件内容在问题中提到了,掺杂了很多无用数据,需要对数据进行处理。

if(!(ptr = strstr(buf,"t=")))   // 指针指向“t”的地址;
    {
        printf("Can't find \"t=\"\n");
        return -6;
        
    }

    ptr+=2;   //让指针指向数据处

    *temp = atof(ptr)/1000.0;

定义一个指针,调用strstr()函数,该函数成功调用返回要找的字符串的首地址,所以,
ptr 指向了 ‘t’ 的地址,再看一遍buf的内容:

ten@Public_RPi:/sys/bus/w1/devices/28-041731f7c0ff$ cat w1_slave 
dc 00 4b 46 7f ff 0c 10 78 : crc=78 YES
dc 00 4b 46 7f ff 0c 10 78 t=13750

ptr += 2;让指针指向我们想要的数据地址;
使用强制类型转化将 字符串类型转化为浮点型,除以1000.0;
将结果传给参数temp!完成。

!!!敲重点!!!
需要注意的是,我们在调用温度获取函数时,传的是temp这个变量的地址,而不是单纯的传变量,赋值,返回;这是因为,C程序内存布局中,我们在函数中定义的temp变量,存储在栈中(未初始化为随机值),栈中的数据是在 { } 中有效,离开自己所属的
{ } 就会失效,当调用了temperature_get() 函数后,失效!main() 函数无法获取该值,所以,我们在定义函数时,将其参数设置为指针形式,传参时传以地址,功能函数操作地址,这样就不会导致main() 无法获取到值了!

运行结果:
在这里插入图片描述

你可能感兴趣的:(Linux系统编程:树莓派 ds18b20温度获取(文件IO相关操作))