树莓派下DS18B20获取实时温度

1. DS18B20温度传感器

  DS18B20是比较常用到的温度传感器,采用单总线控制。是美国DALLAS半导体公司继DS1820之后最新推出的一种改进型智能温度传感器。
  这里对于它就不进行过多的讲解,主要讲解如何在树莓派上用它来获取温度。详细资料参考:DS18B20介绍

2. DS18B20安装及配置

  DS18B20的接线连接参照下图,VCC接3.3v的引脚,GND接地,DQ线连在GPIO04上。
在这里插入图片描述
  接好线之后,我们要配置内核启动后自动加载一线协议驱动,在命令行键入sudo raspi-config,依次选择如下选项,就可以使能内核以一线协议(1-wire)驱动模块。
在这里插入图片描述
在这里插入图片描述  然后命令行键入vim /boot/config.txt,并在最后一行添加 dtoverlay=w1-gpio-pullup,gpiopin=4。(这里的4就是pin7,应该是采用BCM GPIO的标准)。
  随后sudo reboot重启系统,键入 lsmod | grep w1 检查系统是否已经自动加载一线协议的驱动模块。
在这里插入图片描述

  我们知道Linux下“一切皆文件”,DS18B20的文件就在“/sys/bus/w1/devices/”下
在这里插入图片描述

  这个叫“28-041731f7c0ff”的文件夹是我用的这个DS18B20的产品序列号,然后在这个文件夹里面的“w1_slave”里就存放着实时的温度,我们可以看一看
在这里插入图片描述

  前面那些数据我们不管他,可以看到“t=11250”,这个就是我这个设备上的实时温度(11.25℃)。了解到这些之后,我们来捋一捋代码里面要用什么样的方式来读取这个温度,并显示出来。

3.基础代码逻辑分析

3.1 逻辑分析

  如果我们人为的想要在树莓派上读取温度,我们需要转到相应路径下去,查看相应的文件即可,但是我们编写程序的时候,为了得到更广泛的适用性,并不能直接简单粗暴地用read()去读取相应路径下的文件,因为每个DS18B20的产品序列号不一样,那么温度保存文件的路径也就会不一样。

  因为序列号不同的缘故,我们的代码需要逐步实现如下功能:

step1. 用opendir()和readdir()的组合,打开每个设备都一样的默认路径(/sys/bus/w1/devices/),并读取路径下的内容。

step2. 找出默认路径下“28-”开头的的文件夹,并将其名称保存到一个缓存区中,然后用这个缓存区的内容来更新目标路径。

step3. 用open()和read()读取目标路径的内容,找出其中“t=”的部分,并记录下来。随后便可以简单粗暴的一个printf()将实时温度打印到标准输出上。

3.2 阶段代码示例

  依照上面的思路,我们可以先做出前两部分寻找目标路径的代码,这里我用了strstr()来找以“28-”开头的文件夹。还用了strncat()来更新目标文件路径。

strstr()的作用是判断前者字符串中是否包含有后者字符串的内容,若包含,返回其第一次出现的首地址,若无则返回NULL
strncat()的作用是在一个字符串的后面加上另一个字符串,并且能保证前者的剩余空间足够放下后者。

if((dir = opendir(path)) == NULL )							//打开文件夹并判断成败
	{
		printf("Open directory failed: %s", strerror(errno));	//失败打印提示
		return -1;
	}

	while((direntp = readdir(dir)) != NULL)						//读取文件夹内容,用direntp保存其地址
	{

/***************************步骤1和步骤2的假想分割线********************************/

		if(strstr(direntp->d_name, "28"));						//用if循环找“28-”开头的文件夹
		{
			strcpy(dstfile, direntp->d_name);					//将目标路径文件夹保存到缓冲区

			flag = 1;											//成功找到“28-”开头的文件夹的标志,
		}
	}


	closedir(dir);								//关闭文件夹

	if(flag != 1)												//利用标志判断有没有成功找到“28-”开头的文件夹
	{
		printf("Can not found the temperature file.\n");		
		return -2;
	}

	
	strncat(path, dstfile, sizeof(path));				//给默认路径加上找到的“28-”开头的文件夹
	strncat(path, "/w1_slave", sizeof(path));			//继续加上需要打开的文件“w1_slave”,这样就完成了目标路径的更新

  简单介绍一下opendir()、readdir() 和 closedir() 这几个函数的使用,他们的原型和介绍如下:

#include <sys/types.h>  			//opendir()的头文件
#include <dirent.h>					//opendir()的头文件
DIR *opendir(const char *name); 	//打开参数name指定的目录,并返回DIR*类型的目录流,类似于文件描述符


#include<sys/types.h>					//readdir()的头文件
#include <dirent.h>						//readdir()的头文件
struct dirent *readdir(DIR *dir);		//读取某个文件夹里的内容,保存到结构体dirent里,返回该结构体的地址

struct dirent						//readdir返回的结构体内容
{
    ino_t d_ino; 					// 此目录进入点的inode
    ff_t d_off; 					// 目录文件开头至此目录进入点的位移
    signed short int d_reclen;	 	// 文件的的长度, 不包含NULL 字符
    unsigned char d_type;			// 文件类型 
    har d_name[256];				// 文件名(这里我们只关心这个!)
};


#include<sys/types.h> 			//closedir()的头文件
#include <dirent.h>				//closedir()的头文件
closedir(DIR *dir);				//类似cloes,简单粗暴,用完就关

  有了目标路径,那接下来就好办了,用一套open()、read() 和 close() 的组合就能很简单的读出来,同样用strstr()来找到文件中“t=”开头的字符串首地址,但是我们在打印温度的时候需要将这个地址向后偏移两个字节,去掉“t=”

if((fd = open(path, O_RDONLY)) < 0 )				//根据目标路径以只读的形式打开文件,同样加上错误判断
	{
		printf("Open temperature file failed: %s\n", strerror(errno));		//出错提醒
		return -3;
	}

	if((rv = read(fd, buf, sizeof(buf))) < 0)			//读取文件内容,并保存在buf里
	{
		printf("Read temperature data failed: %s\n", strerror(errno));
		return -4;
	}

	close(fd);

	if((ptr = strstr(buf, "t=")) != NULL)			//strstr()找到温度存放的地址,并保存起来
	{
		tem = atof(ptr+2)/1000;						//这里加2是将地址偏移两个字节,跳过文件中的“t=”,只保留有用的温度数据
		printf("The temperature in LingYun Studio is %f now.\n", tem);		//简单粗暴,快哉!
	}
	else
	{
		printf("Can not read the temperature data: %s.\n", strerror(errno));	//buf里面没有读取到温度数据,就会报错
		return -5;
	}

4. 代码及运行结果

4.1 全部代码

  给上面的代码加上头文件,并声明相关参数,就可以得到完整的代码:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define PATH 	"/sys/bus/w1/devices/"		//宏定义默认路径
#define BUF_SIZE	 128
#define PATH_SIZE	 100

int main(int argc, char **argv[])
{
	char			path[PATH_SIZE] = PATH;		//局部变量保存默认路径
	DIR				*dir;						//opendir的返回类型,用来指定打开的文件夹
	struct dirent   *direntp;					//readdir返回的结构体指针,用来指向打开文件夹的内容
	char			dstfile[48];				//目标路径缓存区
	int 			flag = -1;					//初始化文件夹读取成败标志
	int				fd = -1;					//初始化文件描述符
	int				rv = -1;					//初始化文件读取返回值
	char			buf[BUF_SIZE];				//实时温度缓冲区
	double			tem = 0.00;					//初始化实时温度数据	
	char			*ptr = NULL;				//初始化实时温度缓冲区指针


	if((dir = opendir(path)) == NULL )							//打开文件夹并判断成败
	{
		printf("Open directory failed: %s", strerror(errno));	//失败打印提示
		return -1;
	}

	while((direntp = readdir(dir)) != NULL)						//读取文件夹内容,用direntp保存其地址
	{
		if(strstr(direntp->d_name, "28"));						//找到“28-”开头的文件夹
		{
			strcpy(dstfile, direntp->d_name);					//将目标路径文件夹保存到缓冲区

			flag = 1;											//成功找到“28-”开头的文件夹的标志,
		}
	}


	closedir(dir);								//关闭文件夹

	if(flag != 1)												//利用标志判断有没有成功找到“28-”开头的文件夹
	{
		printf("Can not found the temperature file.\n");		
		return -2;
	}

	
	strncat(path, dstfile, sizeof(path));				//给默认路径加上找到的“28-”开头的文件夹
	strncat(path, "/w1_slave", sizeof(path));			//继续加上需要打开的文件“w1_slave”,这样就完成了目标路径的更新


	if((fd = open(path, O_RDONLY)) < 0 )				//根据目标路径以只读的形式打开文件,同样加上错误判断
	{
		printf("Open temperature file failed: %s\n", strerror(errno));		//出错提醒
		return -3;
	}

	if((rv = read(fd, buf, sizeof(buf))) < 0)			//读取文件内容,并保存在buf里
	{
		printf("Read temperature data failed: %s\n", strerror(errno));
		return -4;
	}

	close(fd);

	if((ptr = strstr(buf, "t=")) != NULL)			//strstr()找到温度存放的地址,并保存起来
	{
		tem = atof(ptr+2)/1000;						//这里加2是讲地址便宜两个字节,跳过文件中的“t=”,只保留有用的温度数据
		printf("The temperature in LingYun Studio is %f now.\n", tem);		//简单粗暴,快哉!
	}
	else
	{
		printf("Can not read the temperature data: %s.\n", strerror(errno));	//buf里面没有读取到温度数据,就会报错
		return -5;
	}

	return 0;							// ending
}

4.1 运行结果

  编译后运行就可以得到如下结果:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逆袭猪头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值