当树莓派配置好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;
}
#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这个函数!
#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;就可以读文件了;
关闭打开的目录流;
#include
#include
#include
int open(const char *pathname, int flags);
找到了最终路径,我们要做的就是打开文件,获取有用信息,第一步,使用open()
打开我们已经找到了的最终文件,第二个参数表示只读,该函数成功调用会返回一个文件描述符(当前未被占用文件描述符中最小的一个)指向该文件,失败则返回 -1;
关于open()函数相关参数及更更多描述可参考:C语言open()函数
#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() 无法获取到值了!